From 7b2bea42a245f2e80b5d2700963fd6b143f6d6b8 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 2 Sep 2020 23:12:22 +0900 Subject: Unfreeze string-literal-only interpolated string-literal [Feature #17104] --- ast.c | 12 +++++++++--- compile.c | 12 ++++++++++-- node.c | 1 + parse.y | 41 +++++++++++++++++++++++++++++------------ test/ruby/test_iseq.rb | 2 +- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/ast.c b/ast.c index 87c366550e..2af0b3e530 100644 --- a/ast.c +++ b/ast.c @@ -485,9 +485,15 @@ node_children(rb_ast_t *ast, const NODE *node) case NODE_DXSTR: case NODE_DREGX: case NODE_DSYM: - return rb_ary_new_from_args(3, node->nd_lit, - NEW_CHILD(ast, node->nd_next->nd_head), - NEW_CHILD(ast, node->nd_next->nd_next)); + { + NODE *n = node->nd_next; + VALUE head = Qnil, next = Qnil; + if (n) { + head = NEW_CHILD(ast, n->nd_head); + next = NEW_CHILD(ast, n->nd_next); + } + return rb_ary_new_from_args(3, node->nd_lit, head, next); + } case NODE_EVSTR: return rb_ary_new_from_node_args(ast, 1, node->nd_body); case NODE_ARGSCAT: diff --git a/compile.c b/compile.c index 797a170f5f..0d2d7fbf73 100644 --- a/compile.c +++ b/compile.c @@ -3828,8 +3828,16 @@ static int compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node) { int cnt; - CHECK(compile_dstr_fragments(iseq, ret, node, &cnt)); - ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt)); + if (!node->nd_next) { + VALUE lit = rb_fstring(node->nd_lit); + const int line = (int)nd_line(node); + ADD_INSN1(ret, line, putstring, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + } + else { + CHECK(compile_dstr_fragments(iseq, ret, node, &cnt)); + ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt)); + } return COMPILE_OK; } diff --git a/node.c b/node.c index 7c291a80ad..936043794a 100644 --- a/node.c +++ b/node.c @@ -741,6 +741,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) ANN("example: :\"foo#{ bar }baz\""); dlit: F_LIT(nd_lit, "preceding string"); + if (!node->nd_next) return; F_NODE(nd_next->nd_head, "interpolation"); LAST_NODE; F_NODE(nd_next->nd_next, "tailing strings"); diff --git a/parse.y b/parse.y index 93bc2ef9ff..f8441e867f 100644 --- a/parse.y +++ b/parse.y @@ -9871,12 +9871,24 @@ literal_concat0(struct parser_params *p, VALUE head, VALUE tail) return 1; } +static VALUE +string_literal_head(enum node_type htype, NODE *head) +{ + if (htype != NODE_DSTR) return Qfalse; + if (head->nd_next) { + head = head->nd_next->nd_end->nd_head; + if (!head || nd_type(head) != NODE_STR) return Qfalse; + } + const VALUE lit = head->nd_lit; + ASSUME(lit != Qfalse); + return lit; +} + /* concat two string literals */ static NODE * literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *loc) { enum node_type htype; - NODE *headlast; VALUE lit; if (!head) return tail; @@ -9899,10 +9911,8 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l } switch (nd_type(tail)) { case NODE_STR: - if (htype == NODE_DSTR && (headlast = head->nd_next->nd_end->nd_head) && - nd_type(headlast) == NODE_STR) { + if ((lit = string_literal_head(htype, head)) != Qfalse) { htype = NODE_STR; - lit = headlast->nd_lit; } else { lit = head->nd_lit; @@ -9932,13 +9942,16 @@ literal_concat(struct parser_params *p, NODE *head, NODE *tail, const YYLTYPE *l else if (NIL_P(tail->nd_lit)) { append: head->nd_alen += tail->nd_alen - 1; - head->nd_next->nd_end->nd_next = tail->nd_next; - head->nd_next->nd_end = tail->nd_next->nd_end; + if (!head->nd_next) { + head->nd_next = tail->nd_next; + } + else if (tail->nd_next) { + head->nd_next->nd_end->nd_next = tail->nd_next; + head->nd_next->nd_end = tail->nd_next->nd_end; + } rb_discard_node(p, tail); } - else if (htype == NODE_DSTR && (headlast = head->nd_next->nd_end->nd_head) && - nd_type(headlast) == NODE_STR) { - lit = headlast->nd_lit; + else if ((lit = string_literal_head(htype, head)) != Qfalse) { if (!literal_concat0(p, lit, tail->nd_lit)) goto error; tail->nd_lit = Qnil; @@ -9976,7 +9989,9 @@ new_evstr(struct parser_params *p, NODE *node, const YYLTYPE *loc) if (node) { switch (nd_type(node)) { - case NODE_STR: case NODE_DSTR: case NODE_EVSTR: + case NODE_STR: + nd_set_type(node, NODE_DSTR); + case NODE_DSTR: case NODE_EVSTR: return node; } } @@ -10273,8 +10288,10 @@ new_regexp(struct parser_params *p, NODE *node, int options, const YYLTYPE *loc) node->nd_cflag = options & RE_OPTION_MASK; if (!NIL_P(node->nd_lit)) reg_fragment_check(p, node->nd_lit, options); for (list = (prev = node)->nd_next; list; list = list->nd_next) { - if (nd_type(list->nd_head) == NODE_STR) { - VALUE tail = list->nd_head->nd_lit; + NODE *frag = list->nd_head; + enum node_type type = nd_type(frag); + if (type == NODE_STR || (type == NODE_DSTR && !frag->nd_next)) { + VALUE tail = frag->nd_lit; if (reg_fragment_check(p, tail, options) && prev && !NIL_P(prev->nd_lit)) { VALUE lit = prev == node ? prev->nd_lit : prev->nd_head->nd_lit; if (!literal_concat0(p, lit, tail)) { diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 7fb6268f61..51e3fd0391 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -188,7 +188,7 @@ class TestISeq < Test::Unit::TestCase assert_predicate(s1, :frozen?) assert_predicate(s2, :frozen?) assert_not_predicate(s3, :frozen?) - assert_predicate(s4, :frozen?) # should probably not be frozen, but unrealistic code + assert_not_predicate(s4, :frozen?) end # Safe call chain is not optimized when Coverage is running. -- cgit v1.2.1