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