summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2026-05-11 13:43:47 -0700
committerTakashi Kokubun <takashikkbn@gmail.com>2026-05-11 13:43:47 -0700
commitdd78605b2d06600750c331f307083d60df702814 (patch)
treeb22fa335ca0ce8b83e9befebedb25fde5234817e
parent926b0ec4ed0e181735f3778c3e6b79d891e70848 (diff)
merge revision(s) 526344b56ea968d5704bdefe6e10bb3cf7f4f569, 8ad6baa01746e8de0460f0ccdaee69953a70af17: [Backport #21933]
[PATCH] Fix Box regexp match vars after non-match [PATCH] Use box_ready for $&, $`, $\', $+ These variables have rb_gvar_readonly_setter, so box_ready is sufficient. Only $~ needs box_dynamic due to its custom match_setter.
-rw-r--r--internal/variable.h1
-rw-r--r--re.c5
-rw-r--r--test/ruby/test_box.rb20
-rw-r--r--variable.c23
4 files changed, 44 insertions, 5 deletions
diff --git a/internal/variable.h b/internal/variable.h
index ca5e189c90..f24486be0d 100644
--- a/internal/variable.h
+++ b/internal/variable.h
@@ -29,6 +29,7 @@ rb_gvar_setter_t *rb_gvar_setter_function_of(ID);
void rb_gvar_readonly_setter(VALUE v, ID id, VALUE *_);
void rb_gvar_ractor_local(const char *name);
void rb_gvar_box_ready(const char *name);
+void rb_gvar_box_dynamic(const char *name);
/**
* Sets the name of a module.
diff --git a/re.c b/re.c
index e4d30d2939..3e199bb955 100644
--- a/re.c
+++ b/re.c
@@ -4819,6 +4819,11 @@ Init_Regexp(void)
rb_gvar_ractor_local("$`");
rb_gvar_ractor_local("$'");
rb_gvar_ractor_local("$+");
+ rb_gvar_box_dynamic("$~");
+ rb_gvar_box_ready("$&");
+ rb_gvar_box_ready("$`");
+ rb_gvar_box_ready("$'");
+ rb_gvar_box_ready("$+");
rb_define_virtual_variable("$=", ignorecase_getter, ignorecase_setter);
diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb
index 43a753b311..1e162279d4 100644
--- a/test/ruby/test_box.rb
+++ b/test/ruby/test_box.rb
@@ -531,6 +531,26 @@ class TestBox < Test::Unit::TestCase
end
end
+ def test_match_variables_are_not_cached_in_box
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ /(?<a>foo)/ =~ 'bar'
+ /(?<b>baz)/ =~ 'baz'
+ assert_equal "baz", b
+ assert_equal "baz", $~.to_s
+
+ /foo/ =~ 'bar'
+ assert_nil $~
+ /(?<word>foo)(bar)?/ =~ 'foo'
+ assert_equal "foo", word
+ assert_equal "foo", $~.to_s
+ assert_equal "foo", $&
+ assert_equal "", $`
+ assert_equal "", $'
+ assert_equal "foo", $+
+ end;
+ end
+
def test_load_path_and_loaded_features
setup_box
diff --git a/variable.c b/variable.c
index 11b186063e..04e2eaea52 100644
--- a/variable.c
+++ b/variable.c
@@ -534,6 +534,7 @@ struct rb_global_variable {
rb_gvar_compact_t *compactor;
struct trace_var *trace;
bool box_ready;
+ bool box_dynamic;
};
struct rb_global_entry {
@@ -618,6 +619,13 @@ rb_gvar_box_ready(const char *name)
entry->var->box_ready = true;
}
+void
+rb_gvar_box_dynamic(const char *name)
+{
+ struct rb_global_entry *entry = rb_find_global_entry(rb_intern(name));
+ entry->var->box_dynamic = true;
+}
+
static void
rb_gvar_undef_compactor(void *var)
{
@@ -646,6 +654,7 @@ rb_global_entry(ID id)
var->block_trace = 0;
var->trace = 0;
var->box_ready = false;
+ var->box_dynamic = false;
rb_id_table_insert(rb_global_tbl, id, (VALUE)entry);
}
}
@@ -1000,9 +1009,13 @@ rb_gvar_set_entry(struct rb_global_entry *entry, VALUE val)
return val;
}
-#define USE_BOX_GVAR_TBL(ns,entry) \
- (BOX_USER_P(ns) && \
- (!entry || !entry->var->box_ready || entry->var->setter != rb_gvar_readonly_setter))
+static inline bool
+gvar_use_box_tbl(const rb_box_t *box, const struct rb_global_entry *entry)
+{
+ return BOX_USER_P(box) &&
+ !entry->var->box_dynamic &&
+ (!entry->var->box_ready || entry->var->setter != rb_gvar_readonly_setter);
+}
VALUE
rb_gvar_set(ID id, VALUE val)
@@ -1015,7 +1028,7 @@ rb_gvar_set(ID id, VALUE val)
RB_VM_LOCKING() {
entry = rb_global_entry(id);
- if (USE_BOX_GVAR_TBL(box, entry)) {
+ if (gvar_use_box_tbl(box, entry)) {
use_box_tbl = true;
rb_hash_aset(box->gvar_tbl, rb_id2sym(entry->id), val);
retval = val;
@@ -1048,7 +1061,7 @@ rb_gvar_get(ID id)
entry = rb_global_entry(id);
var = entry->var;
- if (USE_BOX_GVAR_TBL(box, entry)) {
+ if (gvar_use_box_tbl(box, entry)) {
use_box_tbl = true;
gvars = box->gvar_tbl;
key = rb_id2sym(entry->id);