summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJemma Issroff <jemmaissroff@gmail.com>2023-12-12 11:08:40 -0500
committerJemma Issroff <jemmaissroff@gmail.com>2023-12-12 13:45:21 -0500
commitb9dfe04a73b9b4b9a3d3e96a3418a8d105e5c63a (patch)
treefe951447c5aeb6a14837d82afc0a87d01cfc701e
parent68753e44083f7d4d171f66aa092b35ccb41a66f6 (diff)
[PRISM] Implementing forwarding of args for ForwardingSuperNode
ForwardingSuperNodes need to actually forward any applicable arguments. This commit implements that logic, by using the data stored on the local iseq about the parameters to forward the appropriate arguments.
-rw-r--r--prism_compile.c108
-rw-r--r--test/ruby/test_compile_prism.rb16
2 files changed, 119 insertions, 5 deletions
diff --git a/prism_compile.c b/prism_compile.c
index e2045e3303..f1f70a9b63 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -1680,7 +1680,7 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *co
ADD_INSN3(ret, &dummy_line_node, defined, INT2FIX(DEFINED_METHOD), rb_id2sym(method_id), PUSH_VAL(DEFINED_METHOD));
}
else {
- ADD_INSN(ret, &dummy_line_node, putself);
+ PM_PUTSELF;
if (explicit_receiver) {
PM_DUP;
}
@@ -1782,11 +1782,11 @@ pm_compile_defined_expr(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *con
if (lfinish[1]) {
ELEM_INSERT_NEXT(last, &new_insn_body(iseq, &dummy_line_node, BIN(putnil), 0)->link);
- ADD_INSN(ret, &dummy_line_node, swap);
+ PM_SWAP;
if (lfinish[2]) {
ADD_LABEL(ret, lfinish[2]);
}
- ADD_INSN(ret, &dummy_line_node, pop);
+ PM_POP;
ADD_LABEL(ret, lfinish[1]);
}
@@ -2964,7 +2964,105 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block);
}
- ADD_INSN2(ret, &dummy_line_node, invokesuper, new_callinfo(iseq, 0, 0, flag, NULL, block != NULL), block);
+ DECL_ANCHOR(args);
+ INIT_ANCHOR(args);
+
+ struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
+
+ const rb_iseq_t *local_iseq = body->local_iseq;
+ const struct rb_iseq_constant_body *const local_body = ISEQ_BODY(local_iseq);
+
+ int argc = 0;
+ int depth = get_lvar_level(iseq);
+
+ if (local_body->param.flags.has_lead) {
+ /* required arguments */
+ for (int i = 0; i < local_body->param.lead_num; i++) {
+ int idx = local_body->local_table_size - i;
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ }
+ argc += local_body->param.lead_num;
+ }
+
+
+ if (local_body->param.flags.has_opt) {
+ /* optional arguments */
+ for (int j = 0; j < local_body->param.opt_num; j++) {
+ int idx = local_body->local_table_size - (argc + j);
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ }
+ argc += local_body->param.opt_num;
+ }
+
+ if (local_body->param.flags.has_rest) {
+ /* rest argument */
+ int idx = local_body->local_table_size - local_body->param.rest_start;
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ ADD_INSN1(args, &dummy_line_node, splatarray, Qfalse);
+
+ argc = local_body->param.rest_start + 1;
+ flag |= VM_CALL_ARGS_SPLAT;
+ }
+
+ if (local_body->param.flags.has_post) {
+ /* post arguments */
+ int post_len = local_body->param.post_num;
+ int post_start = local_body->param.post_start;
+
+ int j = 0;
+ for (; j < post_len; j++) {
+ int idx = local_body->local_table_size - (post_start + j);
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ }
+
+ if (local_body->param.flags.has_rest) {
+ // argc remains unchanged from rest branch
+ ADD_INSN1(args, &dummy_line_node, newarray, INT2FIX(j));
+ ADD_INSN (args, &dummy_line_node, concatarray);
+ }
+ else {
+ argc = post_len + post_start;
+ }
+ }
+
+ const struct rb_iseq_param_keyword *const local_keyword = local_body->param.keyword;
+ if (local_body->param.flags.has_kw) {
+ int local_size = local_body->local_table_size;
+ argc++;
+
+ ADD_INSN1(args, &dummy_line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+
+ if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_keyword->rest_start;
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ if (local_keyword->num > 0) {
+ ADD_SEND(args, &dummy_line_node, rb_intern("dup"), INT2FIX(0));
+ flag |= VM_CALL_KW_SPLAT_MUT;
+ }
+ }
+ else {
+ ADD_INSN1(args, &dummy_line_node, newhash, INT2FIX(0));
+ flag |= VM_CALL_KW_SPLAT_MUT;
+ }
+ int i = 0;
+ for (; i < local_keyword->num; ++i) {
+ ID id = local_keyword->table[i];
+ int idx = local_size - get_local_var_idx(local_iseq, id);
+ ADD_INSN1(args, &dummy_line_node, putobject, ID2SYM(id));
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ }
+ ADD_SEND(args, &dummy_line_node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
+ flag |= VM_CALL_KW_SPLAT;
+ }
+ else if (local_body->param.flags.has_kwrest) {
+ int idx = local_body->local_table_size - local_keyword->rest_start;
+ ADD_GETLOCAL(args, &dummy_line_node, idx, depth);
+ argc++;
+ flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT;
+ }
+
+ ADD_SEQ(ret, args);
+ ADD_INSN2(ret, &dummy_line_node, invokesuper, new_callinfo(iseq, 0, argc, flag, NULL, block != NULL), block);
PM_POP_IF_POPPED;
return;
}
@@ -5031,7 +5129,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
INIT_ANCHOR(args);
ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
- ADD_INSN(ret, &dummy_line_node, putself);
+ PM_PUTSELF;
int argc = pm_setup_args(super_node->arguments, &flags, &keywords, iseq, ret, src, popped, scope_node, dummy_line_node, parser);
diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb
index 2bc1821936..05db94227e 100644
--- a/test/ruby/test_compile_prism.rb
+++ b/test/ruby/test_compile_prism.rb
@@ -1543,6 +1543,22 @@ module Prism
def test_ForwardingSuperNode
assert_prism_eval("class Forwarding; def to_s; super; end; end")
assert_prism_eval("class Forwarding; def eval(code); super { code }; end; end")
+ assert_prism_eval(<<-CODE)
+ class A
+ def initialize(a, b)
+ end
+ end
+
+ class B < A
+ attr_reader :res
+ def initialize(a, b, *)
+ super
+ @res = [a, b]
+ end
+ end
+
+ B.new(1, 2).res
+ CODE
end
def test_KeywordHashNode