summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreileencodes <[email protected]>2023-12-08 15:00:48 -0500
committerAaron Patterson <[email protected]>2023-12-08 12:45:40 -0800
commit1cbe114d1cf57dc47b4bbc6780f424647dd906d9 (patch)
tree7c9387184e9074dccd7c260704ec45da6a7a2654
parent9e7ca2c3c9d2fbdcdf7f3b6c0b8c63b2dbb08ebe (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.c24
-rw-r--r--test/ruby/test_compile_prism.rb28
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