1*635a8641SAndroid Build Coastguard Workerfrom jinja2.visitor import NodeVisitor 2*635a8641SAndroid Build Coastguard Workerfrom jinja2._compat import iteritems 3*635a8641SAndroid Build Coastguard Worker 4*635a8641SAndroid Build Coastguard Worker 5*635a8641SAndroid Build Coastguard WorkerVAR_LOAD_PARAMETER = 'param' 6*635a8641SAndroid Build Coastguard WorkerVAR_LOAD_RESOLVE = 'resolve' 7*635a8641SAndroid Build Coastguard WorkerVAR_LOAD_ALIAS = 'alias' 8*635a8641SAndroid Build Coastguard WorkerVAR_LOAD_UNDEFINED = 'undefined' 9*635a8641SAndroid Build Coastguard Worker 10*635a8641SAndroid Build Coastguard Worker 11*635a8641SAndroid Build Coastguard Workerdef find_symbols(nodes, parent_symbols=None): 12*635a8641SAndroid Build Coastguard Worker sym = Symbols(parent=parent_symbols) 13*635a8641SAndroid Build Coastguard Worker visitor = FrameSymbolVisitor(sym) 14*635a8641SAndroid Build Coastguard Worker for node in nodes: 15*635a8641SAndroid Build Coastguard Worker visitor.visit(node) 16*635a8641SAndroid Build Coastguard Worker return sym 17*635a8641SAndroid Build Coastguard Worker 18*635a8641SAndroid Build Coastguard Worker 19*635a8641SAndroid Build Coastguard Workerdef symbols_for_node(node, parent_symbols=None): 20*635a8641SAndroid Build Coastguard Worker sym = Symbols(parent=parent_symbols) 21*635a8641SAndroid Build Coastguard Worker sym.analyze_node(node) 22*635a8641SAndroid Build Coastguard Worker return sym 23*635a8641SAndroid Build Coastguard Worker 24*635a8641SAndroid Build Coastguard Worker 25*635a8641SAndroid Build Coastguard Workerclass Symbols(object): 26*635a8641SAndroid Build Coastguard Worker 27*635a8641SAndroid Build Coastguard Worker def __init__(self, parent=None, level=None): 28*635a8641SAndroid Build Coastguard Worker if level is None: 29*635a8641SAndroid Build Coastguard Worker if parent is None: 30*635a8641SAndroid Build Coastguard Worker level = 0 31*635a8641SAndroid Build Coastguard Worker else: 32*635a8641SAndroid Build Coastguard Worker level = parent.level + 1 33*635a8641SAndroid Build Coastguard Worker self.level = level 34*635a8641SAndroid Build Coastguard Worker self.parent = parent 35*635a8641SAndroid Build Coastguard Worker self.refs = {} 36*635a8641SAndroid Build Coastguard Worker self.loads = {} 37*635a8641SAndroid Build Coastguard Worker self.stores = set() 38*635a8641SAndroid Build Coastguard Worker 39*635a8641SAndroid Build Coastguard Worker def analyze_node(self, node, **kwargs): 40*635a8641SAndroid Build Coastguard Worker visitor = RootVisitor(self) 41*635a8641SAndroid Build Coastguard Worker visitor.visit(node, **kwargs) 42*635a8641SAndroid Build Coastguard Worker 43*635a8641SAndroid Build Coastguard Worker def _define_ref(self, name, load=None): 44*635a8641SAndroid Build Coastguard Worker ident = 'l_%d_%s' % (self.level, name) 45*635a8641SAndroid Build Coastguard Worker self.refs[name] = ident 46*635a8641SAndroid Build Coastguard Worker if load is not None: 47*635a8641SAndroid Build Coastguard Worker self.loads[ident] = load 48*635a8641SAndroid Build Coastguard Worker return ident 49*635a8641SAndroid Build Coastguard Worker 50*635a8641SAndroid Build Coastguard Worker def find_load(self, target): 51*635a8641SAndroid Build Coastguard Worker if target in self.loads: 52*635a8641SAndroid Build Coastguard Worker return self.loads[target] 53*635a8641SAndroid Build Coastguard Worker if self.parent is not None: 54*635a8641SAndroid Build Coastguard Worker return self.parent.find_load(target) 55*635a8641SAndroid Build Coastguard Worker 56*635a8641SAndroid Build Coastguard Worker def find_ref(self, name): 57*635a8641SAndroid Build Coastguard Worker if name in self.refs: 58*635a8641SAndroid Build Coastguard Worker return self.refs[name] 59*635a8641SAndroid Build Coastguard Worker if self.parent is not None: 60*635a8641SAndroid Build Coastguard Worker return self.parent.find_ref(name) 61*635a8641SAndroid Build Coastguard Worker 62*635a8641SAndroid Build Coastguard Worker def ref(self, name): 63*635a8641SAndroid Build Coastguard Worker rv = self.find_ref(name) 64*635a8641SAndroid Build Coastguard Worker if rv is None: 65*635a8641SAndroid Build Coastguard Worker raise AssertionError('Tried to resolve a name to a reference that ' 66*635a8641SAndroid Build Coastguard Worker 'was unknown to the frame (%r)' % name) 67*635a8641SAndroid Build Coastguard Worker return rv 68*635a8641SAndroid Build Coastguard Worker 69*635a8641SAndroid Build Coastguard Worker def copy(self): 70*635a8641SAndroid Build Coastguard Worker rv = object.__new__(self.__class__) 71*635a8641SAndroid Build Coastguard Worker rv.__dict__.update(self.__dict__) 72*635a8641SAndroid Build Coastguard Worker rv.refs = self.refs.copy() 73*635a8641SAndroid Build Coastguard Worker rv.loads = self.loads.copy() 74*635a8641SAndroid Build Coastguard Worker rv.stores = self.stores.copy() 75*635a8641SAndroid Build Coastguard Worker return rv 76*635a8641SAndroid Build Coastguard Worker 77*635a8641SAndroid Build Coastguard Worker def store(self, name): 78*635a8641SAndroid Build Coastguard Worker self.stores.add(name) 79*635a8641SAndroid Build Coastguard Worker 80*635a8641SAndroid Build Coastguard Worker # If we have not see the name referenced yet, we need to figure 81*635a8641SAndroid Build Coastguard Worker # out what to set it to. 82*635a8641SAndroid Build Coastguard Worker if name not in self.refs: 83*635a8641SAndroid Build Coastguard Worker # If there is a parent scope we check if the name has a 84*635a8641SAndroid Build Coastguard Worker # reference there. If it does it means we might have to alias 85*635a8641SAndroid Build Coastguard Worker # to a variable there. 86*635a8641SAndroid Build Coastguard Worker if self.parent is not None: 87*635a8641SAndroid Build Coastguard Worker outer_ref = self.parent.find_ref(name) 88*635a8641SAndroid Build Coastguard Worker if outer_ref is not None: 89*635a8641SAndroid Build Coastguard Worker self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref)) 90*635a8641SAndroid Build Coastguard Worker return 91*635a8641SAndroid Build Coastguard Worker 92*635a8641SAndroid Build Coastguard Worker # Otherwise we can just set it to undefined. 93*635a8641SAndroid Build Coastguard Worker self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None)) 94*635a8641SAndroid Build Coastguard Worker 95*635a8641SAndroid Build Coastguard Worker def declare_parameter(self, name): 96*635a8641SAndroid Build Coastguard Worker self.stores.add(name) 97*635a8641SAndroid Build Coastguard Worker return self._define_ref(name, load=(VAR_LOAD_PARAMETER, None)) 98*635a8641SAndroid Build Coastguard Worker 99*635a8641SAndroid Build Coastguard Worker def load(self, name): 100*635a8641SAndroid Build Coastguard Worker target = self.find_ref(name) 101*635a8641SAndroid Build Coastguard Worker if target is None: 102*635a8641SAndroid Build Coastguard Worker self._define_ref(name, load=(VAR_LOAD_RESOLVE, name)) 103*635a8641SAndroid Build Coastguard Worker 104*635a8641SAndroid Build Coastguard Worker def branch_update(self, branch_symbols): 105*635a8641SAndroid Build Coastguard Worker stores = {} 106*635a8641SAndroid Build Coastguard Worker for branch in branch_symbols: 107*635a8641SAndroid Build Coastguard Worker for target in branch.stores: 108*635a8641SAndroid Build Coastguard Worker if target in self.stores: 109*635a8641SAndroid Build Coastguard Worker continue 110*635a8641SAndroid Build Coastguard Worker stores[target] = stores.get(target, 0) + 1 111*635a8641SAndroid Build Coastguard Worker 112*635a8641SAndroid Build Coastguard Worker for sym in branch_symbols: 113*635a8641SAndroid Build Coastguard Worker self.refs.update(sym.refs) 114*635a8641SAndroid Build Coastguard Worker self.loads.update(sym.loads) 115*635a8641SAndroid Build Coastguard Worker self.stores.update(sym.stores) 116*635a8641SAndroid Build Coastguard Worker 117*635a8641SAndroid Build Coastguard Worker for name, branch_count in iteritems(stores): 118*635a8641SAndroid Build Coastguard Worker if branch_count == len(branch_symbols): 119*635a8641SAndroid Build Coastguard Worker continue 120*635a8641SAndroid Build Coastguard Worker target = self.find_ref(name) 121*635a8641SAndroid Build Coastguard Worker assert target is not None, 'should not happen' 122*635a8641SAndroid Build Coastguard Worker 123*635a8641SAndroid Build Coastguard Worker if self.parent is not None: 124*635a8641SAndroid Build Coastguard Worker outer_target = self.parent.find_ref(name) 125*635a8641SAndroid Build Coastguard Worker if outer_target is not None: 126*635a8641SAndroid Build Coastguard Worker self.loads[target] = (VAR_LOAD_ALIAS, outer_target) 127*635a8641SAndroid Build Coastguard Worker continue 128*635a8641SAndroid Build Coastguard Worker self.loads[target] = (VAR_LOAD_RESOLVE, name) 129*635a8641SAndroid Build Coastguard Worker 130*635a8641SAndroid Build Coastguard Worker def dump_stores(self): 131*635a8641SAndroid Build Coastguard Worker rv = {} 132*635a8641SAndroid Build Coastguard Worker node = self 133*635a8641SAndroid Build Coastguard Worker while node is not None: 134*635a8641SAndroid Build Coastguard Worker for name in node.stores: 135*635a8641SAndroid Build Coastguard Worker if name not in rv: 136*635a8641SAndroid Build Coastguard Worker rv[name] = self.find_ref(name) 137*635a8641SAndroid Build Coastguard Worker node = node.parent 138*635a8641SAndroid Build Coastguard Worker return rv 139*635a8641SAndroid Build Coastguard Worker 140*635a8641SAndroid Build Coastguard Worker def dump_param_targets(self): 141*635a8641SAndroid Build Coastguard Worker rv = set() 142*635a8641SAndroid Build Coastguard Worker node = self 143*635a8641SAndroid Build Coastguard Worker while node is not None: 144*635a8641SAndroid Build Coastguard Worker for target, (instr, _) in iteritems(self.loads): 145*635a8641SAndroid Build Coastguard Worker if instr == VAR_LOAD_PARAMETER: 146*635a8641SAndroid Build Coastguard Worker rv.add(target) 147*635a8641SAndroid Build Coastguard Worker node = node.parent 148*635a8641SAndroid Build Coastguard Worker return rv 149*635a8641SAndroid Build Coastguard Worker 150*635a8641SAndroid Build Coastguard Worker 151*635a8641SAndroid Build Coastguard Workerclass RootVisitor(NodeVisitor): 152*635a8641SAndroid Build Coastguard Worker 153*635a8641SAndroid Build Coastguard Worker def __init__(self, symbols): 154*635a8641SAndroid Build Coastguard Worker self.sym_visitor = FrameSymbolVisitor(symbols) 155*635a8641SAndroid Build Coastguard Worker 156*635a8641SAndroid Build Coastguard Worker def _simple_visit(self, node, **kwargs): 157*635a8641SAndroid Build Coastguard Worker for child in node.iter_child_nodes(): 158*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(child) 159*635a8641SAndroid Build Coastguard Worker 160*635a8641SAndroid Build Coastguard Worker visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \ 161*635a8641SAndroid Build Coastguard Worker visit_Scope = visit_If = visit_ScopedEvalContextModifier = \ 162*635a8641SAndroid Build Coastguard Worker _simple_visit 163*635a8641SAndroid Build Coastguard Worker 164*635a8641SAndroid Build Coastguard Worker def visit_AssignBlock(self, node, **kwargs): 165*635a8641SAndroid Build Coastguard Worker for child in node.body: 166*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(child) 167*635a8641SAndroid Build Coastguard Worker 168*635a8641SAndroid Build Coastguard Worker def visit_CallBlock(self, node, **kwargs): 169*635a8641SAndroid Build Coastguard Worker for child in node.iter_child_nodes(exclude=('call',)): 170*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(child) 171*635a8641SAndroid Build Coastguard Worker 172*635a8641SAndroid Build Coastguard Worker def visit_OverlayScope(self, node, **kwargs): 173*635a8641SAndroid Build Coastguard Worker for child in node.body: 174*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(child) 175*635a8641SAndroid Build Coastguard Worker 176*635a8641SAndroid Build Coastguard Worker def visit_For(self, node, for_branch='body', **kwargs): 177*635a8641SAndroid Build Coastguard Worker if for_branch == 'body': 178*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(node.target, store_as_param=True) 179*635a8641SAndroid Build Coastguard Worker branch = node.body 180*635a8641SAndroid Build Coastguard Worker elif for_branch == 'else': 181*635a8641SAndroid Build Coastguard Worker branch = node.else_ 182*635a8641SAndroid Build Coastguard Worker elif for_branch == 'test': 183*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(node.target, store_as_param=True) 184*635a8641SAndroid Build Coastguard Worker if node.test is not None: 185*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(node.test) 186*635a8641SAndroid Build Coastguard Worker return 187*635a8641SAndroid Build Coastguard Worker else: 188*635a8641SAndroid Build Coastguard Worker raise RuntimeError('Unknown for branch') 189*635a8641SAndroid Build Coastguard Worker for item in branch or (): 190*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(item) 191*635a8641SAndroid Build Coastguard Worker 192*635a8641SAndroid Build Coastguard Worker def visit_With(self, node, **kwargs): 193*635a8641SAndroid Build Coastguard Worker for target in node.targets: 194*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(target) 195*635a8641SAndroid Build Coastguard Worker for child in node.body: 196*635a8641SAndroid Build Coastguard Worker self.sym_visitor.visit(child) 197*635a8641SAndroid Build Coastguard Worker 198*635a8641SAndroid Build Coastguard Worker def generic_visit(self, node, *args, **kwargs): 199*635a8641SAndroid Build Coastguard Worker raise NotImplementedError('Cannot find symbols for %r' % 200*635a8641SAndroid Build Coastguard Worker node.__class__.__name__) 201*635a8641SAndroid Build Coastguard Worker 202*635a8641SAndroid Build Coastguard Worker 203*635a8641SAndroid Build Coastguard Workerclass FrameSymbolVisitor(NodeVisitor): 204*635a8641SAndroid Build Coastguard Worker """A visitor for `Frame.inspect`.""" 205*635a8641SAndroid Build Coastguard Worker 206*635a8641SAndroid Build Coastguard Worker def __init__(self, symbols): 207*635a8641SAndroid Build Coastguard Worker self.symbols = symbols 208*635a8641SAndroid Build Coastguard Worker 209*635a8641SAndroid Build Coastguard Worker def visit_Name(self, node, store_as_param=False, **kwargs): 210*635a8641SAndroid Build Coastguard Worker """All assignments to names go through this function.""" 211*635a8641SAndroid Build Coastguard Worker if store_as_param or node.ctx == 'param': 212*635a8641SAndroid Build Coastguard Worker self.symbols.declare_parameter(node.name) 213*635a8641SAndroid Build Coastguard Worker elif node.ctx == 'store': 214*635a8641SAndroid Build Coastguard Worker self.symbols.store(node.name) 215*635a8641SAndroid Build Coastguard Worker elif node.ctx == 'load': 216*635a8641SAndroid Build Coastguard Worker self.symbols.load(node.name) 217*635a8641SAndroid Build Coastguard Worker 218*635a8641SAndroid Build Coastguard Worker def visit_NSRef(self, node, **kwargs): 219*635a8641SAndroid Build Coastguard Worker self.symbols.load(node.name) 220*635a8641SAndroid Build Coastguard Worker 221*635a8641SAndroid Build Coastguard Worker def visit_If(self, node, **kwargs): 222*635a8641SAndroid Build Coastguard Worker self.visit(node.test, **kwargs) 223*635a8641SAndroid Build Coastguard Worker 224*635a8641SAndroid Build Coastguard Worker original_symbols = self.symbols 225*635a8641SAndroid Build Coastguard Worker 226*635a8641SAndroid Build Coastguard Worker def inner_visit(nodes): 227*635a8641SAndroid Build Coastguard Worker self.symbols = rv = original_symbols.copy() 228*635a8641SAndroid Build Coastguard Worker for subnode in nodes: 229*635a8641SAndroid Build Coastguard Worker self.visit(subnode, **kwargs) 230*635a8641SAndroid Build Coastguard Worker self.symbols = original_symbols 231*635a8641SAndroid Build Coastguard Worker return rv 232*635a8641SAndroid Build Coastguard Worker 233*635a8641SAndroid Build Coastguard Worker body_symbols = inner_visit(node.body) 234*635a8641SAndroid Build Coastguard Worker elif_symbols = inner_visit(node.elif_) 235*635a8641SAndroid Build Coastguard Worker else_symbols = inner_visit(node.else_ or ()) 236*635a8641SAndroid Build Coastguard Worker 237*635a8641SAndroid Build Coastguard Worker self.symbols.branch_update([body_symbols, elif_symbols, else_symbols]) 238*635a8641SAndroid Build Coastguard Worker 239*635a8641SAndroid Build Coastguard Worker def visit_Macro(self, node, **kwargs): 240*635a8641SAndroid Build Coastguard Worker self.symbols.store(node.name) 241*635a8641SAndroid Build Coastguard Worker 242*635a8641SAndroid Build Coastguard Worker def visit_Import(self, node, **kwargs): 243*635a8641SAndroid Build Coastguard Worker self.generic_visit(node, **kwargs) 244*635a8641SAndroid Build Coastguard Worker self.symbols.store(node.target) 245*635a8641SAndroid Build Coastguard Worker 246*635a8641SAndroid Build Coastguard Worker def visit_FromImport(self, node, **kwargs): 247*635a8641SAndroid Build Coastguard Worker self.generic_visit(node, **kwargs) 248*635a8641SAndroid Build Coastguard Worker for name in node.names: 249*635a8641SAndroid Build Coastguard Worker if isinstance(name, tuple): 250*635a8641SAndroid Build Coastguard Worker self.symbols.store(name[1]) 251*635a8641SAndroid Build Coastguard Worker else: 252*635a8641SAndroid Build Coastguard Worker self.symbols.store(name) 253*635a8641SAndroid Build Coastguard Worker 254*635a8641SAndroid Build Coastguard Worker def visit_Assign(self, node, **kwargs): 255*635a8641SAndroid Build Coastguard Worker """Visit assignments in the correct order.""" 256*635a8641SAndroid Build Coastguard Worker self.visit(node.node, **kwargs) 257*635a8641SAndroid Build Coastguard Worker self.visit(node.target, **kwargs) 258*635a8641SAndroid Build Coastguard Worker 259*635a8641SAndroid Build Coastguard Worker def visit_For(self, node, **kwargs): 260*635a8641SAndroid Build Coastguard Worker """Visiting stops at for blocks. However the block sequence 261*635a8641SAndroid Build Coastguard Worker is visited as part of the outer scope. 262*635a8641SAndroid Build Coastguard Worker """ 263*635a8641SAndroid Build Coastguard Worker self.visit(node.iter, **kwargs) 264*635a8641SAndroid Build Coastguard Worker 265*635a8641SAndroid Build Coastguard Worker def visit_CallBlock(self, node, **kwargs): 266*635a8641SAndroid Build Coastguard Worker self.visit(node.call, **kwargs) 267*635a8641SAndroid Build Coastguard Worker 268*635a8641SAndroid Build Coastguard Worker def visit_FilterBlock(self, node, **kwargs): 269*635a8641SAndroid Build Coastguard Worker self.visit(node.filter, **kwargs) 270*635a8641SAndroid Build Coastguard Worker 271*635a8641SAndroid Build Coastguard Worker def visit_With(self, node, **kwargs): 272*635a8641SAndroid Build Coastguard Worker for target in node.values: 273*635a8641SAndroid Build Coastguard Worker self.visit(target) 274*635a8641SAndroid Build Coastguard Worker 275*635a8641SAndroid Build Coastguard Worker def visit_AssignBlock(self, node, **kwargs): 276*635a8641SAndroid Build Coastguard Worker """Stop visiting at block assigns.""" 277*635a8641SAndroid Build Coastguard Worker self.visit(node.target, **kwargs) 278*635a8641SAndroid Build Coastguard Worker 279*635a8641SAndroid Build Coastguard Worker def visit_Scope(self, node, **kwargs): 280*635a8641SAndroid Build Coastguard Worker """Stop visiting at scopes.""" 281*635a8641SAndroid Build Coastguard Worker 282*635a8641SAndroid Build Coastguard Worker def visit_Block(self, node, **kwargs): 283*635a8641SAndroid Build Coastguard Worker """Stop visiting at blocks.""" 284*635a8641SAndroid Build Coastguard Worker 285*635a8641SAndroid Build Coastguard Worker def visit_OverlayScope(self, node, **kwargs): 286*635a8641SAndroid Build Coastguard Worker """Do not visit into overlay scopes.""" 287