|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * The Universal Permissive License (UPL), Version 1.0
|
|
58 | 58 | import com.oracle.truffle.api.nodes.RootNode;
|
59 | 59 | import com.oracle.truffle.api.profiles.ConditionProfile;
|
60 | 60 |
|
| 61 | +import java.util.Objects; |
| 62 | + |
61 | 63 | @NodeInfo(shortName = "read_caller_fame")
|
62 | 64 | public final class ReadCallerFrameNode extends Node {
|
63 | 65 | @CompilationFinal private ConditionProfile cachedCallerFrameProfile;
|
@@ -137,19 +139,90 @@ private PFrame.Reference walkLevels(VirtualFrame frame, PFrame.Reference startFr
|
137 | 139 | return currentFrame;
|
138 | 140 | }
|
139 | 141 |
|
| 142 | + /** |
| 143 | + * Walk up the stack to find the currently top Python frame. This method is mostly useful for |
| 144 | + * code that cannot accept a {@code VirtualFrame} parameter (e.g. library code). It is necessary |
| 145 | + * to provide the requesting node because it might be necessary to locate the last |
| 146 | + * {@link IndirectCallNode} that effectively executes the requesting node such that the |
| 147 | + * necessary assumptions can be invalidated to avoid deopt loops.<br/> |
| 148 | + * Consider following situation:<br/> |
| 149 | + * |
| 150 | + * <pre> |
| 151 | + * public class SomeCaller extends PRootNode implements IndirectCallNode { |
| 152 | + * @Child private InteropLibrary lib = ...; |
| 153 | + * public Object execute(VirtualFrame frame, Object callee, Object[] args) { |
| 154 | + * Object state = IndirectCallContext.enter(frame, ctx, this); |
| 155 | + * try { |
| 156 | + * return lib.execute(callee, args); |
| 157 | + * } finally { |
| 158 | + * IndirectCallContext.exit(frame, ctx, state); |
| 159 | + * } |
| 160 | + * } |
| 161 | + * } |
| 162 | + * |
| 163 | + * @ExportLibrary(InteropLibrary.class) |
| 164 | + * public class ExecObject { |
| 165 | + * @ExportMessage |
| 166 | + * boolean isExecutable() { |
| 167 | + * return true; |
| 168 | + * } |
| 169 | + * |
| 170 | + * @ExportMessage |
| 171 | + * Object execute(Object[] args, |
| 172 | + * @Cached SomeNode someNode) { |
| 173 | + * return someNode.execute(args); |
| 174 | + * } |
| 175 | + * } |
| 176 | + * |
| 177 | + * public class SomeNode extends Node { |
| 178 | + * public Object execute(Object[] args) { |
| 179 | + * try { |
| 180 | + * // do some stuff that might throw a PException |
| 181 | + * } catch (PException e) { |
| 182 | + * // read currently top Python frame because it's required for exception reification |
| 183 | + * Frame topPyFrame = ReadCallerFrameNode.getCurrentFrame(this |
| 184 | + * // ... |
| 185 | + * } |
| 186 | + * return PNone.NONE; |
| 187 | + * } |
| 188 | + * } |
| 189 | + * </pre> |
| 190 | + * |
| 191 | + * Assume that we run |
| 192 | + * {@code SomeCaller.create().execute(frame, new ExecObject(), new Object[0])}. It will in the |
| 193 | + * end run {@code SomeNode.execute} and if that tries to get the current frame, we need to do a |
| 194 | + * stack walk in the first run. However, on the second run, node {@code SomeCaller} should |
| 195 | + * already put the current frame reference into the context to avoid subsequent stack walks. |
| 196 | + * Since there is no Truffle call happening, this can only be achieved if we walk the node's |
| 197 | + * parent chain. |
| 198 | + * |
| 199 | + * @param requestingNode - the frame to start counting from or {@code null} to return the top |
| 200 | + * frame |
| 201 | + * @param frameAccess - the desired {@link FrameInstance} access kind |
| 202 | + */ |
| 203 | + public static Frame getCurrentFrame(Node requestingNode, FrameInstance.FrameAccess frameAccess) { |
| 204 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 205 | + return getFrame(Objects.requireNonNull(requestingNode), null, frameAccess, false, 0); |
| 206 | + } |
| 207 | + |
140 | 208 | /**
|
141 | 209 | * Walk up the stack to find the {@code startFrame} and from then ({@code
|
142 | 210 | * level} + 1)-times (counting only non-internal Python frames if {@code
|
143 | 211 | * skipInternal} is true). If {@code startFrame} is {@code null}, return the currently top
|
144 | 212 | * Python frame.
|
145 | 213 | *
|
146 |
| - * @param startFrame - the frame to start counting from or {@code null} to return the top frame |
| 214 | + * @param startFrame - the frame to start counting from (must not be {@code null}) |
147 | 215 | * @param frameAccess - the desired {@link FrameInstance} access kind
|
148 | 216 | * @param skipInternal - declares if Python internal frames should be skipped or counted
|
149 | 217 | * @param level - the stack depth to go to. Ignored if {@code startFrame} is {@code null}
|
150 | 218 | */
|
151 | 219 | public static Frame getCallerFrame(PFrame.Reference startFrame, FrameInstance.FrameAccess frameAccess, boolean skipInternal, int level) {
|
152 | 220 | CompilerDirectives.transferToInterpreterAndInvalidate();
|
| 221 | + return getFrame(null, Objects.requireNonNull(startFrame), frameAccess, skipInternal, level); |
| 222 | + } |
| 223 | + |
| 224 | + private static Frame getFrame(Node requestingNode, PFrame.Reference startFrame, FrameInstance.FrameAccess frameAccess, boolean skipInternal, int level) { |
| 225 | + assert CompilerDirectives.inInterpreter(); |
153 | 226 | final Frame[] outputFrame = new Frame[1];
|
154 | 227 | Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Frame>() {
|
155 | 228 | int i = -1;
|
@@ -187,7 +260,7 @@ public Frame visitFrame(FrameInstance frameInstance) {
|
187 | 260 | RootCallTarget target = (RootCallTarget) frameInstance.getCallTarget();
|
188 | 261 | RootNode rootNode = target.getRootNode();
|
189 | 262 | Node callNode = frameInstance.getCallNode();
|
190 |
| - boolean didMark = IndirectCallNode.setEncapsulatingNeedsToPassCallerFrame(callNode); |
| 263 | + boolean didMark = IndirectCallNode.setEncapsulatingNeedsToPassCallerFrame(callNode != null ? callNode : requestingNode); |
191 | 264 | if (rootNode instanceof PRootNode && outputFrame[0] == null) {
|
192 | 265 | PRootNode pRootNode = (PRootNode) rootNode;
|
193 | 266 | pRootNode.setNeedsCallerFrame();
|
|
0 commit comments