From: "udzura (Uchio KONDO) via ruby-core" Date: 2026-07-04T06:00:16+00:00 Subject: [ruby-core:125924] [Ruby Bug#22176] [Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined Issue #22176 has been reported by udzura (Uchio KONDO). ---------------------------------------- Bug #22176: [Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined https://bugs.ruby-lang.org/issues/22176 * Author: udzura (Uchio KONDO) * Status: Open * ruby -v: ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- ## Summary In `TracePoint` events such as `:call` / `:c_call` / `:return` / `:c_return`, I'd like to propose adding an attribute `TracePoint#defined_box`, which returns the `Ruby::Box` in which the called method was defined. Currently, `tp.defined_class` correctly returns a distinct class/module object per Box, but there is no direct way to know which Box that object belongs to. ## Background / Motivation With `Ruby::Box`, the same class name and the same source file can be loaded independently in multiple Boxes. In this situation, tools that observe and record method calls via TracePoint (profilers, security auditing tools, test support tools, etc.) run into a practical problem: they cannot distinguish "which Box the called method was defined in." ### Reproduction sample1.rb: ```ruby b = Ruby::Box.new b.require_relative 'sample2' require_relative 'sample2' tp = TracePoint.new(:call) do |tp| if tp.method_id == :method1 puts "Calling #{tp.defined_class}##{tp.method_id} from #{tp.path}:#{tp.lineno}" end end tp.enable ins1 = Sample2.new ins1.method1 ins2 = b::Sample2.new ins2.method1 ``` sample2.rb: ```ruby class Sample2 def method1 puts "This is method 1" end end ``` Output: ``` $ RUBY_BOX=1 ./miniruby ./sample1.rb ./miniruby: warning: Ruby::Box is experimental, and the behavior may change in the future! See https://docs.ruby-lang.org/en/master/Ruby/Box.html for known issues, etc. Calling Sample2#method1 from /path/to/sample2.rb:2 This is method 1 Calling Sample2#method1 from /path/to/sample2.rb:2 This is method 1 ``` There is no way to distinguish the two calls using `tp.defined_class`, `tp.method_id`, `tp.path`, or `tp.lineno`. I have confirmed that the class returned by `tp.defined_class` does in fact have a different object id in each case, but there is no way to check which Box it is associated with. ### Known workarounds and their issues #### 1. `tp.binding.eval "Ruby::Box.current.inspect"` It's possible to obtain the Box indirectly by evaluating `Ruby::Box.current` through `tp.binding`. However, `:c_call` / `:c_return` (calls into methods implemented in C) have no corresponding Ruby-level binding, so `tp.binding` itself is unavailable, and this workaround cannot be used there. #### 2. `tp.self.method(tp.method_id).box.inspect` Re-obtaining an `Object#method` reference does work, and this also works for C methods. However, it has the downside of re-constructing a `Method` object inside the TracePoint block every time, which adds overhead, and it isn't very intuitive. ## Proposed API Add `TracePoint#defined_box`, paired with `tp.defined_class`, returning the `Ruby::Box` instance in which the method/class was defined (or `nil` when `RUBY_BOX` is disabled). ```ruby tp = TracePoint.new(:call, :c_call) do |tp| puts "#{tp.defined_class}##{tp.method_id} defined in #{tp.defined_box.inspect}" end ``` ### On naming | Candidate | Verdict | Reason | |---|---|---| | `defined_box` | Proposed | Symmetric with `defined_class` | | `box` | Under consideration | Matches `Method#box`, but less specific | ## Possible implementation I'm considering two directions and would appreciate feedback: - Go through `rb_trace_arg_t *` and use `rb_callable_method_entry_without_refinements()` to look up the method definition from the klass and called_id, then read the box off of it. This may add some overhead. - Add an argument carrying the defining-Box information to `rb_exec_event_hook_orig()`. Since this function is used very broadly, this would come with a higher cost of change. ## Open questions 1. For singleton methods, `defined_class` returns the singleton class per spec. Should `defined_box` likewise return the Box that singleton class belongs to? 2. Is similar information also needed for the `:script_compiled` event, etc.? ## Environment ``` $ ./miniruby --version ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25] ``` ## Use cases ### Security auditing / debugging When multiple Boxes coexist and an unexpected side effect occurs, there is likely demand for being able to trace "which Box the code that actually ran was defined in." In [vivarium](https://github.com/udzura/vivarium), an auditing tool I'm developing, I'm also considering per-Box auditing, but currently the only options are going through `tp.binding` or re-fetching `Method#box` each time, both of which feel redundant. ### Verifying dependency updates One of the use cases for Box is running an old and a new version of a dependency in parallel, each in its own Box, and comparing the resulting responses. For this, a mechanism to visualize which Box's code path was actually executed is needed. ### Application to multi-tenant systems, etc. TracePoint would make it possible to tally how much code from which Box (i.e., which plugin or tenant) was invoked. For example, an APM/profiling tool could use this to visualize per-Box usage, which may be a real demand. Since this seems like a generally useful attribute to trace with respect to Box in the first place, I expect more ideas for use cases to surface as well. ## Related - [Feature #21311] Namespace on read (revised) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/