@@ -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 );
0 commit comments