Bug #17321
closedHaving a singleton class makes cloning imperfect
Description
Problem¶
Running the following reproduction script:
class Foo
def self.foo; end
end
def report(klass, name)
puts " #{name}.instance_methods(false): #{klass.instance_methods(false)}"
end
def clone_and_compare(obj)
cln = obj.clone
report(obj.singleton_class, "obj.singleton_class")
report(cln.singleton_class, "cln.singleton_class")
report(obj.singleton_class.singleton_class, "obj.singleton_class.singleton_class")
report(cln.singleton_class.singleton_class, "cln.singleton_class.singleton_class")
end
puts "## Case 1"
obj = Foo.new
clone_and_compare(obj)
puts "## Case 2"
obj = Foo.new
obj.singleton_class
clone_and_compare(obj)
puts "## Case 3"
obj = Foo.new
obj.singleton_class.singleton_class.send(:define_method, :method_on_s2) {}
clone_and_compare(obj)
gives the following output:
## Case 1
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): []
cln.singleton_class.singleton_class.instance_methods(false): []
## Case 2
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): []
cln.singleton_class.singleton_class.instance_methods(false): [:foo]
## Case 3
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): [:method_on_s2]
cln.singleton_class.singleton_class.instance_methods(false): [:method_on_s2]
Case 2
is surprising, because the cloned object has different contents to the original.
It is surprising that clone.singleton_class.singleton_class
has the method :foo
whereas
obj.singleton_class.singleton_class
does not have any methods.
Case 3
suggests, however, that clone.singleton_class.singleton_class
should have the
same methods as the ones on obj.singleton_class.singleton_class
.
This reproduction script gives the same output all the way from Ruby 2.0 up to Ruby-HEAD:
https://wandbox.org/permlink/hRM9OMgtd6nuscRz
Fix¶
@alanwu (Alan Wu) and me have put together a PR that makes Case 2
output identical to Case 1
output:
https://github.com/ruby/ruby/pull/3761
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
- Status changed from Open to Closed
Fixed by ebb96fa8808317ad53a4977bff26cf755d68077e.
Updated by ufuk (Ufuk Kayserilioglu) over 4 years ago
@jeremyevans0 (Jeremy Evans) Thanks for closing the issue.
Can we get this fix backported to 2.5, 2.6 and 2.7 please? As noted in the original report, this bug is present all the way back to 2.0.
Thank you!
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
- Backport changed from 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN to 2.5: REQUIRED, 2.6: REQUIRED, 2.7: REQUIRED
ufuk (Ufuk Kayserilioglu) wrote in #note-2:
@jeremyevans0 (Jeremy Evans) Thanks for closing the issue.
Can we get this fix backported to 2.5, 2.6 and 2.7 please? As noted in the original report, this bug is present all the way back to 2.0.
2.5 is unlikely, as that is in security maintenance mode and this is not a security fix. 2.6 and 2.7 are possibilities. However, it is up to the branch maintainers whether this will be backported. I've updated the backport information to show that a backport is requested.
Updated by nagachika (Tomoyuki Chikanaga) about 4 years ago
- Backport changed from 2.5: REQUIRED, 2.6: REQUIRED, 2.7: REQUIRED to 2.5: REQUIRED, 2.6: REQUIRED, 2.7: DONE
ruby_2_7 82d72f14e7071218f3fd710f770d1ba31390f027 merged revision(s) ebb96fa8808317ad53a4977bff26cf755d68077e.
Updated by usa (Usaku NAKAMURA) about 4 years ago
- Backport changed from 2.5: REQUIRED, 2.6: REQUIRED, 2.7: DONE to 2.5: REQUIRED, 2.6: DONE, 2.7: DONE
ruby_2_6 r67935 merged revision(s) ebb96fa8808317ad53a4977bff26cf755d68077e.