Skip to content

Commit 4c3a5e4

Browse files
author
Nicolas Laurent
committed
split dispatch between call dispatch and respond-to dispatch
1 parent 37f22c2 commit 4c3a5e4

File tree

10 files changed

+142
-60
lines changed

10 files changed

+142
-60
lines changed

src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.truffleruby.language.arguments.ReadCallerFrameNode;
4242
import org.truffleruby.language.arguments.RubyArguments;
4343
import org.truffleruby.language.control.RaiseException;
44+
import org.truffleruby.language.dispatch.DispatchConfiguration;
4445
import org.truffleruby.language.dispatch.DispatchNode;
4546
import org.truffleruby.language.dispatch.RubyCallNode;
4647
import org.truffleruby.language.eval.CreateEvalSourceNode;
@@ -583,7 +584,7 @@ private boolean lastCallWasVCall(FrameAndCallNode callerFrame) {
583584
@CoreMethod(names = "__send__", needsBlock = true, rest = true, required = 1)
584585
public abstract static class SendNode extends CoreMethodArrayArgumentsNode {
585586

586-
@Child private DispatchNode dispatchNode = DispatchNode.create(DispatchNode.PRIVATE);
587+
@Child private DispatchNode dispatchNode = DispatchNode.create(DispatchConfiguration.PRIVATE);
587588
@Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create();
588589
@Child private NameToJavaStringNode nameToJavaString = NameToJavaStringNode.create();
589590

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

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

1212
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE;
13-
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE_DOES_RESPOND;
1413
import static org.truffleruby.language.dispatch.DispatchConfiguration.PUBLIC;
1514
import static org.truffleruby.language.dispatch.DispatchConfiguration.PUBLIC_DOES_RESPOND;
1615

@@ -102,6 +101,7 @@
102101
import org.truffleruby.language.control.RaiseException;
103102
import org.truffleruby.language.dispatch.DispatchConfiguration;
104103
import org.truffleruby.language.dispatch.DispatchNode;
104+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
105105
import org.truffleruby.language.dispatch.RubyCallNode;
106106
import org.truffleruby.language.eval.CreateEvalSourceNode;
107107
import org.truffleruby.language.globals.ReadGlobalVariableNodeGen;
@@ -1545,19 +1545,19 @@ protected Object send(VirtualFrame frame, Object self, Object name, Object[] arg
15451545
@NodeChild(value = "includeProtectedAndPrivate", type = RubyNode.class)
15461546
public abstract static class RespondToNode extends CoreMethodNode {
15471547

1548-
@Child private DispatchNode dispatch;
1549-
@Child private DispatchNode dispatchIgnoreVisibility;
1550-
@Child private DispatchNode dispatchRespondToMissing;
1548+
@Child private DispatchRespondToNode dispatch;
1549+
@Child private DispatchRespondToNode dispatchIgnoreVisibility;
1550+
@Child private DispatchRespondToNode dispatchRespondToMissing;
15511551
@Child private DispatchNode respondToMissingNode;
15521552
@Child private BooleanCastNode booleanCastNode;
15531553
private final ConditionProfile ignoreVisibilityProfile = ConditionProfile.create();
15541554
private final ConditionProfile isTrueProfile = ConditionProfile.create();
15551555
private final ConditionProfile respondToMissingProfile = ConditionProfile.create();
15561556

15571557
public RespondToNode() {
1558-
dispatch = DispatchNode.create(PUBLIC_DOES_RESPOND);
1559-
dispatchIgnoreVisibility = DispatchNode.create(PRIVATE_DOES_RESPOND);
1560-
dispatchRespondToMissing = DispatchNode.create(PRIVATE_DOES_RESPOND);
1558+
dispatch = DispatchRespondToNode.create(PUBLIC_DOES_RESPOND);
1559+
dispatchIgnoreVisibility = DispatchRespondToNode.create();
1560+
dispatchRespondToMissing = DispatchRespondToNode.create();
15611561
}
15621562

15631563
public abstract boolean executeDoesRespondTo(VirtualFrame frame, Object object, Object name,

src/main/java/org/truffleruby/core/objectspace/ObjectSpaceNodes.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import org.truffleruby.language.RubyDynamicObject;
3030
import org.truffleruby.language.RubyGuards;
3131
import org.truffleruby.language.control.RaiseException;
32-
import org.truffleruby.language.dispatch.DispatchNode;
32+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
3333
import org.truffleruby.language.objects.IsANode;
3434
import org.truffleruby.language.objects.ObjectGraph;
3535
import org.truffleruby.language.objects.ObjectIDOperations;
@@ -42,7 +42,6 @@
4242
import com.oracle.truffle.api.frame.VirtualFrame;
4343
import com.oracle.truffle.api.profiles.BranchProfile;
4444

45-
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE_DOES_RESPOND;
4645

4746
@CoreModule("ObjectSpace")
4847
public abstract class ObjectSpaceNodes {
@@ -197,7 +196,7 @@ public abstract static class DefineFinalizerNode extends CoreMethodArrayArgument
197196

198197
// MRI would do a dynamic call to #respond_to? but it seems better to warn the user earlier.
199198
// Wanting #method_missing(:call) to be called for a finalizer seems highly unlikely.
200-
@Child private DispatchNode respondToCallNode = DispatchNode.create(PRIVATE_DOES_RESPOND);
199+
@Child private DispatchRespondToNode respondToCallNode = DispatchRespondToNode.create();
201200

202201
@Specialization
203202
protected RubyArray defineFinalizer(VirtualFrame frame, RubyDynamicObject object, Object finalizer,

src/main/java/org/truffleruby/language/ImmutableRubyObject.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.truffleruby.interop.ForeignToRubyArgumentsNode;
1616
import org.truffleruby.interop.ForeignToRubyNode;
1717
import org.truffleruby.language.dispatch.DispatchNode;
18+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
1819
import org.truffleruby.language.library.RubyLibrary;
1920
import com.oracle.truffle.api.dsl.Cached;
2021
import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -111,13 +112,13 @@ public Object getMembers(boolean internal,
111112

112113
@ExportMessage
113114
public boolean isMemberReadable(String name,
114-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode) {
115+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode) {
115116
return definedNode.doesRespondTo(null, name, this);
116117
}
117118

118119
@ExportMessage
119120
public Object readMember(String name,
120-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
121+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
121122
@Cached ForeignToRubyNode nameToRubyNode,
122123
@Cached @Exclusive DispatchNode dispatch,
123124
@Shared("errorProfile") @Cached BranchProfile errorProfile)
@@ -133,7 +134,7 @@ public Object readMember(String name,
133134

134135
@ExportMessage
135136
public boolean isMemberInvocable(String name,
136-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode) {
137+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode) {
137138
return definedNode.doesRespondTo(null, name, this);
138139
}
139140

@@ -154,8 +155,8 @@ public Object invokeMember(String name, Object[] arguments,
154155

155156
@ExportMessage
156157
public boolean isMemberInternal(String name,
157-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
158-
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchNode definedPublicNode) {
158+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
159+
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchRespondToNode definedPublicNode) {
159160
// defined but not publicly
160161
return definedNode.doesRespondTo(null, name, this) &&
161162
!definedPublicNode.doesRespondTo(null, name, this);

src/main/java/org/truffleruby/language/RubyDynamicObject.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.truffleruby.interop.TranslateInteropRubyExceptionNode;
2828
import org.truffleruby.language.control.RaiseException;
2929
import org.truffleruby.language.dispatch.DispatchNode;
30+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
3031
import org.truffleruby.language.library.RubyLibrary;
3132
import org.truffleruby.language.objects.LogicalClassNode;
3233
import org.truffleruby.language.objects.WriteObjectFieldNode;
@@ -221,7 +222,6 @@ public long getArraySize(
221222
}
222223

223224
@ExportMessage
224-
@SuppressWarnings("unused") // because of throws in ArrayMessages
225225
public Object readArrayElement(long index,
226226
@Shared("errorProfile") @Cached BranchProfile errorProfile,
227227
@Shared("translateRubyException") @Cached TranslateInteropRubyExceptionNode translateRubyException,
@@ -240,7 +240,6 @@ public Object readArrayElement(long index,
240240
}
241241

242242
@ExportMessage
243-
@SuppressWarnings("unused") // because of throws in ArrayMessages
244243
public void writeArrayElement(long index, Object value,
245244
@Shared("errorProfile") @Cached BranchProfile errorProfile,
246245
@Shared("translateRubyException") @Cached TranslateInteropRubyExceptionNode translateRubyException,
@@ -376,7 +375,7 @@ private static boolean isIVar(String name) {
376375
@ExportMessage
377376
public Object readMember(String name,
378377
@CachedLibrary("this") DynamicObjectLibrary objectLibrary,
379-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
378+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
380379
@Cached @Shared("nameToRubyNode") ForeignToRubyNode nameToRubyNode,
381380
@Cached @Exclusive DispatchNode dispatch,
382381
@Exclusive @Cached(parameters = "PRIVATE_RETURN_MISSING") DispatchNode dispatchNode,
@@ -508,7 +507,7 @@ public Object invokeMember(String name, Object[] arguments,
508507
@ExportMessage
509508
public boolean isMemberReadable(String name,
510509
@CachedLibrary("this") DynamicObjectLibrary objectLibrary,
511-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
510+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
512511
@Exclusive @Cached(parameters = "PRIVATE_RETURN_MISSING") DispatchNode dispatchNode,
513512
@Cached @Shared("nameToRubyNode") ForeignToRubyNode nameToRubyNode,
514513
@Exclusive @Cached BooleanCastNode booleanCastNode,
@@ -606,7 +605,7 @@ public boolean isMemberInsertable(String name,
606605
@ExportMessage
607606
public boolean isMemberInvocable(String name,
608607
@CachedLibrary("this") DynamicObjectLibrary objectLibrary,
609-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
608+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
610609
@Exclusive @Cached(parameters = "PRIVATE_RETURN_MISSING") DispatchNode dispatchNode,
611610
@Cached @Shared("nameToRubyNode") ForeignToRubyNode nameToRubyNode,
612611
@Exclusive @Cached BooleanCastNode booleanCastNode,
@@ -629,8 +628,8 @@ public boolean isMemberInvocable(String name,
629628
@ExportMessage
630629
public boolean isMemberInternal(String name,
631630
@CachedLibrary("this") DynamicObjectLibrary objectLibrary,
632-
@Cached(parameters = "PRIVATE_DOES_RESPOND") @Shared("definedNode") DispatchNode definedNode,
633-
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchNode definedPublicNode,
631+
@Cached @Shared("definedNode") DispatchRespondToNode definedNode,
632+
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchRespondToNode definedPublicNode,
634633
@Exclusive @Cached(parameters = "PRIVATE_RETURN_MISSING") DispatchNode dispatchNode,
635634
@Cached @Shared("nameToRubyNode") ForeignToRubyNode nameToRubyNode,
636635
@Exclusive @Cached BooleanCastNode booleanCastNode,
@@ -686,7 +685,7 @@ public boolean hasMemberWriteSideEffects(String name,
686685
// region Instantiable
687686
@ExportMessage
688687
public boolean isInstantiable(
689-
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchNode doesRespond) {
688+
@Exclusive @Cached(parameters = "PUBLIC_DOES_RESPOND") DispatchRespondToNode doesRespond) {
690689
return doesRespond.doesRespondTo(null, "new", this);
691690
}
692691

src/main/java/org/truffleruby/language/arguments/ReadUserKeywordsHashNode.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
import com.oracle.truffle.api.CompilerDirectives;
1818
import com.oracle.truffle.api.frame.VirtualFrame;
1919
import com.oracle.truffle.api.profiles.ConditionProfile;
20-
21-
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE_DOES_RESPOND;
20+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
2221

2322
public class ReadUserKeywordsHashNode extends RubyContextSourceNode {
2423

2524
private final int minArgumentCount;
2625

27-
@Child private DispatchNode respondToToHashNode;
26+
@Child private DispatchRespondToNode respondToToHashNode;
2827
@Child private DispatchNode callToHashNode;
2928

3029
private final ConditionProfile notEnoughArgumentsProfile = ConditionProfile.create();
@@ -69,7 +68,7 @@ private RubyHash tryConvertToHash(VirtualFrame frame, int argumentCount, Object
6968
private boolean respondToToHash(VirtualFrame frame, Object lastArgument) {
7069
if (respondToToHashNode == null) {
7170
CompilerDirectives.transferToInterpreterAndInvalidate();
72-
respondToToHashNode = insert(DispatchNode.create(PRIVATE_DOES_RESPOND));
71+
respondToToHashNode = insert(DispatchRespondToNode.create());
7372
}
7473
return respondToToHashNode.doesRespondTo(frame, "to_hash", lastArgument);
7574
}

src/main/java/org/truffleruby/language/arguments/ShouldDestructureNode.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@
1111

1212
import org.truffleruby.language.RubyContextSourceNode;
1313
import org.truffleruby.language.RubyGuards;
14-
import org.truffleruby.language.dispatch.DispatchNode;
1514

1615
import com.oracle.truffle.api.CompilerDirectives;
1716
import com.oracle.truffle.api.frame.VirtualFrame;
1817
import com.oracle.truffle.api.profiles.BranchProfile;
18+
import org.truffleruby.language.dispatch.DispatchRespondToNode;
1919

20-
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE_DOES_RESPOND;
2120

2221
public class ShouldDestructureNode extends RubyContextSourceNode {
2322

24-
@Child private DispatchNode respondToToAry;
23+
@Child private DispatchRespondToNode respondToToAry;
2524

2625
private final BranchProfile checkIsArrayProfile = BranchProfile.create();
2726

@@ -41,7 +40,7 @@ public Object execute(VirtualFrame frame) {
4140

4241
if (respondToToAry == null) {
4342
CompilerDirectives.transferToInterpreterAndInvalidate();
44-
respondToToAry = insert(DispatchNode.create(PRIVATE_DOES_RESPOND));
43+
respondToToAry = insert(DispatchRespondToNode.create());
4544
}
4645

4746
// TODO(cseaton): check this is actually a static "find if there is such method" and not a

src/main/java/org/truffleruby/language/dispatch/DispatchNode.java

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,9 @@ private static final class Missing implements TruffleObject {
4848
// values. We also want to use `parameters` rather than factory methods because Truffle uses it to automatically
4949
// generate uncached instances where required.
5050

51-
public static final DispatchConfiguration PRIVATE = DispatchConfiguration.PRIVATE;
5251
public static final DispatchConfiguration PUBLIC = DispatchConfiguration.PUBLIC;
5352
public static final DispatchConfiguration PRIVATE_RETURN_MISSING = DispatchConfiguration.PRIVATE_RETURN_MISSING;
5453
public static final DispatchConfiguration PUBLIC_RETURN_MISSING = DispatchConfiguration.PUBLIC_RETURN_MISSING;
55-
public static final DispatchConfiguration PRIVATE_DOES_RESPOND = DispatchConfiguration.PRIVATE_DOES_RESPOND;
56-
public static final DispatchConfiguration PUBLIC_DOES_RESPOND = DispatchConfiguration.PUBLIC_DOES_RESPOND;
5754

5855
public static DispatchNode create(DispatchConfiguration config) {
5956
return new DispatchNode(config);
@@ -80,7 +77,6 @@ public static DispatchNode getUncached() {
8077
@Child protected DispatchNode callMethodMissing;
8178
@Child protected ToSymbolNode toSymbol;
8279

83-
protected final ConditionProfile nameIsString;
8480
protected final ConditionProfile methodMissing;
8581
protected final ConditionProfile isForeignCall;
8682
protected final BranchProfile methodMissingMissing;
@@ -90,15 +86,13 @@ protected DispatchNode(
9086
MetaClassNode metaclassNode,
9187
LookupMethodNode methodLookup,
9288
CallInternalMethodNode callNode,
93-
ConditionProfile nameIsString,
9489
ConditionProfile methodMissing,
9590
ConditionProfile isForeignCall,
9691
BranchProfile methodMissingMissing) {
9792
this.config = config;
9893
this.metaclassNode = metaclassNode;
9994
this.methodLookup = methodLookup;
10095
this.callNode = callNode;
101-
this.nameIsString = nameIsString;
10296
this.methodMissing = methodMissing;
10397
this.isForeignCall = isForeignCall;
10498
this.methodMissingMissing = methodMissingMissing;
@@ -112,7 +106,6 @@ protected DispatchNode(DispatchConfiguration config) {
112106
CallInternalMethodNode.create(),
113107
ConditionProfile.create(),
114108
ConditionProfile.create(),
115-
ConditionProfile.create(),
116109
BranchProfile.create());
117110
}
118111

@@ -128,13 +121,10 @@ public Object dispatch(VirtualFrame frame, Object receiver, String methodName, R
128121
return execute(frame, receiver, methodName, block, arguments);
129122
}
130123

131-
public boolean doesRespondTo(VirtualFrame frame, String methodName, Object receiver) {
132-
assert config == PRIVATE_DOES_RESPOND || config == PUBLIC_DOES_RESPOND;
133-
return (boolean) execute(frame, receiver, methodName, null, EMPTY_ARGUMENTS);
134-
}
135-
136124
public Object execute(VirtualFrame frame, Object receiver, String methodName, RubyProc block, Object[] arguments) {
137125

126+
assert config.dispatchAction == DispatchAction.CALL_METHOD;
127+
138128
final RubyClass metaclass = metaclassNode.execute(receiver);
139129

140130
if (isForeignCall.profile(metaclass == getContext().getCoreLibrary().truffleInteropForeignClass)) {
@@ -145,24 +135,14 @@ public Object execute(VirtualFrame frame, Object receiver, String methodName, Ru
145135
final InternalMethod method = methodLookup.execute(frame, metaclass, methodName, config);
146136

147137
if (methodMissing.profile(method == null || method.isUndefined())) {
148-
switch (config.dispatchAction) {
149-
case RESPOND_TO_METHOD:
150-
return false;
151-
case CALL_METHOD:
152-
switch (config.missingBehavior) {
153-
case RETURN_MISSING:
154-
return MISSING;
155-
case CALL_METHOD_MISSING:
156-
return callMethodMissing(frame, receiver, methodName, block, arguments);
157-
}
138+
switch (config.missingBehavior) {
139+
case RETURN_MISSING:
140+
return MISSING;
141+
case CALL_METHOD_MISSING:
142+
return callMethodMissing(frame, receiver, methodName, block, arguments);
158143
}
159144
}
160145

161-
if (config.dispatchAction == DispatchAction.RESPOND_TO_METHOD) {
162-
// NOTE: The whole point of an "unimplemented method" is that is is callable, but `respond_to` returns false.
163-
return method.isImplemented();
164-
}
165-
166146
final MaterializedFrame callerFrame = getFrameIfRequired(frame);
167147
final Object[] frameArguments = RubyArguments.pack(null, callerFrame, method, null, receiver, block, arguments);
168148

@@ -261,7 +241,6 @@ protected Uncached(DispatchConfiguration config) {
261241
CallInternalMethodNodeGen.getUncached(),
262242
ConditionProfile.getUncached(),
263243
ConditionProfile.getUncached(),
264-
ConditionProfile.getUncached(),
265244
BranchProfile.getUncached());
266245
}
267246

0 commit comments

Comments
 (0)