Project

General

Profile

Actions

Bug #17806

closed

Bad interaction between method cache, prepend, and refinements

Added by alanwu (Alan Wu) about 1 month ago. Updated 5 days ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin19]
[ruby-core:103469]

Description

I'm running into a couple of issues with Ruby 3's new method cache and
refinements.

The first script raises SystemStackError unexpectedly:

module R1
  refine Hash do
    def foo; :r1; end
  end
end
class Hash
  prepend(Module.new)
end
class Hash
  def foo; end
end
{}.method(:foo) # put it on pCMC
module R2
  refine Hash do
    def foo; :r2; end
  end
end
{}.foo # SystemStackError

The second script calls the wrong method:

klass = Class.new { def foo; end }
_refinement = Module.new do
  refine(klass) { def foo; :refined; end }
end
klass.prepend(Module.new)
klass.new.foo # cache foo
klass.define_method(:foo) { :second }
p klass.new.foo # prints nil. False caching.

I submitted a GitHub PR to fix the issue: https://github.com/ruby/ruby/pull/4386

Actions #1

Updated by alanwu (Alan Wu) 5 days ago

  • Status changed from Open to Closed

Applied in changeset git|39a2ba5cc559900c30c3143da32446c2f20a7484.


Method cache: fix refinement entry handling

To invalidate some callable method entries, we replace the entry in the
class. Most types of method entries are on the method table of the
origin class, but refinement entries without an orig_me are housed in
the method table of the class itself. They are there because refinements
take priority over prepended methods.

By unconditionally inserting a copy of the refinement entry into the
origin class, clearing the method cache created situations where there
are refinement entry duplicates in the lookup chain, leading to infinite
loops and other problems.

Update the replacement logic to use the right class that houses the
method entry. Also, be more selective about cache invalidation when
moving refinement entries for prepend. This avoids calling
clear_method_cache_by_id_in_class() before refinement entries are in the
place it expects.

[Bug #17806]

Actions

Also available in: Atom PDF