diff --git a/prism/options.c b/prism/options.c index 6b52b2f296..4018d4a13f 100644 --- a/prism/options.c +++ b/prism/options.c @@ -163,6 +163,7 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) { scope->locals_count = locals_count; scope->locals = xcalloc(locals_count, sizeof(pm_string_t)); + scope->forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; return scope->locals != NULL; } @@ -174,6 +175,14 @@ pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) { return &scope->locals[index]; } +/** + * Set the forwarding option on the given scope struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) { + scope->forwarding = forwarding; +} + /** * Free the internal memory associated with the options. */ @@ -281,6 +290,9 @@ pm_options_read(pm_options_t *options, const char *data) { return; } + uint8_t forwarding = (uint8_t) *data++; + pm_options_scope_forwarding_set(&options->scopes[scope_index], forwarding); + for (size_t local_index = 0; local_index < locals_count; local_index++) { uint32_t local_length = pm_options_read_u32(data); data += 4; diff --git a/prism/options.h b/prism/options.h index c96fa684ac..05d089e34c 100644 --- a/prism/options.h +++ b/prism/options.h @@ -39,8 +39,26 @@ typedef struct pm_options_scope { /** The names of the locals in the scope. */ pm_string_t *locals; + + /** Flags for the set of forwarding parameters in this scope. */ + uint8_t forwarding; } pm_options_scope_t; +/** The default value for parameters. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE = 0x0; + +/** When the scope is fowarding with the * parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1; + +/** When the scope is fowarding with the ** parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2; + +/** When the scope is fowarding with the & parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4; + +/** When the scope is fowarding with the ... parameter. */ +static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL = 0x8; + // Forward declaration needed by the callback typedef. struct pm_options; @@ -319,6 +337,14 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, si */ PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); +/** + * Set the forwarding option on the given scope struct. + * + * @param scope The scope struct to set the forwarding on. + * @param forwarding The forwarding value to set. + */ +PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding); + /** * Free the internal memory associated with the options. * @@ -367,6 +393,7 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options); * | # bytes | field | * | ------- | -------------------------- | * | `4` | the number of locals | + * | `1` | the forwarding flags | * | ... | the locals | * * Each local is laid out as follows: diff --git a/prism/prism.c b/prism/prism.c index 36b3640559..766ee30ac5 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -22373,7 +22373,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm // Scopes given from the outside are not allowed to have numbered // parameters. - parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED; + parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED; for (size_t local_index = 0; local_index < scope->locals_count; local_index++) { const pm_string_t *local = pm_options_scope_local_get(scope, local_index); diff --git a/prism_compile.c b/prism_compile.c index a63bf490f5..362aecfbb0 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -138,12 +138,6 @@ pm_iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int node #define PM_COMPILE_NOT_POPPED(node) \ pm_compile_node(iseq, (node), ret, false, scope_node) -#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31)) -#define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG)) -#define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG)) -#define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG)) -#define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG)) - #define PM_NODE_START_LOCATION(parser, node) \ ((pm_node_location_t) { .line = pm_newline_list_line(&(parser)->newline_list, ((const pm_node_t *) (node))->location.start, (parser)->start_line), .node_id = ((const pm_node_t *) (node))->node_id }) diff --git a/prism_compile.h b/prism_compile.h index f18fdbf892..c032449bd6 100644 --- a/prism_compile.h +++ b/prism_compile.h @@ -83,6 +83,12 @@ typedef struct { bool parsed; } pm_parse_result_t; +#define PM_SPECIAL_CONSTANT_FLAG ((pm_constant_id_t)(1 << 31)) +#define PM_CONSTANT_AND ((pm_constant_id_t)(idAnd | PM_SPECIAL_CONSTANT_FLAG)) +#define PM_CONSTANT_DOT3 ((pm_constant_id_t)(idDot3 | PM_SPECIAL_CONSTANT_FLAG)) +#define PM_CONSTANT_MULT ((pm_constant_id_t)(idMULT | PM_SPECIAL_CONSTANT_FLAG)) +#define PM_CONSTANT_POW ((pm_constant_id_t)(idPow | PM_SPECIAL_CONSTANT_FLAG)) + VALUE pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error); VALUE pm_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines); VALUE pm_load_parse_file(pm_parse_result_t *result, VALUE filepath, VALUE *script_lines); diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index a226b10d94..62f1d99bdc 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -139,6 +139,11 @@ class TestSyntax < Test::Unit::TestCase inner(&) end assert_equal(10, all_kwrest(nil, nil, nil, nil, okw1: nil, okw2: nil){10}) + + def evaled(&) + eval("inner(&)") + end + assert_equal(1, evaled{1}) end; end @@ -156,8 +161,10 @@ class TestSyntax < Test::Unit::TestCase def b(*); c(*) end def c(*a); a end def d(*); b(*, *) end + def e(*); eval("b(*)") end assert_equal([1, 2], b(1, 2)) assert_equal([1, 2, 1, 2], d(1, 2)) + assert_equal([1, 2], e(1, 2)) end; end @@ -177,10 +184,12 @@ class TestSyntax < Test::Unit::TestCase def d(**); b(k: 1, **) end def e(**); b(**, k: 1) end def f(a: nil, **); b(**) end + def g(**); eval("b(**)") end assert_equal({a: 1, k: 3}, b(a: 1, k: 3)) assert_equal({a: 1, k: 3}, d(a: 1, k: 3)) assert_equal({a: 1, k: 1}, e(a: 1, k: 3)) assert_equal({k: 3}, f(a: 1, k: 3)) + assert_equal({a: 1, k: 3}, g(a: 1, k: 3)) end; end @@ -2010,6 +2019,7 @@ eom obj4 = obj1.clone obj5 = obj1.clone obj1.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__) + obj1.instance_eval('def foo(...) eval("bar(...)") end', __FILE__, __LINE__) obj4.instance_eval("def foo ...\n bar(...)\n""end", __FILE__, __LINE__) obj5.instance_eval("def foo ...; bar(...); end", __FILE__, __LINE__) diff --git a/vm_eval.c b/vm_eval.c index a8c12707ec..74dfd40907 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1692,12 +1692,24 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, iseq = parent; rb_encoding *encoding = rb_enc_get(src); +#define FORWARDING_POSITIONALS_CHR '*' +#define FORWARDING_POSITIONALS_STR "*" +#define FORWARDING_KEYWORDS_CHR ':' +#define FORWARDING_KEYWORDS_STR ":" +#define FORWARDING_BLOCK_CHR '&' +#define FORWARDING_BLOCK_STR "&" +#define FORWARDING_ALL_CHR '.' +#define FORWARDING_ALL_STR "." + for (int scopes_index = 0; scopes_index < scopes_count; scopes_index++) { VALUE iseq_value = (VALUE)iseq; int locals_count = ISEQ_BODY(iseq)->local_table_size; + pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1]; pm_options_scope_init(options_scope, locals_count); + uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; + for (int local_index = 0; local_index < locals_count; local_index++) { pm_string_t *scope_local = &options_scope->locals[local_index]; ID local = ISEQ_BODY(iseq)->local_table[local_index]; @@ -1729,10 +1741,23 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, RB_GC_GUARD(name_obj); - pm_string_owned_init(scope_local, (uint8_t *)name_dup, length); + pm_string_owned_init(scope_local, (uint8_t *) name_dup, length); + } else if (local == idMULT) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS; + pm_string_constant_init(scope_local, FORWARDING_POSITIONALS_STR, 1); + } else if (local == idPow) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS; + pm_string_constant_init(scope_local, FORWARDING_KEYWORDS_STR, 1); + } else if (local == idAnd) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK; + pm_string_constant_init(scope_local, FORWARDING_BLOCK_STR, 1); + } else if (local == idDot3) { + forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL; + pm_string_constant_init(scope_local, FORWARDING_ALL_STR, 1); } } + pm_options_scope_forwarding_set(options_scope, forwarding); iseq = ISEQ_BODY(iseq)->parent_iseq; /* We need to GC guard the iseq because the code above malloc memory @@ -1775,14 +1800,38 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, for (int local_index = 0; local_index < locals_count; local_index++) { const pm_string_t *scope_local = &options_scope->locals[local_index]; - pm_constant_id_t constant_id = 0; - if (pm_string_length(scope_local) > 0) { - constant_id = pm_constant_pool_insert_constant( - &result.parser.constant_pool, pm_string_source(scope_local), - pm_string_length(scope_local)); - st_insert(parent_scope->index_lookup_table, (st_data_t)constant_id, (st_data_t)local_index); + + const uint8_t *source = pm_string_source(scope_local); + size_t length = pm_string_length(scope_local); + + if (length > 0) { + if (length == 1) { + switch (*source) { + case FORWARDING_POSITIONALS_CHR: + constant_id = PM_CONSTANT_MULT; + break; + case FORWARDING_KEYWORDS_CHR: + constant_id = PM_CONSTANT_POW; + break; + case FORWARDING_BLOCK_CHR: + constant_id = PM_CONSTANT_AND; + break; + case FORWARDING_ALL_CHR: + constant_id = PM_CONSTANT_DOT3; + break; + default: + constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length); + break; + } + } + else { + constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length); + } + + st_insert(parent_scope->index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index); } + pm_constant_id_list_append(&parent_scope->locals, constant_id); } @@ -1791,6 +1840,15 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, iseq = ISEQ_BODY(iseq)->parent_iseq; } +#undef FORWARDING_POSITIONALS_CHR +#undef FORWARDING_POSITIONALS_STR +#undef FORWARDING_KEYWORDS_CHR +#undef FORWARDING_KEYWORDS_STR +#undef FORWARDING_BLOCK_CHR +#undef FORWARDING_BLOCK_STR +#undef FORWARDING_ALL_CHR +#undef FORWARDING_ALL_STR + int error_state; iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, 0, &error_state);