Skip to content

Commit a3a2f03

Browse files
committed
Add some recursion-checking tests
1 parent 75562e5 commit a3a2f03

File tree

7 files changed

+97
-42
lines changed

7 files changed

+97
-42
lines changed

src/codegen/ast_interpreter.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,15 @@ Box* ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_b
452452

453453
Box* ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, BST_stmt* start_at) {
454454
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_interpreter");
455-
RECURSIVE_BLOCK(CXX, " in function call");
455+
456+
// Hacky: this is the best place for EnterRecursiveCall since we want it to be before we set up the frame.
457+
// But we already set (and increffed) the globals on the frame.
458+
if (Py_EnterRecursiveCall("in function call")) {
459+
Py_DECREF(interpreter.frame_info.globals);
460+
throwCAPIException();
461+
}
462+
463+
_RecursiveBlockHelper _helper;
456464

457465
return executeInnerAndSetupFrame(interpreter, start_block, start_at);
458466
}

test/lib/test_helper.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,24 @@ def run_test(cmd, cwd, expected, env = None):
7777
print >> sys.stderr, "expected:", expected
7878
assert result == expected
7979

80+
class ExpectationFailedException(Exception):
81+
pass
82+
83+
class ExpectedException(object):
84+
def __init__(self, excs):
85+
if isinstance(excs, BaseException):
86+
excs = (excs,)
87+
self.excs = excs
88+
89+
def __enter__(self):
90+
pass
91+
92+
def __exit__(self, type, val, tback):
93+
if not val:
94+
raise ExpectationFailedException("Didn't raise any exception")
95+
if not isinstance(val, self.excs):
96+
raise ExpectationFailedException("Raised %s instead of %s" % (val, self.excs))
97+
print "Caught", type.__name__
98+
return True
99+
expected_exception = ExpectedException
100+

test/tests/defaults_test.py

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
11
# Test for various defaults arguments in builtin functions:
22

3-
class ExpectationFailedException(Exception):
4-
pass
5-
6-
class ExpectedException(object):
7-
def __init__(self, excs):
8-
if isinstance(excs, BaseException):
9-
excs = (excs,)
10-
self.excs = excs
11-
12-
def __enter__(self):
13-
pass
14-
15-
def __exit__(self, type, val, tback):
16-
if not val:
17-
raise ExpectationFailedException("Didn't raise any exception")
18-
if not isinstance(val, self.excs):
19-
raise ExpectationFailedException("Raised %s instead of %s" % (val, self.excs))
20-
print "Caught", type.__name__
21-
return True
22-
expected_exception = ExpectedException
3+
import os
4+
import sys
5+
sys.path.append(os.path.dirname(__file__) + "/../lib")
6+
from test_helper import expected_exception
237

248
d = {}
259
print d.get(1)

test/tests/exceptions_test.py

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,11 @@
1-
class TestException(Exception):
2-
pass
1+
import os
2+
import sys
3+
sys.path.append(os.path.dirname(__file__) + "/../lib")
4+
from test_helper import expected_exception, ExpectationFailedException
35

4-
class ExpectationFailedException(Exception):
6+
class TestException(Exception):
57
pass
68

7-
class ExpectedException(object):
8-
def __init__(self, excs):
9-
if isinstance(excs, BaseException):
10-
excs = (excs,)
11-
self.excs = excs
12-
13-
def __enter__(self):
14-
pass
15-
16-
def __exit__(self, type, val, tback):
17-
if not val:
18-
raise ExpectationFailedException("Didn't raise any exception")
19-
if not isinstance(val, self.excs):
20-
raise ExpectationFailedException("Raised %s instead of %s" % (val, self.excs))
21-
print "Caught", type.__name__
22-
return True
23-
expected_exception = ExpectedException
24-
259
# Test the expected_exception manager:
2610
with expected_exception(Exception):
2711
raise Exception()

test/tests/recursion_checking.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Make sure that all of these cases get recursion-checked appropriately
2+
3+
import os
4+
import sys
5+
sys.path.append(os.path.dirname(__file__) + "/../lib")
6+
from test_helper import expected_exception
7+
8+
l = []
9+
l2 = []
10+
l.append(l)
11+
l2.append(l2)
12+
13+
with expected_exception(RuntimeError):
14+
l == l2
15+
16+
def recurse():
17+
recurse()
18+
19+
with expected_exception(RuntimeError):
20+
recurse()
21+
22+
def f(n):
23+
if n > 0:
24+
f(n-1)
25+
26+
# If the 'exec' itself is the frame that triggers the recursion depth error,
27+
# it will fail in an obscure way: "KeyError: 'unknown symbol table entry'"
28+
# (This is because it fails during the compilation of the exec'd string.)
29+
# So add some other calls just to try to avoid that
30+
s = """
31+
f(10)
32+
exec s
33+
"""
34+
with expected_exception(RuntimeError):
35+
exec s
36+
37+
s = 'f(10), eval(s)'
38+
with expected_exception(RuntimeError):
39+
eval(s)

test/tests/recursion_checking_llvm.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# expected: fail
2+
# - Recursion that happens entirely within the llvm tier does not get depth-checked
3+
4+
import os
5+
import sys
6+
sys.path.append(os.path.dirname(__file__) + "/../lib")
7+
from test_helper import expected_exception
8+
9+
def f(n):
10+
if n > 0:
11+
f(n-1)
12+
13+
# force llvm-tier:
14+
for i in xrange(3000):
15+
f(5)
16+
17+
with expected_exception(RuntimeError):
18+
f(100000000)

tools/tester.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def set_ulimits():
6666

6767
MAX_MEM_MB = 100
6868
resource.setrlimit(resource.RLIMIT_RSS, (MAX_MEM_MB * 1024 * 1024, MAX_MEM_MB * 1024 * 1024))
69+
resource.setrlimit(resource.RLIMIT_AS, ((MAX_MEM_MB * 1.5) * 1024 * 1024, (MAX_MEM_MB * 1.5) * 1024 * 1024))
6970

7071
EXTMODULE_DIR = None
7172
EXTMODULE_DIR_PYSTON = None

0 commit comments

Comments
 (0)