Skip to content

Commit 578dcfd

Browse files
committed
Improve issues with ruby port.
1 parent e214b90 commit 578dcfd

File tree

3 files changed

+195
-119
lines changed

3 files changed

+195
-119
lines changed

source/loaders/rb_loader/source/rb_loader_impl.c

Lines changed: 150 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ typedef struct loader_impl_rb_funcall_protect_type
8989
ID id;
9090
} * loader_impl_rb_funcall_protect;
9191

92+
typedef struct loader_impl_rb_discover_module_protect_type
93+
{
94+
loader_impl impl;
95+
loader_impl_rb_module rb_module;
96+
context ctx;
97+
} * loader_impl_rb_discover_module_protect;
98+
9299
static class_interface rb_class_interface_singleton(void);
93100
static object_interface rb_object_interface_singleton(void);
94101
static void rb_loader_impl_discover_methods(klass c, VALUE cls, const char *class_name_str, enum class_visibility_id visibility, const char *method_type_str, VALUE methods, int (*register_method)(klass, method));
@@ -1255,7 +1262,9 @@ loader_handle rb_loader_impl_load_from_file(loader_impl impl, const loader_path
12551262
{
12561263
#define rb_str_new_static_size(str) rb_str_new_static(str, sizeof(str) - 1)
12571264

1258-
VALUE wrapped_code = rb_str_plus(rb_str_new_static_size("module "), module_name);
1265+
VALUE wrapped_code = rb_str_new_static_size("require 'rubygems'\n");
1266+
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("module "));
1267+
wrapped_code = rb_str_plus(wrapped_code, module_name);
12591268
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("\n"));
12601269
wrapped_code = rb_str_plus(wrapped_code, module_data);
12611270
wrapped_code = rb_str_plus(wrapped_code, rb_str_new_static_size("\nend"));
@@ -1509,58 +1518,61 @@ void rb_loader_impl_discover_methods(klass c, VALUE cls, const char *class_name_
15091518
VALUE name = rb_sym2str(rb_method);
15101519
const char *method_name_str = RSTRING_PTR(name);
15111520

1512-
VALUE instance_method = rb_funcall(cls, rb_intern(method_type_str), 1, rb_method);
1513-
VALUE parameters = rb_funcallv(instance_method, rb_intern("parameters"), 0, NULL);
1514-
size_t args_it, args_count = RARRAY_LEN(parameters);
1515-
1516-
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);
1517-
1518-
/*
1519-
* TODO:
1520-
* Another alternative (for supporting types), which is not used in the current implementation,
1521-
* but it can simplify the parser, it's the following:
1522-
*
1523-
* - For classes: origin_file, definition_line = MyClass.instance_method(:foo).source_location
1524-
* - For plain functions: origin_file, definition_line = method(:foo).source_location
1525-
*
1526-
* Then:
1527-
* method_signature = IO.readlines(origin_file)[definition_line.pred]
1528-
*
1529-
* Now we have only the method signature, this is going to be less problematic than parsing
1530-
* the whole file as we are doing now (although for multi-line signatures it's going to be
1531-
* a little bit more complicated...)
1532-
*
1533-
* We can switch to completely duck typed approach (refactoring the tests) or we can use this
1534-
* simplified parsing approach and maintain types
1535-
*/
1536-
1537-
method m = method_create(c,
1538-
method_name_str,
1539-
args_count,
1540-
(method_impl)instance_method,
1541-
visibility,
1542-
SYNCHRONOUS, /* There is not async functions in Ruby */
1543-
NULL);
1544-
1545-
signature s = method_signature(m);
1546-
1547-
for (args_it = 0; args_it < args_count; ++args_it)
1521+
if (rb_respond_to(cls, RB_SYM2ID(rb_method)))
15481522
{
1549-
VALUE parameter_pair = rb_ary_entry(parameters, args_it);
1550-
1551-
if (RARRAY_LEN(parameter_pair) == 2)
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);
1526+
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);
1528+
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+
*/
1547+
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);
1555+
1556+
signature s = method_signature(m);
1557+
1558+
for (args_it = 0; args_it < args_count; ++args_it)
15521559
{
1553-
VALUE parameter_name_id = rb_ary_entry(parameter_pair, 1);
1554-
VALUE parameter_name = rb_sym2str(parameter_name_id);
1555-
const char *parameter_name_str = RSTRING_PTR(parameter_name);
1560+
VALUE parameter_pair = rb_ary_entry(parameters, args_it);
15561561

1557-
signature_set(s, args_it, parameter_name_str, NULL);
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);
1567+
1568+
signature_set(s, args_it, parameter_name_str, NULL);
1569+
}
15581570
}
1559-
}
15601571

1561-
if (register_method(c, m) != 0)
1562-
{
1563-
log_write("metacall", LOG_LEVEL_ERROR, "Ruby failed to register method '%s' in class '%s'", method_name_str, class_name_str);
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+
}
15641576
}
15651577
}
15661578
}
@@ -1587,15 +1599,12 @@ void rb_loader_impl_discover_attributes(klass c, const char *class_name_str, VAL
15871599
}
15881600
}
15891601

1590-
int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_module, context ctx)
1602+
static VALUE rb_loader_impl_discover_module_protect(VALUE args)
15911603
{
1592-
log_write("metacall", LOG_LEVEL_DEBUG, "Ruby loader discovering:");
1593-
1594-
if (rb_module->empty == 0)
1595-
{
1596-
return 0;
1597-
}
1598-
1604+
loader_impl_rb_discover_module_protect protect = (loader_impl_rb_discover_module_protect)args;
1605+
loader_impl impl = protect->impl;
1606+
loader_impl_rb_module rb_module = protect->rb_module;
1607+
context ctx = protect->ctx;
15991608
VALUE instance_methods = rb_funcallv(rb_module->module, rb_intern("instance_methods"), 0, NULL);
16001609
VALUE methods_size = rb_funcallv(instance_methods, rb_intern("size"), 0, NULL);
16011610
int index, size = FIX2INT(methods_size);
@@ -1630,7 +1639,7 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
16301639
if (scope_define(sp, function_name(f), v) != 0)
16311640
{
16321641
value_type_destroy(v);
1633-
return 1;
1642+
return INT2NUM(1);
16341643
}
16351644
else
16361645
{
@@ -1640,12 +1649,12 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
16401649
}
16411650
else
16421651
{
1643-
return 1;
1652+
return INT2NUM(1);
16441653
}
16451654
}
16461655
else
16471656
{
1648-
return 1;
1657+
return INT2NUM(1);
16491658
}
16501659
}
16511660
}
@@ -1659,80 +1668,106 @@ int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_mo
16591668
{
16601669
VALUE constant = rb_ary_entry(constants, index);
16611670

1662-
if (constant != Qnil)
1671+
if (constant != Qnil && RB_TYPE_P(constant, T_SYMBOL))
16631672
{
1664-
if (RB_TYPE_P(constant, T_SYMBOL))
1665-
{
1666-
VALUE class_name = rb_sym2str(constant);
1667-
const char *class_name_str = RSTRING_PTR(class_name);
1668-
VALUE cls = rb_const_get_from(rb_module->module, rb_intern(class_name_str));
1669-
loader_impl_rb_class rb_cls = malloc(sizeof(struct loader_impl_rb_class_type));
1670-
klass c = class_create(class_name_str, ACCESSOR_TYPE_DYNAMIC, rb_cls, &rb_class_interface_singleton);
1673+
VALUE class_name = rb_sym2str(constant);
1674+
const char *class_name_str = RSTRING_PTR(class_name);
1675+
VALUE cls = rb_const_get_from(rb_module->module, rb_intern(class_name_str));
1676+
loader_impl_rb_class rb_cls = malloc(sizeof(struct loader_impl_rb_class_type));
1677+
klass c = class_create(class_name_str, ACCESSOR_TYPE_DYNAMIC, rb_cls, &rb_class_interface_singleton);
16711678

1672-
rb_cls->class = cls;
1673-
rb_cls->impl = impl;
1679+
rb_cls->class = cls;
1680+
rb_cls->impl = impl;
16741681

1675-
/* Discover methods */
1676-
VALUE argv[1] = { Qtrue }; /* include_superclasses ? Qtrue : Qfalse; */
1677-
VALUE methods = rb_class_public_instance_methods(1, argv, cls); /* argc, argv, cls */
1678-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "instance_method", methods, &class_register_method);
1682+
/* Discover methods */
1683+
VALUE argv[1] = { Qtrue }; /* include_superclasses ? Qtrue : Qfalse; */
1684+
VALUE methods = rb_class_public_instance_methods(1, argv, cls); /* argc, argv, cls */
1685+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "instance_method", methods, &class_register_method);
16791686

1680-
methods = rb_class_protected_instance_methods(1, argv, cls);
1681-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PROTECTED, "instance_method", methods, &class_register_method);
1687+
methods = rb_class_protected_instance_methods(1, argv, cls);
1688+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PROTECTED, "instance_method", methods, &class_register_method);
16821689

1683-
methods = rb_class_private_instance_methods(1, argv, cls);
1684-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PRIVATE, "instance_method", methods, &class_register_method);
1690+
methods = rb_class_private_instance_methods(1, argv, cls);
1691+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PRIVATE, "instance_method", methods, &class_register_method);
16851692

16861693
#if RUBY_VERSION_MAJOR == 3 && RUBY_VERSION_MINOR >= 0
1687-
methods = rb_obj_public_methods(1, argv, cls);
1688-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "singleton_method", methods, &class_register_static_method);
1694+
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);
16891696

1690-
methods = rb_obj_protected_methods(1, argv, cls);
1691-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PROTECTED, "singleton_method", methods, &class_register_static_method);
1697+
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);
16921699

1693-
methods = rb_obj_private_methods(1, argv, cls);
1694-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PRIVATE, "singleton_method", methods, &class_register_static_method);
1700+
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);
16951702
#else
1696-
methods = rb_obj_singleton_methods(1, argv, cls);
1697-
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "singleton_method", methods, &class_register_static_method);
1703+
methods = rb_obj_singleton_methods(1, argv, cls);
1704+
rb_loader_impl_discover_methods(c, cls, class_name_str, VISIBILITY_PUBLIC, "method", methods, &class_register_static_method);
16981705
#endif
16991706

1700-
/* Discover attributes */
1701-
VALUE static_attributes = rb_mod_class_variables(1, argv, cls);
1702-
rb_loader_impl_discover_attributes(c, class_name_str, static_attributes, &class_register_static_attribute);
1703-
1704-
VALUE instance_attributes = rb_obj_instance_variables(cls);
1705-
rb_loader_impl_discover_attributes(c, class_name_str, instance_attributes, &class_register_attribute);
1706-
1707-
/* Define default constructor. Ruby only supports one constructor, a
1708-
* method called 'initialize'. It can have arguments but when inspected via
1709-
* reflection, the signature is variadic arguments and cannot be inspected:
1710-
*
1711-
* MyClass.methods(:initialize).parameters = [[:rest]] # variadic args notation in Ruby
1712-
*
1713-
* Due to this, we will always register only one default constructor without arguments
1714-
* which will take all the arguments when invoking 'new' and apply them as variadic.
1715-
*/
1716-
constructor ctor = constructor_create(0, VISIBILITY_PUBLIC);
1717-
1718-
if (class_register_constructor(c, ctor) != 0)
1719-
{
1720-
log_write("metacall", LOG_LEVEL_ERROR, "Failed to register default constructor in class %s", class_name_str);
1721-
}
1707+
/* Discover attributes */
1708+
VALUE static_attributes = rb_mod_class_variables(1, argv, cls);
1709+
rb_loader_impl_discover_attributes(c, class_name_str, static_attributes, &class_register_static_attribute);
1710+
1711+
VALUE instance_attributes = rb_obj_instance_variables(cls);
1712+
rb_loader_impl_discover_attributes(c, class_name_str, instance_attributes, &class_register_attribute);
1713+
1714+
/* Define default constructor. Ruby only supports one constructor, a
1715+
* method called 'initialize'. It can have arguments but when inspected via
1716+
* reflection, the signature is variadic arguments and cannot be inspected:
1717+
*
1718+
* MyClass.methods(:initialize).parameters = [[:rest]] # variadic args notation in Ruby
1719+
*
1720+
* Due to this, we will always register only one default constructor without arguments
1721+
* which will take all the arguments when invoking 'new' and apply them as variadic.
1722+
*/
1723+
constructor ctor = constructor_create(0, VISIBILITY_PUBLIC);
1724+
1725+
if (class_register_constructor(c, ctor) != 0)
1726+
{
1727+
log_write("metacall", LOG_LEVEL_ERROR, "Failed to register default constructor in class %s", class_name_str);
1728+
}
17221729

1723-
scope sp = context_scope(ctx);
1724-
value v = value_create_class(c);
1730+
scope sp = context_scope(ctx);
1731+
value v = value_create_class(c);
17251732

1726-
if (scope_define(sp, class_name_str, v) != 0)
1727-
{
1728-
value_type_destroy(v);
1729-
return 1;
1730-
}
1733+
if (scope_define(sp, class_name_str, v) != 0)
1734+
{
1735+
value_type_destroy(v);
1736+
return INT2NUM(1);
17311737
}
17321738
}
17331739
}
17341740

1735-
return 0;
1741+
return INT2NUM(0);
1742+
}
1743+
1744+
int rb_loader_impl_discover_module(loader_impl impl, loader_impl_rb_module rb_module, context ctx)
1745+
{
1746+
struct loader_impl_rb_discover_module_protect_type protect;
1747+
int state;
1748+
VALUE result;
1749+
1750+
log_write("metacall", LOG_LEVEL_DEBUG, "Ruby loader discovering:");
1751+
1752+
if (rb_module->empty == 0)
1753+
{
1754+
return 0;
1755+
}
1756+
1757+
protect.impl = impl;
1758+
protect.rb_module = rb_module;
1759+
protect.ctx = ctx;
1760+
1761+
result = rb_protect(rb_loader_impl_discover_module_protect, (VALUE)&protect, &state);
1762+
1763+
if (state != 0)
1764+
{
1765+
log_write("metacall", LOG_LEVEL_ERROR, "Ruby module discover failed");
1766+
rb_loader_impl_print_exception();
1767+
return 1;
1768+
}
1769+
1770+
return NUM2INT(result);
17361771
}
17371772

17381773
int rb_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx)

0 commit comments

Comments
 (0)