Bug #20253
closed`Proc.dup` and `Proc#clone` don't preserve finalizers
Description
While reviewing the fix for [Bug #20250] @peterzhu2118 (Peter Zhu) pointed that FL_FINALIZE
should probably also be cleared.
However after some extra testing, it appears Object#dup
and Object#clone
do copy over the finalizer (which makes sense).
But for some reason Proc
has its own dup/clone
implementation, which does copy the FL_FINALIZE
flag, but doesn't copy the finalizer:
Test script:
def fin(sym)
->(_) { p sym }
end
obj = Object.new
ObjectSpace.define_finalizer(obj, fin(:obj))
obj.dup
obj.clone
proc = Proc.new { }
ObjectSpace.define_finalizer(proc, fin(:proc))
proc.dup
proc.clone
Expected output:
:proc
:proc
:proc
:obj
:obj
:obj
Actual output:
:proc
:obj
:obj
:obj
This discrepancy is present all the way back to Ruby 1.9.
It's so niche I'm not sure it's worth a backport though...
Updated by byroot (Jean Boussier) 10 months ago
- Related to Bug #20250: Crash with "Object ID seen, but not in mapping table: proc" error added
Updated by byroot (Jean Boussier) 10 months ago
While looking at this, it appears that instance variables are also not copied over in Proc#dup
, but somehow are in Proc#clone
:
def ivar_dup(obj)
obj.instance_variable_set(:@foo, 1)
p [:dup, obj.dup.instance_variable_get(:@foo)]
p [:clone, obj.clone.instance_variable_get(:@foo)]
end
ivar_dup(Object.new)
ivar_dup(Proc.new { })
[:dup, 1]
[:clone, 1]
[:dup, nil]
[:clone, 1]
Which really raise the question of why Proc has a completely different codepath for duping/cloning, AFAIK no other core type does this.
Updated by byroot (Jean Boussier) 10 months ago
We just found yet another issue with @etienne (Étienne Barrié):
>> Proc.new { }.freeze.clone
(irb):1:in `initialize_copy': can't modify frozen Proc: #<Proc:0x000000011e3a96b0 (irb):1> (FrozenError)
from (irb):1:in `initialize_clone'
from (irb):1:in `clone'
from (irb):1:in `<main>'
This one was introduced in Ruby 3.3.0.
Updated by byroot (Jean Boussier) 10 months ago
https://github.com/ruby/ruby/pull/9926 fixes all the issues above and a few others.
Updated by byroot (Jean Boussier) 10 months ago
- Status changed from Open to Closed
Applied in changeset git|de1a586ecc2ee7f465f0c0a69291054136a3a819.
proc.c: get rid of CLONESETUP
[Bug #20253]
All the way down to Ruby 1.9, Proc
, Method
, UnboundMethod
and Binding
always had their own specific clone and dup routine.
This caused various discrepancies with how other objects behave
on dup
and `clone. [Bug #20250], [Bug #20253].
This commit get rid of CLONESETUP
and use the the same codepath
as all other types, so ensure consistency.
NB: It's still not accepting the freeze
keyword argument on clone
.
Co-Authored-By: Étienne Barrié etienne.barrie@gmail.com