Skip to content

Commit 2aa3cab

Browse files
committed
Fix: do not implicitly assume top frame if no caller is given.
1 parent 493a36f commit 2aa3cab

File tree

4 files changed

+46
-25
lines changed

4 files changed

+46
-25
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_frame_tests.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,29 @@ def do_stackwalk(f):
8484
assert expected_prefix == actual_fnames[:len(expected_prefix)]
8585

8686

87+
def test_backref_recursive():
88+
def get_frame():
89+
return sys._getframe(0)
90+
91+
def foo(i):
92+
if i == 1:
93+
f = get_frame()
94+
stack = []
95+
while f:
96+
stack.append(f)
97+
f = f.f_back
98+
return stack
99+
else:
100+
# This recursive call will cause
101+
return foo(i+1)
102+
103+
def bar():
104+
return foo(0)
105+
106+
s = bar()
107+
print([n.f_code for n in s])
108+
109+
87110
def test_code():
88111
code = sys._getframe().f_code
89112
assert code.co_filename == test_code.__code__.co_filename

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.util.function.Supplier;
4141
import java.util.logging.Level;
4242

43+
import org.graalvm.nativeimage.ImageInfo;
44+
4345
import com.oracle.graal.python.PythonLanguage;
4446
import com.oracle.graal.python.builtins.modules.ArrayModuleBuiltins;
4547
import com.oracle.graal.python.builtins.modules.AstModuleBuiltins;
@@ -164,6 +166,7 @@
164166
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetNameNode;
165167
import com.oracle.graal.python.builtins.objects.zipimporter.ZipImporterBuiltins;
166168
import com.oracle.graal.python.nodes.BuiltinNames;
169+
import com.oracle.graal.python.nodes.call.GenericInvokeNode;
167170
import com.oracle.graal.python.runtime.PythonContext;
168171
import com.oracle.graal.python.runtime.PythonCore;
169172
import com.oracle.graal.python.runtime.PythonParser;
@@ -184,8 +187,6 @@
184187
import com.oracle.truffle.api.source.Source;
185188
import com.oracle.truffle.api.source.SourceSection;
186189

187-
import org.graalvm.nativeimage.ImageInfo;
188-
189190
/**
190191
* The core is intended to the immutable part of the interpreter, including most modules and most
191192
* types.
@@ -635,7 +636,7 @@ private void loadFile(String s, String prefix) {
635636
// use an anonymous module for the side-effects
636637
mod = factory().createPythonModule("__anonymous__");
637638
}
638-
callTarget.call(PArguments.withGlobals(mod));
639+
GenericInvokeNode.getUncached().execute(null, callTarget, PArguments.withGlobals(mod));
639640
}
640641

641642
public PythonObjectFactory factory() {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ public static final class CalleeContext extends Node {
195195
@Child private MaterializeFrameNode materializeNode;
196196

197197
@CompilationFinal private boolean everEscaped = false;
198-
@CompilationFinal private boolean firstRequest = true;
199198

200199
/**
201200
* Wrap the execution of a Python callee called from a Python frame.
@@ -230,28 +229,22 @@ public void exit(VirtualFrame frame, PRootNode node) {
230229
// This assumption acts as our branch profile here
231230
PFrame.Reference callerInfo = PArguments.getCallerFrameInfo(frame);
232231
if (callerInfo == null) {
233-
if (firstRequest) {
234-
// we didn't request the caller frame reference. now we need it.
235-
CompilerDirectives.transferToInterpreterAndInvalidate();
236-
firstRequest = false;
237-
238-
// n.b. We need to use 'ReadCallerFrameNode.getCallerFrame' instead of
239-
// 'Truffle.getRuntime().getCallerFrame()' because we still need to skip
240-
// non-Python frames, even if we do not skip frames of builtin functions.
241-
Frame callerFrame = ReadCallerFrameNode.getCallerFrame(info, FrameInstance.FrameAccess.READ_ONLY, false, 0);
242-
if (PArguments.isPythonFrame(callerFrame)) {
243-
callerInfo = PArguments.getCurrentFrameInfo(callerFrame);
244-
} else {
245-
// TODO: frames: an assertion should be that this is one of our
246-
// entry point call nodes
247-
callerInfo = PFrame.Reference.EMPTY;
248-
}
232+
// we didn't request the caller frame reference. now we need it.
233+
CompilerDirectives.transferToInterpreterAndInvalidate();
234+
235+
// n.b. We need to use 'ReadCallerFrameNode.getCallerFrame' instead of
236+
// 'Truffle.getRuntime().getCallerFrame()' because we still need to skip
237+
// non-Python frames, even if we do not skip frames of builtin functions.
238+
Frame callerFrame = ReadCallerFrameNode.getCallerFrame(info, FrameInstance.FrameAccess.READ_ONLY, false, 0);
239+
if (PArguments.isPythonFrame(callerFrame)) {
240+
callerInfo = PArguments.getCurrentFrameInfo(callerFrame);
249241
} else {
250-
// caller info was requested, it must be here if there is
251-
// any. If it isn't, we're in a top-frame.
252-
assert node.needsCallerFrame();
242+
// TODO: frames: an assertion should be that this is one of our
243+
// entry point call nodes
253244
callerInfo = PFrame.Reference.EMPTY;
254245
}
246+
// ReadCallerFrameNode.getCallerFrame must have the assumption invalidated
247+
assert node.needsCallerFrame() : "stack walk did not invalidate caller frame assumption";
255248
}
256249

257250
// force the frame so that it can be accessed later

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
5353
import com.oracle.graal.python.builtins.objects.dict.PDict;
5454
import com.oracle.graal.python.builtins.objects.frame.PFrame;
55+
import com.oracle.graal.python.builtins.objects.frame.PFrame.Reference;
5556
import com.oracle.graal.python.builtins.objects.list.PList;
5657
import com.oracle.graal.python.builtins.objects.module.PythonModule;
5758
import com.oracle.graal.python.builtins.objects.object.PythonObjectLibrary;
@@ -106,8 +107,11 @@ public final class PythonContext {
106107

107108
// TODO: frame: make these three ThreadLocal
108109

109-
/* the reference to the last top frame on the Python stack during interop calls */
110-
private PFrame.Reference topframeref = null;
110+
/*
111+
* The reference to the last top frame on the Python stack during interop calls. Initially, this
112+
* is EMPTY representing the top frame.
113+
*/
114+
private PFrame.Reference topframeref = Reference.EMPTY;
111115

112116
/* corresponds to 'PyThreadState.curexc_*' */
113117
private PException currentException;

0 commit comments

Comments
 (0)