Skip to content

Commit 4eea25b

Browse files
committed
Solve issues with ruby.
1 parent 578dcfd commit 4eea25b

File tree

3 files changed

+109
-61
lines changed

3 files changed

+109
-61
lines changed

source/loaders/rb_loader/source/rb_loader_impl.c

Lines changed: 109 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,12 @@ loader_impl_data rb_loader_impl_initialize(loader_impl impl, configuration confi
968968

969969
ruby_init();
970970

971-
ruby_init_loadpath();
971+
/* Apparently ruby_init_loadpath is not enough to initialize the builtins and gems,
972+
* so we use ruby_options instead
973+
*/
974+
/* ruby_init_loadpath(); */
975+
976+
ruby_options(argc, argv);
972977
}
973978

974979
if (rb_loader_impl_initialize_types(impl) != 0)
@@ -1098,7 +1103,7 @@ VALUE rb_loader_impl_module_eval_protect(VALUE args)
10981103
return rb_mod_module_eval(protect->argc, protect->argv, protect->module);
10991104
}
11001105

1101-
void rb_loader_impl_print_exception(void)
1106+
static void rb_loader_impl_print_exception(void)
11021107
{
11031108
VALUE exception = rb_gv_get("$!");
11041109

@@ -1262,12 +1267,43 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_path
12621267
{
12631268
#define rb_str_new_static_size(str) rb_str_new_static(str, sizeof(str) - 1)
12641269

1265-
VALUE wrapped_code = rb_str_new_static_size("require 'rubygems'\n");
1270+
static const char header[] =
1271+
/* AtExitInterceptor is needed due to Ruby's nature, it calls
1272+
* at_exit during ruby_cleanup and due to this, scripts depending on
1273+
* this feature like unit tests will run after MetaCall has started
1274+
* to destroy the objects of reflect associated to the loader.
1275+
* In order to avoid this, we hook into at_exit and we execute at
1276+
* the end of the main script execution.
1277+
*/
1278+
"module AtExitInterceptor\n"
1279+
" @captured_exit_procs = []\n"
1280+
" def self.captured_exit_procs\n"
1281+
" @captured_exit_procs\n"
1282+
" end\n"
1283+
/* Replace Kernel.at_exit */
1284+
" module ::Kernel\n"
1285+
" alias_method :__original_at_exit, :at_exit\n"
1286+
" def at_exit(&block)\n"
1287+
" AtExitInterceptor.captured_exit_procs << block\n"
1288+
" nil\n"
1289+
" end\n"
1290+
" end\n"
1291+
/* Manual runner */
1292+
" def self.run_all\n"
1293+
/* Run in reverse order to match Ruby's actual behavior */
1294+
" @captured_exit_procs.reverse_each do |proc|\n"
1295+
" proc.call\n"
1296+
" end\n"
1297+
" end\n"
1298+
"end\n";
1299+
1300+
VALUE wrapped_code = rb_str_new_static_size(header);
12661301
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("module "));
12671302
wrapped_code = rb_str_plus(wrapped_code, module_name);
12681303
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("\n"));
12691304
wrapped_code = rb_str_plus(wrapped_code, module_data);
1270-
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("\nend"));
1305+
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("\nend\n"));
1306+
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("AtExitInterceptor.run_all\n"));
12711307

12721308
#undef rb_str_new_static_size
12731309

@@ -1278,9 +1314,28 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_path
12781314

12791315
if (state != 0)
12801316
{
1281-
log_write("metacall", LOG_LEVEL_ERROR, "Ruby evaluation failed %s", paths[0]);
1282-
rb_loader_impl_print_exception();
1283-
goto load_error;
1317+
VALUE err = rb_errinfo();
1318+
VALUE system_exit_class = rb_const_get(rb_cObject, rb_intern("SystemExit"));
1319+
1320+
/* Check if the script exited */
1321+
if (rb_obj_is_kind_of(err, system_exit_class))
1322+
{
1323+
VALUE status = rb_funcall(err, rb_intern("status"), 0);
1324+
int exit_status = NUM2INT(status);
1325+
1326+
if (exit_status != 0)
1327+
{
1328+
exit(exit_status);
1329+
}
1330+
1331+
rb_set_errinfo(Qnil);
1332+
}
1333+
else
1334+
{
1335+
log_write("metacall", LOG_LEVEL_ERROR, "Ruby evaluation failed %s", paths[0]);
1336+
rb_loader_impl_print_exception();
1337+
goto load_error;
1338+
}
12841339
}
12851340

12861341
/* Get the module reference */
@@ -1518,61 +1573,58 @@ void rb_loader_impl_discover_methods(klass c, VALUE cls, const char *class_name_
15181573
VALUE name = rb_sym2str(rb_method);
15191574
const char *method_name_str = RSTRING_PTR(name);
15201575

1521-
if (rb_respond_to(cls, RB_SYM2ID(rb_method)))
1522-
{
1523-
VALUE instance_method = rb_funcall(cls, rb_intern(method_type_str), 1, rb_method);
1524-
VALUE parameters = rb_funcallv(instance_method, rb_intern("parameters"), 0, NULL);
1525-
size_t args_it, args_count = RARRAY_LEN(parameters);
1576+
VALUE instance_method = rb_funcall(cls, rb_intern(method_type_str), 1, rb_method);
1577+
VALUE parameters = rb_funcallv(instance_method, rb_intern("parameters"), 0, NULL);
1578+
size_t args_it, args_count = RARRAY_LEN(parameters);
15261579

1527-
log_write("metacall", LOG_LEVEL_DEBUG, "Method '%s' inside '%s' of type %s with %" PRIuS " parameters", method_name_str, class_name_str, method_type_str, args_count);
1580+
log_write("metacall", LOG_LEVEL_DEBUG, "Method '%s' inside '%s' of type %s with %" PRIuS " parameters", method_name_str, class_name_str, method_type_str, args_count);
15281581

1529-
/*
1530-
* TODO:
1531-
* Another alternative (for supporting types), which is not used in the current implementation,
1532-
* but it can simplify the parser, it's the following:
1533-
*
1534-
* - For classes: origin_file, definition_line = MyClass.instance_method(:foo).source_location
1535-
* - For plain functions: origin_file, definition_line = method(:foo).source_location
1536-
*
1537-
* Then:
1538-
* method_signature = IO.readlines(origin_file)[definition_line.pred]
1539-
*
1540-
* Now we have only the method signature, this is going to be less problematic than parsing
1541-
* the whole file as we are doing now (although for multi-line signatures it's going to be
1542-
* a little bit more complicated...)
1543-
*
1544-
* We can switch to completely duck typed approach (refactoring the tests) or we can use this
1545-
* simplified parsing approach and maintain types
1546-
*/
1582+
/*
1583+
* TODO:
1584+
* Another alternative (for supporting types), which is not used in the current implementation,
1585+
* but it can simplify the parser, it's the following:
1586+
*
1587+
* - For classes: origin_file, definition_line = MyClass.instance_method(:foo).source_location
1588+
* - For plain functions: origin_file, definition_line = method(:foo).source_location
1589+
*
1590+
* Then:
1591+
* method_signature = IO.readlines(origin_file)[definition_line.pred]
1592+
*
1593+
* Now we have only the method signature, this is going to be less problematic than parsing
1594+
* the whole file as we are doing now (although for multi-line signatures it's going to be
1595+
* a little bit more complicated...)
1596+
*
1597+
* We can switch to completely duck typed approach (refactoring the tests) or we can use this
1598+
* simplified parsing approach and maintain types
1599+
*/
15471600

1548-
method m = method_create(c,
1549-
method_name_str,
1550-
args_count,
1551-
(method_impl)instance_method,
1552-
visibility,
1553-
SYNCHRONOUS, /* There is not async functions in Ruby */
1554-
NULL);
1601+
method m = method_create(c,
1602+
method_name_str,
1603+
args_count,
1604+
(method_impl)instance_method,
1605+
visibility,
1606+
SYNCHRONOUS, /* There is not async functions in Ruby */
1607+
NULL);
15551608

1556-
signature s = method_signature(m);
1609+
signature s = method_signature(m);
15571610

1558-
for (args_it = 0; args_it < args_count; ++args_it)
1559-
{
1560-
VALUE parameter_pair = rb_ary_entry(parameters, args_it);
1611+
for (args_it = 0; args_it < args_count; ++args_it)
1612+
{
1613+
VALUE parameter_pair = rb_ary_entry(parameters, args_it);
15611614

1562-
if (RARRAY_LEN(parameter_pair) == 2)
1563-
{
1564-
VALUE parameter_name_id = rb_ary_entry(parameter_pair, 1);
1565-
VALUE parameter_name = rb_sym2str(parameter_name_id);
1566-
const char *parameter_name_str = RSTRING_PTR(parameter_name);
1615+
if (RARRAY_LEN(parameter_pair) == 2)
1616+
{
1617+
VALUE parameter_name_id = rb_ary_entry(parameter_pair, 1);
1618+
VALUE parameter_name = rb_sym2str(parameter_name_id);
1619+
const char *parameter_name_str = RSTRING_PTR(parameter_name);
15671620

1568-
signature_set(s, args_it, parameter_name_str, NULL);
1569-
}
1621+
signature_set(s, args_it, parameter_name_str, NULL);
15701622
}
1623+
}
15711624

1572-
if (register_method(c, m) != 0)
1573-
{
1574-
log_write("metacall", LOG_LEVEL_ERROR, "Ruby failed to register method '%s' in class '%s'", method_name_str, class_name_str);
1575-
}
1625+
if (register_method(c, m) != 0)
1626+
{
1627+
log_write("metacall", LOG_LEVEL_ERROR, "Ruby failed to register method '%s' in class '%s'", method_name_str, class_name_str);
15761628
}
15771629
}
15781630
}
@@ -1680,7 +1732,7 @@ static VALUE rb_loader_impl_discover_module_protect(VALUE args)
16801732
rb_cls->impl = impl;
16811733

16821734
/* Discover methods */
1683-
VALUE argv[1] = { Qtrue }; /* include_superclasses ? Qtrue : Qfalse; */
1735+
VALUE argv[1] = { Qfalse /* include_superclasses ? Qtrue : Qfalse; */ };
16841736
VALUE methods = rb_class_public_instance_methods(1, argv, cls); /* argc, argv, cls */
16851737
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "instance_method", methods, &class_register_method);
16861738

@@ -1692,13 +1744,13 @@ static VALUE rb_loader_impl_discover_module_protect(VALUE args)
16921744

16931745
#if RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 0
16941746
methods = rb_obj_public_methods(1, argv, cls);
1695-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "singleton_method", methods, &class_register_static_method);
1747+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "method", methods, &class_register_static_method);
16961748

16971749
methods = rb_obj_protected_methods(1, argv, cls);
1698-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PROTECTED, "singleton_method", methods, &class_register_static_method);
1750+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PROTECTED, "method", methods, &class_register_static_method);
16991751

17001752
methods = rb_obj_private_methods(1, argv, cls);
1701-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PRIVATE, "singleton_method", methods, &class_register_static_method);
1753+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PRIVATE, "method", methods, &class_register_static_method);
17021754
#else
17031755
methods = rb_obj_singleton_methods(1, argv, cls);
17041756
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "method", methods, &class_register_static_method);

source/ports/rb_port/package/lib/metacall.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ def metacall_module_load
9999

100100
# Check again if the port was loaded
101101
if defined?(MetaCallRbLoaderPort)
102-
103-
104102
return MetaCallRbLoaderPort
105103
else
106104
raise LoadError, 'MetaCall was found but failed to load MetaCallRbLoaderPort'

source/scripts/ruby/klass/source/klass.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,4 @@ def return_class_function()
3939
return MyClass
4040
end
4141

42-
4342
#p return_class_function()::CLASS_CONSTANT
44-

0 commit comments

Comments
 (0)