summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSatoshi Tagomori <s-tagomori@sakura.ad.jp>2025-12-30 14:46:19 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2026-01-05 13:00:14 -0800
commitb3371c6ae5dd6fcecd12128f7b3e1e18e219bd3d (patch)
treec89fcfdbeb0462cca26cd291809beaecb96bc022
parent35c140f92be2c0676c6433e037996926aa935982 (diff)
Box: allocate classes as boxable when it happens in the root box
Without this change, classes (including iclass) are allocated as un-boxable classes after initializing user boxes (after starting script evaluation). Under this situation, iclasses are created as un-boxabled class when core modules are included by a class in the root box, then it causes problems because it's in the root box but it can't have multiple classexts. This change makes it possible to allocate boxable classes even after initializing user boxes. Classes create in the root box will be boxable, and those can have 2 or more classexts.
-rw-r--r--class.c4
-rw-r--r--test/ruby/test_box.rb16
2 files changed, 19 insertions, 1 deletions
diff --git a/class.c b/class.c
index 840bdeb0c2..8c773125e1 100644
--- a/class.c
+++ b/class.c
@@ -491,6 +491,7 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_box_t *bo
while (subclass_entry) {
if (subclass_entry->klass && RB_TYPE_P(subclass_entry->klass, T_ICLASS)) {
iclass = subclass_entry->klass;
+ VM_ASSERT(RB_TYPE_P(iclass, T_ICLASS));
if (RBASIC_CLASS(iclass) == klass) {
// Is the subclass an ICLASS including this module into another class
// If so we need to re-associate it under our box with the new ext
@@ -819,7 +820,8 @@ class_alloc0(enum ruby_value_type type, VALUE klass, bool boxable)
static VALUE
class_alloc(enum ruby_value_type type, VALUE klass)
{
- return class_alloc0(type, klass, false);
+ bool boxable = BOX_ROOT_P(rb_current_box());
+ return class_alloc0(type, klass, boxable);
}
static VALUE
diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb
index a531afa679..cfd21ac0aa 100644
--- a/test/ruby/test_box.rb
+++ b/test/ruby/test_box.rb
@@ -854,4 +854,20 @@ class TestBox < Test::Unit::TestCase
assert_empty(Dir.children(tmpdir))
end
end
+
+ def test_root_box_iclasses_should_be_boxable
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ Ruby::Box.root.eval("class IMath; include Math; end") # (*)
+ module Math
+ def foo = :foo
+ end
+ # This test crashes here if iclasses (created at the line (*) is not boxable)
+ class IMath2; include Math; end
+ assert_equal :foo, IMath2.new.foo
+ assert_raise NoMethodError do
+ Ruby::Box.root.eval("IMath.new.foo")
+ end
+ end;
+ end
end