Skip to content

Commit 0f57156

Browse files
Anselm KruisAnselm Kruis
authored andcommitted
Stackless issue python#168: make Stackless compatible with old Cython modules
Add another test case for a pathological situation.
1 parent d680e72 commit 0f57156

File tree

3 files changed

+32
-7
lines changed

3 files changed

+32
-7
lines changed

Python/ceval.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4052,7 +4052,9 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
40524052
/* and (&(f->f_code))[-1] looks like a valid code object */
40534053
(&(f->f_code))[-1] && PyCode_Check((&(f->f_code))[-1]) &&
40544054
/* and there are arguments */
4055-
(&(f->f_code))[-1]->co_argcount > 0)
4055+
(&(f->f_code))[-1]->co_argcount > 0 &&
4056+
/* the last argument is NULL */
4057+
f->f_localsplus[(&(f->f_code))[-1]->co_argcount - 1] == NULL)
40564058
{
40574059
PyCodeObject * code = (&(f->f_code))[-1];
40584060
memmove(f->f_localsplus, f->f_localsplus-1, code->co_argcount * sizeof(f->f_localsplus[0]));

Stackless/module/stacklessmodule.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,9 +1131,10 @@ by Stackless Python.\n\
11311131
The function creates a frame from code, globals and args and executes the frame.");
11321132

11331133
static PyObject* test_PyEval_EvalFrameEx(PyObject *self, PyObject *args, PyObject *kwds) {
1134-
static char *kwlist[] = {"code", "globals", "args", "alloca", "throw", "oldcython", NULL};
1134+
static char *kwlist[] = {"code", "globals", "args", "alloca", "throw", "oldcython",
1135+
"code2", NULL};
11351136
PyThreadState *tstate = PyThreadState_GET();
1136-
PyCodeObject *co;
1137+
PyCodeObject *co, *code2 = NULL;
11371138
PyObject *globals, *co_args = NULL;
11381139
Py_ssize_t alloca_size = 0;
11391140
PyObject *exc = NULL;
@@ -1143,9 +1144,9 @@ static PyObject* test_PyEval_EvalFrameEx(PyObject *self, PyObject *args, PyObjec
11431144
void *p;
11441145
Py_ssize_t na;
11451146

1146-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|O!nOO!:test_PyEval_EvalFrameEx", kwlist,
1147+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!|O!nOO!O!:test_PyEval_EvalFrameEx", kwlist,
11471148
&PyCode_Type, &co, &PyDict_Type, &globals, &PyTuple_Type, &co_args, &alloca_size,
1148-
&exc, &PyBool_Type, &oldcython))
1149+
&exc, &PyBool_Type, &oldcython, &PyCode_Type, &code2))
11491150
return NULL;
11501151
if (exc && !PyExceptionInstance_Check(exc)) {
11511152
PyErr_SetString(PyExc_TypeError, "exc must be an exception instance");
@@ -1198,6 +1199,10 @@ static PyObject* test_PyEval_EvalFrameEx(PyObject *self, PyObject *args, PyObjec
11981199
if (exc) {
11991200
PyErr_SetObject(PyExceptionInstance_Class(exc), exc);
12001201
}
1202+
if (code2) {
1203+
Py_INCREF(code2);
1204+
Py_SETREF(f->f_code, code2);
1205+
}
12011206
result = PyEval_EvalFrameEx(f, exc != NULL);
12021207
/* result = Py_None; Py_INCREF(Py_None); */
12031208
exit:

Stackless/unittests/test_capi.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
import unittest
3232

3333
from support import test_main # @UnusedImport
34-
from support import StacklessTestCase, withThreads, require_one_thread
34+
from support import (StacklessTestCase, withThreads, require_one_thread,
35+
testcase_leaks_references)
3536

3637
if withThreads:
3738
try:
@@ -165,7 +166,7 @@ def test_oldcython_frame(self):
165166
# A test for Stackless issue #168
166167
self.assertEqual(self.call_PyEval_EvalFrameEx(47110816, oldcython=True), 47110816)
167168

168-
def test_oldcython_frame_code_is_1st_arg(self):
169+
def test_oldcython_frame_code_is_1st_arg_good(self):
169170
# A pathological test for Stackless issue #168
170171
def f(code):
171172
return code
@@ -175,13 +176,30 @@ def f2(code):
175176

176177
self.assertIs(stackless.test_PyEval_EvalFrameEx(f.__code__, f.__globals__, (f.__code__,), oldcython=False), f.__code__)
177178
self.assertIs(stackless.test_PyEval_EvalFrameEx(f.__code__, f.__globals__, (f2.__code__,), oldcython=True), f2.__code__)
179+
180+
@testcase_leaks_references("f->f_code get overwritten without Py_DECREF")
181+
def test_oldcython_frame_code_is_1st_arg_bad(self):
182+
# A pathological test for Stackless issue #168
183+
def f(code):
184+
return code
185+
178186
# we can't fix this particular case:
179187
# - running code object is its 1st arg and
180188
# - oldcython=True,
181189
# because a fix would result in a segmentation fault, if the number of
182190
# arguments is to low (test case test_0_args)
183191
self.assertRaises(UnboundLocalError, stackless.test_PyEval_EvalFrameEx, f.__code__, f.__globals__, (f.__code__,), oldcython=True)
184192

193+
def test_other_code_object(self):
194+
# A pathological test for Stackless issue #168
195+
def f(arg):
196+
return arg
197+
198+
def f2(arg):
199+
return arg
200+
201+
self.assertIs(stackless.test_PyEval_EvalFrameEx(f.__code__, f.__globals__, (f2,), code2=f2.__code__), f2)
202+
185203

186204
if __name__ == "__main__":
187205
if not sys.argv[1:]:

0 commit comments

Comments
 (0)