aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJemma Issroff <jemmaissroff@gmail.com>2023-10-30 16:20:07 -0300
committerJemma Issroff <jemmaissroff@gmail.com>2023-10-30 18:07:04 -0300
commitf0b7895637d4c4e4a1c72cecaa8700495f12177b (patch)
treed890c8d25c48b89eceaeb03594aeafa6e055d356
parentb17b0336b613b0873542f48f1417ebfeb0079efa (diff)
downloadruby-f0b7895637d4c4e4a1c72cecaa8700495f12177b.tar.gz
[PRISM] Implement all argument types for CallNodes
-rw-r--r--prism_compile.c162
1 files changed, 149 insertions, 13 deletions
diff --git a/prism_compile.c b/prism_compile.c
index 55f0bd026d..916e60bea7 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -1382,6 +1382,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
ID method_id = pm_constant_id_lookup(scope_node, call_node->name);
int flags = 0;
int orig_argc = 0;
+ struct rb_callinfo_kwarg *kw_arg = NULL;
if (call_node->receiver == NULL) {
ADD_INSN(ret, &dummy_line_node, putself);
@@ -1393,21 +1394,156 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (flags & VM_CALL_FCALL) {
flags |= VM_CALL_VCALL;
}
- } else {
- pm_arguments_node_t *arguments = call_node->arguments;
- PM_COMPILE_NOT_POPPED((pm_node_t *) arguments);
- orig_argc = (int)arguments->arguments.size;
+ }
+ else {
+ pm_arguments_node_t *arguments_node = call_node->arguments;
+ pm_node_list_t arguments_node_list = arguments_node->arguments;
+
+ bool has_keyword_splat = (arguments_node->base.flags & PM_ARGUMENTS_NODE_FLAGS_KEYWORD_SPLAT);
+ bool has_splat = false;
+
+ // We count the number of elements post the splat node that are not keyword elements to
+ // eventually pass as an argument to newarray
+ int post_splat_counter = 0;
+
+ for (size_t index = 0; index < arguments_node_list.size; index++) {
+ pm_node_t *argument = arguments_node_list.nodes[index];
+
+ switch (PM_NODE_TYPE(argument)) {
+ // A keyword hash node contains all keyword arguments as AssocNodes and AssocSplatNodes
+ case PM_KEYWORD_HASH_NODE: {
+ pm_keyword_hash_node_t *keyword_arg = (pm_keyword_hash_node_t *)argument;
+ size_t len = keyword_arg->elements.size;
+
+ if (has_keyword_splat) {
+ int cur_hash_size = 0;
+ orig_argc++;
+
+ bool new_hash_emitted = false;
+ for (size_t i = 0; i < len; i++) {
+ pm_node_t *cur_node = keyword_arg->elements.nodes[i];
+
+ pm_node_type_t cur_type = PM_NODE_TYPE(cur_node);
+
+ switch (PM_NODE_TYPE(cur_node)) {
+ case PM_ASSOC_NODE: {
+ pm_assoc_node_t *assoc = (pm_assoc_node_t *)cur_node;
+
+ PM_COMPILE(assoc->key);
+ PM_COMPILE(assoc->value);
+ cur_hash_size++;
+
+ // If we're at the last keyword arg, or the last assoc node of this "set",
+ // then we want to either construct a newhash or merge onto previous hashes
+ if (i == (len - 1) || !PM_NODE_TYPE_P(keyword_arg->elements.nodes[i + 1], cur_type)) {
+ if (new_hash_emitted) {
+ ADD_SEND(ret, &dummy_line_node, id_core_hash_merge_ptr, INT2FIX(cur_hash_size * 2 + 1));
+ }
+ else {
+ ADD_INSN1(ret, &dummy_line_node, newhash, INT2FIX(cur_hash_size * 2));
+ cur_hash_size = 0;
+ new_hash_emitted = true;
+ }
+ }
+
+ break;
+ }
+ case PM_ASSOC_SPLAT_NODE: {
+ if (len > 1) {
+ ADD_INSN1(ret, &dummy_line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ if (i == 0) {
+ ADD_INSN1(ret, &dummy_line_node, newhash, INT2FIX(0));
+ new_hash_emitted = true;
+ }
+ else {
+ PM_SWAP;
+ }
+ }
+
+ pm_assoc_splat_node_t *assoc_splat = (pm_assoc_splat_node_t *)cur_node;
+ PM_COMPILE(assoc_splat->value);
+
+ flags |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT;
+
+ ADD_SEND(ret, &dummy_line_node, id_core_hash_merge_kwd, INT2FIX(2));
+
+ if ((i < len - 1) && !PM_NODE_TYPE_P(keyword_arg->elements.nodes[i + 1], cur_type)) {
+ ADD_INSN1(ret, &dummy_line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
+ PM_SWAP;
+ }
+
+ cur_hash_size = 0;
+ break;
+ }
+ default: {
+ rb_bug("Unknown type");
+ }
+ }
+ }
+ break;
+ }
+ else {
+ kw_arg = rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg));
+ flags |= VM_CALL_KWARG;
+ kw_arg->keyword_len += len;
+
+ // TODO: Method callers like `foo(a => b)`
+ for (size_t i = 0; i < len; i++) {
+ pm_assoc_node_t *assoc = (pm_assoc_node_t *)keyword_arg->elements.nodes[i];
+ kw_arg->keywords[i] = pm_static_literal_value(assoc->key, scope_node, parser);
+ PM_COMPILE(assoc->value);
+ }
+ }
+ break;
+ }
+ case PM_SPLAT_NODE: {
+ flags |= VM_CALL_ARGS_SPLAT;
+ pm_splat_node_t *splat_node = (pm_splat_node_t *)argument;
+ if (splat_node->expression) {
+ PM_COMPILE(splat_node->expression);
+ }
+
+ if (!popped) {
+ ADD_INSN1(ret, &dummy_line_node, splatarray, Qtrue);
+ }
+
+ has_splat = true;
+ post_splat_counter = 0;
+
+ break;
+ }
+ default: {
+ orig_argc++;
+ post_splat_counter++;
+ PM_COMPILE(argument);
+
+ if (has_splat) {
+ // If the next node starts the keyword section of parameters
+ if ((index < arguments_node_list.size - 1) && PM_NODE_TYPE_P(arguments_node_list.nodes[index + 1], PM_KEYWORD_HASH_NODE)) {
+
+ ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(post_splat_counter));
+ ADD_INSN1(ret, &dummy_line_node, splatarray, Qfalse);
+ ADD_INSN(ret, &dummy_line_node, concatarray);
+ }
+ // If it's the final node
+ else if (index == arguments_node_list.size - 1) {
+ ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(post_splat_counter));
+ ADD_INSN(ret, &dummy_line_node, concatarray);
+ }
+ }
+ }
+ }
+ }
}
- VALUE block_iseq = Qnil;
+ const rb_iseq_t *block_iseq = NULL;
if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
// Scope associated with the block
pm_scope_node_t next_scope_node;
pm_scope_node_init(call_node->block, &next_scope_node, scope_node, parser);
- const rb_iseq_t *block_iseq = NEW_CHILD_ISEQ(next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
+ block_iseq = NEW_CHILD_ISEQ(next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
ISEQ_COMPILE_DATA(iseq)->current_block = block_iseq;
- ADD_SEND_WITH_BLOCK(ret, &dummy_line_node, method_id, INT2FIX(orig_argc), block_iseq);
}
else {
if (node->flags & PM_CALL_NODE_FLAGS_VARIABLE_CALL) {
@@ -1419,16 +1555,16 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
flags |= VM_CALL_ARGS_BLOCKARG;
}
- if (block_iseq == Qnil && flags == 0) {
+ if (!flags) {
flags |= VM_CALL_ARGS_SIMPLE;
}
+ }
- if (call_node->receiver == NULL) {
- flags |= VM_CALL_FCALL;
- }
-
- ADD_SEND_WITH_FLAG(ret, &dummy_line_node, method_id, INT2NUM(orig_argc), INT2FIX(flags));
+ if (call_node->receiver == NULL) {
+ flags |= VM_CALL_FCALL;
}
+
+ ADD_SEND_R(ret, &dummy_line_node, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg);
PM_POP_IF_POPPED;
return;
}