mirror of
https://github.com/python/cpython.git
synced 2025-01-21 07:55:16 +08:00
when arguments are cells clear the locals slot (backport of #17927)
This commit is contained in:
parent
d486707d2e
commit
e1b4cbc422
@ -1,4 +1,6 @@
|
||||
import unittest
|
||||
import weakref
|
||||
|
||||
from test.support import check_syntax_error, cpython_only, run_unittest
|
||||
|
||||
|
||||
@ -713,6 +715,33 @@ class ScopeTests(unittest.TestCase):
|
||||
def b():
|
||||
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():
|
||||
|
@ -130,6 +130,19 @@ class TestSuper(unittest.TestCase):
|
||||
super()
|
||||
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():
|
||||
support.run_unittest(TestSuper)
|
||||
|
@ -12,6 +12,9 @@ What's New in Python 3.3.2?
|
||||
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 #17408: Avoid using an obsolete instance of the copyreg module when
|
||||
|
@ -6519,6 +6519,18 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
return -1;
|
||||
}
|
||||
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) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"super(): arg[0] deleted");
|
||||
|
@ -3403,10 +3403,14 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
|
||||
int arg;
|
||||
/* Possibly account for the cell variable being an argument. */
|
||||
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));
|
||||
else
|
||||
/* Clear the local copy. */
|
||||
SETLOCAL(arg, NULL);
|
||||
}
|
||||
else {
|
||||
c = PyCell_New(NULL);
|
||||
}
|
||||
if (c == NULL)
|
||||
goto fail;
|
||||
SETLOCAL(co->co_nlocals + i, c);
|
||||
|
Loading…
Reference in New Issue
Block a user