Commit Graph
879 Commits
Author SHA1 Message Date
John Hawthorn 594ae9f073 Give legacy T_DATA the same layout as TypedData
This allows us to treat the two types of T_DATA the same way when
reading instance variables.

Non-TypedData T_DATA are deprecated, so making them faster isn't
particularly important, but it is helpful to avoid needing to test for
TypedData and adding another special case.
2026-05-27 16:18:43 -07:00
Peter Zhu 6bc1aa31cc [Bug #22072] Fix rb_cvar_set for multi-Ractor
rb_cvar_set breaks the RCLASS_CVC_TBL because it creates a new cvc_tbl but
forgets to copy the contents over. This causes the following script to
crash:

    r = Ractor.new {}

    module Foo
      def self.foo = @@foo
    end

    Foo.class_variable_set(:@@foo, nil)
    Foo.class_variable_set(:@@bar, nil)

    puts Foo.foo

Crashes with:

    test.rb:4: [BUG] should have cvar cache entry
    ruby 4.1.0dev (2026-05-16T21:11:21Z define-obj-has-suf.. 642cfc59a7) +PRISM [x86_64-linux]

    -- Control frame information -----------------------------------------------
    c:0003 p:0003 s:0012 e:000011 l:y b:0001 r:(nil) METHOD test.rb:4
    c:0002 p:0036 s:0008 E:001118 l:n b:---- r:(nil) EVAL   test.rb:10 [FINISH]
    c:0001 p:0000 s:0003 E:0013c0 l:y b:---- r:(nil) DUMMY  [FINISH]

    -- Ruby level backtrace information ----------------------------------------
    test.rb:10:in '<main>'
    test.rb:4:in 'foo'

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

    -- C level backtrace information -------------------------------------------
    miniruby(rb_print_backtrace+0x24) [0x5701425120eb] vm_dump.c:1108
    miniruby(rb_vm_bugreport+0x374) [0x5701425128f2] vm_dump.c:1456
    miniruby(rb_bug_without_die_internal+0xa0) [0x5701422ad332] error.c:1107
    miniruby(rb_bug+0xb5) [0x5701422ad4de] error.c:1125
    miniruby(update_classvariable_cache+0xd1) [0x5701424d159c] vm_insnhelper.c:1501
    miniruby(vm_getclassvariable+0xd1) [0x5701424d16e4] vm_insnhelper.c:1532
    miniruby(vm_exec_core+0xfa6) [0x5701424e6fa9] insns.def:243
    miniruby(rb_vm_exec+0x140) [0x570142504532] vm.c:2805
    miniruby(rb_iseq_eval_main+0x3d) [0x5701425054c8] vm.c:3101
    miniruby(rb_ec_exec_node+0x128) [0x5701422b914a] eval.c:284
    miniruby(ruby_run_node+0x8d) [0x5701422b92be] eval.c:322
    miniruby(rb_main+0x4c) [0x5701421ccd8e] main.c:42
    miniruby(main+0x4b) [0x5701421ccdef] main.c:62
2026-05-17 09:09:53 -04:00
Jean Boussier deceb9f62e Rename rb_shape_obj_* into rb_obj_shape_*
For consistency.
2026-05-06 10:10:16 +02:00
Jean Boussier 4eeec614bb shapes: Rename TOO_COMPLEX in just COMPLEX
The `too_` prefix wasn't consistently used and just make the
thing longer for no benefit.
2026-05-06 10:10:16 +02:00
Jean Boussier 678d86f5e4 shape.c: transition to complex when max_capacity is reached
Now that we have 1024B slots, we can store up to 126 fields inline.

Objects larger than this are rare if not non-existent, hence we can
get rid of the `malloc` path for imemo/fields and simply transition
to `TOO_COMPLEX`.

This additionally allows to shrink `attr_index_t` from 16 to 8B.

Note: the ZJIT "ivar on extended" tests are renamed as "complex" because
"extended" AKA malloc allocated imemo/fields no longer exists.
They're now complex fields, AKA st tables.

rb_class_allocate_instance: start as complex when over max_fields

If `RCLASS_MAX_IV_COUNT` is over `max_fields`, allocating a large
slot to end up transitioning to `TOO_COMPLEX` is wasteful.

We might as well start as complex directly.
2026-05-06 09:02:02 +02:00
Nobuyoshi Nakada 31cdf7a9f8 Adjust style 2026-05-04 09:46:54 +09:00
Jean Boussier c9b53473e3 Refactor shape transition functions
Expose both `rb_obj_shape_` functions that take a `VALUE`
and `rb_shape_` functions that take a `shape_id`.

Make common transition functions such as `complex` and `frozen`
inlineable.

Also get rid of RB_SET_SHAPE_ID and rb_set_boxed_class_shape_id.
2026-05-03 11:36:26 +02:00
Jean Boussier 289343cf0f Create imemo/fields with shape_id
Simplify the API by taking a shape_id upon creation.
2026-05-02 10:33:48 +02:00
John Hawthorn f2d0ef269b Add and use rb_ivar_foreach_buffered
Previously, rb_ivar_foreach would walk up the shape tree, but yield
instance variables to the callback as it went. If the object shape was
modified during this callback, particularly with removing an instance
variable, it could result in reading free'd memory.

This commit solves this by adding a version which buffering all instance
variable names and values before calling the callback, giving a snapshot
of the object at the time rb_ivar_foreach_buffered is called.

The buffer is made with ALLOCV_N, so the performance difference should
be minimal, and I don't think this method is particuarly heavily used.

[Bug #21996]
2026-04-26 17:14:39 +09:00
John Hawthorn 96cd1bc765 Fix iv_count for too_complex generic ivar
Previously this branch forgot to assign iv_count when used with a
generic ivar which is too_complex, which could result in it incorrectly
returning 0.
2026-04-26 17:14:39 +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
John Hawthorn ab324956d7 Avoid array resize in rb_obj_instance_variables
We know (an estimate of) the ivar count upfront (+/- hidden internal
fields), from the shape, so we should reserve the expected length
upfront.
2026-04-13 08:46:15 -07:00
Andrii Furmanets 526344b56e Fix Box regexp match vars after non-match 2026-04-12 02:47:11 +09:00
Jean Boussier 7f4db64f28 Emit already initialzed constant warnings in a single call
When decorating `Warning.warn`, it's much more convenient to get
the whole warning in a single string.
2026-04-10 15:06:19 +02:00
John Hawthorn dd3542a0cd Fix class variable cache not invalidated by class_variable_set
The previous invalidation walked subclasses but missed cvars from
included modules, and skipped invalidation when creating a new cvar on
modules entirely. Always invalidate when a new class variable is
created, since this should be relatively rare.

We previously bumped the global state on any module inclusion, which
should be far more common than this.

[Bug #21978]
2026-04-01 19:21:18 -07:00
Jean Boussier 53099633e2 Make ruby_xfree_sized and ruby_xrealloc_sized public
[Feature #21861]
2026-04-01 08:10:07 +01:00
Takashi Kokubun 6a5a2612fd variable.c: Fix -Wmaybe-uninitialized warnings in rb_gvar_set/rb_gvar_get
Initialize `entry` and `var` to NULL. They are assigned inside
RB_VM_LOCKING() but used after it, and the compiler cannot prove the
locked block always executes.
2026-03-31 17:24:01 -07:00
Aaron Patterson ab32c0e690 Allow reading cvars from non-main Ractors (#16308)
Today you can read instance variables from non-main Ractors, but many
Rails applications use cvars, and we cannot read them.

For example:

```ruby
class Foo
  # This is NOT allowed to be read in non-main Ractors
  @@bar = 123
  def self.bar; @@bar; end

  # This is allowed to be read in non-main Ractors
  @baz = 123
  def self.baz; @baz; end
end

  # This is OK
Ractor.new {
  p Foo.baz
}.value

  # Exception here
Ractor.new {
  p Foo.bar
}.value
```

This commit changes the semantics of cvars to be the same as instance
variables:

* It's ok to read Ractor shareable objects from the non-main Ractor
* It's NOT ok to write from the non-main Ractor

[Feature #21942]
2026-03-19 00:41:25 +00: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
Jean Boussier 14eb083152 Speedup repeated calls to rb_gccct_clear_table
Profiling of `ruby --disable-all -e 1` shows between 5 and 10%
of the time is spent in `_platform_memset`, called from `rb_gccct_clear_table()`,
itself called from `clear_method_cache_by_id_in_class()`.

Which makes sense, during boot we define numerous methods, and after
each we have to invalidate the `gccct`, which is done by zeroing
1023 pointers, so roughtly 8kiB of memory.

By keeping track of whether the table was used since the last clear,
we can save a lot of useless memory writes.

This may also be beneficial when loading user defined code, as it's unlikely
the `gccct` would be dirtied between two `def`.

NB: profiling such a short lived process gives a lot of variance, but
with a very high sampling rate and multiple attempts, `clear_method_cache_by_id_in_class`
is always in the top 2 hotspots.
2026-03-16 20:53:49 +01:00
Jean Boussier 4e196af17c Use ruby_sized_xfree 2026-02-01 21:57:59 +01:00
Jean Boussier 050c00141c variable.c: Replace ruby_xfree by ruby_sized_xfree when applicable 2026-01-31 10:35:48 +01:00
aguspe 6e2bf5df4e [Tests] Assert Module#set_temporary_name returns self
The return value of Module#set_temporary_name was changed
to return `self`, but the existing tests
did not verify this.
2025-12-24 11:03:20 +09:00
Daisuke Aritomo 0fe111caa6 Respect encoding of ID in exception messages 2025-12-16 17:15:59 -08:00
Daisuke Aritomo cbcbbb2fbe Let Ractor::IsolationError report correct constant path
Before this patch, Ractor::IsolationError reported an incorrect constant
path when constant was found through `rb_const_get_0()`.

In this code, Ractor::IsolationError reported illegal access against
`M::TOPLEVEL`, where it should be `Object::TOPLEVEL`.

```ruby
TOPLEVEL = [1]

module M
  def self.f
    TOPLEVEL
  end
end

Ractor.new { M.f }.value
```

This was because `rb_const_get_0()` built the "path" part referring to
the module/class passed to it in the first place. When a constant was
found through recursive search upwards, the module/class which the
constant was found should be reported.

This patch fixes this issue by modifying rb_const_search() to take a
VALUE pointer to be filled with the module/class where the constant was
found.

[Bug #21782]
2025-12-16 17:15:59 -08:00
Étienne Barrié aab4f6287d Add the instance variable name and the module in Ractor::IsolationError (#15563) 2025-12-16 11:47:13 -05:00
Étienne Barrié 09a29e1312 Add the class variable and the class itself in Ractor::IsolationError (#15562) 2025-12-16 11:06:33 -05:00
Nobuyoshi Nakada 7f41c3e7b1 Add rb_eval_cmd_call_kw to shortcut 2025-12-04 18:07:49 +09:00
John Hawthorn f9cd94f17d wb-protect autoload_const 2025-12-03 10:13:36 -08:00
Jean Boussier 5770c186d1 Rename rb_obj_exivar_p -> rb_obj_gen_fields_p
The "EXIVAR" terminology has been replaced by "gen fields"
AKA "generic fields".

Exivar implies variable, but generic fields include more than
just variables, e.g. `object_id`.
2025-12-03 15:57:26 +01:00
John Hawthorn a60cd8787c Error if deleting a nonexistent obj from geniv
I don't think this ever happened, but we should raise the same error
we'd raise on lookup
2025-11-26 16:13:38 -08:00
John Hawthorn 5bef7577b3 Ensure we don't dereference fields_obj as Qundef
We rely on the GC to clear this when the GC is run on another EC than
the cache.
2025-11-26 16:13:38 -08:00
John Hawthorn 970b18e9a9 Clear fields obj when removing
This fixes a bug where the gen_fields_cache could become invalid when
the last ivar was removed. Also adds more assertions.
2025-11-26 16:13:38 -08:00
Satoshi Tagomori d2a587c791 renaming internal data structures and functions from namespace to box 2025-11-07 13:14:54 +09:00
Satoshi Tagomori aaa1234702 update referenced filenames from namespace to box 2025-11-07 13:14:54 +09:00
Luke Gruber 033ba3c881 Don't run global variable hook functions with VM lock held (#15053)
We can't run arbitrary ruby code with the VM lock held.
2025-11-04 21:36:55 -05:00
Koichi Sasada bc00c4468e use SET_SHAREABLE
to adopt strict shareable rule.

* (basically) shareable objects only refer shareable objects
* (exception) shareable objects can refere unshareable objects
  but should not leak reference to unshareable objects to Ruby world
2025-10-23 13:08:26 +09:00
Peter Zhu 957c832db1 Fix memory leak in rb_const_remove when using namespace
We need to free the rb_const_entry_t we remove from the RCLASS_WRITABLE_CONST_TBL
otherwise it will leak memory.
2025-10-19 10:22:25 -04:00
Satoshi Tagomori 29adf0bb74 Split gvar space between root and main namespaces 2025-10-14 22:59:14 +09:00
Satoshi Tagomori 4f47327287 Update current namespace management by using control frames and lexical contexts
to fix inconsistent and wrong current namespace detections.

This includes:
* Moving load_path and related things from rb_vm_t to rb_namespace_t to simplify
  accessing those values via namespace (instead of accessing either vm or ns)
* Initializing root_namespace earlier and consolidate builtin_namespace into root_namespace
* Adding VM_FRAME_FLAG_NS_REQUIRE for checkpoints to detect a namespace to load/require files
* Removing implicit refinements in the root namespace which was used to determine
  the namespace to be loaded (replaced by VM_FRAME_FLAG_NS_REQUIRE)
* Removing namespaces from rb_proc_t because its namespace can be identified by lexical context
* Starting to use ep[VM_ENV_DATA_INDEX_SPECVAL] to store the current namespace when
  the frame type is MAGIC_TOP or MAGIC_CLASS (block handlers don't exist in this case)
2025-09-29 01:15:38 +09:00
Peter Zhu ce94add7fb Fix global variable counter for alias
If we alias two variable multiple times, for example like this:

    alias $foo $bar
    alias $foo $bar

Then we will increment the counter for entry2->var->counter each time,
which is not correct because we are not adding more references to
entry2->var. This reports as a memory leak in RUBY_FREE_AT_EXIT because
entry2->var->counter will never reach 0 and thus will never be freed.
2025-09-09 11:29:35 +02:00
Peter Zhu 8d79187523 Fix undefined behaviour in rb_alias_variable
entry1 is allocated using xmalloc (through ALLOC), which returns undefined
memory. We never set the ractor_local field, so the value is undefined.
2025-09-09 01:18:41 +02:00
Jean Boussier 4992d2c298 YJIT: rb_ivar_get_at skip ractor checks
Using `assume_single_ractor_mode` we can skip all ractor safety
checks if we're in single ractor mode.

```
compare-ruby: ruby 3.5.0dev (2025-08-27T14:58:58Z merge-vm-setivar-d.. 5b749d8e53) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-28T21:23:38Z yjit-get-exivar 3cc21b76d4) +YJIT +PRISM [arm64-darwin24]

|                           |compare-ruby|built-ruby|
|:--------------------------|-----------:|---------:|
|vm_ivar_get_on_obj         |     975.981|   975.772|
|                           |       1.00x|         -|
|vm_ivar_get_on_class       |     136.214|   470.912|
|                           |           -|     3.46x|
|vm_ivar_get_on_generic     |     148.315|   299.122|
|                           |           -|     2.02x|
```
2025-08-29 00:02:29 +02:00
Jean BoussierandAaron Patterson b6d4882c05 YJIT: getinstancevariable cache indexes for types other than T_OBJECT
While accessing the ivars of other types is too complicated to
realistically generate the ASM for it, we can at least provide
the ivar index as to not have to lookup the shape tree every
time.

```
compare-ruby: ruby 3.5.0dev (2025-08-27T14:58:58Z merge-vm-setivar-d.. 5b749d8e53) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-28T17:58:32Z yjit-get-exivar efaa8c9b09) +YJIT +PRISM [arm64-darwin24]

|                           |compare-ruby|built-ruby|
|:--------------------------|-----------:|---------:|
|vm_ivar_get_on_obj         |     930.458|   936.865|
|                           |           -|     1.01x|
|vm_ivar_get_on_class       |     134.471|   431.622|
|                           |           -|     3.21x|
|vm_ivar_get_on_generic     |     146.679|   284.408|
|                           |           -|     1.94x|
```

Co-Authored-By: Aaron Patterson <tenderlove@ruby-lang.org>
2025-08-29 00:02:29 +02:00
Jean Boussier 041450ad41 rb_ivar_delete: also re-embed T_IMEMO/fields
Right now JITs don't generate any code to access ivar on types
other than T_OBJECT, but they might soon, so we must ensure
two IMEMO/fields can't have the same `shape_id` but diffent
embed/heap status.
2025-08-28 20:14:01 +02:00
Jean Boussier 05fc01188a rb_ivar_delete: allow complex transition
`rb_ivar_delete` would force a new shape to be created.
And in the case of a shape exhaustion, it wouldn't handle
T_CLASS/T_MODULE correctly.
2025-08-28 15:21:05 +02:00
Jean Boussier e7fb87ee3a Populate ivar caches for types other than T_OBJECT
`vm_setinstancevariable` had a codepath to try to match the inline
cache for types other than T_OBJECT, but the cache population path
in `vm_setivar_slowpath` was exclusive to T_OBJECT, so `vm_setivar_default`
would never match anything.

This commit improves `vm_setivar_slowpath` so that it is capable of
filling the cache for all types, and adds a `vm_setivar_class` codepath
for `T_CLASS` and `T_MODULE`.

`vm_setivar`, `vm_setivar_default` and `vm_setivar_class` could be unified,
but based on the very explicit `NOINLINE` I assume they were split to minimize
codesize.

```
compare-ruby: ruby 3.5.0dev (2025-08-27T14:58:58Z merge-vm-setivar-d.. 5b749d8e53) +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-27T16:30:31Z setivar-cache-gene.. 4fe78ff296) +PRISM [arm64-darwin24]

|                         |compare-ruby|built-ruby|
|:------------------------|-----------:|---------:|
|vm_ivar_set_on_instance  |     161.809|   164.688|
|                         |           -|     1.02x|
|vm_ivar_set_on_generic   |      58.769|   115.638|
|                         |           -|     1.97x|
|vm_ivar_set_on_class     |      70.034|   141.042|
|                         |           -|     2.01x|
```
2025-08-28 09:25:51 +02:00
Jean Boussier 5257e1298c Replace ROBJECT_EMBED by ROBJECT_HEAP
The embed layout is way more common than the heap one,
especially since WVA.

I think it makes for more readable code to inverse the
flag.
2025-08-27 12:41:07 +02:00
Takashi Kokubun d113bc5a6d Fix uninitialized next_shape_id (#14348) 2025-08-26 10:27:25 -07:00
Jean Boussier 6592e585ff rb_obj_field_get: unify codepaths
Now that T_OBJECT and T_IMEMO/fields have identical layout
a lot more codepaths can be shared.
2025-08-26 13:44:59 +02:00