Skip to content

Commit 8d4ff03

Browse files
committed
[GR-59866] Support Proc#initialize_{dup,copy} for subclasses
PullRequest: truffleruby/4425
2 parents 2c1e91b + f2a2e52 commit 8d4ff03

File tree

6 files changed

+84
-34
lines changed

6 files changed

+84
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Compatibility:
4242
* Add `Dir#chdir` (#3681, @andrykonchin).
4343
* Declare `File::SHARE_DELETE` constant (#3745, @andrykonchin).
4444
* Support `symbolize_names` argument to `MatchData#named_captures` (#3681, @rwstauner).
45+
* Support `Proc#initialize_{dup,copy}` for subclasses (#3681, @rwstauner).
4546

4647
Performance:
4748

spec/ruby/core/proc/clone_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require_relative '../../spec_helper'
2+
require_relative 'fixtures/common'
23
require_relative 'shared/dup'
34

45
describe "Proc#clone" do
@@ -12,4 +13,18 @@
1213
proc.clone.frozen?.should == true
1314
end
1415
end
16+
17+
ruby_version_is "3.3" do
18+
it "calls #initialize_copy on subclass" do
19+
obj = ProcSpecs::MyProc2.new(:a, 2) { }
20+
dup = obj.clone
21+
22+
dup.should_not equal(obj)
23+
dup.class.should == ProcSpecs::MyProc2
24+
25+
dup.first.should == :a
26+
dup.second.should == 2
27+
dup.initializer.should == :copy
28+
end
29+
end
1530
end

spec/ruby/core/proc/dup_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require_relative '../../spec_helper'
2+
require_relative 'fixtures/common'
23
require_relative 'shared/dup'
34

45
describe "Proc#dup" do
@@ -10,4 +11,18 @@
1011
proc.frozen?.should == true
1112
proc.dup.frozen?.should == false
1213
end
14+
15+
ruby_version_is "3.3" do
16+
it "calls #initialize_dup on subclass" do
17+
obj = ProcSpecs::MyProc2.new(:a, 2) { }
18+
dup = obj.dup
19+
20+
dup.should_not equal(obj)
21+
dup.class.should == ProcSpecs::MyProc2
22+
23+
dup.first.should == :a
24+
dup.second.should == 2
25+
dup.initializer.should == :dup
26+
end
27+
end
1328
end

spec/ruby/core/proc/fixtures/common.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,21 @@ def initialize(a, b)
3232
@second = b
3333
end
3434

35-
attr_reader :first, :second
35+
attr_reader :first, :second, :initializer
36+
37+
def initialize_copy(other)
38+
super
39+
@initializer = :copy
40+
@first = other.first
41+
@second = other.second
42+
end
43+
44+
def initialize_dup(other)
45+
super
46+
@initializer = :dup
47+
@first = other.first
48+
@second = other.second
49+
end
3650
end
3751

3852
class Arity

src/main/java/org/truffleruby/core/proc/ProcNodes.java

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -86,21 +86,7 @@ RubyProc procSpecial(VirtualFrame frame, RubyClass procClass, Object[] args, Rub
8686
@Cached DispatchNode initialize) {
8787
// Instantiate a new instance of procClass as classes do not correspond
8888

89-
final RubyProc proc = new RubyProc(
90-
procClass,
91-
getLanguage().procShape,
92-
block.type,
93-
block.arity,
94-
block.argumentDescriptors,
95-
block.callTargets,
96-
block.callTarget,
97-
block.declarationFrame,
98-
block.declarationVariables,
99-
block.declaringMethod,
100-
block.frameOnStackMarker,
101-
block.declarationContext);
102-
103-
AllocationTracing.trace(proc, this);
89+
final RubyProc proc = ProcOperations.duplicate(procClass, getLanguage().procShape, block, this);
10490
initialize.callWithDescriptor(proc, "initialize", block, RubyArguments.getDescriptor(frame), args);
10591
return proc;
10692
}
@@ -110,27 +96,26 @@ protected RubyClass metaClass(RubyProc object) {
11096
}
11197
}
11298

113-
@CoreMethod(names = { "dup", "clone" })
99+
@CoreMethod(names = "clone")
100+
public abstract static class CloneNode extends CoreMethodArrayArgumentsNode {
101+
102+
@Specialization
103+
RubyProc clone(RubyProc proc,
104+
@Cached DispatchNode initializeCopyNode) {
105+
final RubyProc copy = ProcOperations.duplicate(proc.getLogicalClass(), getLanguage().procShape, proc, this);
106+
initializeCopyNode.call(copy, "initialize_copy", proc);
107+
return copy;
108+
}
109+
}
110+
111+
@CoreMethod(names = "dup")
114112
public abstract static class DupNode extends CoreMethodArrayArgumentsNode {
115113

116114
@Specialization
117-
RubyProc dup(RubyProc proc) {
118-
final RubyClass logicalClass = proc.getLogicalClass();
119-
final RubyProc copy = new RubyProc(
120-
logicalClass,
121-
getLanguage().procShape,
122-
proc.type,
123-
proc.arity,
124-
proc.argumentDescriptors,
125-
proc.callTargets,
126-
proc.callTarget,
127-
proc.declarationFrame,
128-
proc.declarationVariables,
129-
proc.declaringMethod,
130-
proc.frameOnStackMarker,
131-
proc.declarationContext);
132-
133-
AllocationTracing.trace(copy, this);
115+
RubyProc dup(RubyProc proc,
116+
@Cached DispatchNode initializeDupNode) {
117+
final RubyProc copy = ProcOperations.duplicate(proc.getLogicalClass(), getLanguage().procShape, proc, this);
118+
initializeDupNode.call(copy, "initialize_dup", proc);
134119
return copy;
135120
}
136121
}

src/main/java/org/truffleruby/core/proc/ProcOperations.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.truffleruby.core.proc;
1111

1212
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.nodes.Node;
1314
import com.oracle.truffle.api.object.Shape;
1415
import org.truffleruby.RubyContext;
1516
import org.truffleruby.RubyLanguage;
@@ -20,6 +21,7 @@
2021
import org.truffleruby.language.methods.DeclarationContext;
2122
import org.truffleruby.language.methods.InternalMethod;
2223
import org.truffleruby.language.methods.SharedMethodInfo;
24+
import org.truffleruby.language.objects.AllocationTracing;
2325
import org.truffleruby.language.threadlocal.SpecialVariableStorage;
2426

2527
import com.oracle.truffle.api.RootCallTarget;
@@ -123,6 +125,24 @@ public static RubyProc createProcFromBlock(RubyContext context, RubyLanguage lan
123125
return convertBlock(context, language, block, ProcType.PROC);
124126
}
125127

128+
public static RubyProc duplicate(RubyClass procClass, Shape shape, RubyProc proc, Node node) {
129+
final RubyProc copy = new RubyProc(
130+
procClass,
131+
shape,
132+
proc.type,
133+
proc.arity,
134+
proc.argumentDescriptors,
135+
proc.callTargets,
136+
proc.callTarget,
137+
proc.declarationFrame,
138+
proc.declarationVariables,
139+
proc.declaringMethod,
140+
proc.frameOnStackMarker,
141+
proc.declarationContext);
142+
AllocationTracing.trace(copy, node);
143+
return copy;
144+
}
145+
126146
public static Object getSelf(RubyProc proc) {
127147
return RubyArguments.getSelf(proc.declarationFrame);
128148
}

0 commit comments

Comments
 (0)