aboutsummaryrefslogtreecommitdiffstats
path: root/prism_compile.c
diff options
context:
space:
mode:
authorJemma Issroff <jemmaissroff@gmail.com>2023-11-22 09:49:26 -0500
committerJemma Issroff <jemmaissroff@gmail.com>2023-11-27 12:52:07 -0500
commit95064bb88db7ff0d71bf9c921b0b87fe1ae712d7 (patch)
tree786f3b92cb470c8dac2054d79ef6bf83b107354b /prism_compile.c
parent6d447fa35f877edae96e4a7f98c9f5e70219314b (diff)
downloadruby-95064bb88db7ff0d71bf9c921b0b87fe1ae712d7.tar.gz
[PRISM] Fix compilation for SplatNodes within ArrayNodes
SplatNodes within ArrayNodes (e.g. [*1..2, 3]) need to be special cased in the compiler because they use a combination of concatarray and newarray instructions to treat each sequence of splat or non-splat elements as independent arrays which get concatenated. This commit implements those cases.
Diffstat (limited to 'prism_compile.c')
-rw-r--r--prism_compile.c73
1 files changed, 68 insertions, 5 deletions
diff --git a/prism_compile.c b/prism_compile.c
index b66c2e0ed0..fa5a7aafca 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -1575,15 +1575,78 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
pm_array_node_t *cast = (pm_array_node_t *) node;
pm_node_list_t *elements = &cast->elements;
- for (size_t index = 0; index < elements->size; index++) {
- PM_COMPILE(elements->nodes[index]);
+ // In the case that there is a splat node within the array,
+ // the array gets compiled slightly differently.
+ if (node->flags & PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT) {
+ if (elements->size == 1) {
+ // If the only nodes is a SplatNode, we never
+ // need to emit the newarray or concatarray
+ // instructions
+ PM_COMPILE_NOT_POPPED(elements->nodes[0]);
+ }
+ else {
+ // We treat all sequences of non-splat elements as their
+ // own arrays, followed by a newarray, and then continually
+ // concat the arrays with the SplatNodes
+ int new_array_size = 0;
+ bool need_to_concat_array = false;
+ for (size_t index = 0; index < elements->size; index++) {
+ pm_node_t *array_element = elements->nodes[index];
+ if (PM_NODE_TYPE_P(array_element, PM_SPLAT_NODE)) {
+ pm_splat_node_t *splat_element = (pm_splat_node_t *)array_element;
+
+ // If we already have non-splat elements, we need to emit a newarray
+ // instruction
+ if (new_array_size) {
+ ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(new_array_size));
+
+ // We don't want to emit a concat array in the case where
+ // we're seeing our first splat, and already have elements
+ if (need_to_concat_array) {
+ ADD_INSN(ret, &dummy_line_node, concatarray);
+ }
+
+ new_array_size = 0;
+ }
+
+ PM_COMPILE_NOT_POPPED(splat_element->expression);
+
+ if (index > 0) {
+ ADD_INSN(ret, &dummy_line_node, concatarray);
+ }
+ else {
+ // If this is the first element, we need to splatarray
+ ADD_INSN1(ret, &dummy_line_node, splatarray, Qtrue);
+ }
+
+ need_to_concat_array = true;
+ }
+ else {
+ new_array_size++;
+ PM_COMPILE_NOT_POPPED(array_element);
+ }
+ }
+
+ if (new_array_size) {
+ ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(new_array_size));
+ if (need_to_concat_array) {
+ ADD_INSN(ret, &dummy_line_node, concatarray);
+ }
+ }
+ }
+
+ PM_POP_IF_POPPED;
}
+ else {
+ for (size_t index = 0; index < elements->size; index++) {
+ PM_COMPILE(elements->nodes[index]);
+ }
- if (!popped) {
- ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(elements->size));
+ if (!popped) {
+ ADD_INSN1(ret, &dummy_line_node, newarray, INT2FIX(elements->size));
+ }
}
}
-
return;
}
case PM_ASSOC_NODE: {