when arguments are cells clear the locals slot (backport of #17927)

This commit is contained in:
Benjamin Peterson 2013-05-14 22:31:26 -05:00
parent d486707d2e
commit e1b4cbc422
5 changed files with 63 additions and 2 deletions

View File

@ -1,4 +1,6 @@
import unittest import unittest
import weakref
from test.support import check_syntax_error, cpython_only, run_unittest from test.support import check_syntax_error, cpython_only, run_unittest
@ -713,6 +715,33 @@ class ScopeTests(unittest.TestCase):
def b(): def b():
global a global a
@cpython_only
def testCellLeak(self):
# Issue 17927.
#
# The issue was that if self was part of a cycle involving the
# frame of a method call, *and* the method contained a nested
# function referencing self, thereby forcing 'self' into a
# cell, setting self to None would not be enough to break the
# frame -- the frame had another reference to the instance,
# which could not be cleared by the code running in the frame
# (though it will be cleared when the frame is collected).
# Without the lambda, setting self to None is enough to break
# the cycle.
class Tester:
def dig(self):
if 0:
lambda: self
try:
1/0
except Exception as exc:
self.exc = exc
self = None # Break the cycle
tester = Tester()
tester.dig()
ref = weakref.ref(tester)
del tester
self.assertIsNone(ref())
def test_main(): def test_main():

View File

@ -130,6 +130,19 @@ class TestSuper(unittest.TestCase):
super() super()
self.assertRaises(RuntimeError, X().f) self.assertRaises(RuntimeError, X().f)
def test_cell_as_self(self):
class X:
def meth(self):
super()
def f():
k = X()
def g():
return k
return g
c = f().__closure__[0]
self.assertRaises(TypeError, X.meth, c)
def test_main(): def test_main():
support.run_unittest(TestSuper) support.run_unittest(TestSuper)

View File

@ -12,6 +12,9 @@ What's New in Python 3.3.2?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #17927: Frame objects kept arguments alive if they had been copied into
a cell, even if the cell was cleared.
- Issue #17237: Fix crash in the ASCII decoder on m68k. - Issue #17237: Fix crash in the ASCII decoder on m68k.
- Issue #17408: Avoid using an obsolete instance of the copyreg module when - Issue #17408: Avoid using an obsolete instance of the copyreg module when

View File

@ -6519,6 +6519,18 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
return -1; return -1;
} }
obj = f->f_localsplus[0]; obj = f->f_localsplus[0];
if (obj == NULL && co->co_cell2arg) {
/* The first argument might be a cell. */
n = PyTuple_GET_SIZE(co->co_cellvars);
for (i = 0; i < n; i++) {
if (co->co_cell2arg[i] == 0) {
PyObject *cell = f->f_localsplus[co->co_nlocals + i];
assert(PyCell_Check(cell));
obj = PyCell_GET(cell);
break;
}
}
}
if (obj == NULL) { if (obj == NULL) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"super(): arg[0] deleted"); "super(): arg[0] deleted");

View File

@ -3403,10 +3403,14 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
int arg; int arg;
/* Possibly account for the cell variable being an argument. */ /* Possibly account for the cell variable being an argument. */
if (co->co_cell2arg != NULL && if (co->co_cell2arg != NULL &&
(arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {
c = PyCell_New(GETLOCAL(arg)); c = PyCell_New(GETLOCAL(arg));
else /* Clear the local copy. */
SETLOCAL(arg, NULL);
}
else {
c = PyCell_New(NULL); c = PyCell_New(NULL);
}
if (c == NULL) if (c == NULL)
goto fail; goto fail;
SETLOCAL(co->co_nlocals + i, c); SETLOCAL(co->co_nlocals + i, c);