mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
bpo-38187: Fix a refleak in Tools/c-analyzer. (gh-16304)
The "Slot" helper (descriptor) is leaking references due to its caching mechanism. The change includes a partial fix to Slot, but also adds Variable.storage to replace the problematic use of Slot. https://bugs.python.org/issue38187
This commit is contained in:
parent
9055815809
commit
6693f730e0
13 changed files with 208 additions and 89 deletions
|
@ -0,0 +1,6 @@
|
|||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
|
@ -15,9 +15,6 @@ class FromFileTests(unittest.TestCase):
|
|||
|
||||
_return_read_tsv = ()
|
||||
|
||||
def tearDown(self):
|
||||
Variable._isglobal.instances.clear()
|
||||
|
||||
@property
|
||||
def calls(self):
|
||||
try:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
|
@ -64,7 +64,9 @@ class StaticsFromBinaryTests(_Base):
|
|||
**self.kwargs))
|
||||
|
||||
self.assertEqual(found, [
|
||||
info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'),
|
||||
info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'),
|
||||
info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'),
|
||||
info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'),
|
||||
info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'),
|
||||
])
|
||||
|
@ -299,7 +301,7 @@ class StaticsTest(_Base):
|
|||
info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'),
|
||||
info.Variable.from_parts('src1/spam.c', None, 'var1b', 'const char *'),
|
||||
info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'),
|
||||
info.Variable.from_parts('src1/spam.c', 'ham', 'result', 'int'),
|
||||
info.Variable.from_parts('src1/spam.c', 'ham', 'result', 'int'), # skipped
|
||||
info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'),
|
||||
info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'),
|
||||
info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'),
|
||||
|
@ -318,6 +320,7 @@ class StaticsTest(_Base):
|
|||
|
||||
self.assertEqual(found, [
|
||||
info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'),
|
||||
info.Variable.from_parts('src1/spam.c', None, 'var1b', 'const char *'),
|
||||
info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'),
|
||||
info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'),
|
||||
info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'),
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
|
@ -4,7 +4,7 @@ import unittest
|
|||
from ..util import PseudoStr, StrProxy, Object
|
||||
from .. import tool_imports_for_tests
|
||||
with tool_imports_for_tests():
|
||||
from c_analyzer_common.info import ID
|
||||
from c_analyzer_common.info import ID, UNKNOWN
|
||||
from c_parser.info import (
|
||||
normalize_vartype, Variable,
|
||||
)
|
||||
|
@ -31,38 +31,47 @@ class VariableTests(unittest.TestCase):
|
|||
|
||||
VALID_ARGS = (
|
||||
('x/y/z/spam.c', 'func', 'eggs'),
|
||||
'static',
|
||||
'int',
|
||||
)
|
||||
VALID_KWARGS = dict(zip(Variable._fields, VALID_ARGS))
|
||||
VALID_EXPECTED = VALID_ARGS
|
||||
|
||||
def test_init_typical_global(self):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname=None,
|
||||
name='eggs',
|
||||
),
|
||||
vartype='int',
|
||||
)
|
||||
for storage in ('static', 'extern', 'implicit'):
|
||||
with self.subTest(storage):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname=None,
|
||||
name='eggs',
|
||||
),
|
||||
storage=storage,
|
||||
vartype='int',
|
||||
)
|
||||
|
||||
self.assertEqual(static, (
|
||||
('x/y/z/spam.c', None, 'eggs'),
|
||||
'int',
|
||||
))
|
||||
self.assertEqual(static, (
|
||||
('x/y/z/spam.c', None, 'eggs'),
|
||||
storage,
|
||||
'int',
|
||||
))
|
||||
|
||||
def test_init_typical_local(self):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname='func',
|
||||
name='eggs',
|
||||
),
|
||||
vartype='int',
|
||||
)
|
||||
for storage in ('static', 'local'):
|
||||
with self.subTest(storage):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname='func',
|
||||
name='eggs',
|
||||
),
|
||||
storage=storage,
|
||||
vartype='int',
|
||||
)
|
||||
|
||||
self.assertEqual(static, (
|
||||
('x/y/z/spam.c', 'func', 'eggs'),
|
||||
storage,
|
||||
'int',
|
||||
))
|
||||
|
||||
|
@ -71,10 +80,12 @@ class VariableTests(unittest.TestCase):
|
|||
with self.subTest(repr(value)):
|
||||
static = Variable(
|
||||
id=value,
|
||||
storage=value,
|
||||
vartype=value,
|
||||
)
|
||||
|
||||
self.assertEqual(static, (
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
))
|
||||
|
@ -89,34 +100,42 @@ class VariableTests(unittest.TestCase):
|
|||
PseudoStr('func'),
|
||||
PseudoStr('spam'),
|
||||
),
|
||||
storage=PseudoStr('static'),
|
||||
vartype=PseudoStr('int'),
|
||||
),
|
||||
(id,
|
||||
'static',
|
||||
'int',
|
||||
)),
|
||||
('non-str 1',
|
||||
dict(
|
||||
id=id,
|
||||
storage=Object(),
|
||||
vartype=Object(),
|
||||
),
|
||||
(id,
|
||||
'<object>',
|
||||
'<object>',
|
||||
)),
|
||||
('non-str 2',
|
||||
dict(
|
||||
id=id,
|
||||
storage=StrProxy('static'),
|
||||
vartype=StrProxy('variable'),
|
||||
),
|
||||
(id,
|
||||
'static',
|
||||
'variable',
|
||||
)),
|
||||
('non-str',
|
||||
dict(
|
||||
id=id,
|
||||
vartype=('a', 'b', 'c'),
|
||||
storage=('a', 'b', 'c'),
|
||||
vartype=('x', 'y', 'z'),
|
||||
),
|
||||
(id,
|
||||
"('a', 'b', 'c')",
|
||||
"('x', 'y', 'z')",
|
||||
)),
|
||||
]
|
||||
for summary, kwargs, expected in tests:
|
||||
|
@ -134,36 +153,43 @@ class VariableTests(unittest.TestCase):
|
|||
def test_iterable(self):
|
||||
static = Variable(**self.VALID_KWARGS)
|
||||
|
||||
id, vartype = static
|
||||
id, storage, vartype = static
|
||||
|
||||
values = (id, vartype)
|
||||
values = (id, storage, vartype)
|
||||
for value, expected in zip(values, self.VALID_EXPECTED):
|
||||
self.assertEqual(value, expected)
|
||||
|
||||
def test_fields(self):
|
||||
static = Variable(('a', 'b', 'z'), 'x')
|
||||
static = Variable(('a', 'b', 'z'), 'x', 'y')
|
||||
|
||||
self.assertEqual(static.id, ('a', 'b', 'z'))
|
||||
self.assertEqual(static.vartype, 'x')
|
||||
self.assertEqual(static.storage, 'x')
|
||||
self.assertEqual(static.vartype, 'y')
|
||||
|
||||
def test___getattr__(self):
|
||||
static = Variable(('a', 'b', 'z'), 'x')
|
||||
static = Variable(('a', 'b', 'z'), 'x', 'y')
|
||||
|
||||
self.assertEqual(static.filename, 'a')
|
||||
self.assertEqual(static.funcname, 'b')
|
||||
self.assertEqual(static.name, 'z')
|
||||
|
||||
def test_validate_typical(self):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname='func',
|
||||
name='eggs',
|
||||
),
|
||||
vartype='int',
|
||||
)
|
||||
validstorage = ('static', 'extern', 'implicit', 'local')
|
||||
self.assertEqual(set(validstorage), set(Variable.STORAGE))
|
||||
|
||||
static.validate() # This does not fail.
|
||||
for storage in validstorage:
|
||||
with self.subTest(storage):
|
||||
static = Variable(
|
||||
id=ID(
|
||||
filename='x/y/z/spam.c',
|
||||
funcname='func',
|
||||
name='eggs',
|
||||
),
|
||||
storage=storage,
|
||||
vartype='int',
|
||||
)
|
||||
|
||||
static.validate() # This does not fail.
|
||||
|
||||
def test_validate_missing_field(self):
|
||||
for field in Variable._fields:
|
||||
|
@ -171,6 +197,13 @@ class VariableTests(unittest.TestCase):
|
|||
static = Variable(**self.VALID_KWARGS)
|
||||
static = static._replace(**{field: None})
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
static.validate()
|
||||
for field in ('storage', 'vartype'):
|
||||
with self.subTest(field):
|
||||
static = Variable(**self.VALID_KWARGS)
|
||||
static = static._replace(**{field: UNKNOWN})
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
static.validate()
|
||||
|
||||
|
@ -185,6 +218,7 @@ class VariableTests(unittest.TestCase):
|
|||
) + badch
|
||||
tests = [
|
||||
('id', ()), # Any non-empty str is okay.
|
||||
('storage', ('external', 'global') + notnames),
|
||||
('vartype', ()), # Any non-empty str is okay.
|
||||
]
|
||||
seen = set()
|
||||
|
@ -199,6 +233,8 @@ class VariableTests(unittest.TestCase):
|
|||
static.validate()
|
||||
|
||||
for field, invalid in tests:
|
||||
if field == 'id':
|
||||
continue
|
||||
valid = seen - set(invalid)
|
||||
for value in valid:
|
||||
with self.subTest(f'{field}={value!r}'):
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import os.path
|
||||
from test.support import load_package_tests
|
||||
|
||||
|
||||
def load_tests(*args):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
Loading…
Add table
Add a link
Reference in a new issue