mruby-regexp: convert a Bigint String#split limit - #7045
Conversation
`String#split` converted its `limit` only when the limit was not already an
Integer. A Bigint is an Integer, so it passed that check unconverted and the
regexp path ran the split loop with a limit that does not fit `mrb_int`. The
nil and String patterns delegate to `__split`, which takes an `mrb_int` and
raises. One argument, two answers, and the regexp one is neither CRuby's:
```ruby
big = 2 ** 70
"a,b,c".split(/,/, big) # mruby: ["a", "b", "c"] CRuby: RangeError
"a,b,c".split(",", big) # mruby: RangeError: integer out of range CRuby: RangeError
```
Convert every given limit through `Integer.__ensure`, which is
`mrb_ensure_int_type()`: it returns an Integer that fits `mrb_int` unchanged
and narrows a Bigint with `mrb_bint_as_int()`, raising `RangeError` when it
does not fit. Both paths now raise, as CRuby does.
The check existed so that an ordinary Integer limit skipped the conversion,
and it read the type with `Module#===` rather than the redefinable `is_a?` so
that a limit could not claim to be an Integer and skip it as well. Converting
unconditionally covers that case too, at the cost of one call for an Integer
limit that used to skip it.
A limit between `INT_MAX` and `MRB_INT_MAX` is still accepted, where CRuby
raises. That follows from `mrb_int` being the integer type of this
implementation and is left alone.
📝 WalkthroughWalkthrough
ChangesString split limit validation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
mrbgems/mruby-regexp/test/regexp.rb (1)
1169-1171: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the negative String-pattern assertion.
The test covers
limiton both pattern paths, but it covers-limitonly for the regexp path. Add the symmetric assertion to protect the String-pattern behavior.Proposed test
assert_raise(RangeError) { "a,b,c".split(/,/, -limit) } + assert_raise(RangeError) { "a,b,c".split(",", -limit) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@mrbgems/mruby-regexp/test/regexp.rb` around lines 1169 - 1171, Add a RangeError assertion beside the existing split limit tests in regexp.rb, covering `"a,b,c".split(",", -limit)` to mirror the negative-limit regexp assertion and verify String-pattern behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@mrbgems/mruby-regexp/test/regexp.rb`:
- Around line 1169-1171: Add a RangeError assertion beside the existing split
limit tests in regexp.rb, covering `"a,b,c".split(",", -limit)` to mirror the
negative-limit regexp assertion and verify String-pattern behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8383ebd0-2ea2-4259-802f-8ec46a185d2e
📒 Files selected for processing (2)
mrbgems/mruby-regexp/mrblib/string_regexp.rbmrbgems/mruby-regexp/test/regexp.rb
String#splitinmruby-regexpconverts itslimitin mrblib(
mrbgems/mruby-regexp/mrblib/string_regexp.rb:144-146),and the conversion is guarded so that it only runs for a limit that is not already an Integer:
A Bigint is a real
Integer, so it passes the guard and is never converted. The regexp paththen runs the split loop with it. The nil and String patterns do not: they delegate to the core
__split, which takes anmrb_intand raises.Two calls to one method with one argument, two answers, and the regexp one is not CRuby's.
mruby-bigintships inmath.gembox, whichdefault.gemboxincludes, andmruby-regexpcomes in through
stdlib.gembox, so a plain build reproduces this:This has the same shape as the
is_a?divergence #7004 closed, on the same argument, but theguard admits a Bigint under every spelling it has had:
limit.is_a?(Integer)andInteger === limitboth answer true for one.A Float limit is already right
Everything that is not an Integer goes through
Integer.__ensure, so an out-of-range Floatnever reaches the loop:
Both mruby paths agree with CRuby on the class here and differ only in wording. The Bigint is
the one case the guard lets through.
The fix
Integer.__ensureismrb_ensure_int_type()(
src/numeric.c:2331-2337,src/object.c:675-685),which returns an Integer that fits
mrb_intunchanged and narrows a Bigint throughmrb_bint_as_int(), raisingRangeErrorwhen it does not fit(
mrbgems/mruby-bigint/core/bigint.c:5506-5517).Both paths then raise, as CRuby does.
With the patch,
"a,b,c".split(/,/, 2 ** 70)and"a,b,c".split(",", 2 ** 70)both raiseRangeError: integer out of range, and a negative Bigint limit raises as well.The guard existed so that an ordinary Integer limit skipped the conversion, and it read the
type with
Module#===rather than the redefinableis_a?so that a limit could not claim tobe an Integer and skip it too. Converting unconditionally covers that case as well, so removing
the guard does not reopen #7004. The cost is one conversion call for an Integer limit that used
to skip it, and
mrb_ensure_int_type()returns such a limit unchanged.Tests
rake testcovered none of this: 1967 tests, 1949 OK, 0 failures both with and without thepatch. The
String#split with a Bigint limitassertions added tomrbgems/mruby-regexp/test/regexp.rbfail on master and pass with the fix, and the suite isthen 1968 tests, 1950 OK, 0 failures. They skip when the build has no
mruby-bigint, and theexponent is a variable because a constant power out of
mrb_intrange fails the build ratherthan raising.
Measured on
9360b3fd0against CRuby 4.0.6 on x86_64 Linux, wheremrb_intis 64 bits.Not addressed here
A limit that fits
mrb_intbut not a Cintis accepted by both mruby paths, where CRubyraises:
The two paths agree with each other there, and accepting a wider limit follows from
mrb_intbeing the integer type of this implementation. Narrowing it to
intis not proposed here.Summary by CodeRabbit
Bug Fixes
String#splithandling for explicitly provided limits, including large integer values.RangeErrorinstead of being processed incorrectly.Tests