Skip to content

Commit 2d367a0

Browse files
committed
Materialize backref frame if still on stack.
1 parent 38f7fac commit 2d367a0

File tree

3 files changed

+111
-3
lines changed

3 files changed

+111
-3
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import sys
41+
42+
def get_frame():
43+
return sys._getframe(0)
44+
45+
def foo():
46+
f = get_frame()
47+
stack = []
48+
while f:
49+
stack.append(f)
50+
f = f.f_back
51+
return stack
52+
53+
def bar():
54+
return foo()
55+
56+
# NOTE: we need to call it in the module because the test framework will already provide the materialized caller frame
57+
# and then the test will succeed in any case.
58+
stack = bar()
59+
60+
def test_backref_chain():
61+
actual_fnames = [n.f_code.co_name for n in stack]
62+
expected_prefix = ['get_frame', 'foo', 'bar']
63+
assert len(stack) >= len(expected_prefix)
64+
assert expected_prefix == actual_fnames[:len(expected_prefix)]
65+

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -64,6 +64,25 @@ def foo():
6464
return sys._getframe(0).f_back
6565
assert foo().f_locals['a'] == 'test_backref'
6666

67+
def get_frame():
68+
return sys._getframe(0)
69+
70+
def get_frame_caller():
71+
return get_frame()
72+
73+
def do_stackwalk(f):
74+
stack = []
75+
while f:
76+
stack.append(f)
77+
f = f.f_back
78+
return stack
79+
80+
stack = do_stackwalk(get_frame_caller())
81+
actual_fnames = [n.f_code.co_name for n in stack]
82+
expected_prefix = ['get_frame', 'get_frame_caller', 'test_backref']
83+
assert len(stack) >= len(expected_prefix)
84+
assert expected_prefix == actual_fnames[:len(expected_prefix)]
85+
6786

6887
def test_code():
6988
code = sys._getframe().f_code

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.oracle.graal.python.builtins.PythonBuiltins;
3434
import com.oracle.graal.python.builtins.objects.PNone;
3535
import com.oracle.graal.python.builtins.objects.code.CodeNodes;
36+
import com.oracle.graal.python.builtins.objects.frame.PFrame.Reference;
3637
import com.oracle.graal.python.builtins.objects.function.PArguments;
3738
import com.oracle.graal.python.builtins.objects.module.PythonModule;
3839
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.DictNode;
@@ -172,6 +173,7 @@ public abstract static class GetBackrefNode extends PythonBuiltinNode {
172173
Object getBackref(VirtualFrame frame, PFrame self,
173174
@Cached BranchProfile noBackref,
174175
@Cached BranchProfile topRef,
176+
@Cached("createBinaryProfile()") ConditionProfile notMaterialized,
175177
@Cached ReadCallerFrameNode readCallerFrame) {
176178
PFrame.Reference backref;
177179
for (PFrame cur = self;; cur = backref.getPyFrame()) {
@@ -199,15 +201,37 @@ Object getBackref(VirtualFrame frame, PFrame self,
199201
}
200202
}
201203

202-
if (backref.getPyFrame() == null) {
204+
if (backref == Reference.EMPTY) {
203205
return PNone.NONE;
204206
} else if (!PRootNode.isPythonInternal(backref.getCallNode().getRootNode())) {
205-
return backref.getPyFrame();
207+
PFrame fback = materialize(frame, readCallerFrame, backref, notMaterialized);
208+
assert fback.getRef() == backref;
209+
return fback;
206210
}
207211

208212
assert backref.getPyFrame() != null;
209213
}
210214
}
215+
216+
private static PFrame materialize(VirtualFrame frame, ReadCallerFrameNode readCallerFrameNode, PFrame.Reference backref, ConditionProfile notMaterialized) {
217+
if (notMaterialized.profile(backref.getPyFrame() == null)) {
218+
// Special case: the backref's PFrame object is not yet available; this is because
219+
// the frame is still on the stack. So we need to find and materialize it.
220+
for (int i = 0;; i++) {
221+
PFrame caller = readCallerFrameNode.executeWith(frame, i);
222+
if (caller == null) {
223+
break;
224+
} else if (caller.getRef() == backref) {
225+
// now, the PFrame object is available since the readCallerFrameNode
226+
// materialized it
227+
assert backref.getPyFrame() != null;
228+
return caller;
229+
}
230+
}
231+
assert false : "could not find frame of backref on the stack";
232+
}
233+
return backref.getPyFrame();
234+
}
211235
}
212236

213237
@Builtin(name = "clear", minNumOfPositionalArgs = 1)

0 commit comments

Comments
 (0)