Project

General

Profile

Actions

Bug #20253

closed

`Proc.dup` and `Proc#clone` don't preserve finalizers

Added by byroot (Jean Boussier) 3 months ago. Updated 3 months ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:116656]

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...


Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #20250: Crash with "Object ID seen, but not in mapping table: proc" errorClosedActions
Actions #1

Updated by byroot (Jean Boussier) 3 months ago

  • Related to Bug #20250: Crash with "Object ID seen, but not in mapping table: proc" error added

Updated by byroot (Jean Boussier) 3 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) 3 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) 3 months ago

https://github.com/ruby/ruby/pull/9926 fixes all the issues above and a few others.

Actions #5

Updated by byroot (Jean Boussier) 3 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é

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0