Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 20 additions & 33 deletions error.c
Original file line number Diff line number Diff line change
Expand Up @@ -1313,16 +1313,28 @@ rb_builtin_class_name(VALUE x)
COLDFUNC NORETURN(static void unexpected_type(VALUE, int, int));
#define UNDEF_LEAKED "undef leaked to the Ruby space"

void
rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected)
{
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
actual->wrap_struct_name, expected->wrap_struct_name);
}

void
rb_unexpected_object_type(VALUE obj, const char *expected)
{
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
displaying_class_of(obj), expected);
}

static void
unexpected_type(VALUE x, int xt, int t)
{
const char *tname = rb_builtin_type_name(t);
VALUE mesg, exc = rb_eFatal;

if (tname) {
mesg = rb_sprintf("wrong argument type %"PRIsVALUE" (expected %s)",
displaying_class_of(x), tname);
exc = rb_eTypeError;
rb_unexpected_object_type(x, tname);
}
else if (xt > T_MASK && xt <= 0x3f) {
mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
Expand Down Expand Up @@ -1367,24 +1379,18 @@ rb_unexpected_type(VALUE x, int t)
unexpected_type(x, TYPE(x), t);
}

#undef rb_typeddata_inherited_p
int
rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent)
{
while (child) {
if (child == parent) return 1;
child = child->parent;
}
return 0;
return rb_typeddata_inherited_p_inline(child, parent);
}

#undef rb_typeddata_is_kind_of
int
rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
{
if (!RB_TYPE_P(obj, T_DATA) ||
!RTYPEDDATA_P(obj) || !rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
return 0;
}
return 1;
return rb_typeddata_is_kind_of_inline(obj, data_type);
}

#undef rb_typeddata_is_instance_of
Expand All @@ -1397,26 +1403,7 @@ rb_typeddata_is_instance_of(VALUE obj, const rb_data_type_t *data_type)
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
VALUE actual;

if (!RB_TYPE_P(obj, T_DATA)) {
actual = displaying_class_of(obj);
}
else if (!RTYPEDDATA_P(obj)) {
actual = displaying_class_of(obj);
}
else if (!rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
const char *name = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
actual = rb_str_new_cstr(name); /* or rb_fstring_cstr? not sure... */
}
else {
return RTYPEDDATA_GET_DATA(obj);
}

const char *expected = data_type->wrap_struct_name;
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
actual, expected);
UNREACHABLE_RETURN(NULL);
return rbimpl_check_typeddata(obj, data_type);
}

/* exception classes */
Expand Down
109 changes: 92 additions & 17 deletions include/ruby/internal/core/rtypeddata.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ RBIMPL_ATTR_NONNULL((3))
*/
VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type);

RBIMPL_ATTR_NONNULL((3))
/**
* Identical to rb_data_typed_object_wrap(), except it allocates a new data
* region internally instead of taking an existing one. The allocation is done
Expand All @@ -411,6 +412,7 @@ VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *
*/
VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type);

RBIMPL_ATTR_NONNULL(())
/**
* Checks for the domestic relationship between the two.
*
Expand All @@ -425,6 +427,7 @@ VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t
*/
int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent);

RBIMPL_ATTR_NONNULL((2))
/**
* Checks if the given object is of given kind.
*
Expand All @@ -435,6 +438,7 @@ int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *
*/
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type);

RBIMPL_ATTR_NONNULL((2))
/**
* Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead
* of returning false.
Expand All @@ -446,6 +450,36 @@ int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type);
* @post Upon successful return `obj`'s type is guaranteed `data_type`.
*/
void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type);

RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_NONNULL((2))
/**
* @private
*
* Fails with the given object's type incompatibility to the type.
*
* This is an implementation detail of Check_Type. People don't use it
* directly.
*
* @param[in] obj The object in question.
* @param[in] expected Name of expected data type of `obj`.
*/
void rb_unexpected_object_type(VALUE obj, const char *expected);

RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_NONNULL(())
/**
* @private
*
* Fails with the given object's type incompatibility to the type.
*
* This is an implementation detail of #TypedData_Make_Struct. People don't
* use it directly.
*
* @param[in] actual Actual data type.
* @param[in] expected Expected data type.
*/
void rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected);
RBIMPL_SYMBOL_EXPORT_END()

/**
Expand Down Expand Up @@ -532,7 +566,7 @@ RTYPEDDATA_GET_DATA(VALUE obj)
#if RUBY_DEBUG
if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) {
Check_Type(obj, RUBY_T_DATA);
RBIMPL_UNREACHABLE_RETURN(false);
RBIMPL_UNREACHABLE_RETURN(NULL);
}
#endif

Expand Down Expand Up @@ -561,6 +595,27 @@ rbimpl_rtypeddata_p(VALUE obj)
return FL_TEST_RAW(obj, RUBY_TYPED_FL_IS_TYPED_DATA);
}

RBIMPL_ATTR_PURE()
RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
* Identical to rbimpl_rtypeddata_p(), except it is allowed to call on non-data
* objects.
*
* This is an implementation detail of inline functions defined in this file.
* People don't use it directly.
*
* @param[in] obj Object in question
* @retval true `obj` is an instance of ::RTypedData.
* @retval false `obj` is not an instance of ::RTypedData
*/
static inline bool
rbimpl_obj_typeddata_p(VALUE obj)
{
return RB_TYPE_P(obj, RUBY_T_DATA) && rbimpl_rtypeddata_p(obj);
}

RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
Expand All @@ -586,7 +641,7 @@ RTYPEDDATA_P(VALUE obj)

RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
RBIMPL_ATTR_RETURNS_NONNULL()
/**
* Queries for the type of given object.
*
Expand All @@ -605,10 +660,35 @@ RTYPEDDATA_TYPE(VALUE obj)
#endif

VALUE type = RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK;
return RBIMPL_CAST((const rb_data_type_t *)type);
const rb_data_type_t *ptr = RBIMPL_CAST((const rb_data_type_t *)type);
RBIMPL_ASSERT_OR_ASSUME(ptr);
return ptr;
}

RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL(())
static inline bool
rb_typeddata_inherited_p_inline(const rb_data_type_t *child, const rb_data_type_t *parent)
{
do {
if (RB_LIKELY(child == parent)) return true;
} while ((child = child->parent) != NULL);
return false;
}
#define rb_typeddata_inherited_p rb_typeddata_inherited_p_inline

RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL((2))
static inline bool
rb_typeddata_is_kind_of_inline(VALUE obj, const rb_data_type_t *data_type)
{
if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) return false;
return rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type);
}
#define rb_typeddata_is_kind_of rb_typeddata_is_kind_of_inline

RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL((2))
/**
* @private
*
Expand All @@ -618,22 +698,16 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline void *
rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *expected_type)
{
if (RB_LIKELY(RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj))) {
const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj);
void *data = RTYPEDDATA_GET_DATA(obj);
if (RB_LIKELY(actual_type == expected_type)) {
return data;
}

while (actual_type) {
actual_type = actual_type->parent;
if (actual_type == expected_type) {
return data;
}
}
if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) {
rb_unexpected_object_type(obj, expected_type->wrap_struct_name);
}

const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj);
if (RB_UNLIKELY(!rb_typeddata_inherited_p(actual_type, expected_type))){
rb_unexpected_typeddata(actual_type, expected_type);
}

return rb_check_typeddata(obj, expected_type);
return RTYPEDDATA_GET_DATA(obj);
}


Expand All @@ -650,6 +724,7 @@ rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *expected_type)
#define TypedData_Get_Struct(obj,type,data_type,sval) \
((sval) = RBIMPL_CAST((type *)rbimpl_check_typeddata((obj), (data_type))))

RBIMPL_ATTR_NONNULL((2))
/**
* While we don't stop you from using this function, it seems to be an
* implementation detail of #TypedData_Make_Struct, which is preferred over
Expand Down
7 changes: 4 additions & 3 deletions include/ruby/internal/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,11 +421,12 @@ void rb_readwrite_syserr_fail(enum rb_io_wait_readwrite waiting, int err, const
RBIMPL_ATTR_COLD()
RBIMPL_ATTR_NORETURN()
/**
* @private
*
* Fails with the given object's type incompatibility to the type.
*
* It seems this function is visible from extension libraries only because
* RTYPEDDATA_TYPE() uses it on RUBY_DEBUG. So you can basically ignore it;
* use some other fine-grained method instead.
* This is an implementation detail of Check_Type. People don't use it
* directly.
*
* @param[in] self The object in question.
* @param[in] t Expected type of the object.
Expand Down
3 changes: 2 additions & 1 deletion internal/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,11 @@ rb_key_err_raise(VALUE mesg, VALUE recv, VALUE name)
rb_exc_raise(exc);
}

RBIMPL_ATTR_NONNULL((2))
static inline bool
rb_typeddata_is_instance_of_inline(VALUE obj, const rb_data_type_t *data_type)
{
return RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj) && (RTYPEDDATA_TYPE(obj) == data_type);
return rbimpl_obj_typeddata_p(obj) && (RTYPEDDATA_TYPE(obj) == data_type);
}

typedef enum {
Expand Down
2 changes: 1 addition & 1 deletion pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
else if (ISDIGIT(*p)) {
errno = 0;
len = STRTOUL(p, (char**)&p, 10);
if (errno) {
if (len < 0 || errno) {
rb_raise(rb_eRangeError, "pack length too big");
}
}
Expand Down