Skip to content

Commit

Permalink
[GR-59866] Support Proc#initialize_{dup,copy} for subclasses
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4425
  • Loading branch information
andrykonchin committed Dec 17, 2024
2 parents 2c1e91b + f2a2e52 commit 8d4ff03
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 34 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Compatibility:
* Add `Dir#chdir` (#3681, @andrykonchin).
* Declare `File::SHARE_DELETE` constant (#3745, @andrykonchin).
* Support `symbolize_names` argument to `MatchData#named_captures` (#3681, @rwstauner).
* Support `Proc#initialize_{dup,copy}` for subclasses (#3681, @rwstauner).

Performance:

Expand Down
15 changes: 15 additions & 0 deletions spec/ruby/core/proc/clone_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
require_relative 'shared/dup'

describe "Proc#clone" do
Expand All @@ -12,4 +13,18 @@
proc.clone.frozen?.should == true
end
end

ruby_version_is "3.3" do
it "calls #initialize_copy on subclass" do
obj = ProcSpecs::MyProc2.new(:a, 2) { }
dup = obj.clone

dup.should_not equal(obj)
dup.class.should == ProcSpecs::MyProc2

dup.first.should == :a
dup.second.should == 2
dup.initializer.should == :copy
end
end
end
15 changes: 15 additions & 0 deletions spec/ruby/core/proc/dup_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative 'fixtures/common'
require_relative 'shared/dup'

describe "Proc#dup" do
Expand All @@ -10,4 +11,18 @@
proc.frozen?.should == true
proc.dup.frozen?.should == false
end

ruby_version_is "3.3" do
it "calls #initialize_dup on subclass" do
obj = ProcSpecs::MyProc2.new(:a, 2) { }
dup = obj.dup

dup.should_not equal(obj)
dup.class.should == ProcSpecs::MyProc2

dup.first.should == :a
dup.second.should == 2
dup.initializer.should == :dup
end
end
end
16 changes: 15 additions & 1 deletion spec/ruby/core/proc/fixtures/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,21 @@ def initialize(a, b)
@second = b
end

attr_reader :first, :second
attr_reader :first, :second, :initializer

def initialize_copy(other)
super
@initializer = :copy
@first = other.first
@second = other.second
end

def initialize_dup(other)
super
@initializer = :dup
@first = other.first
@second = other.second
end
end

class Arity
Expand Down
51 changes: 18 additions & 33 deletions src/main/java/org/truffleruby/core/proc/ProcNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,7 @@ RubyProc procSpecial(VirtualFrame frame, RubyClass procClass, Object[] args, Rub
@Cached DispatchNode initialize) {
// Instantiate a new instance of procClass as classes do not correspond

final RubyProc proc = new RubyProc(
procClass,
getLanguage().procShape,
block.type,
block.arity,
block.argumentDescriptors,
block.callTargets,
block.callTarget,
block.declarationFrame,
block.declarationVariables,
block.declaringMethod,
block.frameOnStackMarker,
block.declarationContext);

AllocationTracing.trace(proc, this);
final RubyProc proc = ProcOperations.duplicate(procClass, getLanguage().procShape, block, this);
initialize.callWithDescriptor(proc, "initialize", block, RubyArguments.getDescriptor(frame), args);
return proc;
}
Expand All @@ -110,27 +96,26 @@ protected RubyClass metaClass(RubyProc object) {
}
}

@CoreMethod(names = { "dup", "clone" })
@CoreMethod(names = "clone")
public abstract static class CloneNode extends CoreMethodArrayArgumentsNode {

@Specialization
RubyProc clone(RubyProc proc,
@Cached DispatchNode initializeCopyNode) {
final RubyProc copy = ProcOperations.duplicate(proc.getLogicalClass(), getLanguage().procShape, proc, this);
initializeCopyNode.call(copy, "initialize_copy", proc);
return copy;
}
}

@CoreMethod(names = "dup")
public abstract static class DupNode extends CoreMethodArrayArgumentsNode {

@Specialization
RubyProc dup(RubyProc proc) {
final RubyClass logicalClass = proc.getLogicalClass();
final RubyProc copy = new RubyProc(
logicalClass,
getLanguage().procShape,
proc.type,
proc.arity,
proc.argumentDescriptors,
proc.callTargets,
proc.callTarget,
proc.declarationFrame,
proc.declarationVariables,
proc.declaringMethod,
proc.frameOnStackMarker,
proc.declarationContext);

AllocationTracing.trace(copy, this);
RubyProc dup(RubyProc proc,
@Cached DispatchNode initializeDupNode) {
final RubyProc copy = ProcOperations.duplicate(proc.getLogicalClass(), getLanguage().procShape, proc, this);
initializeDupNode.call(copy, "initialize_dup", proc);
return copy;
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/truffleruby/core/proc/ProcOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.truffleruby.core.proc;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.Shape;
import org.truffleruby.RubyContext;
import org.truffleruby.RubyLanguage;
Expand All @@ -20,6 +21,7 @@
import org.truffleruby.language.methods.DeclarationContext;
import org.truffleruby.language.methods.InternalMethod;
import org.truffleruby.language.methods.SharedMethodInfo;
import org.truffleruby.language.objects.AllocationTracing;
import org.truffleruby.language.threadlocal.SpecialVariableStorage;

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

public static RubyProc duplicate(RubyClass procClass, Shape shape, RubyProc proc, Node node) {
final RubyProc copy = new RubyProc(
procClass,
shape,
proc.type,
proc.arity,
proc.argumentDescriptors,
proc.callTargets,
proc.callTarget,
proc.declarationFrame,
proc.declarationVariables,
proc.declaringMethod,
proc.frameOnStackMarker,
proc.declarationContext);
AllocationTracing.trace(copy, node);
return copy;
}

public static Object getSelf(RubyProc proc) {
return RubyArguments.getSelf(proc.declarationFrame);
}
Expand Down

0 comments on commit 8d4ff03

Please sign in to comment.