diff options
author | eileencodes <[email protected]> | 2023-12-08 15:00:48 -0500 |
---|---|---|
committer | Aaron Patterson <[email protected]> | 2023-12-08 12:45:40 -0800 |
commit | 1cbe114d1cf57dc47b4bbc6780f424647dd906d9 (patch) | |
tree | 7c9387184e9074dccd7c260704ec45da6a7a2654 | |
parent | 9e7ca2c3c9d2fbdcdf7f3b6c0b8c63b2dbb08ebe (diff) |
[PRISM] Fix `PM_CALL_NODE` assignment
This PR fixes ruby/prism#1963. Array and variable assignment was broken
for call nodes. The change checks if the `method_id` is related to
assignment and if is adds a `putnil`, `setn` and a `pop`.
The incorrect instructions meant that in some cases (demonstrated in
tests) the wrong value would be returned.
I verified that this fixes the test mentioned in the issue
(run: `RUBY_ISEQ_DUMP_DEBUG=prism make test/-ext-/st/test_numhash.rb`)
Incorrect instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(4,10)>
0000 putnil ( 4)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:3 (3,0)-(3,10)>
0000 putself ( 3)[Li]
0001 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0004 putself
0005 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0008 putself
0009 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0012 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0015 leave
```
Fixed instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(4,10)>
0000 putnil ( 4)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:3 (3,0)-(3,10)>
0000 putnil ( 3)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
```
Fixes ruby/prism#1963
-rw-r--r-- | prism_compile.c | 24 | ||||
-rw-r--r-- | test/ruby/test_compile_prism.rb | 28 |
2 files changed, 47 insertions, 5 deletions
diff --git a/prism_compile.c b/prism_compile.c index d4ee1b4be3..cc600b6ba6 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -1465,7 +1465,7 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_ } } -static void pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node); +static void pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node, ID method_id); void pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node, NODE dummy_line_node, int lineno, bool in_condition, LABEL **lfinish, bool explicit_receiver) @@ -1637,7 +1637,8 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *co pm_compile_defined_expr0(iseq, call_node->receiver, ret, src, popped, scope_node, dummy_line_node, lineno, true, lfinish, true); if (PM_NODE_TYPE_P(call_node->receiver, PM_CALL_NODE)) { ADD_INSNL(ret, &dummy_line_node, branchunless, lfinish[2]); - pm_compile_call(iseq, (const pm_call_node_t *)call_node->receiver, ret, src, popped, scope_node); + ID method_id = pm_constant_id_lookup(scope_node, call_node->name); + pm_compile_call(iseq, (const pm_call_node_t *)call_node->receiver, ret, src, popped, scope_node, method_id); } else { ADD_INSNL(ret, &dummy_line_node, branchunless, lfinish[1]); @@ -1765,14 +1766,13 @@ pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *con } static void -pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node) +pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node, ID method_id) { pm_parser_t *parser = scope_node->parser; pm_newline_list_t newline_list = parser->newline_list; int lineno = (int)pm_newline_list_line_column(&newline_list, ((pm_node_t *)call_node)->location.start).line; NODE dummy_line_node = generate_dummy_line_node(lineno, lineno); - ID method_id = pm_constant_id_lookup(scope_node, call_node->name); int flags = 0; struct rb_callinfo_kwarg *kw_arg = NULL; @@ -1806,7 +1806,16 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c flags |= VM_CALL_FCALL; } + if (rb_is_attrset_id(method_id)) { + ADD_INSN1(ret, &dummy_line_node, setn, INT2FIX(orig_argc + 1)); + } + ADD_SEND_R(ret, &dummy_line_node, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg); + + if (rb_is_attrset_id(method_id)) { + PM_POP; + } + PM_POP_IF_POPPED; } @@ -2149,13 +2158,18 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, case PM_CALL_NODE: { pm_call_node_t *call_node = (pm_call_node_t *) node; + ID method_id = pm_constant_id_lookup(scope_node, call_node->name); + if (rb_is_attrset_id(method_id)) { + PM_PUTNIL; + } + if (call_node->receiver == NULL) { PM_PUTSELF; } else { PM_COMPILE_NOT_POPPED(call_node->receiver); } - pm_compile_call(iseq, call_node, ret, src, popped, scope_node); + pm_compile_call(iseq, call_node, ret, src, popped, scope_node, method_id); return; } case PM_CALL_AND_WRITE_NODE: { diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index 3d7e16a59a..c2cb60251c 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -1301,6 +1301,34 @@ module Prism CODE assert_prism_eval("prism_test_call_node_splat(*[], 1, 2)") + + assert_prism_eval(<<-CODE) + class Foo + def []=(a, b) + 1234 + end + end + + def self.foo(i, j) + tbl = Foo.new + tbl[i] = j + end + foo(1, 2) + CODE + + assert_prism_eval(<<-CODE) + class Foo + def i=(a) + 1234 + end + end + + def self.foo(j) + tbl = Foo.new + tbl.i = j + end + foo(1) + CODE end def test_CallAndWriteNode |