@@ -94,8 +94,9 @@ typedef struct loader_impl_rb_module_eval_protect_type
9494 VALUE module ;
9595} * loader_impl_rb_module_eval_protect ;
9696
97- class_interface rb_class_interface_singleton ();
98- object_interface rb_object_interface_singleton ();
97+ static class_interface rb_class_interface_singleton (void );
98+ static object_interface rb_object_interface_singleton (void );
99+ static void rb_loader_impl_discover_methods (klass c , VALUE cls , const char * class_name_str , const char * method_type_str , VALUE methods , int (* register_method )(klass , method ));
99100
100101int function_rb_interface_create (function func , function_impl impl )
101102{
@@ -1347,6 +1348,73 @@ loader_impl_rb_function rb_function_create(loader_impl_rb_module rb_module, ID i
13471348 return NULL ;
13481349}
13491350
1351+ void rb_loader_impl_discover_methods (klass c , VALUE cls , const char * class_name_str , const char * method_type_str , VALUE methods , int (* register_method )(klass , method ))
1352+ {
1353+ VALUE methods_v_size = rb_funcall (methods , rb_intern ("size" ), 0 );
1354+ int method_index , methods_size = FIX2INT (methods_v_size );
1355+
1356+ for (method_index = 0 ; method_index < methods_size ; ++ method_index )
1357+ {
1358+ VALUE rb_method = rb_ary_entry (methods , method_index );
1359+ VALUE name = rb_funcall (rb_method , rb_intern ("id2name" ), 0 );
1360+ const char * method_name_str = RSTRING_PTR (name );
1361+
1362+ VALUE instance_method = rb_funcall (cls , rb_intern (method_type_str ), 1 , rb_method );
1363+ VALUE parameters = rb_funcall (instance_method , rb_intern ("parameters" ), 0 );
1364+ size_t args_it , args_count = RARRAY_LEN (parameters );
1365+
1366+ 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 );
1367+
1368+ /*
1369+ * TODO:
1370+ * Another alternative (for supporting types), which is not used in the current implementation,
1371+ * but it can simplify the parser, it's the following:
1372+ *
1373+ * - For classes: origin_file, definition_line = MyClass.instance_method(:foo).source_location
1374+ * - For plain functions: origin_file, definition_line = method(:foo).source_location
1375+ *
1376+ * Then:
1377+ * method_signature = IO.readlines(origin_file)[definition_line.pred]
1378+ *
1379+ * Now we have only the method signature, this is going to be less problematic than parsing
1380+ * the whole file as we are doing now (although for multi-line signatures it's going to be
1381+ * a little bit more complicated...)
1382+ *
1383+ * We can switch to completely duck typed approach (refactoring the tests) or we can use this
1384+ * simplified parsing approach and maintain types
1385+ */
1386+
1387+ method m = method_create (c ,
1388+ method_name_str ,
1389+ args_count ,
1390+ (method_impl )instance_method ,
1391+ VISIBILITY_PUBLIC , /* TODO: Check previous TODO inside this function */
1392+ SYNCHRONOUS , /* There is not async functions in Ruby */
1393+ NULL );
1394+
1395+ signature s = method_signature (m );
1396+
1397+ for (args_it = 0 ; args_it < args_count ; ++ args_it )
1398+ {
1399+ VALUE parameter_pair = rb_ary_entry (parameters , args_it );
1400+
1401+ if (RARRAY_LEN (parameter_pair ) == 2 )
1402+ {
1403+ VALUE parameter_name_id = rb_ary_entry (parameter_pair , 1 );
1404+ VALUE parameter_name = rb_funcall (parameter_name_id , rb_intern ("id2name" ), 0 );
1405+ const char * parameter_name_str = RSTRING_PTR (parameter_name );
1406+
1407+ signature_set (s , args_it , parameter_name_str , NULL );
1408+ }
1409+ }
1410+
1411+ if (register_method (c , m ) == 0 )
1412+ {
1413+ log_write ("metacall" , LOG_LEVEL_ERROR , "Ruby failed to register method '%s'" , method_name_str );
1414+ }
1415+ }
1416+ }
1417+
13501418int rb_loader_impl_discover_module (loader_impl impl , loader_impl_rb_module rb_module , context ctx )
13511419{
13521420 log_write ("metacall" , LOG_LEVEL_DEBUG , "Ruby loader discovering:" );
@@ -1357,9 +1425,7 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
13571425 }
13581426
13591427 VALUE instance_methods = rb_funcall (rb_module -> module , rb_intern ("instance_methods" ), 0 );
1360-
13611428 VALUE methods_size = rb_funcall (instance_methods , rb_intern ("size" ), 0 );
1362-
13631429 int index , size = FIX2INT (methods_size );
13641430
13651431 for (index = 0 ; index < size ; ++ index )
@@ -1411,9 +1477,7 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
14111477
14121478 /* Now discover classes */
14131479 VALUE constants = rb_funcall (rb_module -> module , rb_intern ("constants" ), 0 );
1414-
14151480 VALUE constants_size = rb_funcall (constants , rb_intern ("size" ), 0 );
1416-
14171481 size = FIX2INT (constants_size );
14181482
14191483 for (index = 0 ; index < size ; index ++ )
@@ -1426,63 +1490,24 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
14261490 {
14271491 VALUE class_name = rb_funcall (constant , rb_intern ("id2name" ), 0 );
14281492 const char * class_name_str = RSTRING_PTR (class_name );
1429- VALUE class = rb_const_get_from (rb_module -> module , rb_intern (class_name_str ));
1493+ VALUE cls = rb_const_get_from (rb_module -> module , rb_intern (class_name_str ));
14301494 loader_impl_rb_class rb_cls = malloc (sizeof (struct loader_impl_rb_class_type ));
14311495 klass c = class_create (class_name_str , rb_cls , & rb_class_interface_singleton );
14321496
14331497 rb_cls -> impl = impl ;
1434- rb_cls -> class = class ;
1435-
1436- // TODO:
1437- // rb_obj_private_methods, rb_obj_protected_methods, rb_obj_public_methods and
1438- // rb_obj_singleton_methods, can be used instead of rb_class_instance_methods
1498+ rb_cls -> class = cls ;
14391499
1440- VALUE argv [1 ] = { Qtrue }; // include_superclasses ? Qtrue : Qfalse;
1441- VALUE methods = rb_class_instance_methods (1 , argv , class ); // argc, argv, class
1442- VALUE load_path_array_size = rb_funcall (methods , rb_intern ("size" ), 0 );
1443- int method_index , methods_size = FIX2INT (load_path_array_size );
1500+ /*
1501+ * TODO:
1502+ * rb_obj_private_methods, rb_obj_protected_methods, rb_obj_public_methods and
1503+ * rb_obj_singleton_methods, can be used instead of rb_class_instance_methods
1504+ */
1505+ VALUE argv [1 ] = { Qtrue }; /* include_superclasses ? Qtrue : Qfalse; */
1506+ VALUE methods = rb_class_instance_methods (1 , argv , cls ); /* argc, argv, class */
1507+ rb_loader_impl_discover_methods (c , cls , class_name_str , "instance_method" , methods , & class_register_method );
14441508
1445- for (method_index = 0 ; method_index < methods_size ; method_index ++ )
1446- {
1447- VALUE rb_method = rb_ary_entry (methods , method_index );
1448- VALUE name = rb_funcall (rb_method , rb_intern ("id2name" ), 0 );
1449- const char * method_name_str = RSTRING_PTR (name );
1450-
1451- log_write ("metacall" , LOG_LEVEL_DEBUG , "Method inside '%s' %s" , class_name_str , method_name_str );
1452-
1453- /* TODO */
1454- /*
1455- method m = method_create(c,
1456- method_name_str,
1457- args_count, // TODO: method(:foo).parameters.length
1458- rb_method,
1459- VISIBILITY_PUBLIC, // TODO: Check line 1434 of this file
1460- SYNCHRONOUS, // There is not async functions in Ruby
1461- NULL);
1462-
1463- signature s = method_signature(m);
1464-
1465- // TODO: Iterate through each parameter (method(:foo).parameters),
1466- // get each pair second element and store the name, the arguments of the methods
1467- // can be without types, so there's no need to use the parser
1468-
1469- // Another alternative (for maintaining types), which is not used in the current implementation,
1470- // but it can simplify the parser, it's the following:
1471- //
1472- // - For classes: origin_file, definition_line = MyClass.instance_method(:foo).source_location
1473- // - For plain functions: origin_file, definition_line = method(:foo).source_location
1474- //
1475- // Then:
1476- // method_signature = IO.readlines(origin_file)[definition_line.pred]
1477- //
1478- // Now we have only the method signature, this is going to be less problematic than parsing
1479- // the whole file as we are doing now (although for multi-line signatures it's going to be
1480- // a little bit more complicated...)
1481- //
1482- // We can switch to completely duck typed approach (refactoring the tests) or we can use this
1483- // simplified parsing approach and maintain types
1484- */
1485- }
1509+ methods = rb_obj_singleton_methods (1 , argv , cls ); /* argc, argv, class */
1510+ rb_loader_impl_discover_methods (c , cls , class_name_str , "singleton_method" , methods , & class_register_static_method );
14861511
14871512 /* Define default constructor. Ruby only supports one constructor, a
14881513 * method called 'initialize'. It can have arguments but when inspected via
0 commit comments