mirror of
https://github.com/ruby/ruby.git
synced 2025-09-23 20:44:00 +02:00
Fix origin iclass pointer for modules
If a module has an origin, and that module is included in another module or class, previously the iclass created for the module had an origin pointer to the module's origin instead of the iclass's origin. Setting the origin pointer correctly requires using a stack, since the origin iclass is not created until after the iclass itself. Use a hidden ruby array to implement that stack. Correctly assigning the origin pointers in the iclass caused a use-after-free in GC. If a module with an origin is included in a class, the iclass shares a method table with the module and the iclass origin shares a method table with module origin. Mark iclass origin with a flag that notes that even though the iclass is an origin, it shares a method table, so the method table should not be garbage collected. The shared method table will be garbage collected when the module origin is garbage collected. I've tested that this does not introduce a memory leak. This change caused a VM assertion failure, which was traced to callable method entries using the incorrect defined_class. Update rb_vm_check_redefinition_opt_method and find_defined_class_by_owner to treat iclass origins different than class origins to avoid this issue. This also includes a fix for Module#included_modules to skip iclasses with origins. Fixes [Bug #16736]
This commit is contained in:
parent
fdb31aa7ab
commit
ad729a1d11
Notes:
git
2020-05-23 12:31:46 +09:00
6 changed files with 104 additions and 14 deletions
|
@ -479,6 +479,28 @@ class TestModule < Test::Unit::TestCase
|
|||
assert_raise(ArgumentError) { Module.new { include } }
|
||||
end
|
||||
|
||||
def test_gc_prepend_chain
|
||||
assert_separately([], <<-EOS)
|
||||
10000.times { |i|
|
||||
m1 = Module.new do
|
||||
def foo; end
|
||||
end
|
||||
m2 = Module.new do
|
||||
prepend m1
|
||||
def bar; end
|
||||
end
|
||||
m3 = Module.new do
|
||||
def baz; end
|
||||
prepend m2
|
||||
end
|
||||
Class.new do
|
||||
prepend m3
|
||||
end
|
||||
}
|
||||
GC.start
|
||||
EOS
|
||||
end
|
||||
|
||||
def test_include_into_module_already_included
|
||||
c = Class.new{def foo; [:c] end}
|
||||
modules = lambda do ||
|
||||
|
@ -540,6 +562,16 @@ class TestModule < Test::Unit::TestCase
|
|||
assert_equal([Comparable, Kernel], String.included_modules - mixins)
|
||||
end
|
||||
|
||||
def test_included_modules_with_prepend
|
||||
m1 = Module.new
|
||||
m2 = Module.new
|
||||
m3 = Module.new
|
||||
|
||||
m2.prepend m1
|
||||
m3.include m2
|
||||
assert_equal([m1, m2], m3.included_modules)
|
||||
end
|
||||
|
||||
def test_instance_methods
|
||||
assert_equal([:user, :user2], User.instance_methods(false).sort)
|
||||
assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
|
||||
|
@ -2043,6 +2075,33 @@ class TestModule < Test::Unit::TestCase
|
|||
assert_include(im, mixin, bug8025)
|
||||
end
|
||||
|
||||
def test_prepended_module_with_super_and_alias
|
||||
bug16736 = '[Bug #16736]'
|
||||
|
||||
a = labeled_class("A") do
|
||||
def m; "A"; end
|
||||
end
|
||||
m = labeled_module("M") do
|
||||
prepend Module.new
|
||||
|
||||
def self.included(base)
|
||||
base.alias_method :base_m, :m
|
||||
end
|
||||
|
||||
def m
|
||||
super + "M"
|
||||
end
|
||||
|
||||
def m2
|
||||
base_m
|
||||
end
|
||||
end
|
||||
b = labeled_class("B", a) do
|
||||
include m
|
||||
end
|
||||
assert_equal("AM", b.new.m2, bug16736)
|
||||
end
|
||||
|
||||
def test_prepend_super_in_alias
|
||||
bug7842 = '[Bug #7842]'
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue