668 Commits
Author SHA1 Message Date
Koichi SasadaandClaude Opus 5.5 a4131efb5a Prohibit copying a class/module created by another Ractor
Module#initialize_copy of a class/module that another Ractor owns used to
check that its constants and fields are all shareable, and copy it if so.
That check made the copying Ractor walk (and flag) the owner's objects,
and the fields were collected into a buffer and checked one by one, so a
later value sat only in that buffer, which the owner's GC cannot see.

Raise Ractor::IsolationError instead, whatever the class holds.  The
metaclass is checked as well, since its tables are copied too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 04:13:52 +09:00
Koichi SasadaandClaude Opus 5.5 5771bd7a39 Read another Ractor's constant entries under the VM lock
rb_const_remove deleted the entry from the constant table without the VM
lock and then freed it, while a Ractor that does not own the class looks
the table up under the VM lock but reads the entry after releasing it.
Reading a constant while its owner removes it could crash in the table
lookup or return garbage (an Integer, Float or false that was never
assigned).

Delete under the VM lock, and have the readers that may not own the class
copy the entry while holding it (rb_const_lookup_copy).  Class#dup and
the singleton class clone walk the original's table under the lock for
the same reason; the Class#dup check collects the candidates under the
lock and runs rb_ractor_shareable_p after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 05:49:20 +09:00
Burdette Lamar f4dac309f5 [DOC] Harmonize module doc 2026-10-19 08:29:39 +09:00
John Hawthorn f6963df7eb Remove code checking for uninitialized class 2026-10-13 17:02:46 -07:00
Yusuke EndohandClaude Opus 5.5 6d68304c38 Keep autoload registrations in the constant entries
An autoload registration has been kept in a per-module table under the
hidden instance variable __autoload__, in addition to the Qundef entry
in the constant table.  With Ruby::Box, the constant table of a class is
copied entry by entry for each box, but the instance variables are
copied shallowly, so all the boxes shared one autoload table.

Once an autoload registered in the master box (e.g., Ruby::Prism) is
resolved in one box, the registration was removed from the shared
table, while the boxes that have their own copy of the class kept their
Qundef entries.  There, the constant was neither defined nor
autoloadable, and referring to it raised NameError.  Also, autoloads of
the same name registered in two boxes overwrote each other.

Keep the registration in the Qundef constant entry instead (in a union
with the file, which is moved to the registration), so each box has its
own registration with its own constant table, and the two never get out
of sync.

As a dup'ed module now shares the registration (instead of the table),
check that the mutex is locked to see if someone else is loading it, so
that the registration is not considered available after the feature is
loaded.

[Bug #22305]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:53:36 +09:00
Peter Zhu 4fb7434598 Fix assertion error when duplicating uninitialized class
If the class is uninitialized, Class#dup will set the duplicated class to
initialized.

The following code will cause an assertion error:

    Class.allocate.dup.new

Assertion error is:

    vm_method.c:1861: Assertion Failed: rb_get_alloc_func:allocator
    ruby 4.1.0dev (2026-10-08T09:57:55Z master 95db6abe45) +PRISM [arm64-darwin27]

    -- Crash Report log information --------------------------------------------
      See Crash Report log file in one of the following locations:
        * ~/Library/Logs/DiagnosticReports
        * /Library/Logs/DiagnosticReports
      for more details.
    Don't forget to include the above Crash Report log file in bug reports.

    -- Control frame information -----------------------------------------------
    c:0002 p:0011 s:0008 e:000005 l:n b:---- r:0x0 EVAL   test.rb:1 [FINISH]
    c:0001 p:0000 s:0003 E:002568 l:y b:---- r:0x0 DUMMY  [FINISH]

    -- Ruby level backtrace information ----------------------------------------
    test.rb:1:in '<main>'

    -- Threading information ---------------------------------------------------
    Total ractor count: 1
    Ruby thread count for this ractor: 1

    -- C level backtrace information -------------------------------------------
    miniruby(rb_assert_failure_detail+0xf0) [0x100d5f2a4] error.c:1252
    miniruby(rb_assert_failure_detail) (null):0
    miniruby(rb_assert_failure_detail+0x0) [0x100d5f1b4] error.c:1228
    miniruby(rb_assert_failure) (null):0
    miniruby(rb_get_alloc_func.cold.1+0x28) [0x100d6e660] vm_method.c:1861
    miniruby(rb_get_alloc_func.cold.3) (null):0
    miniruby(rb_type+0x0) [0x100ab75c0] vm_method.c:1861
    miniruby(rb_get_alloc_func) (null):0
    miniruby(rb_class_alloc+0x48) [0x10095e210] object.c:2286
    miniruby(vm_exec_core+0x469c) [0x100aae39c] insns.def:936
    miniruby(vm_exec_loop+0x0) [0x100aa74e4] vm.c:2909
    miniruby(rb_vm_exec) vm.c:2912
    miniruby(rb_ec_exec_node+0xa0) [0x1008b4460] eval.c:300
    miniruby(ruby_run_node+0x64) [0x1008b4364] eval.c:338
    miniruby(rb_main+0x1c) [0x1007e89b0] main.c:42
    miniruby(main) main.c:62
2026-10-09 14:32:41 +09:00
Hiroshi SHIBATAandClaude Opus 5 975c2a45f1 Use rb_len_t for Array and String lengths in the VM
Argument counts stay `int` or `long`. The int range checks in proc.c and compile.c now depend on SIZEOF_VALUE so that they are kept once rb_len_t widens.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 14:00:15 +09:00
Peter Zhu 004f523bbd Fix assertion failure in allocating instance of uninitialized class
If a class is allocated and has a singleton class, it is incorrectly marked
as initialized which can cause an assertion error when an instance is
allocated.

For example, the following code causes an assertion error:

    c = Class.allocate
    c.singleton_class
    c.new

Crashes with:

    vm_method.c:1861: Assertion Failed: rb_get_alloc_func:allocator
    ruby 4.1.0dev (2026-10-07T14:31:40Z io-uninitialized-e.. 67f4ea55a3) +PRISM [arm64-darwin27]

    -- Crash Report log information --------------------------------------------
      See Crash Report log file in one of the following locations:
        * ~/Library/Logs/DiagnosticReports
        * /Library/Logs/DiagnosticReports
      for more details.
    Don't forget to include the above Crash Report log file in bug reports.

    -- Control frame information -----------------------------------------------
    c:0002 p:0017 s:0009 E:000780 l:n b:---- r:0x0 EVAL   test.rb:3 [FINISH]
    c:0001 p:0000 s:0003 E:000128 l:y b:---- r:0x0 DUMMY  [FINISH]

    -- Ruby level backtrace information ----------------------------------------
    test.rb:3:in '<main>'

    -- Threading information ---------------------------------------------------
    Total ractor count: 1
    Ruby thread count for this ractor: 1

    -- C level backtrace information -------------------------------------------
    miniruby(rb_assert_failure_detail+0xf0) [0x102b57024] error.c:1268
    miniruby(rb_assert_failure_detail) (null):0
    miniruby(rb_assert_failure_detail+0x0) [0x102b56f34] error.c:1244
    miniruby(rb_assert_failure) (null):0
    miniruby(rb_get_alloc_func.cold.1+0x28) [0x102b663e0] vm_method.c:1861
    miniruby(rb_get_alloc_func.cold.3) (null):0
    miniruby(rb_type+0x0) [0x1028af338] vm_method.c:1861
    miniruby(rb_get_alloc_func) (null):0
    miniruby(rb_class_alloc+0x48) [0x102756210] object.c:2285
    miniruby(vm_exec_core+0x469c) [0x1028a6114] insns.def:936
    miniruby(vm_exec_loop+0x0) [0x10289f25c] vm.c:2909
    miniruby(rb_vm_exec) vm.c:2912
    miniruby(rb_ec_exec_node+0xa0) [0x1026ac450] eval.c:300
    miniruby(ruby_run_node+0x64) [0x1026ac354] eval.c:338
    miniruby(rb_main+0x1c) [0x1025e09b0] main.c:42
    miniruby(main) main.c:62
2026-10-08 17:41:51 +09:00
Luke Gruber bec5e51c85 Fix Module/Class#dup of frozen object
This code should not raise an error:
```
@var = Class.new.freeze.dup # unfrozen copy
@var.instance_variable_set(:@test, 123)
```

It was raising `Ractor::IsolationError` because of a bug in `rb_fields_tbl_copy`.

This patch also fixes the following assertion failure in debug mode:

```
Module.new.freeze.singleton_class.instance_variable_set("@a", 1)
```

Now we get the proper behavior:

```
can't modify frozen Class: #<Class:#<Module:0x00000001094d9900>> (FrozenError)
```

Fixes [Bug #22378]
2026-09-25 14:53:06 -04:00
Hiroshi SHIBATAandClaude Opus 5 44fff96b0b Attach a box classext to its class before filling it in
rb_class_duplicate_classext builds the whole copy first and the caller only
then puts it in the class's table, so a GC in the middle collects the tables
it has made and marking the class afterwards walks into a freed object.  Put
the classext in the table first instead.  Marking it while it is still half
built is fine, since ZALLOC leaves every field zero.

    $ RUBY_BOX=1 ruby --disable-gems -e 'GC.stress = true; module Enumerable; def m; end; end'
    -e:1: [BUG] try to mark T_NONE object (obj: out-of-heap:0x..., parent: ... T_MODULE/Module Enumerable)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 16:29:51 +09:00
Nobuyoshi Nakada 92cef2802c Mark rb_freeze_singleton_class as internal
`rb_freeze_singleton_class` is an implementation detail of
`RB_OBJ_FREEZE`, so deprecate direct calls from extension libraries
before removing its public declaration in Ruby 4.2.
2026-09-19 18:51:40 +09:00
Koichi SasadaandClaude Fable 5 f5a57659d6 Ractor: move a singleton class's ownership with its object
A singleton class belongs to the owner of the object it is attached to, and
Ractor#send(move: true) hands that object over.  Without this the receiver could
define singleton methods on a moved object only when its singleton class had not
been materialized before the move.  The eigenclass chain above it goes too: each
of those is attached to the class below it, so by the same rule they follow.

The move is refused when any of them holds unshareable constants or variables:
the receiver would become their owner, and so allowed to read them, while the
sender still holds the objects themselves.  The check runs in the preflight pass,
before anything is mutated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:02:03 +09:00
Koichi SasadaandClaude Fable 5 3cb67a2072 Ractor: check unshareable leaks when copying a foreign class
Module#initialize_copy (Class#dup etc.) of a class/module created by another
Ractor makes a copy owned by the copying Ractor.  Since an owner may read the
unshareable constant and instance variable values of its own classes, a raw
table copy would hand the copier unshareable objects belonging to the source's
Ractor.  Raise Ractor::IsolationError in that case.

The metaclass is checked as well: rb_singleton_class_clone_and_attach copies its
fields and constants into the copy's own metaclass, which the copier then owns.

Copying classes whose constants and fields are all shareable still works, which
gives a Ractor a way to "monkey-patch" a foreign class without mutating it: dup
it and modify the copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:02:03 +09:00
Koichi SasadaandClaude Fable 5 3f2e84dd36 Ractor: record the creator Ractor of classes/modules
Every class/module records the id of the Ractor that created it in
rb_classext_t::owner_ractor_id; only that Ractor may modify it.  0 means the
main Ractor, so a program that never leaves it stores nothing.

An id rather than the Ractor object: ids come from a monotonic counter and are
never reused, so the comparison can never meet a dangling or recycled identity,
there is nothing for the GC to trace or move, and a class does not keep its
creator alive.  It also gives the termination rule for free -- once the owner
is gone no live Ractor carries its id, so no comparison can succeed and the
class is read-only for everybody.

Singleton classes and metaclasses inherit the owner of the attached
class/module instead of the Ractor which happened to trigger their lazy
creation.

rb_class_owned_p() is inline: later commits put it on the class instance
variable and constant read paths, where it replaces what used to be a single
load of ruby_single_main_ractor.  Only the non-main-owner half is out of line.
That needs rb_ractor_main_p() in internal/class.h, hence the include guard
ractor_core.h was missing.

This commit only records the ownership and introduces the
rb_class_owned_p()/rb_class_owner_check() helpers; no behavior changes yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:02:03 +09:00
Koichi SasadaandClaude Opus 5 17baadc072 Make Class#attached_object respect Ractor isolation
A singleton class is shareable whatever it is attached to, so it can be sent
to another Ractor, and #attached_object then handed over the object itself:

    $ ruby -W0 -e 'Ractor.new(p(Object.new).singleton_class) { |o| p o.attached_object }.join'
    #<Object:0x0000000120768c40>
    #<Object:0x0000000120768c40>       # the same object in both Ractors

Raise Ractor::IsolationError when the attached object is unshareable and
lives in another Ractor's objspace.  Guarding the reader covers every way a
singleton class is made: Object#clone (rb_singleton_class_clone_and_attach)
and the metaclass of a singleton class (make_metaclass) allocate one without
going through make_singleton_class, so making singleton classes unshareable
instead would have to catch all of them.

An object's owner is known from the objspace of its page, so a single-objspace
GC (mmtk) cannot make this check; the test is skipped there.

[Bug #21649]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:38:20 +09:00
Luke Gruber 3a0ec78790 Don't create CCs for hook methods called from C
Calling these hook methods shouldn't create CCs because these are not hot methods.
These CCs were only looked up in the gccct table (global call cache lookup table)
for rb_funcall and friends.

For one of our apps after bootup we reduced CCs significantly:

|                       | A control  | B (your commit) | B − A                 |
|-----------------------|------------|-----------------|-----------------------|
| callcaches (after GC) | 705,957    | 651,747         | −54,210 (−7.7%)       |
| cc tables             | 46,361     | 43,051          | −3,310                |
| total cc memory       | 86,441,345 | 79,823,142      | −6,618,203 (−6.3 MB)  |
2026-09-04 10:33:08 -04:00
Hiroshi SHIBATAandClaude Fable 5 fdd520806d Fix prepend ancestry order in user boxes
ensure_origin read the m_tbl before the first write created the box-local classext, so the origin ICLASS captured the prime m_tbl while the subclass ICLASSes referred the box-local copy made by the CoW. rb_prepend_module then missed the shared m_tbl check and inserted the prepended module after the target module instead of before it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 08:18:30 +09:00
Matt Valentine-House c59c4d717a Fix stale super cache on prepend after include
Prepending a module to a module that already has includers backfills an
origin iclass into each includer's ancestor chain using rb_prepend_module.

The backfilled iclass is never added to a subclasses list, so
rb_clear_method_cache can't reach it when one of the module's methods is
later redefined.

Calling super through a call site that has cached the backfilled iclass
calls the old entry, even though it should be redefined.

```ruby
module M; def foo; :m; end; end
class D; include M; end
M.prepend(Module.new { def foo; super; end })
D.new.foo                          # prime the super cache
M.send(:define_method, :foo) { :hooked }
D.new.foo                          # => got :m, expected :hooked
```

This commit makes sure that the backfilled iclass is registered in the
subclasses list.
2026-08-21 19:35:35 +01:00
Shugo MaedaandClaude Opus 5 de56b5e2f1 Make Module#descendants follow the ancestors of the current box
The subclasses lists are kept only in the prime classext and thus shared
by all boxes, so a module included into a builtin class in a box was
reported as a descendant from other boxes, where the include is not in
the ancestors of the class.

Check that the ICLASS is in the ancestors of its includer as seen from
the current box.  An include into a class that has no classext per box,
e.g. a class defined outside the box, stays visible because it is in the
ancestors from any box.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:56:38 +09:00
Shugo MaedaandClaude Fable 5 9658933451 [Feature #9779] Add Module#descendants
Module#descendants returns an array of classes and modules that have
the receiver in their ancestors; it is the inverse of Module#ancestors.
The receiver itself, singleton classes, and refinements are not included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 23:05:57 +09:00
Shugo MaedaandClaude Fable 5 ca7ec5a0c3 Register clones with their superclass's subclass list
Since a2531ba293, class_associate_super()
maintains subclass lists only for direct T_CLASS -> T_CLASS links. When
a class that includes or prepends a module is cloned, rb_mod_init_copy()
connects the clone (or its origin) to a T_ICLASS, so the clone was never
added to its real superclass's subclass list and Class#subclasses did
not return it.

Register the clone with its superclass explicitly after the chain is
built. rb_class_superclass() can be used because the superclass table
is already up to date at this point and the superclass always exists
(the root class cannot be cloned). Clones of classes without modules
are registered by class_associate_super() as before.

[Bug #22190]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:28:14 +09:00
Jean Boussier cd7e59d45b Improve inlinability of rb_define_class and rb_define_module
By moving the `rb_intern` to the caller, we allow compilers to
precompute the `strlen` part of `rb_intern` which isn't a huge win,
but happens quite a lot as part of Ruby's boot process and when
requiring native gems.
2026-06-19 17:49:17 +02:00
Jean Boussier 143f28bed5 class.c: refactor rb_define_module
Merge it with `rb_define_module_under` to reduce duplication.
2026-06-19 17:49:17 +02:00
Jean Boussier b5c53030cc class.c: refactor rb_define_class
Merge it with `rb_define_class_under` to reduce duplication.
2026-06-19 17:49:17 +02:00
Aaron Patterson c4d09f416c Reapply "Reserve 2 bits for expressing object layout (#17139)" (#17158)
This reverts commit ddb5055d96.
2026-06-01 19:30:58 +00:00
Aaron Patterson ddb5055d96 Revert "Reserve 2 bits for expressing object layout (#17139)"
This reverts commit 63d9f090b5.
2026-05-29 16:41:31 -07:00
63d9f090b5 Reserve 2 bits for expressing object layout (#17139)
* Reserve 2 bits for expressing object layout

We would like to make instance variable reads in the JIT compiler faster
(as well as simplify the JIT implementation).  Currently, in order to
read an instance variable, we have to:

1. Test for heap object
2. Load object to a 64 bit register
3. Mask the object header
4. Bit test against the masked header
5. JNE
6. Load field

We would like to:

1. Test for heap object
2. Load object shape to a 32 bit register
3. Bit test against the shape
4. JNE
5. Load field

The way we fetch instance variables is not consistent across objects.
In order to realize our goal, we need to encode object layout inside the
shape.  If we encode object layout inside the shape, then the shape
itself will guarantee that the access pattern generated by the JIT
compiler is correct.

We should encode the following load patterns into the shape tag bits.
This way we can share shapes on transitions, but be able to
differentiate the access patterns for the JIT compiler.  In other words,
two objects can have an `@a -> @b -> @c` transition and share the same
shape, but the tag bits can differentiate the access pattern so that the
JIT compiler can be confident that the machine code is correct.

Here are the patterns:

1. Embedded/Extended T_OBJECT Instance Variables

Objects with direct references to instance variables or via malloc
buffer

2. Objects with fields_objects fields

These are Data and TypedData objects.  They have an associated axillary
imemo/fields object that stores the instance variables.  The access
pattern is `object[2] + 2`.  The fields object is the 3rd field, and the
instance variables start at +2 inside the fields object.  The fields
object itself is a Ruby object, so it contains the usual header bits +
class headers.

3. Non Boxable Classes / Modules

This is similar to Objects with fields_objects, but the fields object is
stored at a different offset.  We’re differentiating this from boxable
classes and modules because those are harder to support.

4. Other

"Other" pattern is for objects that are rare, or have
difficult-to-implement access patterns.  This includes:

* Boxable classes and modules
* Structs (for now)
* Objects that use the geniv table

Proposed shape bit layout:

```
  Current shape_id_t is 32 bits:
  31        28 27 26 25 24 23 22        19 18                         0
  +-----------+--+--+--+--+--+------------+----------------------------+
  | unused    |L1|L0|OI|FR|CX| heap index | shape tree offset          |
  +-----------+--+--+--+--+--+------------+----------------------------+
               |  |  |  |  |  |            |
               |  |  |  |  |  |            +-- bits 0-18: SHAPE_ID_OFFSET_MASK
               |  |  |  |  |  +--------------- bits 19-22: SHAPE_ID_HEAP_INDEX_MASK
               |  |  |  |  +------------------ bit 23: SHAPE_ID_FL_COMPLEX
               |  |  |  +--------------------- bit 24: SHAPE_ID_FL_FROZEN
               |  |  +------------------------ bit 25: SHAPE_ID_FL_HAS_OBJECT_ID
               +--+--------------------------- bits 26-27: SHAPE_ID_LAYOUT_MASK
```

The important part about these layout patterns is that they do not
reflect the _type_ of object, only how the object is laid out in memory.
For example, we currently treat structs as "other", but we can refactor
them to have the same layout as "Objects with fields_objects", and when
we do that they should get a different bit in the shape header.

This commit only reserves the two bits, it doesn't use them in the JIT
compiler yet.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Max Bernstein <tekknolagi@gmail.com>

* Update gc.c

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>

* Update shape.h

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>

* fix function name

* Update shape.c

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>

* fix function name

* Revert "Update shape.c"

This reverts commit 900711defc6c541a93f3393a350819ae88cf87f1.

* add comment

---------

Co-authored-by: John Hawthorn <john@hawthorn.email>
Co-authored-by: Max Bernstein <tekknolagi@gmail.com>
Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2026-05-29 15:09:34 -07:00
John Hawthorn f71840a230 Replace subclasses linked list with weakref array 2026-05-13 18:36:51 -07:00
John Hawthorn 88095eb196 Remove CREF rewriting for cloned classes/modules
When a class or module was cloned, rb_vm_rewrite_cref would rewrite the
CREF chain of each cloned method to point at the new class. Remove this
special case so that clone behaves consistently with all other ways of
adopting a method.

This also removes the RCLASS_CLONED flag which was used to prevent
constant inline cache sharing between cloned classes [Bug #15877], and
the vm_cref_new_use_prev helper which only existed for CREF rewriting.

[Feature #21981]
2026-05-13 13:40:46 -07:00
Peter Zhu 24a1b097ce Don't use obj_id in Box classext_cow_classes
Using obj_id in classext_cow_classes is slow and also is a problem when
freeing classes because the lookup requires the EC, which is not available
if it is running on a GC thread. This will cause it to crash on MMTk.

We don't need to use the obj_id as the key in the classext_cow_classes
st_table because the class itself is pinned and won't move so we can use
it as the key instead.
2026-05-11 19:16:45 -04:00
Satoshi Tagomori 276f0d9b3e [Bug #21881] Separate the master and root box
This change separates the master box from the root box, as the single
master copy of boxes.
Before this change, the root box is the source of copies, and also
it runs builtin classes' code. Builtin code makes changes by
requiring files, thus the source of copies is mutable, be different
from the past one. This causes different internal states of boxes,
depending on when it is created.
After this change, all boxes are created from the master box, and
the master box never runs code, so it realizes the immutable source
of copies.

This also makes is possible to separate RubyGems (and other default
gems) from each boxes. All boxes will have their own copies of RubyGems.
RubyGems has its internal state, but it'll be separated between boxes
after this change.
2026-05-10 09:38:45 +09:00
Jean Boussier 45d586ad60 Never increment max_iv_count on Object / BasicObject.
Otherwise some code defining ivars on a naked object early during
program boot can cause all objects to be larger than needed.

This seem like it was always the intention, but wasn't quite
properly prevented.

Also stop updating `max_iv_count` during GC marking as it is
redundant.
2026-05-02 08:46:36 +02:00
Jean Boussier e4667e5109 Refactor NEWOB_OF for the common protected use case
Almost every objects are now WB protected, with just a few
exceptions, hence we can provide a much simpler interface.

It's also much easier to find the remaining unprotected objects.
2026-04-28 11:11:29 +09:00
Jean Boussier c202bc08e1 Simplify NEWOBJ_OF and introduce EC_NEWOBJ_OF
We only very rarely have the execution context available, so
it's preferable to provide a simpler default macro.
2026-04-28 11:11:29 +09:00
Jean Boussier ac0a319dd5 Get rid of RGENGC_WB_PROTECTED_CLASS
It has been the default for a very long time and there is no good reason to
disable it.
2026-04-28 09:39:07 +09:00
John Hawthorn 2c72910676 Fix writebarrier on rb_class_set_box_classext 2026-04-26 18:07:33 +09:00
John Hawthorn b90415b5d7 Issue writebarrier_remember after set const tbl 2026-04-26 18:07:33 +09:00
Jean Boussier 47e061277a class.c: Make cvc_tbl a managed object
[Bug #21952]

Solves the double-free or use after-free concern with boxes.
Now entries can safely be used for copy-on-write.

Also is likely necessary to make it save to read cvar from
secondary ractors, as allowed since: ab32c0e690
2026-04-21 16:30:14 +09:00
Jean Boussier c0d86a0103 class.c: rb_class_duplicate_classext also dup content of cvc_tbl
[Bug #21952]

Shallow copying the table result in the same memory being shared
between multiple box, causing double free when one of the box
is garbage collected.
2026-04-11 07:57:07 +02:00
John Hawthorn 202114608b Use RB_BUILTIN_TYPE in RCLASS_SINGLETON_P
This should avoid us emitting the check for a special constant.
2026-04-06 15:36:54 -07:00
John Hawthorn 3c251f79a3 Don't add singleton classes to subclasses list
Singleton classes don't need to be iterated in the subclasses list, we
only need to know that a subclass exists so that we can use the "method
entry" path for invalidation.
2026-03-30 18:26:58 -07:00
John Hawthorn 111215de27 Copy allocator to subclasses at boot and on change
Copy the allocator from superclass to subclass at class creation time,
so rb_get_alloc_func no longer needs to walk the ancestor chain.

This expands on 68ffc8db08, which did this
copy, but only for classes defined in Ruby (ex. `class Foo`). And allows
us to simple read the allocator field for any class to get the correct
value.

To keep this correct when rb_define_alloc_func is called after
subclasses already exist, propagate the new allocator down to all
subclasses. An RCLASS_ALLOCATOR_DEFINED flag distinguishes classes with
an explicitly set allocator from those that inherited one, so
propagation stops at subclasses that define their own.
2026-03-25 12:51:46 -07:00
John Hawthorn a2531ba293 Simplify subclasses list, remove from Box
Currently we maintain the subclasses list for two separate purposes (we essentially have to different relationships we're putting into the same list):

1. On a T_MODULE, we track the T_ICLASSes created to include it into
   other classes. Used for method invalidation and propagating includes
   on the module that happen after it's been used
2. On a T_CLASS/T_ICLASS, we track the T_CLASS/T_ICLASS which are the
   immediate children of the class. We use this for method invalidation,
   some cvar things, and to iterate through subclasses.

Purpose 1 does not have any issues with box, the T_ICLASS always belongs
to one specific module and that's immutable. This list can be box-global
(always use the prime classext or hoist it out) and only needs to be
pruned during free. If we care about behaviour under a particular box
(ie. the propagating includes), we should look up the current box being
modified on the ICLASS itself.

Purpose 2 is more complicated. It currently tracks the immediate
children, the T_CLASS or T_ICLASS whose super points back. Because super
is per-box and is mutable (include/prepend insert ICLASSes into the
chain) we need to update the list on include/prepend, entries must be
per-box, and we can have multiple entries per-box. *I propose we
simplify this by no longer tracking the immediate subclass*, but instead
tracking the T_CLASS -> ... -> T_CLASS relationship, ie. the inverse of
rb_class_superclass. That relationship is the same across all boxes and
immutable after Class creation.

As a special case the ICLASS for refinements are also added to the
purpose 2 list (on T_CLASS). As those ICLASS do not chain to an eventual
leaf T_CLASS.

When we need to find the classes which have included a module, we can
use the module subclasses list to find the ICLASS and then use
RCLASS_INCLUDER. If we needed to iterate all T_ICLASS, we could then
walk up the CLASS_SUPER chain, but I didn't find anywhere we needed to
do that.
2026-03-17 14:42:07 -07:00
Jeremy Evans 8e9eb698b6 Freeze singleton class chain
The following code:

```ruby
x = Object.new
sc1 = x.singleton_class
sc2 = sc1.singleton_class
x.freeze
```

Would freeze sc1 but not sc2, even though sc1 would be frozen.

Handle this by walking the class pointer chain for the object.
If the class is a singleton class, and it isn't frozen, and the
attached object for the singleton class is the object, the
singleton class should be frozen, and we move to the next iteration.

Fixes [Bug #20319]
2026-02-24 12:01:06 +09:00
Jean Boussier a2ca4aa9ae [DOC] Update incorrect class documentation
`RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE` doesn't exist, it's
`RCLASS_PRIME_CLASSEXT_WRITABLE`.
2026-02-02 17:17:06 +01:00
Jean Boussier a7d4a9df03 class.c: Replace ruby_xfree by ruby_sized_xfree when applicable 2026-01-31 10:35:48 +01:00
Satoshi Tagomori 966dbba8db Box: skip checking the current box is the root box
Because checking the current box is not a cheap process.
2025-12-30 22:31:51 +09:00
Satoshi Tagomori d5af8d7858 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.
2025-12-30 17:06:34 +09:00
Luke Gruber bfd28d581c make rb_singleton_class ractor safe (#15591)
Since singleton classes are created lazily, we need to make sure that
we lock around their creation.  Unfortunately, that means we need to
lock around every shareable object's call to `singleton_class`,
including classes and modules.
2025-12-18 12:37:27 -05:00
Luke Gruber de2c2bd60f Take VM lock in class_switch_superclass (#15356)
Safe multi-ractor subclass list mutation

We need to lock around mutation and accesses of a class's subclasses
list. Unfortunately we also need to do this when creating singleton
classes, as the singleton class does need to go into `super`'s
subclasses list for CC invalidation purposes.
2025-12-04 16:10:21 -05:00