Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Proc#initialize_{dup,copy} for subclasses #3744

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Compatibility:
* Add `Dir.for_fd` (#3681, @andrykonchin).
* Add `Dir.fchdir` (#3681, @andrykonchin).
* Add `Dir#chdir` (#3681, @andrykonchin).
* 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
Loading