tracer: fix STORE_DEREF handling, add EXTENDED_ARG support.

This fixes the following issues:

- on Python 3.10 and earlier, storing to free variables is now handled
  correctly
- on Python 3.11, `_varname_from_oparg` is now used, fixing problems
  with cell variables that are also arguments
- on all supported versions, EXTENDED_ARG is now parsed, ensuring proper
  handling for long functions

Fixes #792.
This commit is contained in:
Marcelina Kościelnicka 2023-06-01 20:03:51 +02:00 committed by Catherine
parent 2a45d0e9ad
commit c343e879d3
2 changed files with 105 additions and 10 deletions

View file

@ -26,24 +26,40 @@ def get_var_name(depth=2, default=_raise_exception):
break
if call_opc not in ("CALL_FUNCTION", "CALL_FUNCTION_KW", "CALL_FUNCTION_EX",
"CALL_METHOD", "CALL"):
if default is _raise_exception:
raise NameNotFound
else:
return default
index = call_index + 2
imm = 0
while True:
opc = opname[code.co_code[index]]
if opc in ("STORE_NAME", "STORE_ATTR"):
name_index = int(code.co_code[index + 1])
return code.co_names[name_index]
if opc == 'EXTENDED_ARG':
imm |= int(code.co_code[index + 1])
imm <<= 8
index += 2
elif opc in ("STORE_NAME", "STORE_ATTR"):
imm |= int(code.co_code[index + 1])
return code.co_names[imm]
elif opc == "STORE_FAST":
name_index = int(code.co_code[index + 1])
return code.co_varnames[name_index]
elif opc == "STORE_DEREF":
name_index = int(code.co_code[index + 1])
imm |= int(code.co_code[index + 1])
if sys.version_info >= (3, 11):
name_index -= code.co_nlocals
return code.co_cellvars[name_index]
return code._varname_from_oparg(imm)
else:
return code.co_varnames[imm]
elif opc == "STORE_DEREF":
imm |= int(code.co_code[index + 1])
if sys.version_info >= (3, 11):
return code._varname_from_oparg(imm)
else:
if imm < len(code.co_cellvars):
return code.co_cellvars[imm]
else:
return code.co_freevars[imm - len(code.co_cellvars)]
elif opc in ("LOAD_GLOBAL", "LOAD_NAME", "LOAD_ATTR", "LOAD_FAST", "LOAD_DEREF",
"DUP_TOP", "BUILD_LIST", "CACHE", "COPY"):
imm = 0
index += 2
else:
if default is _raise_exception:

79
tests/test_tracer.py Normal file
View file

@ -0,0 +1,79 @@
from amaranth.hdl.ast import *
from types import SimpleNamespace
from .utils import *
class TracerTestCase(FHDLTestCase):
def test_fast(self):
s1 = Signal()
self.assertEqual(s1.name, "s1")
s2 = Signal()
self.assertEqual(s2.name, "s2")
def test_name(self):
class Dummy:
s1 = Signal()
self.assertEqual(s1.name, "s1")
s2 = Signal()
self.assertEqual(s2.name, "s2")
def test_attr(self):
ns = SimpleNamespace()
ns.s1 = Signal()
self.assertEqual(ns.s1.name, "s1")
ns.s2 = Signal()
self.assertEqual(ns.s2.name, "s2")
def test_index(self):
l = [None]
l[0] = Signal()
self.assertEqual(l[0].name, "$signal")
def test_deref_cell(self):
s1 = Signal()
self.assertEqual(s1.name, "s1")
s2 = Signal()
self.assertEqual(s2.name, "s2")
def dummy():
return s1, s2
def test_deref_free(self):
def inner():
nonlocal s3, s4
s3 = Signal()
s4 = Signal()
return s1, s2
s1 = Signal()
s2 = Signal()
s3 = None
s4 = None
inner()
self.assertEqual(s1.name, "s1")
self.assertEqual(s2.name, "s2")
self.assertEqual(s3.name, "s3")
self.assertEqual(s4.name, "s4")
def test_long(self):
test = ""
for i in range(100000):
test += f"dummy{i} = None\n"
test += "s1 = Signal()\n"
test += "s2 = Signal()\n"
ns = {"Signal": Signal}
exec(test, ns)
self.assertEqual(ns["s1"].name, "s1")
self.assertEqual(ns["s2"].name, "s2")
def test_deref_fast(self):
def inner(s2):
s1 = Signal()
s2 = Signal()
self.assertEqual(s1.name, "s1")
self.assertEqual(s2.name, "s2")
def dummy():
return s1, s2
inner(None)