Skip to content

Commit fae9df9

Browse files
committed
Make the CallTarget for Method#to_proc context-independent
1 parent a85d07e commit fae9df9

File tree

2 files changed

+24
-22
lines changed

2 files changed

+24
-22
lines changed

src/main/java/org/truffleruby/core/method/MethodNodes.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.truffleruby.language.control.RaiseException;
4646
import org.truffleruby.language.methods.CallInternalMethodNode;
4747
import org.truffleruby.language.methods.InternalMethod;
48+
import org.truffleruby.language.methods.SharedMethodInfo;
4849
import org.truffleruby.language.objects.AllocationTracing;
4950
import org.truffleruby.language.objects.MetaClassNode;
5051
import org.truffleruby.language.threadlocal.SpecialVariableStorage;
@@ -279,28 +280,29 @@ protected RubyUnboundMethod unbind(RubyMethod method) {
279280
@CoreMethod(names = "to_proc")
280281
public abstract static class ToProcNode extends CoreMethodArrayArgumentsNode {
281282

282-
@Specialization(guards = "methodObject == cachedMethodObject", limit = "getCacheLimit()")
283-
protected RubyProc toProcCached(RubyMethod methodObject,
283+
@Specialization(guards = { "isSingleContext()", "methodObject == cachedMethodObject" },
284+
limit = "getCacheLimit()")
285+
protected RubyProc toProcCachedSingleContext(RubyMethod methodObject,
284286
@Cached("methodObject") RubyMethod cachedMethodObject,
285287
@Cached("toProcUncached(cachedMethodObject)") RubyProc proc) {
286288
return proc;
287289
}
288290

289291
@Specialization(
290-
guards = "cachedMethod == methodObject.method",
292+
guards = "methodObject.method.getCallTarget() == methodCallTarget",
291293
limit = "getCacheLimit()",
292-
replaces = "toProcCached")
294+
replaces = "toProcCachedSingleContext")
293295
protected RubyProc toProcCachedTarget(RubyMethod methodObject,
294-
@Cached("methodObject.method") InternalMethod cachedMethod,
295-
@Cached("methodCallTarget(cachedMethod)") RootCallTarget callTarget) {
296-
return createProc(callTarget, cachedMethod, methodObject.receiver);
296+
@Cached("methodObject.method.getCallTarget()") RootCallTarget methodCallTarget,
297+
@Cached("procCallTargetToCallRubyMethod(methodCallTarget)") RootCallTarget procCallTarget) {
298+
return createProc(procCallTarget, methodObject.method, methodObject.receiver);
297299
}
298300

299-
@Specialization
301+
@Specialization(replaces = { "toProcCachedSingleContext", "toProcCachedTarget" })
300302
protected RubyProc toProcUncached(RubyMethod methodObject) {
301303
final InternalMethod method = methodObject.method;
302-
final RootCallTarget callTarget = methodCallTarget(method);
303304
final Object receiver = methodObject.receiver;
305+
final RootCallTarget callTarget = procCallTargetToCallRubyMethod(method.getCallTarget());
304306
return createProc(callTarget, method, receiver);
305307
}
306308

@@ -324,26 +326,26 @@ private RubyProc createProc(RootCallTarget callTarget, InternalMethod method, Ob
324326
}
325327

326328
@TruffleBoundary
327-
protected RootCallTarget methodCallTarget(InternalMethod method) {
329+
protected RootCallTarget procCallTargetToCallRubyMethod(RootCallTarget callTarget) {
328330
// translate to something like:
329331
// lambda { |same args list| method.call(args) }
330332
// We need to preserve the method receiver and we want to have the same argument list.
331333
// We create a new CallTarget for the Proc that calls the method CallTarget and passes the correct receiver.
332334

333-
final SourceSection sourceSection = method.getSharedMethodInfo().getSourceSection();
334-
final RubyRootNode methodRootNode = RubyRootNode.of(method.getCallTarget());
335+
final RubyRootNode methodRootNode = RubyRootNode.of(callTarget);
336+
final SharedMethodInfo sharedMethodInfo = methodRootNode.getSharedMethodInfo();
335337

336-
var callWithRubyMethodReceiverNode = new CallWithRubyMethodReceiverNode(method);
338+
var callWithRubyMethodReceiverNode = new CallWithRubyMethodReceiverNode();
337339
final RubyLambdaRootNode wrapRootNode = new RubyLambdaRootNode(
338340
getLanguage(),
339-
sourceSection,
341+
sharedMethodInfo.getSourceSection(),
340342
methodRootNode.getFrameDescriptor(),
341-
method.getSharedMethodInfo(),
343+
sharedMethodInfo,
342344
callWithRubyMethodReceiverNode,
343345
methodRootNode.getSplit(),
344346
methodRootNode.returnID,
345347
BreakID.INVALID,
346-
method.getSharedMethodInfo().getArity());
348+
sharedMethodInfo.getArity());
347349
return wrapRootNode.getCallTarget();
348350
}
349351

@@ -352,16 +354,13 @@ protected int getCacheLimit() {
352354
}
353355

354356
private static class CallWithRubyMethodReceiverNode extends RubyContextSourceNode {
355-
private final InternalMethod method;
356357
@Child private CallInternalMethodNode callInternalMethodNode = CallInternalMethodNode.create();
357358

358-
public CallWithRubyMethodReceiverNode(InternalMethod method) {
359-
this.method = method;
360-
}
361-
362359
@Override
363360
public Object execute(VirtualFrame frame) {
364-
final Object receiver = RubyArguments.getSelf(RubyArguments.getDeclarationFrame(frame));
361+
final MaterializedFrame declarationFrame = RubyArguments.getDeclarationFrame(frame);
362+
final Object receiver = RubyArguments.getSelf(declarationFrame);
363+
final InternalMethod method = RubyArguments.getMethod(declarationFrame);
365364
return CallNode.callBoundMethod(frame, method, receiver, frame.getArguments(), callInternalMethodNode);
366365
}
367366
}

src/main/java/org/truffleruby/language/methods/InternalMethod.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.truffleruby.language.LexicalScope;
2323
import org.truffleruby.language.Nil;
2424
import org.truffleruby.language.RubyBaseNode;
25+
import org.truffleruby.language.RubyRootNode;
2526
import org.truffleruby.language.Visibility;
2627
import org.truffleruby.language.objects.ObjectGraphNode;
2728

@@ -160,6 +161,7 @@ private InternalMethod(
160161
assert declaringModule != null;
161162
assert lexicalScope != null;
162163
assert !sharedMethodInfo.isBlock() : sharedMethodInfo;
164+
assert callTarget == null || RubyRootNode.of(callTarget).getSharedMethodInfo() == sharedMethodInfo;
163165
assert capturedBlock instanceof Nil || capturedBlock instanceof RubyProc : capturedBlock;
164166
this.sharedMethodInfo = sharedMethodInfo;
165167
this.lexicalScope = lexicalScope;
@@ -235,6 +237,7 @@ public RootCallTarget getCallTarget() {
235237
if (callTarget == null) {
236238
CompilerDirectives.transferToInterpreterAndInvalidate();
237239
callTarget = callTargetSupplier.get();
240+
assert RubyRootNode.of(callTarget).getSharedMethodInfo() == sharedMethodInfo;
238241
}
239242
return callTarget;
240243
}

0 commit comments

Comments
 (0)