[PRISM] Pre-concatenate Strings in InterpolatedStringNode

This commit concatenates String VALUEs within
InterpolatedStringNodes to allow us to preserve frozenness of
concatenated strings such as `"a""b"`

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
Jemma Issroff 2023-12-15 16:28:29 -05:00 committed by Aaron Patterson
parent e59dd7094f
commit 84f14ff089
2 changed files with 55 additions and 15 deletions

View File

@ -730,30 +730,62 @@ pm_compile_while(rb_iseq_t *iseq, int lineno, pm_node_flags_t flags, enum pm_nod
return; return;
} }
static void static int
pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_line_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node, pm_parser_t *parser) pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_line_node, LINK_ANCHOR *const ret, const uint8_t *src, bool popped, pm_scope_node_t *scope_node, pm_parser_t *parser)
{ {
int number_of_items_pushed = 0;
size_t parts_size = parts->size; size_t parts_size = parts->size;
if (parts_size > 0) { if (parts_size > 0) {
VALUE current_string = Qnil;
for (size_t index = 0; index < parts_size; index++) { for (size_t index = 0; index < parts_size; index++) {
pm_node_t *part = parts->nodes[index]; pm_node_t *part = parts->nodes[index];
if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) { if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
pm_string_node_t *string_node = (pm_string_node_t *) part; pm_string_node_t *string_node = (pm_string_node_t *)part;
ADD_INSN1(ret, &dummy_line_node, putobject, parse_string(&string_node->unescaped, parser)); VALUE string_value = parse_string(&string_node->unescaped, parser);
if (RTEST(current_string)) {
current_string = rb_str_concat(current_string, string_value);
}
else {
current_string = string_value;
}
} }
else { else {
if (RTEST(current_string)) {
if (parser->frozen_string_literal) {
ADD_INSN1(ret, &dummy_line_node, putobject, rb_str_freeze(current_string));
}
else {
ADD_INSN1(ret, &dummy_line_node, putstring, current_string);
}
current_string = Qnil;
number_of_items_pushed++;
}
PM_COMPILE_NOT_POPPED(part); PM_COMPILE_NOT_POPPED(part);
PM_DUP; PM_DUP;
ADD_INSN1(ret, &dummy_line_node, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE)); ADD_INSN1(ret, &dummy_line_node, objtostring, new_callinfo(iseq, idTo_s, 0, VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE , NULL, FALSE));
ADD_INSN(ret, &dummy_line_node, anytostring); ADD_INSN(ret, &dummy_line_node, anytostring);
number_of_items_pushed++;
} }
} }
if (RTEST(current_string)) {
if (parser->frozen_string_literal) {
ADD_INSN1(ret, &dummy_line_node, putobject, rb_str_freeze(current_string));
}
else {
ADD_INSN1(ret, &dummy_line_node, putstring, current_string);
}
current_string = Qnil;
number_of_items_pushed++;
}
} }
else { else {
PM_PUTNIL; PM_PUTNIL;
} }
return number_of_items_pushed;
} }
// This recurses through scopes and finds the local index at any scope level // This recurses through scopes and finds the local index at any scope level
@ -4706,11 +4738,10 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
} }
case PM_INTERPOLATED_STRING_NODE: { case PM_INTERPOLATED_STRING_NODE: {
pm_interpolated_string_node_t *interp_string_node = (pm_interpolated_string_node_t *) node; pm_interpolated_string_node_t *interp_string_node = (pm_interpolated_string_node_t *) node;
pm_interpolated_node_compile(&interp_string_node->parts, iseq, dummy_line_node, ret, src, popped, scope_node, parser); int number_of_items_pushed = pm_interpolated_node_compile(&interp_string_node->parts, iseq, dummy_line_node, ret, src, popped, scope_node, parser);
size_t parts_size = interp_string_node->parts.size; if (number_of_items_pushed > 1) {
if (parts_size > 1) { ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX(number_of_items_pushed));
ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX((int)(parts_size)));
} }
PM_POP_IF_POPPED; PM_POP_IF_POPPED;
@ -4718,11 +4749,10 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
} }
case PM_INTERPOLATED_SYMBOL_NODE: { case PM_INTERPOLATED_SYMBOL_NODE: {
pm_interpolated_symbol_node_t *interp_symbol_node = (pm_interpolated_symbol_node_t *) node; pm_interpolated_symbol_node_t *interp_symbol_node = (pm_interpolated_symbol_node_t *) node;
pm_interpolated_node_compile(&interp_symbol_node->parts, iseq, dummy_line_node, ret, src, popped, scope_node, parser); int number_of_items_pushed = pm_interpolated_node_compile(&interp_symbol_node->parts, iseq, dummy_line_node, ret, src, popped, scope_node, parser);
size_t parts_size = interp_symbol_node->parts.size; if (number_of_items_pushed > 1) {
if (parts_size > 1) { ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX(number_of_items_pushed));
ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX((int)(parts_size)));
} }
if (!popped) { if (!popped) {
@ -4737,11 +4767,10 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
case PM_INTERPOLATED_X_STRING_NODE: { case PM_INTERPOLATED_X_STRING_NODE: {
pm_interpolated_x_string_node_t *interp_x_string_node = (pm_interpolated_x_string_node_t *) node; pm_interpolated_x_string_node_t *interp_x_string_node = (pm_interpolated_x_string_node_t *) node;
PM_PUTSELF; PM_PUTSELF;
pm_interpolated_node_compile(&interp_x_string_node->parts, iseq, dummy_line_node, ret, src, false, scope_node, parser); int number_of_items_pushed = pm_interpolated_node_compile(&interp_x_string_node->parts, iseq, dummy_line_node, ret, src, false, scope_node, parser);
size_t parts_size = interp_x_string_node->parts.size; if (number_of_items_pushed > 1) {
if (parts_size > 1) { ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX(number_of_items_pushed));
ADD_INSN1(ret, &dummy_line_node, concatstrings, INT2FIX((int)(parts_size)));
} }
ADD_SEND_WITH_FLAG(ret, &dummy_line_node, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE)); ADD_SEND_WITH_FLAG(ret, &dummy_line_node, idBackquote, INT2NUM(1), INT2FIX(VM_CALL_FCALL | VM_CALL_ARGS_SIMPLE));

View File

@ -589,6 +589,17 @@ module Prism
assert_prism_eval('$pit = 1; "1 #$pit 1"') assert_prism_eval('$pit = 1; "1 #$pit 1"')
assert_prism_eval('"1 #{1 + 2} 1"') assert_prism_eval('"1 #{1 + 2} 1"')
assert_prism_eval('"Prism" "::" "TestCompilePrism"') assert_prism_eval('"Prism" "::" "TestCompilePrism"')
assert_prism_eval('("a""b").frozen?')
assert_prism_eval(<<-CODE)
# frozen_string_literal: true
("a""b").frozen?
CODE
assert_prism_eval(<<-CODE)
# frozen_string_literal: true
("a""b""#{1}").frozen?
CODE
end end
def test_InterpolatedSymbolNode def test_InterpolatedSymbolNode