11cb0ef41Sopenharmony_cifrom jinja2.visitor import NodeVisitor 21cb0ef41Sopenharmony_cifrom jinja2._compat import iteritems 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciVAR_LOAD_PARAMETER = 'param' 61cb0ef41Sopenharmony_ciVAR_LOAD_RESOLVE = 'resolve' 71cb0ef41Sopenharmony_ciVAR_LOAD_ALIAS = 'alias' 81cb0ef41Sopenharmony_ciVAR_LOAD_UNDEFINED = 'undefined' 91cb0ef41Sopenharmony_ci 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_cidef find_symbols(nodes, parent_symbols=None): 121cb0ef41Sopenharmony_ci sym = Symbols(parent=parent_symbols) 131cb0ef41Sopenharmony_ci visitor = FrameSymbolVisitor(sym) 141cb0ef41Sopenharmony_ci for node in nodes: 151cb0ef41Sopenharmony_ci visitor.visit(node) 161cb0ef41Sopenharmony_ci return sym 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cidef symbols_for_node(node, parent_symbols=None): 201cb0ef41Sopenharmony_ci sym = Symbols(parent=parent_symbols) 211cb0ef41Sopenharmony_ci sym.analyze_node(node) 221cb0ef41Sopenharmony_ci return sym 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ciclass Symbols(object): 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci def __init__(self, parent=None, level=None): 281cb0ef41Sopenharmony_ci if level is None: 291cb0ef41Sopenharmony_ci if parent is None: 301cb0ef41Sopenharmony_ci level = 0 311cb0ef41Sopenharmony_ci else: 321cb0ef41Sopenharmony_ci level = parent.level + 1 331cb0ef41Sopenharmony_ci self.level = level 341cb0ef41Sopenharmony_ci self.parent = parent 351cb0ef41Sopenharmony_ci self.refs = {} 361cb0ef41Sopenharmony_ci self.loads = {} 371cb0ef41Sopenharmony_ci self.stores = set() 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci def analyze_node(self, node, **kwargs): 401cb0ef41Sopenharmony_ci visitor = RootVisitor(self) 411cb0ef41Sopenharmony_ci visitor.visit(node, **kwargs) 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ci def _define_ref(self, name, load=None): 441cb0ef41Sopenharmony_ci ident = 'l_%d_%s' % (self.level, name) 451cb0ef41Sopenharmony_ci self.refs[name] = ident 461cb0ef41Sopenharmony_ci if load is not None: 471cb0ef41Sopenharmony_ci self.loads[ident] = load 481cb0ef41Sopenharmony_ci return ident 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci def find_load(self, target): 511cb0ef41Sopenharmony_ci if target in self.loads: 521cb0ef41Sopenharmony_ci return self.loads[target] 531cb0ef41Sopenharmony_ci if self.parent is not None: 541cb0ef41Sopenharmony_ci return self.parent.find_load(target) 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_ci def find_ref(self, name): 571cb0ef41Sopenharmony_ci if name in self.refs: 581cb0ef41Sopenharmony_ci return self.refs[name] 591cb0ef41Sopenharmony_ci if self.parent is not None: 601cb0ef41Sopenharmony_ci return self.parent.find_ref(name) 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci def ref(self, name): 631cb0ef41Sopenharmony_ci rv = self.find_ref(name) 641cb0ef41Sopenharmony_ci if rv is None: 651cb0ef41Sopenharmony_ci raise AssertionError('Tried to resolve a name to a reference that ' 661cb0ef41Sopenharmony_ci 'was unknown to the frame (%r)' % name) 671cb0ef41Sopenharmony_ci return rv 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci def copy(self): 701cb0ef41Sopenharmony_ci rv = object.__new__(self.__class__) 711cb0ef41Sopenharmony_ci rv.__dict__.update(self.__dict__) 721cb0ef41Sopenharmony_ci rv.refs = self.refs.copy() 731cb0ef41Sopenharmony_ci rv.loads = self.loads.copy() 741cb0ef41Sopenharmony_ci rv.stores = self.stores.copy() 751cb0ef41Sopenharmony_ci return rv 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci def store(self, name): 781cb0ef41Sopenharmony_ci self.stores.add(name) 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci # If we have not see the name referenced yet, we need to figure 811cb0ef41Sopenharmony_ci # out what to set it to. 821cb0ef41Sopenharmony_ci if name not in self.refs: 831cb0ef41Sopenharmony_ci # If there is a parent scope we check if the name has a 841cb0ef41Sopenharmony_ci # reference there. If it does it means we might have to alias 851cb0ef41Sopenharmony_ci # to a variable there. 861cb0ef41Sopenharmony_ci if self.parent is not None: 871cb0ef41Sopenharmony_ci outer_ref = self.parent.find_ref(name) 881cb0ef41Sopenharmony_ci if outer_ref is not None: 891cb0ef41Sopenharmony_ci self._define_ref(name, load=(VAR_LOAD_ALIAS, outer_ref)) 901cb0ef41Sopenharmony_ci return 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci # Otherwise we can just set it to undefined. 931cb0ef41Sopenharmony_ci self._define_ref(name, load=(VAR_LOAD_UNDEFINED, None)) 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci def declare_parameter(self, name): 961cb0ef41Sopenharmony_ci self.stores.add(name) 971cb0ef41Sopenharmony_ci return self._define_ref(name, load=(VAR_LOAD_PARAMETER, None)) 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci def load(self, name): 1001cb0ef41Sopenharmony_ci target = self.find_ref(name) 1011cb0ef41Sopenharmony_ci if target is None: 1021cb0ef41Sopenharmony_ci self._define_ref(name, load=(VAR_LOAD_RESOLVE, name)) 1031cb0ef41Sopenharmony_ci 1041cb0ef41Sopenharmony_ci def branch_update(self, branch_symbols): 1051cb0ef41Sopenharmony_ci stores = {} 1061cb0ef41Sopenharmony_ci for branch in branch_symbols: 1071cb0ef41Sopenharmony_ci for target in branch.stores: 1081cb0ef41Sopenharmony_ci if target in self.stores: 1091cb0ef41Sopenharmony_ci continue 1101cb0ef41Sopenharmony_ci stores[target] = stores.get(target, 0) + 1 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci for sym in branch_symbols: 1131cb0ef41Sopenharmony_ci self.refs.update(sym.refs) 1141cb0ef41Sopenharmony_ci self.loads.update(sym.loads) 1151cb0ef41Sopenharmony_ci self.stores.update(sym.stores) 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci for name, branch_count in iteritems(stores): 1181cb0ef41Sopenharmony_ci if branch_count == len(branch_symbols): 1191cb0ef41Sopenharmony_ci continue 1201cb0ef41Sopenharmony_ci target = self.find_ref(name) 1211cb0ef41Sopenharmony_ci assert target is not None, 'should not happen' 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci if self.parent is not None: 1241cb0ef41Sopenharmony_ci outer_target = self.parent.find_ref(name) 1251cb0ef41Sopenharmony_ci if outer_target is not None: 1261cb0ef41Sopenharmony_ci self.loads[target] = (VAR_LOAD_ALIAS, outer_target) 1271cb0ef41Sopenharmony_ci continue 1281cb0ef41Sopenharmony_ci self.loads[target] = (VAR_LOAD_RESOLVE, name) 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci def dump_stores(self): 1311cb0ef41Sopenharmony_ci rv = {} 1321cb0ef41Sopenharmony_ci node = self 1331cb0ef41Sopenharmony_ci while node is not None: 1341cb0ef41Sopenharmony_ci for name in node.stores: 1351cb0ef41Sopenharmony_ci if name not in rv: 1361cb0ef41Sopenharmony_ci rv[name] = self.find_ref(name) 1371cb0ef41Sopenharmony_ci node = node.parent 1381cb0ef41Sopenharmony_ci return rv 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci def dump_param_targets(self): 1411cb0ef41Sopenharmony_ci rv = set() 1421cb0ef41Sopenharmony_ci node = self 1431cb0ef41Sopenharmony_ci while node is not None: 1441cb0ef41Sopenharmony_ci for target, (instr, _) in iteritems(self.loads): 1451cb0ef41Sopenharmony_ci if instr == VAR_LOAD_PARAMETER: 1461cb0ef41Sopenharmony_ci rv.add(target) 1471cb0ef41Sopenharmony_ci node = node.parent 1481cb0ef41Sopenharmony_ci return rv 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ciclass RootVisitor(NodeVisitor): 1521cb0ef41Sopenharmony_ci 1531cb0ef41Sopenharmony_ci def __init__(self, symbols): 1541cb0ef41Sopenharmony_ci self.sym_visitor = FrameSymbolVisitor(symbols) 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci def _simple_visit(self, node, **kwargs): 1571cb0ef41Sopenharmony_ci for child in node.iter_child_nodes(): 1581cb0ef41Sopenharmony_ci self.sym_visitor.visit(child) 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci visit_Template = visit_Block = visit_Macro = visit_FilterBlock = \ 1611cb0ef41Sopenharmony_ci visit_Scope = visit_If = visit_ScopedEvalContextModifier = \ 1621cb0ef41Sopenharmony_ci _simple_visit 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci def visit_AssignBlock(self, node, **kwargs): 1651cb0ef41Sopenharmony_ci for child in node.body: 1661cb0ef41Sopenharmony_ci self.sym_visitor.visit(child) 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci def visit_CallBlock(self, node, **kwargs): 1691cb0ef41Sopenharmony_ci for child in node.iter_child_nodes(exclude=('call',)): 1701cb0ef41Sopenharmony_ci self.sym_visitor.visit(child) 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci def visit_OverlayScope(self, node, **kwargs): 1731cb0ef41Sopenharmony_ci for child in node.body: 1741cb0ef41Sopenharmony_ci self.sym_visitor.visit(child) 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci def visit_For(self, node, for_branch='body', **kwargs): 1771cb0ef41Sopenharmony_ci if for_branch == 'body': 1781cb0ef41Sopenharmony_ci self.sym_visitor.visit(node.target, store_as_param=True) 1791cb0ef41Sopenharmony_ci branch = node.body 1801cb0ef41Sopenharmony_ci elif for_branch == 'else': 1811cb0ef41Sopenharmony_ci branch = node.else_ 1821cb0ef41Sopenharmony_ci elif for_branch == 'test': 1831cb0ef41Sopenharmony_ci self.sym_visitor.visit(node.target, store_as_param=True) 1841cb0ef41Sopenharmony_ci if node.test is not None: 1851cb0ef41Sopenharmony_ci self.sym_visitor.visit(node.test) 1861cb0ef41Sopenharmony_ci return 1871cb0ef41Sopenharmony_ci else: 1881cb0ef41Sopenharmony_ci raise RuntimeError('Unknown for branch') 1891cb0ef41Sopenharmony_ci for item in branch or (): 1901cb0ef41Sopenharmony_ci self.sym_visitor.visit(item) 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci def visit_With(self, node, **kwargs): 1931cb0ef41Sopenharmony_ci for target in node.targets: 1941cb0ef41Sopenharmony_ci self.sym_visitor.visit(target) 1951cb0ef41Sopenharmony_ci for child in node.body: 1961cb0ef41Sopenharmony_ci self.sym_visitor.visit(child) 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci def generic_visit(self, node, *args, **kwargs): 1991cb0ef41Sopenharmony_ci raise NotImplementedError('Cannot find symbols for %r' % 2001cb0ef41Sopenharmony_ci node.__class__.__name__) 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ciclass FrameSymbolVisitor(NodeVisitor): 2041cb0ef41Sopenharmony_ci """A visitor for `Frame.inspect`.""" 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ci def __init__(self, symbols): 2071cb0ef41Sopenharmony_ci self.symbols = symbols 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci def visit_Name(self, node, store_as_param=False, **kwargs): 2101cb0ef41Sopenharmony_ci """All assignments to names go through this function.""" 2111cb0ef41Sopenharmony_ci if store_as_param or node.ctx == 'param': 2121cb0ef41Sopenharmony_ci self.symbols.declare_parameter(node.name) 2131cb0ef41Sopenharmony_ci elif node.ctx == 'store': 2141cb0ef41Sopenharmony_ci self.symbols.store(node.name) 2151cb0ef41Sopenharmony_ci elif node.ctx == 'load': 2161cb0ef41Sopenharmony_ci self.symbols.load(node.name) 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci def visit_NSRef(self, node, **kwargs): 2191cb0ef41Sopenharmony_ci self.symbols.load(node.name) 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci def visit_If(self, node, **kwargs): 2221cb0ef41Sopenharmony_ci self.visit(node.test, **kwargs) 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_ci original_symbols = self.symbols 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci def inner_visit(nodes): 2271cb0ef41Sopenharmony_ci self.symbols = rv = original_symbols.copy() 2281cb0ef41Sopenharmony_ci for subnode in nodes: 2291cb0ef41Sopenharmony_ci self.visit(subnode, **kwargs) 2301cb0ef41Sopenharmony_ci self.symbols = original_symbols 2311cb0ef41Sopenharmony_ci return rv 2321cb0ef41Sopenharmony_ci 2331cb0ef41Sopenharmony_ci body_symbols = inner_visit(node.body) 2341cb0ef41Sopenharmony_ci elif_symbols = inner_visit(node.elif_) 2351cb0ef41Sopenharmony_ci else_symbols = inner_visit(node.else_ or ()) 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci self.symbols.branch_update([body_symbols, elif_symbols, else_symbols]) 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci def visit_Macro(self, node, **kwargs): 2401cb0ef41Sopenharmony_ci self.symbols.store(node.name) 2411cb0ef41Sopenharmony_ci 2421cb0ef41Sopenharmony_ci def visit_Import(self, node, **kwargs): 2431cb0ef41Sopenharmony_ci self.generic_visit(node, **kwargs) 2441cb0ef41Sopenharmony_ci self.symbols.store(node.target) 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci def visit_FromImport(self, node, **kwargs): 2471cb0ef41Sopenharmony_ci self.generic_visit(node, **kwargs) 2481cb0ef41Sopenharmony_ci for name in node.names: 2491cb0ef41Sopenharmony_ci if isinstance(name, tuple): 2501cb0ef41Sopenharmony_ci self.symbols.store(name[1]) 2511cb0ef41Sopenharmony_ci else: 2521cb0ef41Sopenharmony_ci self.symbols.store(name) 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci def visit_Assign(self, node, **kwargs): 2551cb0ef41Sopenharmony_ci """Visit assignments in the correct order.""" 2561cb0ef41Sopenharmony_ci self.visit(node.node, **kwargs) 2571cb0ef41Sopenharmony_ci self.visit(node.target, **kwargs) 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci def visit_For(self, node, **kwargs): 2601cb0ef41Sopenharmony_ci """Visiting stops at for blocks. However the block sequence 2611cb0ef41Sopenharmony_ci is visited as part of the outer scope. 2621cb0ef41Sopenharmony_ci """ 2631cb0ef41Sopenharmony_ci self.visit(node.iter, **kwargs) 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci def visit_CallBlock(self, node, **kwargs): 2661cb0ef41Sopenharmony_ci self.visit(node.call, **kwargs) 2671cb0ef41Sopenharmony_ci 2681cb0ef41Sopenharmony_ci def visit_FilterBlock(self, node, **kwargs): 2691cb0ef41Sopenharmony_ci self.visit(node.filter, **kwargs) 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci def visit_With(self, node, **kwargs): 2721cb0ef41Sopenharmony_ci for target in node.values: 2731cb0ef41Sopenharmony_ci self.visit(target) 2741cb0ef41Sopenharmony_ci 2751cb0ef41Sopenharmony_ci def visit_AssignBlock(self, node, **kwargs): 2761cb0ef41Sopenharmony_ci """Stop visiting at block assigns.""" 2771cb0ef41Sopenharmony_ci self.visit(node.target, **kwargs) 2781cb0ef41Sopenharmony_ci 2791cb0ef41Sopenharmony_ci def visit_Scope(self, node, **kwargs): 2801cb0ef41Sopenharmony_ci """Stop visiting at scopes.""" 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci def visit_Block(self, node, **kwargs): 2831cb0ef41Sopenharmony_ci """Stop visiting at blocks.""" 2841cb0ef41Sopenharmony_ci 2851cb0ef41Sopenharmony_ci def visit_OverlayScope(self, node, **kwargs): 2861cb0ef41Sopenharmony_ci """Do not visit into overlay scopes.""" 287