@@ -9656,20 +9656,29 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
96569656 LABEL *not_basic_new = NEW_LABEL(nd_line(node));
96579657 LABEL *not_basic_new_finish = NEW_LABEL(nd_line(node));
96589658
9659- // Detect String.new and String.new(..., capacity: <n>): emit opt_string_new
9660- // so the allocation can be pre-sized. The instruction guards at runtime that
9661- // the receiver is String and "new" is unredefined, so a compile-time name
9662- // match is sufficient here. string_new_capa_loc is the TOPN index of the
9663- // capacity argument, or -1 when not applicable. When the only arguments are
9664- // ones the allocation already satisfies (nothing, or just capacity),
9665- // initialize is a no-op and string_new_skip_init lets us omit the call.
9659+ // Detect String.new with no positional arguments: allocate via the
9660+ // String-specific opt_string_new instead of the generic opt_new. The
9661+ // instruction guards at runtime that the receiver is String and "new" is
9662+ // unredefined, so a compile-time name match is sufficient here.
9663+ //
9664+ // - string_new_capa_loc is the TOPN offset of the capacity: argument, or
9665+ // -1 when none is given. It can be deeper than 0 because other keywords
9666+ // (e.g. encoding:) may sit above it on the stack.
9667+ // - string_new_skip_init means the allocation already satisfies the call
9668+ // (nothing, or just capacity), so #initialize is omitted. Otherwise a
9669+ // trailing initialize send applies the remaining keywords, like opt_new.
96669670 int string_new_capa_loc = -1;
9671+ bool string_new = false;
96679672 bool string_new_skip_init = false;
96689673 if (inline_new &&
96699674 !(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT | VM_CALL_FORWARDING))) {
96709675 const NODE *recv_node = get_nd_recv(node);
9676+ // NUM2INT(argc) is the positional count (new_callinfo adds the keywords
9677+ // separately), so it must be zero for both the bare and capacity forms.
96719678 if (recv_node && nd_type_p(recv_node, NODE_CONST) &&
9672- RNODE_CONST(recv_node)->nd_vid == rb_intern("String")) {
9679+ RNODE_CONST(recv_node)->nd_vid == rb_intern("String") &&
9680+ NUM2INT(argc) == 0) {
9681+ string_new = true;
96739682 int keyword_len = keywords ? keywords->keyword_len : 0;
96749683 if (keywords) {
96759684 VALUE capacity_sym = ID2SYM(rb_intern("capacity"));
@@ -9680,11 +9689,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
96809689 }
96819690 }
96829691 }
9683- // NUM2INT(argc) is the positional count (new_callinfo adds the
9684- // keywords separately). Skip initialize when there are no
9685- // positional args and either no keywords or only capacity.
9686- if (NUM2INT(argc) == 0 &&
9687- (keyword_len == 0 || (keyword_len == 1 && string_new_capa_loc >= 0))) {
9692+ if (keyword_len == 0 || (keyword_len == 1 && string_new_capa_loc >= 0)) {
96889693 string_new_skip_init = true;
96899694 }
96909695 }
@@ -9699,10 +9704,11 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
96999704 else {
97009705 ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc), flag, keywords, 0);
97019706 }
9702- if (string_new_skip_init) {
9703- // opt_string_new allocates the String and skips initialize entirely.
9704- // capa_loc is unused for the bare form (argc == 0).
9705- ADD_INSN3(ret, node, opt_string_new, ci, not_basic_new, INT2FIX(string_new_capa_loc >= 0 ? string_new_capa_loc : 0));
9707+ if (string_new) {
9708+ // opt_string_new allocates the String via the String-specific
9709+ // allocator (pre-sized when capacity: is present). Whether
9710+ // #initialize runs is decided by the trailing bytecode below.
9711+ ADD_INSN3(ret, node, opt_string_new, ci, not_basic_new, INT2FIX(string_new_capa_loc));
97069712 }
97079713 else {
97089714 ADD_INSN2(ret, node, opt_new, ci, not_basic_new);
0 commit comments