From 192651bb6c1482d394075eedbcbd552a65ab8ed7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 09:12:12 +0900 Subject: [PATCH 01/11] Ignore *.ilk incremental link artifacts MSVC builds leave dump_ast.ilk at the top of the tree, which was the only build product not already covered by .gitignore. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7ead3a81f506b0..6ec036860829d4 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ lcov*.info /*-fake.rb /*.dll /*.exe +/*.ilk /*.res /*.pc /*.rc From 0be4f3ef378aa7643521de36aad88d221f3528e7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 10:42:47 +0900 Subject: [PATCH 02/11] Fix flaky test_define_method_on_return Same failure mode as 2bfacb56c59: in parallel test-all, finalizers of Tempfile objects left by other tests run on the main thread at an interrupt checkpoint while this test's trace is enabled, injecting spurious call/return events for FinalizerManager#call that the target_thread? guard cannot reject. Extract the caller_locations check into event_from_this_file? and apply it to all four trace handlers here, rejecting events whose innermost non-internal frame is not this file. The set_trace_func handler must rely on caller_locations rather than its own file argument. Co-Authored-By: Claude Opus 4.8 --- test/ruby/test_settracefunc.rb | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index b4af8fa98625b8..fefe93f674decb 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -26,6 +26,19 @@ def target_thread? Thread.current == @target_thread end + # Reject trace events from other code interrupting this thread, such as + # finalizers of objects left by other tests (e.g. Tempfile's), whose + # frames are pushed on top of the interrupted frame of this file. An + # event is ours iff the innermost frame, ignoring core methods written + # in Ruby ( such as Kernel#tap), belongs to this file. + # + # +locations+ must be the caller_locations captured directly in the + # trace handler; capturing it here would add this method's own frame. + def event_from_this_file?(locations) + innermost = locations.drop_while{|loc| loc.path.start_with?("), - # belongs to this file. - innermost = caller_locations.drop_while{|loc| loc.path.start_with?(" Date: Tue, 30 Jun 2026 14:04:58 +0900 Subject: [PATCH 03/11] [ruby/mmtk] Simplify rb_gc_impl_heap_id_for_size https://github.com/ruby/mmtk/commit/1eb7011040 --- gc/mmtk/mmtk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 39b6fb4c87f094..d996b923525430 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -965,8 +965,7 @@ size_t rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size) { for (int i = 0; i < MMTK_HEAP_COUNT; i++) { - if (size == heap_sizes[i]) return i; - if (size < heap_sizes[i]) return i; + if (size <= heap_sizes[i]) return i; } rb_bug("size too big"); From e2a743242ae26b6613d9e2e2b057a49878ef3760 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 30 Jun 2026 14:29:24 +0900 Subject: [PATCH 04/11] Separate test-spec tmpdir spec/mspec/lib/mspec/helpers/tmp.rb also tries to remove SPEC_TEMP_DIR at exit, before this `END`. At that time, the "tmp" marker directory causes an ENOTEMPTY. --- tool/lib/_tmpdir.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tool/lib/_tmpdir.rb b/tool/lib/_tmpdir.rb index f4ac29ffb64fd9..ec3189f16f94c5 100644 --- a/tool/lib/_tmpdir.rb +++ b/tool/lib/_tmpdir.rb @@ -110,4 +110,5 @@ def list_tree(parent, indent = "", &block) end } -ENV["TMPDIR"] = ENV["SPEC_TEMP_DIR"] = ENV["GEM_TEST_TMPDIR"] = tmpdir +ENV["TMPDIR"] = ENV["GEM_TEST_TMPDIR"] = tmpdir +ENV["SPEC_TEMP_DIR"] = File.join(tmpdir, "spec") From 22df05278bec469f1abb903fe226329a68bc33b6 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 30 Jun 2026 15:01:36 +0900 Subject: [PATCH 05/11] test_settracefunc.rb: Rewrite without deprecated ObjectSpace._id2ref --- test/ruby/test_settracefunc.rb | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index fefe93f674decb..95fc7f45536217 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -2978,6 +2978,10 @@ def test_tracepoint_thread_end_with_exception assert_kind_of(Thread, target_thread) end + private def finalized(done) + proc {done[0] = true} + end + def test_tracepoint_garbage_collected_when_disable before_count_stat = 0 before_count_objspace = 0 @@ -2992,18 +2996,17 @@ def test_tracepoint_garbage_collected_when_disable tp.enable Class.inspect # c_call, c_return invoked tp.disable - tp_id = tp.object_id + done = [false] + ObjectSpace.define_finalizer(tp, finalized(done)) tp = nil gc_times = 0 gc_max_retries = 10 - EnvUtil.suppress_warning do - until (ObjectSpace._id2ref(tp_id) rescue nil).nil? - GC.start - gc_times += 1 - if gc_times == gc_max_retries - break - end + until done[0] + GC.start + gc_times += 1 + if gc_times == gc_max_retries + break end end return if gc_times == gc_max_retries @@ -3012,12 +3015,12 @@ def test_tracepoint_garbage_collected_when_disable TracePoint.stat.each do |v| after_count_stat += 1 end - assert after_count_stat <= before_count_stat + assert_operator after_count_stat, :<=, before_count_stat after_count_objspace = 0 ObjectSpace.each_object(TracePoint) do after_count_objspace += 1 end - assert after_count_objspace <= before_count_objspace + assert_operator after_count_objspace, :<=, before_count_objspace end def test_tp_ractor_local_untargeted @@ -3111,7 +3114,9 @@ def foo def test_tracepoints_not_disabled_by_ractor_gc assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}") begin; - $-w = nil # uses ObjectSpace._id2ref + def finalized(done) + proc {done[0] = true} + end def hi = "hi" greetings = 0 tp_target = TracePoint.new(:call) do |tp| @@ -3127,12 +3132,13 @@ def hi = "hi" r = Ractor.new { 10 } r.join - ractor_id = r.object_id + done = [false] + ObjectSpace.define_finalizer(r, finalized(done)) r = nil # allow gc for ractor gc_max_retries = 15 gc_times = 0 # force GC of ractor (or try, because we have a conservative GC) - until (ObjectSpace._id2ref(ractor_id) rescue nil).nil? + until done[0] GC.start gc_times += 1 if gc_times == gc_max_retries @@ -3150,11 +3156,7 @@ def hi = "hi" tp_target.disable tp_global.disable assert_equal 5, greetings - if gc_times == gc_max_retries # _id2ref never raised - assert_equal 6, raises - else - assert_equal 7, raises - end + assert_equal 6, raises end; end From e5388655c73b33962902cb0e6afeb32137b09df7 Mon Sep 17 00:00:00 2001 From: tompng Date: Mon, 29 Jun 2026 17:23:57 +0900 Subject: [PATCH 06/11] [ruby/json] Fix ResumableParser#partial_value SEGV on incomplete nested containers partial_value rebuilds the open containers on a scratch copy of the value stack. Folding an empty innermost container pops nothing and pushes its result, so head climbs one past the live size; the scratch buffer was sized to exactly the live size, so the push triggered rvalue_stack_grow which reallocated the ALLOCV buffer (a non-malloc pointer) and crashed. A parent fold always reclaims its child's slot, so head exceeds the live size by at most one -- either for the missing-value placeholder or for an empty innermost fold, never both. Size the buffer to capa + 1. https://github.com/ruby/json/commit/94c1af25b5 Co-Authored-By: Claude Opus 4.8 (1M context) --- ext/json/parser/parser.c | 13 +++++++++---- test/json/resumable_parser_test.rb | 11 +++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index a6154c83ec52de..a60f6a3c95511b 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -2625,11 +2625,16 @@ static VALUE cResumableParser_partial_value_body(VALUE self) missing_object_value = 1; } - // Copy the value stack as we need to mutate it. + // Copy the value stack as we need to mutate it. The collapse loop folds each + // open container by popping its entries and pushing the single result, so a + // parent always reclaims its child's slot; head exceeds its live size by at + // most one, either for the missing-value placeholder pushed below or for the + // result of folding an empty innermost container. That one spare slot keeps + // rvalue_stack_push from growing (reallocating) this ALLOCV buffer. long capa = parser.value_stack.head; - parser.value_stack.capa = (capa + missing_object_value); - VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, capa + missing_object_value); - MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, parser.value_stack.capa); + parser.value_stack.capa = capa + 1; + VALUE tmpbuf, *value_stack_buffer = ALLOCV_N(VALUE, tmpbuf, parser.value_stack.capa); + MEMCPY(value_stack_buffer, parser.value_stack.ptr, VALUE, capa); parser.value_stack.ptr = value_stack_buffer; JSON_ParserState *state = &parser.state; diff --git a/test/json/resumable_parser_test.rb b/test/json/resumable_parser_test.rb index 4cc6c085dff7c3..734d6e220b769d 100644 --- a/test/json/resumable_parser_test.rb +++ b/test/json/resumable_parser_test.rb @@ -247,6 +247,17 @@ def test_partial_value assert_partial_value([1, { "a" => 1, "b" => { "c" => nil } }], '[1, { "a": 1, "b": { "c"') end + def test_partial_value_collapses_nested_incomplete_containers + # partial_value rebuilds the open containers on a scratch value stack; folding + # an empty inner container pushes a value, so that stack must hold more than its + # live size or the push reallocates the scratch buffer. + assert_partial_value({ "abc" => {} }, '{"abc":{"d') + assert_partial_value({ "a" => { "b" => { "c" => {} } } }, '{"a":{"b":{"c":{"e') + assert_partial_value([1, { "a" => {} }], '[1,{"a":{"d') + assert_partial_value({ "a" => [1, { "b" => [2, { "c" => nil }] }] }, '{"a":[1,{"b":[2,{"c"') + assert_partial_value([1, [2, [3, { "x" => nil }]]], '[1,[2,[3,{"x":[') + end + def test_partial_value_issue_1005 data = <<~JSON [ From e6efa098a84dc8f6ee9fe6fd3673ff62f785ee46 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 15:34:40 +0900 Subject: [PATCH 07/11] [ruby/rubygems] Probe 127.0.0.1 instead of 0.0.0.0 in the mirror socket spec The TCP probe connects to the mirror URI host. connect(0.0.0.0) only behaves as loopback on Linux; on Windows it fails with an invalid address, so the example was skipped there. Probing 127.0.0.1 works on every platform. Co-Authored-By: Claude Opus 4.8 --- spec/bundler/bundler/mirror_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/bundler/bundler/mirror_spec.rb b/spec/bundler/bundler/mirror_spec.rb index ba1c6ed413daf8..34e61644fdf031 100644 --- a/spec/bundler/bundler/mirror_spec.rb +++ b/spec/bundler/bundler/mirror_spec.rb @@ -298,15 +298,13 @@ context "with a listening TCP Server" do def with_server_and_mirror - server = TCPServer.new("0.0.0.0", 0) - mirror = Bundler::Settings::Mirror.new("http://0.0.0.0:#{server.addr[1]}", 1) + server = TCPServer.new("127.0.0.1", 0) + mirror = Bundler::Settings::Mirror.new("http://127.0.0.1:#{server.addr[1]}", 1) yield server, mirror server.close unless server.closed? end it "probes the server correctly" do - skip "obscure error" if Gem.win_platform? - with_server_and_mirror do |server, mirror| expect(server.closed?).to be_falsey expect(probe.replies?(mirror)).to be_truthy From bf072c80e42546ef94bcf9b8a2affb77fa660541 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 15:34:40 +0900 Subject: [PATCH 08/11] [ruby/rubygems] Run test_validate_files wherever symlinks work The "is a symlink" validation warning is platform-independent, so the test only needs a working symlink rather than a blanket Windows skip. Gate it on symlink_supported? so it also runs on Windows with Developer Mode enabled. Co-Authored-By: Claude Opus 4.8 --- test/rubygems/test_gem_specification.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 79be0c996d2371..c63e68be47dd77 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -3159,14 +3159,14 @@ def test_validate_require_paths_with_invalid_types end def test_validate_files - pend "test_validate_files skipped on MS Windows (symlink)" if Gem.win_platform? + pend "Symlinks not supported or not enabled" unless symlink_supported? util_setup_validate @a1.files += ["lib", "lib2"] @a1.extensions << "ext/a/extconf.rb" Dir.chdir @tempdir do - FileUtils.ln_s "lib/code.rb", "lib2" unless vc_windows? + FileUtils.ln_s "lib/code.rb", "lib2" use_ui @ui do @a1.validate From fc15696c527fe996b95eba77843cba9c252809d2 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 15:34:40 +0900 Subject: [PATCH 09/11] [ruby/rubygems] Assert CacheFile.write preserves the read-only bit Windows cannot express 0o600 via chmod, but the read-only bit does survive the temp-file-and-rename. Assert that bit instead of the full mode so the permission-preservation test runs on every platform. Co-Authored-By: Claude Opus 4.8 --- test/rubygems/test_gem_compact_index_client_cache_file.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/rubygems/test_gem_compact_index_client_cache_file.rb b/test/rubygems/test_gem_compact_index_client_cache_file.rb index c782f6336165ee..193ae327e9c24b 100644 --- a/test/rubygems/test_gem_compact_index_client_cache_file.rb +++ b/test/rubygems/test_gem_compact_index_client_cache_file.rb @@ -123,13 +123,12 @@ def test_commit_after_close_raises end def test_write_preserves_permissions - pend "chmod is unreliable on Windows" if Gem.win_platform? - @path.binwrite "old" - @path.chmod 0o600 + @path.chmod 0o400 CacheFile.write(@path, "new") - assert_equal 0o600, @path.stat.mode & 0o777 + assert_equal "new", @path.binread + assert_equal 0, @path.stat.mode & 0o200, "expected CacheFile.write to preserve the original read-only permission" end end From 373f873c32f74c432e2d66e1b5909027172f7a2d Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 30 Jun 2026 15:34:40 +0900 Subject: [PATCH 10/11] [ruby/rubygems] Stop skipping the branch~num git ref spec on Windows The "maybe branch~num notation doesn't work" skip was never confirmed. main~2 ancestry refs resolve fine on current Windows git, so the example runs and passes there. Co-Authored-By: Claude Opus 4.8 --- spec/bundler/install/git_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb index 1172d661ae69e2..57c4743e85f9e9 100644 --- a/spec/bundler/install/git_spec.rb +++ b/spec/bundler/install/git_spec.rb @@ -40,8 +40,6 @@ end it "displays the ref of the gem repository when using branch~num as a ref" do - skip "maybe branch~num notation doesn't work on Windows' git" if Gem.win_platform? - build_git "foo", "1.0", path: lib_path("foo") rev = revision_for(lib_path("foo"))[0..6] update_git "foo", "2.0", path: lib_path("foo"), gemspec: true From 8927a8c33a7a118a7c96550d2a377588b482e92e Mon Sep 17 00:00:00 2001 From: git Date: Tue, 30 Jun 2026 08:02:46 +0000 Subject: [PATCH 11/11] [DOC] Update bundled gems list at 373f873c32f74c432e2d66e1b59090 --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 004e151df07bd9..3ba44ddeb87e75 100644 --- a/NEWS.md +++ b/NEWS.md @@ -131,7 +131,7 @@ releases. * pstore 0.2.1 * 0.2.0 to [v0.2.1][pstore-v0.2.1] * rdoc 8.0.0 - * 7.0.3 to [v7.0.4][rdoc-v7.0.4], [v7.1.0][rdoc-v7.1.0], [v7.2.0][rdoc-v7.2.0] + * 7.0.3 to [v7.0.4][rdoc-v7.0.4], [v7.1.0][rdoc-v7.1.0], [v7.2.0][rdoc-v7.2.0], [v8.0.0][rdoc-v8.0.0] * win32ole 1.9.3 * 1.9.2 to [v1.9.3][win32ole-v1.9.3] * irb 1.18.0 @@ -286,6 +286,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [rdoc-v7.0.4]: https://github.com/ruby/rdoc/releases/tag/v7.0.4 [rdoc-v7.1.0]: https://github.com/ruby/rdoc/releases/tag/v7.1.0 [rdoc-v7.2.0]: https://github.com/ruby/rdoc/releases/tag/v7.2.0 +[rdoc-v8.0.0]: https://github.com/ruby/rdoc/releases/tag/v8.0.0 [win32ole-v1.9.3]: https://github.com/ruby/win32ole/releases/tag/v1.9.3 [irb-v1.17.0]: https://github.com/ruby/irb/releases/tag/v1.17.0 [irb-v1.18.0]: https://github.com/ruby/irb/releases/tag/v1.18.0