From 535f9b43ba813520db6221661b06168a201a2842 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 6 Aug 2026 10:08:35 +0900 Subject: [PATCH 1/2] Return nil for empty integer captures JRuby must match the native implementation when captures become empty. --- lib/strscan/strscan.rb | 3 ++- test/strscan/test_stringscanner.rb | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/strscan/strscan.rb b/lib/strscan/strscan.rb index 5e262f4007..8836eea1d1 100644 --- a/lib/strscan/strscan.rb +++ b/lib/strscan/strscan.rb @@ -3,7 +3,8 @@ class StringScanner unless method_defined?(:integer_at) # For JRuby def integer_at(specifier, *to_i_args) - self[specifier]&.to_i(*to_i_args) + value = self[specifier] + value.to_i(*to_i_args) unless value.nil? || value.empty? end end diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb index 966d62b226..df5aa089ff 100644 --- a/test/strscan/test_stringscanner.rb +++ b/test/strscan/test_stringscanner.rb @@ -578,6 +578,13 @@ def test_integer_at_base_auto assert_integer_at(s, 0, 0) # 0xaf end + def test_integer_at_empty + s = create_string_scanner("") + assert_equal("", s.scan(/()/)) + assert_nil(s.integer_at(0)) + assert_nil(s.integer_at(1)) + end + def test_integer_at_shrunk omit("not supported on TruffleRuby") if RUBY_ENGINE == "truffleruby" From 435095d898f826b27175c3cae274b65d50ea23aa Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 6 Aug 2026 10:20:28 +0900 Subject: [PATCH 2/2] Handle shrunk captures on TruffleRuby `integer_at` must read captures from the scanner current string. --- lib/strscan/truffleruby.rb | 10 +++++++++- test/strscan/test_stringscanner.rb | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/strscan/truffleruby.rb b/lib/strscan/truffleruby.rb index 0d98242779..c95f2d0987 100644 --- a/lib/strscan/truffleruby.rb +++ b/lib/strscan/truffleruby.rb @@ -133,7 +133,15 @@ def [](group) end end - def integer_at(group, *to_i_args) = self[group]&.to_i(*to_i_args) + def integer_at(group, *to_i_args) + return if self[group].nil? + + group += @last_match.size if Primitive.is_a?(group, Integer) && group < 0 + from = @last_match.bytebegin(group) + to = [@last_match.byteend(group), @string.bytesize].min + value = @string.byteslice(from, to - from) + value.to_i(*to_i_args) unless value.nil? || value.empty? + end def values_at(*groups) = @last_match&.values_at(*groups) diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb index df5aa089ff..47b1e2a155 100644 --- a/test/strscan/test_stringscanner.rb +++ b/test/strscan/test_stringscanner.rb @@ -586,8 +586,6 @@ def test_integer_at_empty end def test_integer_at_shrunk - omit("not supported on TruffleRuby") if RUBY_ENGINE == "truffleruby" - s = create_string_scanner(+"before 29 after") s.skip_until(" ") assert_equal("29", s.scan(/\d+/)) @@ -596,8 +594,6 @@ def test_integer_at_shrunk end def test_integer_at_shrunk_partial - omit("not supported on TruffleRuby") if RUBY_ENGINE == "truffleruby" - s = create_string_scanner(+"before 29 after") s.skip_until(" ") assert_equal("29", s.scan(/\d+/))