-
Notifications
You must be signed in to change notification settings - Fork 0
/
idtracking.py
136 lines (108 loc) · 3.96 KB
/
idtracking.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from visitor import NodeVisitor
VAR_LOAD_RESOLVE = 'resolve'
VAR_LOAD_PARAM = 'param'
VAR_LOAD_STORE = 'store'
VAR_LOAD_ALIAS = 'alias'
VAR_LOAD_UNDEFINED = 'undefined'
class Symbols:
def __init__(self, parent=None, level=None):
if level is None:
if parent is not None:
level = parent.level + 1
else:
level = 0
self.parent = parent
self.level = level
self.refs = {}
self.loads = {}
self.stores = set()
def copy(self):
rv = object.__new__(self.__class__)
rv.__dict__.update(self.__dict__)
rv.refs = self.refs.copy()
rv.loads = self.loads.copy()
rv.stores = self.stores.copy()
return rv
def analyze_node(self, node, **kwargs):
visitor = RootVisitor(self)
visitor.visit(node, **kwargs)
def find_ref(self, name):
if name in self.refs:
return self.refs[name]
if self.parent is not None:
return self.parent.find_ref(name)
def find_load(self, target):
if target in self.loads:
return self.loads[target]
if self.parent is not None:
return self.parent.find_load(target)
def _define_ref(self, name, load=None):
ident = 'l_%s_%s' % (self.level, name)
self.refs[name] = ident
if load is not None:
self.loads[ident] = load
return ident
def load(self, name):
target = self.find_ref(name)
if not target:
self._define_ref(name, load=(VAR_LOAD_RESOLVE, name))
def ref(self, name):
rv = self.find_ref(name)
assert rv is not None, ('Tried to resolve a name to a reference that'
'was unknown to the frame (%r)' % name)
return rv
def declare_parameter(self, name):
self.stores.add(name)
self._define_ref(name, load=(VAR_LOAD_PARAM, None))
def store(self, name):
pass
def branch_update(self):
pass
class RootVisitor(NodeVisitor):
def __init__(self, symbols):
self.sym_visitor = FrameSymbolVisitor(symbols)
def _generic_visit(self, node):
for child in node.iter_child_nodes():
self.sym_visitor.visit(child)
visit_Template = visit_Block = visit_If = _generic_visit
def visit_For(self, node, for_branch='body', **kwargs):
if for_branch == 'body':
self.sym_visitor.visit(node.target, store_as_param=True)
branch = node.body
elif for_branch == 'else':
branch = node.else_
elif for_branch == 'test':
self.sym_visitor.visit(node.target, store_as_param=True)
if node.test is not None:
self.sym_visitor.visit(node.test)
return
else:
raise RuntimeError('unknown for branch')
for item in branch:
self.sym_visitor.visit(item)
def visit_With(self, node, **kwargs):
for target in node.targets:
self.sym_visitor.visit(target)
for child in node.body:
self.sym_visitor.visit(child)
class FrameSymbolVisitor(NodeVisitor):
def __init__(self, symbols):
self.symbols = symbols
def visit_Name(self, node, store_as_param=False, **kwargs):
if store_as_param or node.ctx == 'param':
self.symbols.declare_parameter(node.name)
elif node.ctx == 'load':
self.symbols.load(node.name)
def visit_If(self, node, **kwargs):
self.visit(node.test, **kwargs)
def inner_visit(nodes):
for subnode in nodes:
self.visit(subnode, **kwargs)
tuple(map(inner_visit, (node.body, node.elif_, node.else_)))
def visit_For(self, node, **kwargs):
self.visit(node.iter, **kwargs)
def visit_With(self, node):
for target in node.values:
self.visit(target)
def visit_Block(self, node, **kwargs):
"""Visiting stops at blocks"""