Skip to content

Commit d6399a4

Browse files
committed
AST-inline Symbol#to_proc in the & node
* This handles cases like `foo(&:bar)` and `sym = :bar; foo(&sym)`, but not `var = :bar.to_proc` which seems uncommon.
1 parent 519e5d6 commit d6399a4

File tree

3 files changed

+47
-10
lines changed

3 files changed

+47
-10
lines changed

src/main/java/org/truffleruby/core/cast/ToProcNode.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
*/
1010
package org.truffleruby.core.cast;
1111

12+
import org.truffleruby.core.module.RubyModule;
1213
import org.truffleruby.core.proc.RubyProc;
14+
import org.truffleruby.core.symbol.RubySymbol;
15+
import org.truffleruby.core.symbol.SymbolNodes;
1316
import org.truffleruby.language.Nil;
1417
import org.truffleruby.language.RubyContextSourceNode;
1518
import org.truffleruby.language.RubyNode;
19+
import org.truffleruby.language.arguments.RubyArguments;
1620
import org.truffleruby.language.control.RaiseException;
1721
import org.truffleruby.language.dispatch.DispatchNode;
1822

@@ -22,7 +26,9 @@
2226
import com.oracle.truffle.api.frame.VirtualFrame;
2327
import com.oracle.truffle.api.profiles.BranchProfile;
2428

25-
/** Casts an object to a Ruby Proc object. */
29+
import java.util.Map;
30+
31+
/** The `&` in `foo(&block)`. Converts the passed block to a RubyProc or nil. */
2632
@NodeChild(value = "child", type = RubyNode.class)
2733
public abstract class ToProcNode extends RubyContextSourceNode {
2834

@@ -36,10 +42,23 @@ protected RubyProc doRubyProc(RubyProc proc) {
3642
return proc;
3743
}
3844

39-
@Specialization(guards = { "!isNil(object)", "!isRubyProc(object)" })
45+
// AST-inlined version of Symbol#to_proc
46+
// No need to guard the refinements here since refinements are always the same in a given source location
47+
@Specialization(
48+
guards = "symbol == cachedSymbol",
49+
assumptions = "getContext().getCoreMethods().symbolToProcAssumption",
50+
limit = "1")
51+
protected Object doRubySymbolASTInlined(VirtualFrame frame, RubySymbol symbol,
52+
@Cached("symbol") RubySymbol cachedSymbol,
53+
@Cached("getProcForSymbol(getRefinements(frame), cachedSymbol)") RubyProc cachedProc) {
54+
return cachedProc;
55+
}
56+
57+
@Specialization(guards = { "!isNil(object)", "!isRubyProc(object)" }, replaces = "doRubySymbolASTInlined")
4058
protected RubyProc doObject(VirtualFrame frame, Object object,
4159
@Cached DispatchNode toProc,
4260
@Cached BranchProfile errorProfile) {
61+
// The semantics are to call #method_missing here
4362
final Object coerced;
4463
try {
4564
coerced = toProc.dispatch(frame, object, "to_proc", null, EMPTY_ARGUMENTS);
@@ -64,4 +83,12 @@ protected RubyProc doObject(VirtualFrame frame, Object object,
6483
}
6584
}
6685

86+
protected RubyProc getProcForSymbol(Map<RubyModule, RubyModule[]> refinements, RubySymbol symbol) {
87+
return SymbolNodes.ToProcNode.getOrCreateProc(getContext(), refinements, symbol);
88+
}
89+
90+
protected static Map<RubyModule, RubyModule[]> getRefinements(VirtualFrame frame) {
91+
return RubyArguments.getDeclarationContext(frame).getRefinements();
92+
}
93+
6794
}

src/main/java/org/truffleruby/core/inlined/CoreMethods.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public class CoreMethods {
7474

7575
final Assumption nilClassIsNilAssumption;
7676

77+
public final Assumption symbolToProcAssumption;
78+
7779
public final InternalMethod EXCEPTION_BACKTRACE;
7880
public final InternalMethod BLOCK_GIVEN;
7981
public final InternalMethod LAMBDA;
@@ -134,6 +136,8 @@ public CoreMethods(RubyContext context) {
134136

135137
nilClassIsNilAssumption = registerAssumption(nilClass, "nil?");
136138

139+
symbolToProcAssumption = registerAssumption(symbolClass, "to_proc");
140+
137141
BLOCK_GIVEN = getMethod(kernelModule, "block_given?");
138142
LAMBDA = getMethod(kernelModule, "lambda");
139143
BINDING = getMethod(kernelModule, "binding");

src/main/java/org/truffleruby/core/symbol/SymbolNodes.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import com.oracle.truffle.api.RootCallTarget;
4242
import com.oracle.truffle.api.Truffle;
4343
import com.oracle.truffle.api.dsl.Cached;
44-
import com.oracle.truffle.api.dsl.ReportPolymorphism;
4544
import com.oracle.truffle.api.dsl.Specialization;
4645
import com.oracle.truffle.api.frame.FrameDescriptor;
4746
import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -96,12 +95,17 @@ protected boolean isPreInitializing() {
9695

9796
}
9897

99-
@ReportPolymorphism
10098
@CoreMethod(names = "to_proc")
10199
public abstract static class ToProcNode extends CoreMethodArrayArgumentsNode {
102100

103101
public static final Arity ARITY = new Arity(0, 0, true);
104102

103+
public static ToProcNode create() {
104+
return SymbolNodesFactory.ToProcNodeFactory.create(null);
105+
}
106+
107+
public abstract RubyProc execute(VirtualFrame frame, RubySymbol symbol);
108+
105109
@Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create();
106110

107111
@Specialization(
@@ -110,27 +114,29 @@ public abstract static class ToProcNode extends CoreMethodArrayArgumentsNode {
110114
protected RubyProc toProcCached(VirtualFrame frame, RubySymbol symbol,
111115
@Cached("symbol") RubySymbol cachedSymbol,
112116
@Cached("getRefinements(frame)") Map<RubyModule, RubyModule[]> cachedRefinements,
113-
@Cached("getOrCreateProc(cachedRefinements, symbol)") RubyProc cachedProc) {
117+
@Cached("getOrCreateProc(getContext(), cachedRefinements, symbol)") RubyProc cachedProc) {
114118
return cachedProc;
115119
}
116120

117-
@Specialization
121+
@Specialization(replaces = "toProcCached")
118122
protected RubyProc toProcUncached(VirtualFrame frame, RubySymbol symbol) {
119123
final Map<RubyModule, RubyModule[]> refinements = getRefinements(frame);
120-
return getOrCreateProc(refinements, symbol);
124+
return getOrCreateProc(getContext(), refinements, symbol);
121125
}
122126

123127
@TruffleBoundary
124-
protected RubyProc getOrCreateProc(Map<RubyModule, RubyModule[]> refinements, RubySymbol symbol) {
128+
public static RubyProc getOrCreateProc(RubyContext context,
129+
Map<RubyModule, RubyModule[]> refinements,
130+
RubySymbol symbol) {
125131
// TODO (eregon, 23 Sep 2020): this should ideally cache on the refinements by comparing classes, and not by identity.
126132
return ConcurrentOperations.getOrCompute(
127133
symbol.getCachedProcs(),
128134
refinements,
129-
key -> createProc(getContext(), key, symbol));
135+
key -> createProc(context, key, symbol));
130136
}
131137

132138
@TruffleBoundary
133-
protected static RubyProc createProc(RubyContext context, Map<RubyModule, RubyModule[]> refinements,
139+
private static RubyProc createProc(RubyContext context, Map<RubyModule, RubyModule[]> refinements,
134140
RubySymbol symbol) {
135141
final InternalMethod method = context.getCoreMethods().SYMBOL_TO_PROC;
136142
final SourceSection sourceSection = CoreLibrary.UNAVAILABLE_SOURCE_SECTION;

0 commit comments

Comments
 (0)