From 9d8d41006b6c7aa3dede76fbdad3884254e86bdd Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Mon, 11 May 2026 12:45:43 +0900 Subject: [PATCH] mruby-regexp: alias original String#split before overriding it `class String; def split` in mruby-regexp/mrblib/string_regexp.rb replaced the C-defined String#split rather than overriding it, so the in-Ruby `return super if pattern.nil?` paths raised NoMethodError for any `"x".split(...)` call once mruby-regexp was loaded (the default full-core production binary). The regression wasn't caught by the test suite because per-gem tests run under mrb_open_core() with only the gem's dep_list, so the broken override is never visible from test/t/string.rb (mruby-test, not mruby-regexp). Add `alias __split split` at the top of the override class body, which captures the C-defined method, and change the Ruby override to delegate via `__split(pattern, limit)` for non-regexp fallback paths. Add a bintest under mruby-bin-mruby that runs through bin/mruby (full gem load) to catch this regression class. Co-authored-by: Claude --- mrbgems/mruby-bin-mruby/bintest/mruby.rb | 14 ++++++++++++++ mrbgems/mruby-regexp/mrblib/string_regexp.rb | 13 +++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index 27283d3d9..0301871d0 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -176,3 +176,17 @@ assert('top level local variables are in file scope') do assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-r", crb.path, drb.path]) assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-b", "-r", cmrb.path, dmrb.path]) end + +assert('String#split still works when mruby-regexp is loaded') do + # The regexp-aware override in mruby-regexp/mrblib/string_regexp.rb used to + # replace the C-defined String#split, leaving its `return super if ...` + # fast paths with no method to delegate to (NoMethodError). Now the + # override delegates via `__split`, an alias of the original C method + # installed in mrb_mruby_regexp_gem_init before mrblib runs. + assert_mruby(%Q(["a", "b", "c"]\n), "", true, + ["-e", 'p "a,b,c".split(",")']) + assert_mruby(%Q(["abc", "abc", "abc"]\n), "", true, + ["-e", 'p "abc abc abc".split']) + assert_mruby(%Q(["hello", "world"]\n), "", true, + ["-e", 'p "hello world".split(/\s+/)']) +end diff --git a/mrbgems/mruby-regexp/mrblib/string_regexp.rb b/mrbgems/mruby-regexp/mrblib/string_regexp.rb index f1f15f26a..e15e507a3 100644 --- a/mrbgems/mruby-regexp/mrblib/string_regexp.rb +++ b/mrbgems/mruby-regexp/mrblib/string_regexp.rb @@ -1,4 +1,9 @@ class String + # Capture the C-defined String#split under `__split` before the override + # below replaces it, so the override can delegate non-regexp patterns + # back to the core implementation. + alias __split split + def match(re, pos = 0) re = Regexp.new(re) if re.is_a?(String) re.match(self, pos) @@ -59,10 +64,14 @@ class String end end + # Regexp-aware split. Falls back to the C-defined split (aliased as + # `__split` in mrb_mruby_regexp_gem_init before this override loads) for + # nil or simple-string patterns; converts string-with-backslash to a + # Regexp and handles regexp patterns in Ruby. def split(pattern = nil, limit = -1) - return super if pattern.nil? + return __split(pattern, limit) if pattern.nil? if pattern.is_a?(String) - return super if pattern.length == 1 || !pattern.include?('\\') + return __split(pattern, limit) if pattern.length == 1 || !pattern.include?('\\') pattern = Regexp.new(Regexp.escape(pattern)) end result = []