Skip to content

instance_eval should defer creation of singleton class #8638

@headius

Description

@headius

Currently, when calling instance_eval against an object, JRuby will eagerly create a singleton class in preparation for any method definitions that might be coming. CRuby, on the other hand, defers creation until methods actually need to be defined, making instance_eval much cheaper in the absence of defs.

We can see this effect by doing instance_eval against chilled strings, which triggers verbose warnings under 3.4 (JRuby 10):

[] jruby $ cx 3.4 ruby -v -e '"".instance_eval { }'
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
[] jruby $ ruby -v -e '"".instance_eval { }'       
jruby 10.0.0.0-SNAPSHOT (3.4.0) 2025-02-18 493d0005c4 OpenJDK 64-Bit Server VM 21.0.5+11-LTS on 21.0.5+11-LTS +indy +jit [arm64-darwin]
-e:1: warning: literal string will be frozen in the future

In addition to causing warnings (and eventually errors) for tests that instance_eval against "frozen" strings, it greatly increases the overhead of non-def instance_eval:

[] jruby $ cx 3.4 ruby -v -rbenchmark -e '5.times { puts Benchmark.measure { 10_000_000.times { Object.new.instance_eval {} } } }'
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
  1.497619   0.009447   1.507066 (  1.512211)
  1.511998   0.011041   1.523039 (  1.632321)
  1.525667   0.017188   1.542855 (  1.581538)
  1.527588   0.011892   1.539480 (  1.554302)
  1.519021   0.012526   1.531547 (  1.571109)
[] jruby $ ruby -v -rbenchmark -e '5.times { puts Benchmark.measure { 10_000_000.times { Object.new.instance_eval {} } } }' 
jruby 10.0.0.0-SNAPSHOT (3.4.0) 2025-02-18 493d0005c4 OpenJDK 64-Bit Server VM 21.0.5+11-LTS on 21.0.5+11-LTS +indy +jit [arm64-darwin]
  8.730000   0.470000   9.200000 (  4.080593)
  6.840000   0.470000   7.310000 (  3.754422)
  8.690000   0.440000   9.130000 (  4.102935)
  6.740000   1.020000   7.760000 (  3.863980)
  8.910000   0.740000   9.650000 (  4.235179)

CRuby avoids creating the singleton eagerly by setting a singleton flag into the cref scope here:

https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/vm_eval.c#L2233

This flag is later used when acquiring the class to be used for method definitions:

https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/eval_intern.h#L192-L201

The performance improvement for def-less instance_eval alone would be worth making this change, but we eventually must eliminate the errors that happen for instance_eval against frozen objects (and in the shorter term, the warnings that come from instance_eval on a "chilled" string).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions