diff --git a/compile.c b/compile.c index ac1bdfe0f9278b..68cf715c4efb5d 100644 --- a/compile.c +++ b/compile.c @@ -7067,7 +7067,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod DECL_ANCHOR(body_seq); DECL_ANCHOR(cond_seq); int only_special_literals = 1; - VALUE literals = rb_hash_new(); + VALUE literals = rb_hash_new_with_size_and_type(0, 0, &cdhash_type); int line; enum node_type type; const NODE *line_node; @@ -7078,8 +7078,6 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod INIT_ANCHOR(body_seq); INIT_ANCHOR(cond_seq); - RHASH_TBL_RAW(literals)->type = &cdhash_type; - CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head)); branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); @@ -7165,7 +7163,6 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) { ADD_INSN(ret, orig_node, dup); - rb_obj_hide(literals); ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel); RB_OBJ_WRITTEN(iseq, Qundef, literals); LABEL_REF(elselabel); @@ -12167,9 +12164,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, case TS_CDHASH: { int i; - VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2); + VALUE map = rb_hash_new_with_size_and_type(0, RARRAY_LEN(op)/2, &cdhash_type); - RHASH_TBL_RAW(map)->type = &cdhash_type; op = rb_to_array_type(op); for (i=0; itype = &cdhash_type; - rb_hash_rehash(v); // hash function changed - RB_OBJ_SET_SHAREABLE(freeze_hide_obj(v)); + VALUE src = ibf_load_object(load, op); + VALUE v = rb_hash_new_with_size_and_type(0, RHASH_SIZE(src), &cdhash_type); + rb_hash_rehash(cdhash_copy(v, src)); + RB_OBJ_SET_SHAREABLE(v); // Overwrite the existing hash in the object list. This // is to keep the object alive during load time. diff --git a/hash.c b/hash.c index 8507c9c8f5328d..751d4564d5749e 100644 --- a/hash.c +++ b/hash.c @@ -1490,6 +1490,14 @@ rb_hash_new_with_size(st_index_t size) return ret; } +VALUE +rb_hash_new_with_size_and_type(VALUE klass, st_index_t size, const struct st_hash_type *type) +{ + VALUE ret = hash_alloc_flags(klass, 0, Qnil, true); + hash_st_table_init(ret, type, size); + return ret; +} + VALUE rb_hash_new_capa(long capa) { diff --git a/internal/hash.h b/internal/hash.h index 9688478d1ee661..3a5be14ad76ef3 100644 --- a/internal/hash.h +++ b/internal/hash.h @@ -112,6 +112,7 @@ int rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t RUBY_SYMBOL_EXPORT_END VALUE rb_hash_new_with_size(st_index_t size); +VALUE rb_hash_new_with_size_and_type(VALUE klass, st_index_t size, const struct st_hash_type *type); VALUE rb_hash_resurrect(VALUE hash); int rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval); VALUE rb_hash_keys(VALUE hash); diff --git a/internal/variable.h b/internal/variable.h index ca5e189c904fe3..f24486be0d12a3 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 563b1e988b3169..55a9cb4c88d237 100644 --- a/re.c +++ b/re.c @@ -4900,6 +4900,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 c6a2c5423ea9d8..e4afb8fcc02a03 100644 --- a/test/ruby/test_box.rb +++ b/test/ruby/test_box.rb @@ -563,6 +563,26 @@ def test_global_variables 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; + /(?foo)/ =~ 'bar' + /(?baz)/ =~ 'baz' + assert_equal "baz", b + assert_equal "baz", $~.to_s + + /foo/ =~ 'bar' + assert_nil $~ + /(?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 0eb10e69120230..274964c595e0dd 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);