Skip to content

Instantly share code, notes, and snippets.

@kwilczynski
Last active June 12, 2021 19:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kwilczynski/55e9b6a7ce59cfdec477af2ad25369df to your computer and use it in GitHub Desktop.
Save kwilczynski/55e9b6a7ce59cfdec477af2ad25369df to your computer and use it in GitHub Desktop.
Custom Ruby type check to fix problem with JRuby and TruffleRuby
# frozen_string_literal: true
require 'mkmf'
dir_config('test')
create_header
create_makefile('test')
#if defined(__cplusplus)
extern "C" {
#endif
#include <ruby.h>
#define ARRAY_SIZE(array) (int)(sizeof(array) / sizeof((array)[0]))
#define CLASS_NAME(o) (NIL_P((o)) ? "nil" : rb_obj_classname((o)))
#define error(t) errors[(t)]
enum error {
E_UNKNOWN = 0,
E_ARGUMENT_TYPE_INVALID,
E_ARGUMENT_TYPE_UNKNOWN
};
static const char *errors[] = {
[E_ARGUMENT_TYPE_INVALID] = "wrong argument type %s (expected %s)",
[E_ARGUMENT_TYPE_UNKNOWN] = "unknown type 0x%x (0x%x given)",
NULL
};
static const char *ruby_types[] = {
"", /* Not an object */
[T_OBJECT] = "Object",
[T_CLASS] = "Class",
[T_MODULE] = "Module",
[T_FLOAT] = "Float",
[T_STRING] = "String",
[T_REGEXP] = "Regexp",
[T_ARRAY] = "Array",
[T_HASH] = "Hash",
[T_STRUCT] = "Struct",
[T_BIGNUM] = "Integer",
[T_FILE] = "File",
"", /* Internal use */
[T_MATCH] = "MatchData",
[T_COMPLEX] = "Complex",
[T_RATIONAL] = "Rational",
"", /* Internal use */
[T_NIL] = "nil",
[T_TRUE] = "true",
[T_FALSE] = "false",
[T_SYMBOL] = "Symbol",
[T_FIXNUM] = "Integer",
"", /* Internal use */
NULL
};
static const char *
ruby_type_name(int type)
{
const char *name;
if (type >= ARRAY_SIZE(ruby_types))
return NULL;
name = ruby_types[type];
if (name)
return name;
return NULL;
}
static const char *
ruby_class_name(VALUE object)
{
const char *name = NULL;
if (NIL_P(object))
name = "nil";
else if (RB_TYPE_P(object, T_TRUE))
name = "true";
else if (RB_TYPE_P(object, T_FALSE))
name = "false";
else
name = CLASS_NAME(object);
return name;
}
void
check_ruby_type(VALUE object, int type)
{
const char *name;
int object_type = TYPE(object);
if (object == Qundef)
rb_bug("invalid type leaked to the Ruby space");
if (object_type > T_MASK || object_type == T_DATA)
goto error;
if (object_type == type)
return;
name = ruby_type_name(type);
if (name)
rb_raise(rb_eTypeError, error(E_ARGUMENT_TYPE_INVALID),
ruby_class_name(object),
name);
error:
rb_raise(rb_eTypeError, error(E_ARGUMENT_TYPE_UNKNOWN),
object_type,
type);
}
void check_type(VALUE object, int type)
{
const char *name;
int object_type = TYPE(object);
if (object_type == type)
return;
name = ruby_type_name(type);
if (name)
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
ruby_class_name(object),
name);
rb_raise(rb_eTypeError, "unknown type 0x%x (0x%x given)",
object_type,
type);
}
void
Init_test(void)
{
check_type(rb_cArray, T_STRING);
/* check_type(rb_cArray, 123); */
/* check_type(rb_cArray, rb_str_new2("")); */
}
#if defined(__cplusplus)
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment