From d0bb17e0791573d82e27b0dc18b8212475faf0d1 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Mon, 28 Jul 2025 14:19:22 +0200 Subject: [PATCH 1/3] [ruby/shellwords] shellwords.gemspec: Avoid configuring exe/ directory This gem does not ship any executables. https://github.com/ruby/shellwords/commit/7da06b91ff --- lib/shellwords.gemspec | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/shellwords.gemspec b/lib/shellwords.gemspec index 8d0c518ca570bd..b601508f946c7b 100644 --- a/lib/shellwords.gemspec +++ b/lib/shellwords.gemspec @@ -25,7 +25,5 @@ Gem::Specification.new do |spec| spec.files = Dir.chdir(srcdir) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:(?:test|spec|features)/|\.git|Rake)}) || f == gemspec_file} end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] end From 7913aff2b34b64cf2bc5f0a638bb9ac5b9abdc53 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 24 Jul 2025 16:06:49 -0400 Subject: [PATCH 2/3] [DOC] Fix return value of rb_str_hash_cmp rb_str_hash_cmp returns 0 if the two strings are identical and 1 if they are different. --- include/ruby/internal/intern/string.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ruby/internal/intern/string.h b/include/ruby/internal/intern/string.h index 4573ec12626a89..7b1bf5cc543f45 100644 --- a/include/ruby/internal/intern/string.h +++ b/include/ruby/internal/intern/string.h @@ -970,8 +970,8 @@ st_index_t rb_str_hash(VALUE str); * * @param[in] str1 A string. * @param[in] str2 Another string. - * @retval 1 They have identical contents, length, and encodings. - * @retval 0 Otherwise. + * @retval 0 They have identical contents, length, and encodings. + * @retval 1 Otherwise. * @pre Both objects must not be any arbitrary objects except * ::RString. * From 5ca71364ff6cf4a9c445cb2701b1eb4770e79579 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Tue, 22 Jul 2025 12:10:45 -0700 Subject: [PATCH 3/3] Avoid GC while operands in inconsistent state compile_data_calloc2 may run GC (though because it allocates from an arena this is rare in practice). When this happened when resizing operands there was a risk of seeing the insn in an inconsistent state. To solve this we need to make any allocations before we start modifying the instrucitons. This refactors the code to use a new insn_replace_with_operands() function that allocates the new operands array before modifying the instruction object. Co-authored-by: Aaron Patterson --- compile.c | 97 +++++++++++++++++++++++-------------------------------- 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/compile.c b/compile.c index 8d5cb45904c6e7..bda18c1c424ab6 100644 --- a/compile.c +++ b/compile.c @@ -1440,6 +1440,30 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type i return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands); } +static INSN * +insn_replace_with_operands(rb_iseq_t *iseq, INSN *iobj, enum ruby_vminsn_type insn_id, int argc, ...) +{ + VALUE *operands = 0; + va_list argv; + if (argc > 0) { + int i; + va_start(argv, argc); + operands = compile_data_alloc2(iseq, sizeof(VALUE), argc); + for (i = 0; i < argc; i++) { + VALUE v = va_arg(argv, VALUE); + operands[i] = v; + } + va_end(argv); + } + + iobj->insn_id = insn_id; + iobj->operand_size = argc; + iobj->operands = operands; + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + + return iobj; +} + static const struct rb_callinfo * new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq) { @@ -3439,11 +3463,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal VALUE ary = iobj->operands[0]; rb_obj_reveal(ary, rb_cArray); - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = ary; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3465,11 +3485,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal VALUE hash = iobj->operands[0]; rb_obj_reveal(hash, rb_cHash); - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = hash; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3488,11 +3504,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cArray_empty_frozen); - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3511,11 +3523,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cHash_empty_frozen); - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -4109,17 +4117,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal static int insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) { - iobj->insn_id = insn_id; - iobj->operand_size = insn_len(insn_id) - 1; - iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; - if (insn_id == BIN(opt_neq)) { VALUE original_ci = iobj->operands[0]; - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); - iobj->operands[1] = original_ci; + VALUE new_ci = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); + insn_replace_with_operands(iseq, iobj, insn_id, 2, new_ci, original_ci); } + else { + iobj->insn_id = insn_id; + iobj->operand_size = insn_len(insn_id) - 1; + } + iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; return COMPILE_OK; } @@ -4151,12 +4158,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (method != INT2FIX(0)) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = num; - iobj->operands[1] = method; - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, num, method); ELEM_REMOVE(&niobj->link); return COMPILE_OK; } @@ -4168,12 +4170,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 1); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK); - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK)); ELEM_REMOVE(&iobj->link); ELEM_REMOVE(niobj->link.next); ELEM_INSERT_NEXT(&niobj->link, &iobj->link); @@ -4191,12 +4188,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (vm_ci_mid(ci) == idPack && vm_ci_argc(ci) == 2 && (kwarg && kwarg->keyword_len == 1 && kwarg->keywords[0] == rb_id2sym(idBuffer))) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 2); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER); - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 2), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER)); // Remove the "send" insn. ELEM_REMOVE((niobj->link.next)->next); // Remove the modified insn from its original "newarray" position... @@ -4230,11 +4222,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) { VALUE num = iobj->operands[0]; INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_newarray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = FIXNUM_INC(num, 1); - sendins->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P); + insn_replace_with_operands(iseq, sendins, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P)); // Remove the original "newarray" insn. ELEM_REMOVE(&iobj->link); return COMPILE_OK; @@ -4272,12 +4260,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) rb_obj_reveal(ary, rb_cArray); INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_duparray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1;; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = ary; - sendins->operands[1] = rb_id2sym(idIncludeP); - sendins->operands[2] = INT2FIX(1); + insn_replace_with_operands(iseq, sendins, BIN(opt_duparray_send), 3, ary, rb_id2sym(idIncludeP), INT2FIX(1)); // Remove the duparray insn. ELEM_REMOVE(&iobj->link);