xref: /third_party/jinja2/compiler.py (revision e31aef6a)
1e31aef6aSopenharmony_ci"""Compiles nodes from the parser into Python code."""
2e31aef6aSopenharmony_ciimport typing as t
3e31aef6aSopenharmony_cifrom contextlib import contextmanager
4e31aef6aSopenharmony_cifrom functools import update_wrapper
5e31aef6aSopenharmony_cifrom io import StringIO
6e31aef6aSopenharmony_cifrom itertools import chain
7e31aef6aSopenharmony_cifrom keyword import iskeyword as is_python_keyword
8e31aef6aSopenharmony_ci
9e31aef6aSopenharmony_cifrom markupsafe import escape
10e31aef6aSopenharmony_cifrom markupsafe import Markup
11e31aef6aSopenharmony_ci
12e31aef6aSopenharmony_cifrom . import nodes
13e31aef6aSopenharmony_cifrom .exceptions import TemplateAssertionError
14e31aef6aSopenharmony_cifrom .idtracking import Symbols
15e31aef6aSopenharmony_cifrom .idtracking import VAR_LOAD_ALIAS
16e31aef6aSopenharmony_cifrom .idtracking import VAR_LOAD_PARAMETER
17e31aef6aSopenharmony_cifrom .idtracking import VAR_LOAD_RESOLVE
18e31aef6aSopenharmony_cifrom .idtracking import VAR_LOAD_UNDEFINED
19e31aef6aSopenharmony_cifrom .nodes import EvalContext
20e31aef6aSopenharmony_cifrom .optimizer import Optimizer
21e31aef6aSopenharmony_cifrom .utils import _PassArg
22e31aef6aSopenharmony_cifrom .utils import concat
23e31aef6aSopenharmony_cifrom .visitor import NodeVisitor
24e31aef6aSopenharmony_ci
25e31aef6aSopenharmony_ciif t.TYPE_CHECKING:
26e31aef6aSopenharmony_ci    import typing_extensions as te
27e31aef6aSopenharmony_ci    from .environment import Environment
28e31aef6aSopenharmony_ci
29e31aef6aSopenharmony_ciF = t.TypeVar("F", bound=t.Callable[..., t.Any])
30e31aef6aSopenharmony_ci
31e31aef6aSopenharmony_cioperators = {
32e31aef6aSopenharmony_ci    "eq": "==",
33e31aef6aSopenharmony_ci    "ne": "!=",
34e31aef6aSopenharmony_ci    "gt": ">",
35e31aef6aSopenharmony_ci    "gteq": ">=",
36e31aef6aSopenharmony_ci    "lt": "<",
37e31aef6aSopenharmony_ci    "lteq": "<=",
38e31aef6aSopenharmony_ci    "in": "in",
39e31aef6aSopenharmony_ci    "notin": "not in",
40e31aef6aSopenharmony_ci}
41e31aef6aSopenharmony_ci
42e31aef6aSopenharmony_ci
43e31aef6aSopenharmony_cidef optimizeconst(f: F) -> F:
44e31aef6aSopenharmony_ci    def new_func(
45e31aef6aSopenharmony_ci        self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
46e31aef6aSopenharmony_ci    ) -> t.Any:
47e31aef6aSopenharmony_ci        # Only optimize if the frame is not volatile
48e31aef6aSopenharmony_ci        if self.optimizer is not None and not frame.eval_ctx.volatile:
49e31aef6aSopenharmony_ci            new_node = self.optimizer.visit(node, frame.eval_ctx)
50e31aef6aSopenharmony_ci
51e31aef6aSopenharmony_ci            if new_node != node:
52e31aef6aSopenharmony_ci                return self.visit(new_node, frame)
53e31aef6aSopenharmony_ci
54e31aef6aSopenharmony_ci        return f(self, node, frame, **kwargs)
55e31aef6aSopenharmony_ci
56e31aef6aSopenharmony_ci    return update_wrapper(t.cast(F, new_func), f)
57e31aef6aSopenharmony_ci
58e31aef6aSopenharmony_ci
59e31aef6aSopenharmony_cidef _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]:
60e31aef6aSopenharmony_ci    @optimizeconst
61e31aef6aSopenharmony_ci    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
62e31aef6aSopenharmony_ci        if (
63e31aef6aSopenharmony_ci            self.environment.sandboxed
64e31aef6aSopenharmony_ci            and op in self.environment.intercepted_binops  # type: ignore
65e31aef6aSopenharmony_ci        ):
66e31aef6aSopenharmony_ci            self.write(f"environment.call_binop(context, {op!r}, ")
67e31aef6aSopenharmony_ci            self.visit(node.left, frame)
68e31aef6aSopenharmony_ci            self.write(", ")
69e31aef6aSopenharmony_ci            self.visit(node.right, frame)
70e31aef6aSopenharmony_ci        else:
71e31aef6aSopenharmony_ci            self.write("(")
72e31aef6aSopenharmony_ci            self.visit(node.left, frame)
73e31aef6aSopenharmony_ci            self.write(f" {op} ")
74e31aef6aSopenharmony_ci            self.visit(node.right, frame)
75e31aef6aSopenharmony_ci
76e31aef6aSopenharmony_ci        self.write(")")
77e31aef6aSopenharmony_ci
78e31aef6aSopenharmony_ci    return visitor
79e31aef6aSopenharmony_ci
80e31aef6aSopenharmony_ci
81e31aef6aSopenharmony_cidef _make_unop(
82e31aef6aSopenharmony_ci    op: str,
83e31aef6aSopenharmony_ci) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]:
84e31aef6aSopenharmony_ci    @optimizeconst
85e31aef6aSopenharmony_ci    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
86e31aef6aSopenharmony_ci        if (
87e31aef6aSopenharmony_ci            self.environment.sandboxed
88e31aef6aSopenharmony_ci            and op in self.environment.intercepted_unops  # type: ignore
89e31aef6aSopenharmony_ci        ):
90e31aef6aSopenharmony_ci            self.write(f"environment.call_unop(context, {op!r}, ")
91e31aef6aSopenharmony_ci            self.visit(node.node, frame)
92e31aef6aSopenharmony_ci        else:
93e31aef6aSopenharmony_ci            self.write("(" + op)
94e31aef6aSopenharmony_ci            self.visit(node.node, frame)
95e31aef6aSopenharmony_ci
96e31aef6aSopenharmony_ci        self.write(")")
97e31aef6aSopenharmony_ci
98e31aef6aSopenharmony_ci    return visitor
99e31aef6aSopenharmony_ci
100e31aef6aSopenharmony_ci
101e31aef6aSopenharmony_cidef generate(
102e31aef6aSopenharmony_ci    node: nodes.Template,
103e31aef6aSopenharmony_ci    environment: "Environment",
104e31aef6aSopenharmony_ci    name: t.Optional[str],
105e31aef6aSopenharmony_ci    filename: t.Optional[str],
106e31aef6aSopenharmony_ci    stream: t.Optional[t.TextIO] = None,
107e31aef6aSopenharmony_ci    defer_init: bool = False,
108e31aef6aSopenharmony_ci    optimized: bool = True,
109e31aef6aSopenharmony_ci) -> t.Optional[str]:
110e31aef6aSopenharmony_ci    """Generate the python source for a node tree."""
111e31aef6aSopenharmony_ci    if not isinstance(node, nodes.Template):
112e31aef6aSopenharmony_ci        raise TypeError("Can't compile non template nodes")
113e31aef6aSopenharmony_ci
114e31aef6aSopenharmony_ci    generator = environment.code_generator_class(
115e31aef6aSopenharmony_ci        environment, name, filename, stream, defer_init, optimized
116e31aef6aSopenharmony_ci    )
117e31aef6aSopenharmony_ci    generator.visit(node)
118e31aef6aSopenharmony_ci
119e31aef6aSopenharmony_ci    if stream is None:
120e31aef6aSopenharmony_ci        return generator.stream.getvalue()  # type: ignore
121e31aef6aSopenharmony_ci
122e31aef6aSopenharmony_ci    return None
123e31aef6aSopenharmony_ci
124e31aef6aSopenharmony_ci
125e31aef6aSopenharmony_cidef has_safe_repr(value: t.Any) -> bool:
126e31aef6aSopenharmony_ci    """Does the node have a safe representation?"""
127e31aef6aSopenharmony_ci    if value is None or value is NotImplemented or value is Ellipsis:
128e31aef6aSopenharmony_ci        return True
129e31aef6aSopenharmony_ci
130e31aef6aSopenharmony_ci    if type(value) in {bool, int, float, complex, range, str, Markup}:
131e31aef6aSopenharmony_ci        return True
132e31aef6aSopenharmony_ci
133e31aef6aSopenharmony_ci    if type(value) in {tuple, list, set, frozenset}:
134e31aef6aSopenharmony_ci        return all(has_safe_repr(v) for v in value)
135e31aef6aSopenharmony_ci
136e31aef6aSopenharmony_ci    if type(value) is dict:
137e31aef6aSopenharmony_ci        return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
138e31aef6aSopenharmony_ci
139e31aef6aSopenharmony_ci    return False
140e31aef6aSopenharmony_ci
141e31aef6aSopenharmony_ci
142e31aef6aSopenharmony_cidef find_undeclared(
143e31aef6aSopenharmony_ci    nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
144e31aef6aSopenharmony_ci) -> t.Set[str]:
145e31aef6aSopenharmony_ci    """Check if the names passed are accessed undeclared.  The return value
146e31aef6aSopenharmony_ci    is a set of all the undeclared names from the sequence of names found.
147e31aef6aSopenharmony_ci    """
148e31aef6aSopenharmony_ci    visitor = UndeclaredNameVisitor(names)
149e31aef6aSopenharmony_ci    try:
150e31aef6aSopenharmony_ci        for node in nodes:
151e31aef6aSopenharmony_ci            visitor.visit(node)
152e31aef6aSopenharmony_ci    except VisitorExit:
153e31aef6aSopenharmony_ci        pass
154e31aef6aSopenharmony_ci    return visitor.undeclared
155e31aef6aSopenharmony_ci
156e31aef6aSopenharmony_ci
157e31aef6aSopenharmony_ciclass MacroRef:
158e31aef6aSopenharmony_ci    def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
159e31aef6aSopenharmony_ci        self.node = node
160e31aef6aSopenharmony_ci        self.accesses_caller = False
161e31aef6aSopenharmony_ci        self.accesses_kwargs = False
162e31aef6aSopenharmony_ci        self.accesses_varargs = False
163e31aef6aSopenharmony_ci
164e31aef6aSopenharmony_ci
165e31aef6aSopenharmony_ciclass Frame:
166e31aef6aSopenharmony_ci    """Holds compile time information for us."""
167e31aef6aSopenharmony_ci
168e31aef6aSopenharmony_ci    def __init__(
169e31aef6aSopenharmony_ci        self,
170e31aef6aSopenharmony_ci        eval_ctx: EvalContext,
171e31aef6aSopenharmony_ci        parent: t.Optional["Frame"] = None,
172e31aef6aSopenharmony_ci        level: t.Optional[int] = None,
173e31aef6aSopenharmony_ci    ) -> None:
174e31aef6aSopenharmony_ci        self.eval_ctx = eval_ctx
175e31aef6aSopenharmony_ci
176e31aef6aSopenharmony_ci        # the parent of this frame
177e31aef6aSopenharmony_ci        self.parent = parent
178e31aef6aSopenharmony_ci
179e31aef6aSopenharmony_ci        if parent is None:
180e31aef6aSopenharmony_ci            self.symbols = Symbols(level=level)
181e31aef6aSopenharmony_ci
182e31aef6aSopenharmony_ci            # in some dynamic inheritance situations the compiler needs to add
183e31aef6aSopenharmony_ci            # write tests around output statements.
184e31aef6aSopenharmony_ci            self.require_output_check = False
185e31aef6aSopenharmony_ci
186e31aef6aSopenharmony_ci            # inside some tags we are using a buffer rather than yield statements.
187e31aef6aSopenharmony_ci            # this for example affects {% filter %} or {% macro %}.  If a frame
188e31aef6aSopenharmony_ci            # is buffered this variable points to the name of the list used as
189e31aef6aSopenharmony_ci            # buffer.
190e31aef6aSopenharmony_ci            self.buffer: t.Optional[str] = None
191e31aef6aSopenharmony_ci
192e31aef6aSopenharmony_ci            # the name of the block we're in, otherwise None.
193e31aef6aSopenharmony_ci            self.block: t.Optional[str] = None
194e31aef6aSopenharmony_ci
195e31aef6aSopenharmony_ci        else:
196e31aef6aSopenharmony_ci            self.symbols = Symbols(parent.symbols, level=level)
197e31aef6aSopenharmony_ci            self.require_output_check = parent.require_output_check
198e31aef6aSopenharmony_ci            self.buffer = parent.buffer
199e31aef6aSopenharmony_ci            self.block = parent.block
200e31aef6aSopenharmony_ci
201e31aef6aSopenharmony_ci        # a toplevel frame is the root + soft frames such as if conditions.
202e31aef6aSopenharmony_ci        self.toplevel = False
203e31aef6aSopenharmony_ci
204e31aef6aSopenharmony_ci        # the root frame is basically just the outermost frame, so no if
205e31aef6aSopenharmony_ci        # conditions.  This information is used to optimize inheritance
206e31aef6aSopenharmony_ci        # situations.
207e31aef6aSopenharmony_ci        self.rootlevel = False
208e31aef6aSopenharmony_ci
209e31aef6aSopenharmony_ci        # variables set inside of loops and blocks should not affect outer frames,
210e31aef6aSopenharmony_ci        # but they still needs to be kept track of as part of the active context.
211e31aef6aSopenharmony_ci        self.loop_frame = False
212e31aef6aSopenharmony_ci        self.block_frame = False
213e31aef6aSopenharmony_ci
214e31aef6aSopenharmony_ci        # track whether the frame is being used in an if-statement or conditional
215e31aef6aSopenharmony_ci        # expression as it determines which errors should be raised during runtime
216e31aef6aSopenharmony_ci        # or compile time.
217e31aef6aSopenharmony_ci        self.soft_frame = False
218e31aef6aSopenharmony_ci
219e31aef6aSopenharmony_ci    def copy(self) -> "Frame":
220e31aef6aSopenharmony_ci        """Create a copy of the current one."""
221e31aef6aSopenharmony_ci        rv = object.__new__(self.__class__)
222e31aef6aSopenharmony_ci        rv.__dict__.update(self.__dict__)
223e31aef6aSopenharmony_ci        rv.symbols = self.symbols.copy()
224e31aef6aSopenharmony_ci        return rv
225e31aef6aSopenharmony_ci
226e31aef6aSopenharmony_ci    def inner(self, isolated: bool = False) -> "Frame":
227e31aef6aSopenharmony_ci        """Return an inner frame."""
228e31aef6aSopenharmony_ci        if isolated:
229e31aef6aSopenharmony_ci            return Frame(self.eval_ctx, level=self.symbols.level + 1)
230e31aef6aSopenharmony_ci        return Frame(self.eval_ctx, self)
231e31aef6aSopenharmony_ci
232e31aef6aSopenharmony_ci    def soft(self) -> "Frame":
233e31aef6aSopenharmony_ci        """Return a soft frame.  A soft frame may not be modified as
234e31aef6aSopenharmony_ci        standalone thing as it shares the resources with the frame it
235e31aef6aSopenharmony_ci        was created of, but it's not a rootlevel frame any longer.
236e31aef6aSopenharmony_ci
237e31aef6aSopenharmony_ci        This is only used to implement if-statements and conditional
238e31aef6aSopenharmony_ci        expressions.
239e31aef6aSopenharmony_ci        """
240e31aef6aSopenharmony_ci        rv = self.copy()
241e31aef6aSopenharmony_ci        rv.rootlevel = False
242e31aef6aSopenharmony_ci        rv.soft_frame = True
243e31aef6aSopenharmony_ci        return rv
244e31aef6aSopenharmony_ci
245e31aef6aSopenharmony_ci    __copy__ = copy
246e31aef6aSopenharmony_ci
247e31aef6aSopenharmony_ci
248e31aef6aSopenharmony_ciclass VisitorExit(RuntimeError):
249e31aef6aSopenharmony_ci    """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
250e31aef6aSopenharmony_ci
251e31aef6aSopenharmony_ci
252e31aef6aSopenharmony_ciclass DependencyFinderVisitor(NodeVisitor):
253e31aef6aSopenharmony_ci    """A visitor that collects filter and test calls."""
254e31aef6aSopenharmony_ci
255e31aef6aSopenharmony_ci    def __init__(self) -> None:
256e31aef6aSopenharmony_ci        self.filters: t.Set[str] = set()
257e31aef6aSopenharmony_ci        self.tests: t.Set[str] = set()
258e31aef6aSopenharmony_ci
259e31aef6aSopenharmony_ci    def visit_Filter(self, node: nodes.Filter) -> None:
260e31aef6aSopenharmony_ci        self.generic_visit(node)
261e31aef6aSopenharmony_ci        self.filters.add(node.name)
262e31aef6aSopenharmony_ci
263e31aef6aSopenharmony_ci    def visit_Test(self, node: nodes.Test) -> None:
264e31aef6aSopenharmony_ci        self.generic_visit(node)
265e31aef6aSopenharmony_ci        self.tests.add(node.name)
266e31aef6aSopenharmony_ci
267e31aef6aSopenharmony_ci    def visit_Block(self, node: nodes.Block) -> None:
268e31aef6aSopenharmony_ci        """Stop visiting at blocks."""
269e31aef6aSopenharmony_ci
270e31aef6aSopenharmony_ci
271e31aef6aSopenharmony_ciclass UndeclaredNameVisitor(NodeVisitor):
272e31aef6aSopenharmony_ci    """A visitor that checks if a name is accessed without being
273e31aef6aSopenharmony_ci    declared.  This is different from the frame visitor as it will
274e31aef6aSopenharmony_ci    not stop at closure frames.
275e31aef6aSopenharmony_ci    """
276e31aef6aSopenharmony_ci
277e31aef6aSopenharmony_ci    def __init__(self, names: t.Iterable[str]) -> None:
278e31aef6aSopenharmony_ci        self.names = set(names)
279e31aef6aSopenharmony_ci        self.undeclared: t.Set[str] = set()
280e31aef6aSopenharmony_ci
281e31aef6aSopenharmony_ci    def visit_Name(self, node: nodes.Name) -> None:
282e31aef6aSopenharmony_ci        if node.ctx == "load" and node.name in self.names:
283e31aef6aSopenharmony_ci            self.undeclared.add(node.name)
284e31aef6aSopenharmony_ci            if self.undeclared == self.names:
285e31aef6aSopenharmony_ci                raise VisitorExit()
286e31aef6aSopenharmony_ci        else:
287e31aef6aSopenharmony_ci            self.names.discard(node.name)
288e31aef6aSopenharmony_ci
289e31aef6aSopenharmony_ci    def visit_Block(self, node: nodes.Block) -> None:
290e31aef6aSopenharmony_ci        """Stop visiting a blocks."""
291e31aef6aSopenharmony_ci
292e31aef6aSopenharmony_ci
293e31aef6aSopenharmony_ciclass CompilerExit(Exception):
294e31aef6aSopenharmony_ci    """Raised if the compiler encountered a situation where it just
295e31aef6aSopenharmony_ci    doesn't make sense to further process the code.  Any block that
296e31aef6aSopenharmony_ci    raises such an exception is not further processed.
297e31aef6aSopenharmony_ci    """
298e31aef6aSopenharmony_ci
299e31aef6aSopenharmony_ci
300e31aef6aSopenharmony_ciclass CodeGenerator(NodeVisitor):
301e31aef6aSopenharmony_ci    def __init__(
302e31aef6aSopenharmony_ci        self,
303e31aef6aSopenharmony_ci        environment: "Environment",
304e31aef6aSopenharmony_ci        name: t.Optional[str],
305e31aef6aSopenharmony_ci        filename: t.Optional[str],
306e31aef6aSopenharmony_ci        stream: t.Optional[t.TextIO] = None,
307e31aef6aSopenharmony_ci        defer_init: bool = False,
308e31aef6aSopenharmony_ci        optimized: bool = True,
309e31aef6aSopenharmony_ci    ) -> None:
310e31aef6aSopenharmony_ci        if stream is None:
311e31aef6aSopenharmony_ci            stream = StringIO()
312e31aef6aSopenharmony_ci        self.environment = environment
313e31aef6aSopenharmony_ci        self.name = name
314e31aef6aSopenharmony_ci        self.filename = filename
315e31aef6aSopenharmony_ci        self.stream = stream
316e31aef6aSopenharmony_ci        self.created_block_context = False
317e31aef6aSopenharmony_ci        self.defer_init = defer_init
318e31aef6aSopenharmony_ci        self.optimizer: t.Optional[Optimizer] = None
319e31aef6aSopenharmony_ci
320e31aef6aSopenharmony_ci        if optimized:
321e31aef6aSopenharmony_ci            self.optimizer = Optimizer(environment)
322e31aef6aSopenharmony_ci
323e31aef6aSopenharmony_ci        # aliases for imports
324e31aef6aSopenharmony_ci        self.import_aliases: t.Dict[str, str] = {}
325e31aef6aSopenharmony_ci
326e31aef6aSopenharmony_ci        # a registry for all blocks.  Because blocks are moved out
327e31aef6aSopenharmony_ci        # into the global python scope they are registered here
328e31aef6aSopenharmony_ci        self.blocks: t.Dict[str, nodes.Block] = {}
329e31aef6aSopenharmony_ci
330e31aef6aSopenharmony_ci        # the number of extends statements so far
331e31aef6aSopenharmony_ci        self.extends_so_far = 0
332e31aef6aSopenharmony_ci
333e31aef6aSopenharmony_ci        # some templates have a rootlevel extends.  In this case we
334e31aef6aSopenharmony_ci        # can safely assume that we're a child template and do some
335e31aef6aSopenharmony_ci        # more optimizations.
336e31aef6aSopenharmony_ci        self.has_known_extends = False
337e31aef6aSopenharmony_ci
338e31aef6aSopenharmony_ci        # the current line number
339e31aef6aSopenharmony_ci        self.code_lineno = 1
340e31aef6aSopenharmony_ci
341e31aef6aSopenharmony_ci        # registry of all filters and tests (global, not block local)
342e31aef6aSopenharmony_ci        self.tests: t.Dict[str, str] = {}
343e31aef6aSopenharmony_ci        self.filters: t.Dict[str, str] = {}
344e31aef6aSopenharmony_ci
345e31aef6aSopenharmony_ci        # the debug information
346e31aef6aSopenharmony_ci        self.debug_info: t.List[t.Tuple[int, int]] = []
347e31aef6aSopenharmony_ci        self._write_debug_info: t.Optional[int] = None
348e31aef6aSopenharmony_ci
349e31aef6aSopenharmony_ci        # the number of new lines before the next write()
350e31aef6aSopenharmony_ci        self._new_lines = 0
351e31aef6aSopenharmony_ci
352e31aef6aSopenharmony_ci        # the line number of the last written statement
353e31aef6aSopenharmony_ci        self._last_line = 0
354e31aef6aSopenharmony_ci
355e31aef6aSopenharmony_ci        # true if nothing was written so far.
356e31aef6aSopenharmony_ci        self._first_write = True
357e31aef6aSopenharmony_ci
358e31aef6aSopenharmony_ci        # used by the `temporary_identifier` method to get new
359e31aef6aSopenharmony_ci        # unique, temporary identifier
360e31aef6aSopenharmony_ci        self._last_identifier = 0
361e31aef6aSopenharmony_ci
362e31aef6aSopenharmony_ci        # the current indentation
363e31aef6aSopenharmony_ci        self._indentation = 0
364e31aef6aSopenharmony_ci
365e31aef6aSopenharmony_ci        # Tracks toplevel assignments
366e31aef6aSopenharmony_ci        self._assign_stack: t.List[t.Set[str]] = []
367e31aef6aSopenharmony_ci
368e31aef6aSopenharmony_ci        # Tracks parameter definition blocks
369e31aef6aSopenharmony_ci        self._param_def_block: t.List[t.Set[str]] = []
370e31aef6aSopenharmony_ci
371e31aef6aSopenharmony_ci        # Tracks the current context.
372e31aef6aSopenharmony_ci        self._context_reference_stack = ["context"]
373e31aef6aSopenharmony_ci
374e31aef6aSopenharmony_ci    @property
375e31aef6aSopenharmony_ci    def optimized(self) -> bool:
376e31aef6aSopenharmony_ci        return self.optimizer is not None
377e31aef6aSopenharmony_ci
378e31aef6aSopenharmony_ci    # -- Various compilation helpers
379e31aef6aSopenharmony_ci
380e31aef6aSopenharmony_ci    def fail(self, msg: str, lineno: int) -> "te.NoReturn":
381e31aef6aSopenharmony_ci        """Fail with a :exc:`TemplateAssertionError`."""
382e31aef6aSopenharmony_ci        raise TemplateAssertionError(msg, lineno, self.name, self.filename)
383e31aef6aSopenharmony_ci
384e31aef6aSopenharmony_ci    def temporary_identifier(self) -> str:
385e31aef6aSopenharmony_ci        """Get a new unique identifier."""
386e31aef6aSopenharmony_ci        self._last_identifier += 1
387e31aef6aSopenharmony_ci        return f"t_{self._last_identifier}"
388e31aef6aSopenharmony_ci
389e31aef6aSopenharmony_ci    def buffer(self, frame: Frame) -> None:
390e31aef6aSopenharmony_ci        """Enable buffering for the frame from that point onwards."""
391e31aef6aSopenharmony_ci        frame.buffer = self.temporary_identifier()
392e31aef6aSopenharmony_ci        self.writeline(f"{frame.buffer} = []")
393e31aef6aSopenharmony_ci
394e31aef6aSopenharmony_ci    def return_buffer_contents(
395e31aef6aSopenharmony_ci        self, frame: Frame, force_unescaped: bool = False
396e31aef6aSopenharmony_ci    ) -> None:
397e31aef6aSopenharmony_ci        """Return the buffer contents of the frame."""
398e31aef6aSopenharmony_ci        if not force_unescaped:
399e31aef6aSopenharmony_ci            if frame.eval_ctx.volatile:
400e31aef6aSopenharmony_ci                self.writeline("if context.eval_ctx.autoescape:")
401e31aef6aSopenharmony_ci                self.indent()
402e31aef6aSopenharmony_ci                self.writeline(f"return Markup(concat({frame.buffer}))")
403e31aef6aSopenharmony_ci                self.outdent()
404e31aef6aSopenharmony_ci                self.writeline("else:")
405e31aef6aSopenharmony_ci                self.indent()
406e31aef6aSopenharmony_ci                self.writeline(f"return concat({frame.buffer})")
407e31aef6aSopenharmony_ci                self.outdent()
408e31aef6aSopenharmony_ci                return
409e31aef6aSopenharmony_ci            elif frame.eval_ctx.autoescape:
410e31aef6aSopenharmony_ci                self.writeline(f"return Markup(concat({frame.buffer}))")
411e31aef6aSopenharmony_ci                return
412e31aef6aSopenharmony_ci        self.writeline(f"return concat({frame.buffer})")
413e31aef6aSopenharmony_ci
414e31aef6aSopenharmony_ci    def indent(self) -> None:
415e31aef6aSopenharmony_ci        """Indent by one."""
416e31aef6aSopenharmony_ci        self._indentation += 1
417e31aef6aSopenharmony_ci
418e31aef6aSopenharmony_ci    def outdent(self, step: int = 1) -> None:
419e31aef6aSopenharmony_ci        """Outdent by step."""
420e31aef6aSopenharmony_ci        self._indentation -= step
421e31aef6aSopenharmony_ci
422e31aef6aSopenharmony_ci    def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
423e31aef6aSopenharmony_ci        """Yield or write into the frame buffer."""
424e31aef6aSopenharmony_ci        if frame.buffer is None:
425e31aef6aSopenharmony_ci            self.writeline("yield ", node)
426e31aef6aSopenharmony_ci        else:
427e31aef6aSopenharmony_ci            self.writeline(f"{frame.buffer}.append(", node)
428e31aef6aSopenharmony_ci
429e31aef6aSopenharmony_ci    def end_write(self, frame: Frame) -> None:
430e31aef6aSopenharmony_ci        """End the writing process started by `start_write`."""
431e31aef6aSopenharmony_ci        if frame.buffer is not None:
432e31aef6aSopenharmony_ci            self.write(")")
433e31aef6aSopenharmony_ci
434e31aef6aSopenharmony_ci    def simple_write(
435e31aef6aSopenharmony_ci        self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
436e31aef6aSopenharmony_ci    ) -> None:
437e31aef6aSopenharmony_ci        """Simple shortcut for start_write + write + end_write."""
438e31aef6aSopenharmony_ci        self.start_write(frame, node)
439e31aef6aSopenharmony_ci        self.write(s)
440e31aef6aSopenharmony_ci        self.end_write(frame)
441e31aef6aSopenharmony_ci
442e31aef6aSopenharmony_ci    def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
443e31aef6aSopenharmony_ci        """Visit a list of nodes as block in a frame.  If the current frame
444e31aef6aSopenharmony_ci        is no buffer a dummy ``if 0: yield None`` is written automatically.
445e31aef6aSopenharmony_ci        """
446e31aef6aSopenharmony_ci        try:
447e31aef6aSopenharmony_ci            self.writeline("pass")
448e31aef6aSopenharmony_ci            for node in nodes:
449e31aef6aSopenharmony_ci                self.visit(node, frame)
450e31aef6aSopenharmony_ci        except CompilerExit:
451e31aef6aSopenharmony_ci            pass
452e31aef6aSopenharmony_ci
453e31aef6aSopenharmony_ci    def write(self, x: str) -> None:
454e31aef6aSopenharmony_ci        """Write a string into the output stream."""
455e31aef6aSopenharmony_ci        if self._new_lines:
456e31aef6aSopenharmony_ci            if not self._first_write:
457e31aef6aSopenharmony_ci                self.stream.write("\n" * self._new_lines)
458e31aef6aSopenharmony_ci                self.code_lineno += self._new_lines
459e31aef6aSopenharmony_ci                if self._write_debug_info is not None:
460e31aef6aSopenharmony_ci                    self.debug_info.append((self._write_debug_info, self.code_lineno))
461e31aef6aSopenharmony_ci                    self._write_debug_info = None
462e31aef6aSopenharmony_ci            self._first_write = False
463e31aef6aSopenharmony_ci            self.stream.write("    " * self._indentation)
464e31aef6aSopenharmony_ci            self._new_lines = 0
465e31aef6aSopenharmony_ci        self.stream.write(x)
466e31aef6aSopenharmony_ci
467e31aef6aSopenharmony_ci    def writeline(
468e31aef6aSopenharmony_ci        self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
469e31aef6aSopenharmony_ci    ) -> None:
470e31aef6aSopenharmony_ci        """Combination of newline and write."""
471e31aef6aSopenharmony_ci        self.newline(node, extra)
472e31aef6aSopenharmony_ci        self.write(x)
473e31aef6aSopenharmony_ci
474e31aef6aSopenharmony_ci    def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
475e31aef6aSopenharmony_ci        """Add one or more newlines before the next write."""
476e31aef6aSopenharmony_ci        self._new_lines = max(self._new_lines, 1 + extra)
477e31aef6aSopenharmony_ci        if node is not None and node.lineno != self._last_line:
478e31aef6aSopenharmony_ci            self._write_debug_info = node.lineno
479e31aef6aSopenharmony_ci            self._last_line = node.lineno
480e31aef6aSopenharmony_ci
481e31aef6aSopenharmony_ci    def signature(
482e31aef6aSopenharmony_ci        self,
483e31aef6aSopenharmony_ci        node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
484e31aef6aSopenharmony_ci        frame: Frame,
485e31aef6aSopenharmony_ci        extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
486e31aef6aSopenharmony_ci    ) -> None:
487e31aef6aSopenharmony_ci        """Writes a function call to the stream for the current node.
488e31aef6aSopenharmony_ci        A leading comma is added automatically.  The extra keyword
489e31aef6aSopenharmony_ci        arguments may not include python keywords otherwise a syntax
490e31aef6aSopenharmony_ci        error could occur.  The extra keyword arguments should be given
491e31aef6aSopenharmony_ci        as python dict.
492e31aef6aSopenharmony_ci        """
493e31aef6aSopenharmony_ci        # if any of the given keyword arguments is a python keyword
494e31aef6aSopenharmony_ci        # we have to make sure that no invalid call is created.
495e31aef6aSopenharmony_ci        kwarg_workaround = any(
496e31aef6aSopenharmony_ci            is_python_keyword(t.cast(str, k))
497e31aef6aSopenharmony_ci            for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
498e31aef6aSopenharmony_ci        )
499e31aef6aSopenharmony_ci
500e31aef6aSopenharmony_ci        for arg in node.args:
501e31aef6aSopenharmony_ci            self.write(", ")
502e31aef6aSopenharmony_ci            self.visit(arg, frame)
503e31aef6aSopenharmony_ci
504e31aef6aSopenharmony_ci        if not kwarg_workaround:
505e31aef6aSopenharmony_ci            for kwarg in node.kwargs:
506e31aef6aSopenharmony_ci                self.write(", ")
507e31aef6aSopenharmony_ci                self.visit(kwarg, frame)
508e31aef6aSopenharmony_ci            if extra_kwargs is not None:
509e31aef6aSopenharmony_ci                for key, value in extra_kwargs.items():
510e31aef6aSopenharmony_ci                    self.write(f", {key}={value}")
511e31aef6aSopenharmony_ci        if node.dyn_args:
512e31aef6aSopenharmony_ci            self.write(", *")
513e31aef6aSopenharmony_ci            self.visit(node.dyn_args, frame)
514e31aef6aSopenharmony_ci
515e31aef6aSopenharmony_ci        if kwarg_workaround:
516e31aef6aSopenharmony_ci            if node.dyn_kwargs is not None:
517e31aef6aSopenharmony_ci                self.write(", **dict({")
518e31aef6aSopenharmony_ci            else:
519e31aef6aSopenharmony_ci                self.write(", **{")
520e31aef6aSopenharmony_ci            for kwarg in node.kwargs:
521e31aef6aSopenharmony_ci                self.write(f"{kwarg.key!r}: ")
522e31aef6aSopenharmony_ci                self.visit(kwarg.value, frame)
523e31aef6aSopenharmony_ci                self.write(", ")
524e31aef6aSopenharmony_ci            if extra_kwargs is not None:
525e31aef6aSopenharmony_ci                for key, value in extra_kwargs.items():
526e31aef6aSopenharmony_ci                    self.write(f"{key!r}: {value}, ")
527e31aef6aSopenharmony_ci            if node.dyn_kwargs is not None:
528e31aef6aSopenharmony_ci                self.write("}, **")
529e31aef6aSopenharmony_ci                self.visit(node.dyn_kwargs, frame)
530e31aef6aSopenharmony_ci                self.write(")")
531e31aef6aSopenharmony_ci            else:
532e31aef6aSopenharmony_ci                self.write("}")
533e31aef6aSopenharmony_ci
534e31aef6aSopenharmony_ci        elif node.dyn_kwargs is not None:
535e31aef6aSopenharmony_ci            self.write(", **")
536e31aef6aSopenharmony_ci            self.visit(node.dyn_kwargs, frame)
537e31aef6aSopenharmony_ci
538e31aef6aSopenharmony_ci    def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
539e31aef6aSopenharmony_ci        """Find all filter and test names used in the template and
540e31aef6aSopenharmony_ci        assign them to variables in the compiled namespace. Checking
541e31aef6aSopenharmony_ci        that the names are registered with the environment is done when
542e31aef6aSopenharmony_ci        compiling the Filter and Test nodes. If the node is in an If or
543e31aef6aSopenharmony_ci        CondExpr node, the check is done at runtime instead.
544e31aef6aSopenharmony_ci
545e31aef6aSopenharmony_ci        .. versionchanged:: 3.0
546e31aef6aSopenharmony_ci            Filters and tests in If and CondExpr nodes are checked at
547e31aef6aSopenharmony_ci            runtime instead of compile time.
548e31aef6aSopenharmony_ci        """
549e31aef6aSopenharmony_ci        visitor = DependencyFinderVisitor()
550e31aef6aSopenharmony_ci
551e31aef6aSopenharmony_ci        for node in nodes:
552e31aef6aSopenharmony_ci            visitor.visit(node)
553e31aef6aSopenharmony_ci
554e31aef6aSopenharmony_ci        for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
555e31aef6aSopenharmony_ci            self.tests,
556e31aef6aSopenharmony_ci            visitor.tests,
557e31aef6aSopenharmony_ci            "tests",
558e31aef6aSopenharmony_ci        ):
559e31aef6aSopenharmony_ci            for name in sorted(names):
560e31aef6aSopenharmony_ci                if name not in id_map:
561e31aef6aSopenharmony_ci                    id_map[name] = self.temporary_identifier()
562e31aef6aSopenharmony_ci
563e31aef6aSopenharmony_ci                # add check during runtime that dependencies used inside of executed
564e31aef6aSopenharmony_ci                # blocks are defined, as this step may be skipped during compile time
565e31aef6aSopenharmony_ci                self.writeline("try:")
566e31aef6aSopenharmony_ci                self.indent()
567e31aef6aSopenharmony_ci                self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
568e31aef6aSopenharmony_ci                self.outdent()
569e31aef6aSopenharmony_ci                self.writeline("except KeyError:")
570e31aef6aSopenharmony_ci                self.indent()
571e31aef6aSopenharmony_ci                self.writeline("@internalcode")
572e31aef6aSopenharmony_ci                self.writeline(f"def {id_map[name]}(*unused):")
573e31aef6aSopenharmony_ci                self.indent()
574e31aef6aSopenharmony_ci                self.writeline(
575e31aef6aSopenharmony_ci                    f'raise TemplateRuntimeError("No {dependency[:-1]}'
576e31aef6aSopenharmony_ci                    f' named {name!r} found.")'
577e31aef6aSopenharmony_ci                )
578e31aef6aSopenharmony_ci                self.outdent()
579e31aef6aSopenharmony_ci                self.outdent()
580e31aef6aSopenharmony_ci
581e31aef6aSopenharmony_ci    def enter_frame(self, frame: Frame) -> None:
582e31aef6aSopenharmony_ci        undefs = []
583e31aef6aSopenharmony_ci        for target, (action, param) in frame.symbols.loads.items():
584e31aef6aSopenharmony_ci            if action == VAR_LOAD_PARAMETER:
585e31aef6aSopenharmony_ci                pass
586e31aef6aSopenharmony_ci            elif action == VAR_LOAD_RESOLVE:
587e31aef6aSopenharmony_ci                self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
588e31aef6aSopenharmony_ci            elif action == VAR_LOAD_ALIAS:
589e31aef6aSopenharmony_ci                self.writeline(f"{target} = {param}")
590e31aef6aSopenharmony_ci            elif action == VAR_LOAD_UNDEFINED:
591e31aef6aSopenharmony_ci                undefs.append(target)
592e31aef6aSopenharmony_ci            else:
593e31aef6aSopenharmony_ci                raise NotImplementedError("unknown load instruction")
594e31aef6aSopenharmony_ci        if undefs:
595e31aef6aSopenharmony_ci            self.writeline(f"{' = '.join(undefs)} = missing")
596e31aef6aSopenharmony_ci
597e31aef6aSopenharmony_ci    def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
598e31aef6aSopenharmony_ci        if not with_python_scope:
599e31aef6aSopenharmony_ci            undefs = []
600e31aef6aSopenharmony_ci            for target in frame.symbols.loads:
601e31aef6aSopenharmony_ci                undefs.append(target)
602e31aef6aSopenharmony_ci            if undefs:
603e31aef6aSopenharmony_ci                self.writeline(f"{' = '.join(undefs)} = missing")
604e31aef6aSopenharmony_ci
605e31aef6aSopenharmony_ci    def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
606e31aef6aSopenharmony_ci        return async_value if self.environment.is_async else sync_value
607e31aef6aSopenharmony_ci
608e31aef6aSopenharmony_ci    def func(self, name: str) -> str:
609e31aef6aSopenharmony_ci        return f"{self.choose_async()}def {name}"
610e31aef6aSopenharmony_ci
611e31aef6aSopenharmony_ci    def macro_body(
612e31aef6aSopenharmony_ci        self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
613e31aef6aSopenharmony_ci    ) -> t.Tuple[Frame, MacroRef]:
614e31aef6aSopenharmony_ci        """Dump the function def of a macro or call block."""
615e31aef6aSopenharmony_ci        frame = frame.inner()
616e31aef6aSopenharmony_ci        frame.symbols.analyze_node(node)
617e31aef6aSopenharmony_ci        macro_ref = MacroRef(node)
618e31aef6aSopenharmony_ci
619e31aef6aSopenharmony_ci        explicit_caller = None
620e31aef6aSopenharmony_ci        skip_special_params = set()
621e31aef6aSopenharmony_ci        args = []
622e31aef6aSopenharmony_ci
623e31aef6aSopenharmony_ci        for idx, arg in enumerate(node.args):
624e31aef6aSopenharmony_ci            if arg.name == "caller":
625e31aef6aSopenharmony_ci                explicit_caller = idx
626e31aef6aSopenharmony_ci            if arg.name in ("kwargs", "varargs"):
627e31aef6aSopenharmony_ci                skip_special_params.add(arg.name)
628e31aef6aSopenharmony_ci            args.append(frame.symbols.ref(arg.name))
629e31aef6aSopenharmony_ci
630e31aef6aSopenharmony_ci        undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
631e31aef6aSopenharmony_ci
632e31aef6aSopenharmony_ci        if "caller" in undeclared:
633e31aef6aSopenharmony_ci            # In older Jinja versions there was a bug that allowed caller
634e31aef6aSopenharmony_ci            # to retain the special behavior even if it was mentioned in
635e31aef6aSopenharmony_ci            # the argument list.  However thankfully this was only really
636e31aef6aSopenharmony_ci            # working if it was the last argument.  So we are explicitly
637e31aef6aSopenharmony_ci            # checking this now and error out if it is anywhere else in
638e31aef6aSopenharmony_ci            # the argument list.
639e31aef6aSopenharmony_ci            if explicit_caller is not None:
640e31aef6aSopenharmony_ci                try:
641e31aef6aSopenharmony_ci                    node.defaults[explicit_caller - len(node.args)]
642e31aef6aSopenharmony_ci                except IndexError:
643e31aef6aSopenharmony_ci                    self.fail(
644e31aef6aSopenharmony_ci                        "When defining macros or call blocks the "
645e31aef6aSopenharmony_ci                        'special "caller" argument must be omitted '
646e31aef6aSopenharmony_ci                        "or be given a default.",
647e31aef6aSopenharmony_ci                        node.lineno,
648e31aef6aSopenharmony_ci                    )
649e31aef6aSopenharmony_ci            else:
650e31aef6aSopenharmony_ci                args.append(frame.symbols.declare_parameter("caller"))
651e31aef6aSopenharmony_ci            macro_ref.accesses_caller = True
652e31aef6aSopenharmony_ci        if "kwargs" in undeclared and "kwargs" not in skip_special_params:
653e31aef6aSopenharmony_ci            args.append(frame.symbols.declare_parameter("kwargs"))
654e31aef6aSopenharmony_ci            macro_ref.accesses_kwargs = True
655e31aef6aSopenharmony_ci        if "varargs" in undeclared and "varargs" not in skip_special_params:
656e31aef6aSopenharmony_ci            args.append(frame.symbols.declare_parameter("varargs"))
657e31aef6aSopenharmony_ci            macro_ref.accesses_varargs = True
658e31aef6aSopenharmony_ci
659e31aef6aSopenharmony_ci        # macros are delayed, they never require output checks
660e31aef6aSopenharmony_ci        frame.require_output_check = False
661e31aef6aSopenharmony_ci        frame.symbols.analyze_node(node)
662e31aef6aSopenharmony_ci        self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
663e31aef6aSopenharmony_ci        self.indent()
664e31aef6aSopenharmony_ci
665e31aef6aSopenharmony_ci        self.buffer(frame)
666e31aef6aSopenharmony_ci        self.enter_frame(frame)
667e31aef6aSopenharmony_ci
668e31aef6aSopenharmony_ci        self.push_parameter_definitions(frame)
669e31aef6aSopenharmony_ci        for idx, arg in enumerate(node.args):
670e31aef6aSopenharmony_ci            ref = frame.symbols.ref(arg.name)
671e31aef6aSopenharmony_ci            self.writeline(f"if {ref} is missing:")
672e31aef6aSopenharmony_ci            self.indent()
673e31aef6aSopenharmony_ci            try:
674e31aef6aSopenharmony_ci                default = node.defaults[idx - len(node.args)]
675e31aef6aSopenharmony_ci            except IndexError:
676e31aef6aSopenharmony_ci                self.writeline(
677e31aef6aSopenharmony_ci                    f'{ref} = undefined("parameter {arg.name!r} was not provided",'
678e31aef6aSopenharmony_ci                    f" name={arg.name!r})"
679e31aef6aSopenharmony_ci                )
680e31aef6aSopenharmony_ci            else:
681e31aef6aSopenharmony_ci                self.writeline(f"{ref} = ")
682e31aef6aSopenharmony_ci                self.visit(default, frame)
683e31aef6aSopenharmony_ci            self.mark_parameter_stored(ref)
684e31aef6aSopenharmony_ci            self.outdent()
685e31aef6aSopenharmony_ci        self.pop_parameter_definitions()
686e31aef6aSopenharmony_ci
687e31aef6aSopenharmony_ci        self.blockvisit(node.body, frame)
688e31aef6aSopenharmony_ci        self.return_buffer_contents(frame, force_unescaped=True)
689e31aef6aSopenharmony_ci        self.leave_frame(frame, with_python_scope=True)
690e31aef6aSopenharmony_ci        self.outdent()
691e31aef6aSopenharmony_ci
692e31aef6aSopenharmony_ci        return frame, macro_ref
693e31aef6aSopenharmony_ci
694e31aef6aSopenharmony_ci    def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
695e31aef6aSopenharmony_ci        """Dump the macro definition for the def created by macro_body."""
696e31aef6aSopenharmony_ci        arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
697e31aef6aSopenharmony_ci        name = getattr(macro_ref.node, "name", None)
698e31aef6aSopenharmony_ci        if len(macro_ref.node.args) == 1:
699e31aef6aSopenharmony_ci            arg_tuple += ","
700e31aef6aSopenharmony_ci        self.write(
701e31aef6aSopenharmony_ci            f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
702e31aef6aSopenharmony_ci            f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
703e31aef6aSopenharmony_ci            f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
704e31aef6aSopenharmony_ci        )
705e31aef6aSopenharmony_ci
706e31aef6aSopenharmony_ci    def position(self, node: nodes.Node) -> str:
707e31aef6aSopenharmony_ci        """Return a human readable position for the node."""
708e31aef6aSopenharmony_ci        rv = f"line {node.lineno}"
709e31aef6aSopenharmony_ci        if self.name is not None:
710e31aef6aSopenharmony_ci            rv = f"{rv} in {self.name!r}"
711e31aef6aSopenharmony_ci        return rv
712e31aef6aSopenharmony_ci
713e31aef6aSopenharmony_ci    def dump_local_context(self, frame: Frame) -> str:
714e31aef6aSopenharmony_ci        items_kv = ", ".join(
715e31aef6aSopenharmony_ci            f"{name!r}: {target}"
716e31aef6aSopenharmony_ci            for name, target in frame.symbols.dump_stores().items()
717e31aef6aSopenharmony_ci        )
718e31aef6aSopenharmony_ci        return f"{{{items_kv}}}"
719e31aef6aSopenharmony_ci
720e31aef6aSopenharmony_ci    def write_commons(self) -> None:
721e31aef6aSopenharmony_ci        """Writes a common preamble that is used by root and block functions.
722e31aef6aSopenharmony_ci        Primarily this sets up common local helpers and enforces a generator
723e31aef6aSopenharmony_ci        through a dead branch.
724e31aef6aSopenharmony_ci        """
725e31aef6aSopenharmony_ci        self.writeline("resolve = context.resolve_or_missing")
726e31aef6aSopenharmony_ci        self.writeline("undefined = environment.undefined")
727e31aef6aSopenharmony_ci        self.writeline("concat = environment.concat")
728e31aef6aSopenharmony_ci        # always use the standard Undefined class for the implicit else of
729e31aef6aSopenharmony_ci        # conditional expressions
730e31aef6aSopenharmony_ci        self.writeline("cond_expr_undefined = Undefined")
731e31aef6aSopenharmony_ci        self.writeline("if 0: yield None")
732e31aef6aSopenharmony_ci
733e31aef6aSopenharmony_ci    def push_parameter_definitions(self, frame: Frame) -> None:
734e31aef6aSopenharmony_ci        """Pushes all parameter targets from the given frame into a local
735e31aef6aSopenharmony_ci        stack that permits tracking of yet to be assigned parameters.  In
736e31aef6aSopenharmony_ci        particular this enables the optimization from `visit_Name` to skip
737e31aef6aSopenharmony_ci        undefined expressions for parameters in macros as macros can reference
738e31aef6aSopenharmony_ci        otherwise unbound parameters.
739e31aef6aSopenharmony_ci        """
740e31aef6aSopenharmony_ci        self._param_def_block.append(frame.symbols.dump_param_targets())
741e31aef6aSopenharmony_ci
742e31aef6aSopenharmony_ci    def pop_parameter_definitions(self) -> None:
743e31aef6aSopenharmony_ci        """Pops the current parameter definitions set."""
744e31aef6aSopenharmony_ci        self._param_def_block.pop()
745e31aef6aSopenharmony_ci
746e31aef6aSopenharmony_ci    def mark_parameter_stored(self, target: str) -> None:
747e31aef6aSopenharmony_ci        """Marks a parameter in the current parameter definitions as stored.
748e31aef6aSopenharmony_ci        This will skip the enforced undefined checks.
749e31aef6aSopenharmony_ci        """
750e31aef6aSopenharmony_ci        if self._param_def_block:
751e31aef6aSopenharmony_ci            self._param_def_block[-1].discard(target)
752e31aef6aSopenharmony_ci
753e31aef6aSopenharmony_ci    def push_context_reference(self, target: str) -> None:
754e31aef6aSopenharmony_ci        self._context_reference_stack.append(target)
755e31aef6aSopenharmony_ci
756e31aef6aSopenharmony_ci    def pop_context_reference(self) -> None:
757e31aef6aSopenharmony_ci        self._context_reference_stack.pop()
758e31aef6aSopenharmony_ci
759e31aef6aSopenharmony_ci    def get_context_ref(self) -> str:
760e31aef6aSopenharmony_ci        return self._context_reference_stack[-1]
761e31aef6aSopenharmony_ci
762e31aef6aSopenharmony_ci    def get_resolve_func(self) -> str:
763e31aef6aSopenharmony_ci        target = self._context_reference_stack[-1]
764e31aef6aSopenharmony_ci        if target == "context":
765e31aef6aSopenharmony_ci            return "resolve"
766e31aef6aSopenharmony_ci        return f"{target}.resolve"
767e31aef6aSopenharmony_ci
768e31aef6aSopenharmony_ci    def derive_context(self, frame: Frame) -> str:
769e31aef6aSopenharmony_ci        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
770e31aef6aSopenharmony_ci
771e31aef6aSopenharmony_ci    def parameter_is_undeclared(self, target: str) -> bool:
772e31aef6aSopenharmony_ci        """Checks if a given target is an undeclared parameter."""
773e31aef6aSopenharmony_ci        if not self._param_def_block:
774e31aef6aSopenharmony_ci            return False
775e31aef6aSopenharmony_ci        return target in self._param_def_block[-1]
776e31aef6aSopenharmony_ci
777e31aef6aSopenharmony_ci    def push_assign_tracking(self) -> None:
778e31aef6aSopenharmony_ci        """Pushes a new layer for assignment tracking."""
779e31aef6aSopenharmony_ci        self._assign_stack.append(set())
780e31aef6aSopenharmony_ci
781e31aef6aSopenharmony_ci    def pop_assign_tracking(self, frame: Frame) -> None:
782e31aef6aSopenharmony_ci        """Pops the topmost level for assignment tracking and updates the
783e31aef6aSopenharmony_ci        context variables if necessary.
784e31aef6aSopenharmony_ci        """
785e31aef6aSopenharmony_ci        vars = self._assign_stack.pop()
786e31aef6aSopenharmony_ci        if (
787e31aef6aSopenharmony_ci            not frame.block_frame
788e31aef6aSopenharmony_ci            and not frame.loop_frame
789e31aef6aSopenharmony_ci            and not frame.toplevel
790e31aef6aSopenharmony_ci            or not vars
791e31aef6aSopenharmony_ci        ):
792e31aef6aSopenharmony_ci            return
793e31aef6aSopenharmony_ci        public_names = [x for x in vars if x[:1] != "_"]
794e31aef6aSopenharmony_ci        if len(vars) == 1:
795e31aef6aSopenharmony_ci            name = next(iter(vars))
796e31aef6aSopenharmony_ci            ref = frame.symbols.ref(name)
797e31aef6aSopenharmony_ci            if frame.loop_frame:
798e31aef6aSopenharmony_ci                self.writeline(f"_loop_vars[{name!r}] = {ref}")
799e31aef6aSopenharmony_ci                return
800e31aef6aSopenharmony_ci            if frame.block_frame:
801e31aef6aSopenharmony_ci                self.writeline(f"_block_vars[{name!r}] = {ref}")
802e31aef6aSopenharmony_ci                return
803e31aef6aSopenharmony_ci            self.writeline(f"context.vars[{name!r}] = {ref}")
804e31aef6aSopenharmony_ci        else:
805e31aef6aSopenharmony_ci            if frame.loop_frame:
806e31aef6aSopenharmony_ci                self.writeline("_loop_vars.update({")
807e31aef6aSopenharmony_ci            elif frame.block_frame:
808e31aef6aSopenharmony_ci                self.writeline("_block_vars.update({")
809e31aef6aSopenharmony_ci            else:
810e31aef6aSopenharmony_ci                self.writeline("context.vars.update({")
811e31aef6aSopenharmony_ci            for idx, name in enumerate(vars):
812e31aef6aSopenharmony_ci                if idx:
813e31aef6aSopenharmony_ci                    self.write(", ")
814e31aef6aSopenharmony_ci                ref = frame.symbols.ref(name)
815e31aef6aSopenharmony_ci                self.write(f"{name!r}: {ref}")
816e31aef6aSopenharmony_ci            self.write("})")
817e31aef6aSopenharmony_ci        if not frame.block_frame and not frame.loop_frame and public_names:
818e31aef6aSopenharmony_ci            if len(public_names) == 1:
819e31aef6aSopenharmony_ci                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
820e31aef6aSopenharmony_ci            else:
821e31aef6aSopenharmony_ci                names_str = ", ".join(map(repr, public_names))
822e31aef6aSopenharmony_ci                self.writeline(f"context.exported_vars.update(({names_str}))")
823e31aef6aSopenharmony_ci
824e31aef6aSopenharmony_ci    # -- Statement Visitors
825e31aef6aSopenharmony_ci
826e31aef6aSopenharmony_ci    def visit_Template(
827e31aef6aSopenharmony_ci        self, node: nodes.Template, frame: t.Optional[Frame] = None
828e31aef6aSopenharmony_ci    ) -> None:
829e31aef6aSopenharmony_ci        assert frame is None, "no root frame allowed"
830e31aef6aSopenharmony_ci        eval_ctx = EvalContext(self.environment, self.name)
831e31aef6aSopenharmony_ci
832e31aef6aSopenharmony_ci        from .runtime import exported, async_exported
833e31aef6aSopenharmony_ci
834e31aef6aSopenharmony_ci        if self.environment.is_async:
835e31aef6aSopenharmony_ci            exported_names = sorted(exported + async_exported)
836e31aef6aSopenharmony_ci        else:
837e31aef6aSopenharmony_ci            exported_names = sorted(exported)
838e31aef6aSopenharmony_ci
839e31aef6aSopenharmony_ci        self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
840e31aef6aSopenharmony_ci
841e31aef6aSopenharmony_ci        # if we want a deferred initialization we cannot move the
842e31aef6aSopenharmony_ci        # environment into a local name
843e31aef6aSopenharmony_ci        envenv = "" if self.defer_init else ", environment=environment"
844e31aef6aSopenharmony_ci
845e31aef6aSopenharmony_ci        # do we have an extends tag at all?  If not, we can save some
846e31aef6aSopenharmony_ci        # overhead by just not processing any inheritance code.
847e31aef6aSopenharmony_ci        have_extends = node.find(nodes.Extends) is not None
848e31aef6aSopenharmony_ci
849e31aef6aSopenharmony_ci        # find all blocks
850e31aef6aSopenharmony_ci        for block in node.find_all(nodes.Block):
851e31aef6aSopenharmony_ci            if block.name in self.blocks:
852e31aef6aSopenharmony_ci                self.fail(f"block {block.name!r} defined twice", block.lineno)
853e31aef6aSopenharmony_ci            self.blocks[block.name] = block
854e31aef6aSopenharmony_ci
855e31aef6aSopenharmony_ci        # find all imports and import them
856e31aef6aSopenharmony_ci        for import_ in node.find_all(nodes.ImportedName):
857e31aef6aSopenharmony_ci            if import_.importname not in self.import_aliases:
858e31aef6aSopenharmony_ci                imp = import_.importname
859e31aef6aSopenharmony_ci                self.import_aliases[imp] = alias = self.temporary_identifier()
860e31aef6aSopenharmony_ci                if "." in imp:
861e31aef6aSopenharmony_ci                    module, obj = imp.rsplit(".", 1)
862e31aef6aSopenharmony_ci                    self.writeline(f"from {module} import {obj} as {alias}")
863e31aef6aSopenharmony_ci                else:
864e31aef6aSopenharmony_ci                    self.writeline(f"import {imp} as {alias}")
865e31aef6aSopenharmony_ci
866e31aef6aSopenharmony_ci        # add the load name
867e31aef6aSopenharmony_ci        self.writeline(f"name = {self.name!r}")
868e31aef6aSopenharmony_ci
869e31aef6aSopenharmony_ci        # generate the root render function.
870e31aef6aSopenharmony_ci        self.writeline(
871e31aef6aSopenharmony_ci            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
872e31aef6aSopenharmony_ci        )
873e31aef6aSopenharmony_ci        self.indent()
874e31aef6aSopenharmony_ci        self.write_commons()
875e31aef6aSopenharmony_ci
876e31aef6aSopenharmony_ci        # process the root
877e31aef6aSopenharmony_ci        frame = Frame(eval_ctx)
878e31aef6aSopenharmony_ci        if "self" in find_undeclared(node.body, ("self",)):
879e31aef6aSopenharmony_ci            ref = frame.symbols.declare_parameter("self")
880e31aef6aSopenharmony_ci            self.writeline(f"{ref} = TemplateReference(context)")
881e31aef6aSopenharmony_ci        frame.symbols.analyze_node(node)
882e31aef6aSopenharmony_ci        frame.toplevel = frame.rootlevel = True
883e31aef6aSopenharmony_ci        frame.require_output_check = have_extends and not self.has_known_extends
884e31aef6aSopenharmony_ci        if have_extends:
885e31aef6aSopenharmony_ci            self.writeline("parent_template = None")
886e31aef6aSopenharmony_ci        self.enter_frame(frame)
887e31aef6aSopenharmony_ci        self.pull_dependencies(node.body)
888e31aef6aSopenharmony_ci        self.blockvisit(node.body, frame)
889e31aef6aSopenharmony_ci        self.leave_frame(frame, with_python_scope=True)
890e31aef6aSopenharmony_ci        self.outdent()
891e31aef6aSopenharmony_ci
892e31aef6aSopenharmony_ci        # make sure that the parent root is called.
893e31aef6aSopenharmony_ci        if have_extends:
894e31aef6aSopenharmony_ci            if not self.has_known_extends:
895e31aef6aSopenharmony_ci                self.indent()
896e31aef6aSopenharmony_ci                self.writeline("if parent_template is not None:")
897e31aef6aSopenharmony_ci            self.indent()
898e31aef6aSopenharmony_ci            if not self.environment.is_async:
899e31aef6aSopenharmony_ci                self.writeline("yield from parent_template.root_render_func(context)")
900e31aef6aSopenharmony_ci            else:
901e31aef6aSopenharmony_ci                self.writeline(
902e31aef6aSopenharmony_ci                    "async for event in parent_template.root_render_func(context):"
903e31aef6aSopenharmony_ci                )
904e31aef6aSopenharmony_ci                self.indent()
905e31aef6aSopenharmony_ci                self.writeline("yield event")
906e31aef6aSopenharmony_ci                self.outdent()
907e31aef6aSopenharmony_ci            self.outdent(1 + (not self.has_known_extends))
908e31aef6aSopenharmony_ci
909e31aef6aSopenharmony_ci        # at this point we now have the blocks collected and can visit them too.
910e31aef6aSopenharmony_ci        for name, block in self.blocks.items():
911e31aef6aSopenharmony_ci            self.writeline(
912e31aef6aSopenharmony_ci                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
913e31aef6aSopenharmony_ci                block,
914e31aef6aSopenharmony_ci                1,
915e31aef6aSopenharmony_ci            )
916e31aef6aSopenharmony_ci            self.indent()
917e31aef6aSopenharmony_ci            self.write_commons()
918e31aef6aSopenharmony_ci            # It's important that we do not make this frame a child of the
919e31aef6aSopenharmony_ci            # toplevel template.  This would cause a variety of
920e31aef6aSopenharmony_ci            # interesting issues with identifier tracking.
921e31aef6aSopenharmony_ci            block_frame = Frame(eval_ctx)
922e31aef6aSopenharmony_ci            block_frame.block_frame = True
923e31aef6aSopenharmony_ci            undeclared = find_undeclared(block.body, ("self", "super"))
924e31aef6aSopenharmony_ci            if "self" in undeclared:
925e31aef6aSopenharmony_ci                ref = block_frame.symbols.declare_parameter("self")
926e31aef6aSopenharmony_ci                self.writeline(f"{ref} = TemplateReference(context)")
927e31aef6aSopenharmony_ci            if "super" in undeclared:
928e31aef6aSopenharmony_ci                ref = block_frame.symbols.declare_parameter("super")
929e31aef6aSopenharmony_ci                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
930e31aef6aSopenharmony_ci            block_frame.symbols.analyze_node(block)
931e31aef6aSopenharmony_ci            block_frame.block = name
932e31aef6aSopenharmony_ci            self.writeline("_block_vars = {}")
933e31aef6aSopenharmony_ci            self.enter_frame(block_frame)
934e31aef6aSopenharmony_ci            self.pull_dependencies(block.body)
935e31aef6aSopenharmony_ci            self.blockvisit(block.body, block_frame)
936e31aef6aSopenharmony_ci            self.leave_frame(block_frame, with_python_scope=True)
937e31aef6aSopenharmony_ci            self.outdent()
938e31aef6aSopenharmony_ci
939e31aef6aSopenharmony_ci        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
940e31aef6aSopenharmony_ci        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
941e31aef6aSopenharmony_ci        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
942e31aef6aSopenharmony_ci        self.writeline(f"debug_info = {debug_kv_str!r}")
943e31aef6aSopenharmony_ci
944e31aef6aSopenharmony_ci    def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
945e31aef6aSopenharmony_ci        """Call a block and register it for the template."""
946e31aef6aSopenharmony_ci        level = 0
947e31aef6aSopenharmony_ci        if frame.toplevel:
948e31aef6aSopenharmony_ci            # if we know that we are a child template, there is no need to
949e31aef6aSopenharmony_ci            # check if we are one
950e31aef6aSopenharmony_ci            if self.has_known_extends:
951e31aef6aSopenharmony_ci                return
952e31aef6aSopenharmony_ci            if self.extends_so_far > 0:
953e31aef6aSopenharmony_ci                self.writeline("if parent_template is None:")
954e31aef6aSopenharmony_ci                self.indent()
955e31aef6aSopenharmony_ci                level += 1
956e31aef6aSopenharmony_ci
957e31aef6aSopenharmony_ci        if node.scoped:
958e31aef6aSopenharmony_ci            context = self.derive_context(frame)
959e31aef6aSopenharmony_ci        else:
960e31aef6aSopenharmony_ci            context = self.get_context_ref()
961e31aef6aSopenharmony_ci
962e31aef6aSopenharmony_ci        if node.required:
963e31aef6aSopenharmony_ci            self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
964e31aef6aSopenharmony_ci            self.indent()
965e31aef6aSopenharmony_ci            self.writeline(
966e31aef6aSopenharmony_ci                f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
967e31aef6aSopenharmony_ci                node,
968e31aef6aSopenharmony_ci            )
969e31aef6aSopenharmony_ci            self.outdent()
970e31aef6aSopenharmony_ci
971e31aef6aSopenharmony_ci        if not self.environment.is_async and frame.buffer is None:
972e31aef6aSopenharmony_ci            self.writeline(
973e31aef6aSopenharmony_ci                f"yield from context.blocks[{node.name!r}][0]({context})", node
974e31aef6aSopenharmony_ci            )
975e31aef6aSopenharmony_ci        else:
976e31aef6aSopenharmony_ci            self.writeline(
977e31aef6aSopenharmony_ci                f"{self.choose_async()}for event in"
978e31aef6aSopenharmony_ci                f" context.blocks[{node.name!r}][0]({context}):",
979e31aef6aSopenharmony_ci                node,
980e31aef6aSopenharmony_ci            )
981e31aef6aSopenharmony_ci            self.indent()
982e31aef6aSopenharmony_ci            self.simple_write("event", frame)
983e31aef6aSopenharmony_ci            self.outdent()
984e31aef6aSopenharmony_ci
985e31aef6aSopenharmony_ci        self.outdent(level)
986e31aef6aSopenharmony_ci
987e31aef6aSopenharmony_ci    def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
988e31aef6aSopenharmony_ci        """Calls the extender."""
989e31aef6aSopenharmony_ci        if not frame.toplevel:
990e31aef6aSopenharmony_ci            self.fail("cannot use extend from a non top-level scope", node.lineno)
991e31aef6aSopenharmony_ci
992e31aef6aSopenharmony_ci        # if the number of extends statements in general is zero so
993e31aef6aSopenharmony_ci        # far, we don't have to add a check if something extended
994e31aef6aSopenharmony_ci        # the template before this one.
995e31aef6aSopenharmony_ci        if self.extends_so_far > 0:
996e31aef6aSopenharmony_ci            # if we have a known extends we just add a template runtime
997e31aef6aSopenharmony_ci            # error into the generated code.  We could catch that at compile
998e31aef6aSopenharmony_ci            # time too, but i welcome it not to confuse users by throwing the
999e31aef6aSopenharmony_ci            # same error at different times just "because we can".
1000e31aef6aSopenharmony_ci            if not self.has_known_extends:
1001e31aef6aSopenharmony_ci                self.writeline("if parent_template is not None:")
1002e31aef6aSopenharmony_ci                self.indent()
1003e31aef6aSopenharmony_ci            self.writeline('raise TemplateRuntimeError("extended multiple times")')
1004e31aef6aSopenharmony_ci
1005e31aef6aSopenharmony_ci            # if we have a known extends already we don't need that code here
1006e31aef6aSopenharmony_ci            # as we know that the template execution will end here.
1007e31aef6aSopenharmony_ci            if self.has_known_extends:
1008e31aef6aSopenharmony_ci                raise CompilerExit()
1009e31aef6aSopenharmony_ci            else:
1010e31aef6aSopenharmony_ci                self.outdent()
1011e31aef6aSopenharmony_ci
1012e31aef6aSopenharmony_ci        self.writeline("parent_template = environment.get_template(", node)
1013e31aef6aSopenharmony_ci        self.visit(node.template, frame)
1014e31aef6aSopenharmony_ci        self.write(f", {self.name!r})")
1015e31aef6aSopenharmony_ci        self.writeline("for name, parent_block in parent_template.blocks.items():")
1016e31aef6aSopenharmony_ci        self.indent()
1017e31aef6aSopenharmony_ci        self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
1018e31aef6aSopenharmony_ci        self.outdent()
1019e31aef6aSopenharmony_ci
1020e31aef6aSopenharmony_ci        # if this extends statement was in the root level we can take
1021e31aef6aSopenharmony_ci        # advantage of that information and simplify the generated code
1022e31aef6aSopenharmony_ci        # in the top level from this point onwards
1023e31aef6aSopenharmony_ci        if frame.rootlevel:
1024e31aef6aSopenharmony_ci            self.has_known_extends = True
1025e31aef6aSopenharmony_ci
1026e31aef6aSopenharmony_ci        # and now we have one more
1027e31aef6aSopenharmony_ci        self.extends_so_far += 1
1028e31aef6aSopenharmony_ci
1029e31aef6aSopenharmony_ci    def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1030e31aef6aSopenharmony_ci        """Handles includes."""
1031e31aef6aSopenharmony_ci        if node.ignore_missing:
1032e31aef6aSopenharmony_ci            self.writeline("try:")
1033e31aef6aSopenharmony_ci            self.indent()
1034e31aef6aSopenharmony_ci
1035e31aef6aSopenharmony_ci        func_name = "get_or_select_template"
1036e31aef6aSopenharmony_ci        if isinstance(node.template, nodes.Const):
1037e31aef6aSopenharmony_ci            if isinstance(node.template.value, str):
1038e31aef6aSopenharmony_ci                func_name = "get_template"
1039e31aef6aSopenharmony_ci            elif isinstance(node.template.value, (tuple, list)):
1040e31aef6aSopenharmony_ci                func_name = "select_template"
1041e31aef6aSopenharmony_ci        elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1042e31aef6aSopenharmony_ci            func_name = "select_template"
1043e31aef6aSopenharmony_ci
1044e31aef6aSopenharmony_ci        self.writeline(f"template = environment.{func_name}(", node)
1045e31aef6aSopenharmony_ci        self.visit(node.template, frame)
1046e31aef6aSopenharmony_ci        self.write(f", {self.name!r})")
1047e31aef6aSopenharmony_ci        if node.ignore_missing:
1048e31aef6aSopenharmony_ci            self.outdent()
1049e31aef6aSopenharmony_ci            self.writeline("except TemplateNotFound:")
1050e31aef6aSopenharmony_ci            self.indent()
1051e31aef6aSopenharmony_ci            self.writeline("pass")
1052e31aef6aSopenharmony_ci            self.outdent()
1053e31aef6aSopenharmony_ci            self.writeline("else:")
1054e31aef6aSopenharmony_ci            self.indent()
1055e31aef6aSopenharmony_ci
1056e31aef6aSopenharmony_ci        skip_event_yield = False
1057e31aef6aSopenharmony_ci        if node.with_context:
1058e31aef6aSopenharmony_ci            self.writeline(
1059e31aef6aSopenharmony_ci                f"{self.choose_async()}for event in template.root_render_func("
1060e31aef6aSopenharmony_ci                "template.new_context(context.get_all(), True,"
1061e31aef6aSopenharmony_ci                f" {self.dump_local_context(frame)})):"
1062e31aef6aSopenharmony_ci            )
1063e31aef6aSopenharmony_ci        elif self.environment.is_async:
1064e31aef6aSopenharmony_ci            self.writeline(
1065e31aef6aSopenharmony_ci                "for event in (await template._get_default_module_async())"
1066e31aef6aSopenharmony_ci                "._body_stream:"
1067e31aef6aSopenharmony_ci            )
1068e31aef6aSopenharmony_ci        else:
1069e31aef6aSopenharmony_ci            self.writeline("yield from template._get_default_module()._body_stream")
1070e31aef6aSopenharmony_ci            skip_event_yield = True
1071e31aef6aSopenharmony_ci
1072e31aef6aSopenharmony_ci        if not skip_event_yield:
1073e31aef6aSopenharmony_ci            self.indent()
1074e31aef6aSopenharmony_ci            self.simple_write("event", frame)
1075e31aef6aSopenharmony_ci            self.outdent()
1076e31aef6aSopenharmony_ci
1077e31aef6aSopenharmony_ci        if node.ignore_missing:
1078e31aef6aSopenharmony_ci            self.outdent()
1079e31aef6aSopenharmony_ci
1080e31aef6aSopenharmony_ci    def _import_common(
1081e31aef6aSopenharmony_ci        self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
1082e31aef6aSopenharmony_ci    ) -> None:
1083e31aef6aSopenharmony_ci        self.write(f"{self.choose_async('await ')}environment.get_template(")
1084e31aef6aSopenharmony_ci        self.visit(node.template, frame)
1085e31aef6aSopenharmony_ci        self.write(f", {self.name!r}).")
1086e31aef6aSopenharmony_ci
1087e31aef6aSopenharmony_ci        if node.with_context:
1088e31aef6aSopenharmony_ci            f_name = f"make_module{self.choose_async('_async')}"
1089e31aef6aSopenharmony_ci            self.write(
1090e31aef6aSopenharmony_ci                f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
1091e31aef6aSopenharmony_ci            )
1092e31aef6aSopenharmony_ci        else:
1093e31aef6aSopenharmony_ci            self.write(f"_get_default_module{self.choose_async('_async')}(context)")
1094e31aef6aSopenharmony_ci
1095e31aef6aSopenharmony_ci    def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1096e31aef6aSopenharmony_ci        """Visit regular imports."""
1097e31aef6aSopenharmony_ci        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1098e31aef6aSopenharmony_ci        if frame.toplevel:
1099e31aef6aSopenharmony_ci            self.write(f"context.vars[{node.target!r}] = ")
1100e31aef6aSopenharmony_ci
1101e31aef6aSopenharmony_ci        self._import_common(node, frame)
1102e31aef6aSopenharmony_ci
1103e31aef6aSopenharmony_ci        if frame.toplevel and not node.target.startswith("_"):
1104e31aef6aSopenharmony_ci            self.writeline(f"context.exported_vars.discard({node.target!r})")
1105e31aef6aSopenharmony_ci
1106e31aef6aSopenharmony_ci    def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1107e31aef6aSopenharmony_ci        """Visit named imports."""
1108e31aef6aSopenharmony_ci        self.newline(node)
1109e31aef6aSopenharmony_ci        self.write("included_template = ")
1110e31aef6aSopenharmony_ci        self._import_common(node, frame)
1111e31aef6aSopenharmony_ci        var_names = []
1112e31aef6aSopenharmony_ci        discarded_names = []
1113e31aef6aSopenharmony_ci        for name in node.names:
1114e31aef6aSopenharmony_ci            if isinstance(name, tuple):
1115e31aef6aSopenharmony_ci                name, alias = name
1116e31aef6aSopenharmony_ci            else:
1117e31aef6aSopenharmony_ci                alias = name
1118e31aef6aSopenharmony_ci            self.writeline(
1119e31aef6aSopenharmony_ci                f"{frame.symbols.ref(alias)} ="
1120e31aef6aSopenharmony_ci                f" getattr(included_template, {name!r}, missing)"
1121e31aef6aSopenharmony_ci            )
1122e31aef6aSopenharmony_ci            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1123e31aef6aSopenharmony_ci            self.indent()
1124e31aef6aSopenharmony_ci            message = (
1125e31aef6aSopenharmony_ci                "the template {included_template.__name__!r}"
1126e31aef6aSopenharmony_ci                f" (imported on {self.position(node)})"
1127e31aef6aSopenharmony_ci                f" does not export the requested name {name!r}"
1128e31aef6aSopenharmony_ci            )
1129e31aef6aSopenharmony_ci            self.writeline(
1130e31aef6aSopenharmony_ci                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1131e31aef6aSopenharmony_ci            )
1132e31aef6aSopenharmony_ci            self.outdent()
1133e31aef6aSopenharmony_ci            if frame.toplevel:
1134e31aef6aSopenharmony_ci                var_names.append(alias)
1135e31aef6aSopenharmony_ci                if not alias.startswith("_"):
1136e31aef6aSopenharmony_ci                    discarded_names.append(alias)
1137e31aef6aSopenharmony_ci
1138e31aef6aSopenharmony_ci        if var_names:
1139e31aef6aSopenharmony_ci            if len(var_names) == 1:
1140e31aef6aSopenharmony_ci                name = var_names[0]
1141e31aef6aSopenharmony_ci                self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1142e31aef6aSopenharmony_ci            else:
1143e31aef6aSopenharmony_ci                names_kv = ", ".join(
1144e31aef6aSopenharmony_ci                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1145e31aef6aSopenharmony_ci                )
1146e31aef6aSopenharmony_ci                self.writeline(f"context.vars.update({{{names_kv}}})")
1147e31aef6aSopenharmony_ci        if discarded_names:
1148e31aef6aSopenharmony_ci            if len(discarded_names) == 1:
1149e31aef6aSopenharmony_ci                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1150e31aef6aSopenharmony_ci            else:
1151e31aef6aSopenharmony_ci                names_str = ", ".join(map(repr, discarded_names))
1152e31aef6aSopenharmony_ci                self.writeline(
1153e31aef6aSopenharmony_ci                    f"context.exported_vars.difference_update(({names_str}))"
1154e31aef6aSopenharmony_ci                )
1155e31aef6aSopenharmony_ci
1156e31aef6aSopenharmony_ci    def visit_For(self, node: nodes.For, frame: Frame) -> None:
1157e31aef6aSopenharmony_ci        loop_frame = frame.inner()
1158e31aef6aSopenharmony_ci        loop_frame.loop_frame = True
1159e31aef6aSopenharmony_ci        test_frame = frame.inner()
1160e31aef6aSopenharmony_ci        else_frame = frame.inner()
1161e31aef6aSopenharmony_ci
1162e31aef6aSopenharmony_ci        # try to figure out if we have an extended loop.  An extended loop
1163e31aef6aSopenharmony_ci        # is necessary if the loop is in recursive mode if the special loop
1164e31aef6aSopenharmony_ci        # variable is accessed in the body if the body is a scoped block.
1165e31aef6aSopenharmony_ci        extended_loop = (
1166e31aef6aSopenharmony_ci            node.recursive
1167e31aef6aSopenharmony_ci            or "loop"
1168e31aef6aSopenharmony_ci            in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1169e31aef6aSopenharmony_ci            or any(block.scoped for block in node.find_all(nodes.Block))
1170e31aef6aSopenharmony_ci        )
1171e31aef6aSopenharmony_ci
1172e31aef6aSopenharmony_ci        loop_ref = None
1173e31aef6aSopenharmony_ci        if extended_loop:
1174e31aef6aSopenharmony_ci            loop_ref = loop_frame.symbols.declare_parameter("loop")
1175e31aef6aSopenharmony_ci
1176e31aef6aSopenharmony_ci        loop_frame.symbols.analyze_node(node, for_branch="body")
1177e31aef6aSopenharmony_ci        if node.else_:
1178e31aef6aSopenharmony_ci            else_frame.symbols.analyze_node(node, for_branch="else")
1179e31aef6aSopenharmony_ci
1180e31aef6aSopenharmony_ci        if node.test:
1181e31aef6aSopenharmony_ci            loop_filter_func = self.temporary_identifier()
1182e31aef6aSopenharmony_ci            test_frame.symbols.analyze_node(node, for_branch="test")
1183e31aef6aSopenharmony_ci            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1184e31aef6aSopenharmony_ci            self.indent()
1185e31aef6aSopenharmony_ci            self.enter_frame(test_frame)
1186e31aef6aSopenharmony_ci            self.writeline(self.choose_async("async for ", "for "))
1187e31aef6aSopenharmony_ci            self.visit(node.target, loop_frame)
1188e31aef6aSopenharmony_ci            self.write(" in ")
1189e31aef6aSopenharmony_ci            self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1190e31aef6aSopenharmony_ci            self.write(":")
1191e31aef6aSopenharmony_ci            self.indent()
1192e31aef6aSopenharmony_ci            self.writeline("if ", node.test)
1193e31aef6aSopenharmony_ci            self.visit(node.test, test_frame)
1194e31aef6aSopenharmony_ci            self.write(":")
1195e31aef6aSopenharmony_ci            self.indent()
1196e31aef6aSopenharmony_ci            self.writeline("yield ")
1197e31aef6aSopenharmony_ci            self.visit(node.target, loop_frame)
1198e31aef6aSopenharmony_ci            self.outdent(3)
1199e31aef6aSopenharmony_ci            self.leave_frame(test_frame, with_python_scope=True)
1200e31aef6aSopenharmony_ci
1201e31aef6aSopenharmony_ci        # if we don't have an recursive loop we have to find the shadowed
1202e31aef6aSopenharmony_ci        # variables at that point.  Because loops can be nested but the loop
1203e31aef6aSopenharmony_ci        # variable is a special one we have to enforce aliasing for it.
1204e31aef6aSopenharmony_ci        if node.recursive:
1205e31aef6aSopenharmony_ci            self.writeline(
1206e31aef6aSopenharmony_ci                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1207e31aef6aSopenharmony_ci            )
1208e31aef6aSopenharmony_ci            self.indent()
1209e31aef6aSopenharmony_ci            self.buffer(loop_frame)
1210e31aef6aSopenharmony_ci
1211e31aef6aSopenharmony_ci            # Use the same buffer for the else frame
1212e31aef6aSopenharmony_ci            else_frame.buffer = loop_frame.buffer
1213e31aef6aSopenharmony_ci
1214e31aef6aSopenharmony_ci        # make sure the loop variable is a special one and raise a template
1215e31aef6aSopenharmony_ci        # assertion error if a loop tries to write to loop
1216e31aef6aSopenharmony_ci        if extended_loop:
1217e31aef6aSopenharmony_ci            self.writeline(f"{loop_ref} = missing")
1218e31aef6aSopenharmony_ci
1219e31aef6aSopenharmony_ci        for name in node.find_all(nodes.Name):
1220e31aef6aSopenharmony_ci            if name.ctx == "store" and name.name == "loop":
1221e31aef6aSopenharmony_ci                self.fail(
1222e31aef6aSopenharmony_ci                    "Can't assign to special loop variable in for-loop target",
1223e31aef6aSopenharmony_ci                    name.lineno,
1224e31aef6aSopenharmony_ci                )
1225e31aef6aSopenharmony_ci
1226e31aef6aSopenharmony_ci        if node.else_:
1227e31aef6aSopenharmony_ci            iteration_indicator = self.temporary_identifier()
1228e31aef6aSopenharmony_ci            self.writeline(f"{iteration_indicator} = 1")
1229e31aef6aSopenharmony_ci
1230e31aef6aSopenharmony_ci        self.writeline(self.choose_async("async for ", "for "), node)
1231e31aef6aSopenharmony_ci        self.visit(node.target, loop_frame)
1232e31aef6aSopenharmony_ci        if extended_loop:
1233e31aef6aSopenharmony_ci            self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1234e31aef6aSopenharmony_ci        else:
1235e31aef6aSopenharmony_ci            self.write(" in ")
1236e31aef6aSopenharmony_ci
1237e31aef6aSopenharmony_ci        if node.test:
1238e31aef6aSopenharmony_ci            self.write(f"{loop_filter_func}(")
1239e31aef6aSopenharmony_ci        if node.recursive:
1240e31aef6aSopenharmony_ci            self.write("reciter")
1241e31aef6aSopenharmony_ci        else:
1242e31aef6aSopenharmony_ci            if self.environment.is_async and not extended_loop:
1243e31aef6aSopenharmony_ci                self.write("auto_aiter(")
1244e31aef6aSopenharmony_ci            self.visit(node.iter, frame)
1245e31aef6aSopenharmony_ci            if self.environment.is_async and not extended_loop:
1246e31aef6aSopenharmony_ci                self.write(")")
1247e31aef6aSopenharmony_ci        if node.test:
1248e31aef6aSopenharmony_ci            self.write(")")
1249e31aef6aSopenharmony_ci
1250e31aef6aSopenharmony_ci        if node.recursive:
1251e31aef6aSopenharmony_ci            self.write(", undefined, loop_render_func, depth):")
1252e31aef6aSopenharmony_ci        else:
1253e31aef6aSopenharmony_ci            self.write(", undefined):" if extended_loop else ":")
1254e31aef6aSopenharmony_ci
1255e31aef6aSopenharmony_ci        self.indent()
1256e31aef6aSopenharmony_ci        self.enter_frame(loop_frame)
1257e31aef6aSopenharmony_ci
1258e31aef6aSopenharmony_ci        self.writeline("_loop_vars = {}")
1259e31aef6aSopenharmony_ci        self.blockvisit(node.body, loop_frame)
1260e31aef6aSopenharmony_ci        if node.else_:
1261e31aef6aSopenharmony_ci            self.writeline(f"{iteration_indicator} = 0")
1262e31aef6aSopenharmony_ci        self.outdent()
1263e31aef6aSopenharmony_ci        self.leave_frame(
1264e31aef6aSopenharmony_ci            loop_frame, with_python_scope=node.recursive and not node.else_
1265e31aef6aSopenharmony_ci        )
1266e31aef6aSopenharmony_ci
1267e31aef6aSopenharmony_ci        if node.else_:
1268e31aef6aSopenharmony_ci            self.writeline(f"if {iteration_indicator}:")
1269e31aef6aSopenharmony_ci            self.indent()
1270e31aef6aSopenharmony_ci            self.enter_frame(else_frame)
1271e31aef6aSopenharmony_ci            self.blockvisit(node.else_, else_frame)
1272e31aef6aSopenharmony_ci            self.leave_frame(else_frame)
1273e31aef6aSopenharmony_ci            self.outdent()
1274e31aef6aSopenharmony_ci
1275e31aef6aSopenharmony_ci        # if the node was recursive we have to return the buffer contents
1276e31aef6aSopenharmony_ci        # and start the iteration code
1277e31aef6aSopenharmony_ci        if node.recursive:
1278e31aef6aSopenharmony_ci            self.return_buffer_contents(loop_frame)
1279e31aef6aSopenharmony_ci            self.outdent()
1280e31aef6aSopenharmony_ci            self.start_write(frame, node)
1281e31aef6aSopenharmony_ci            self.write(f"{self.choose_async('await ')}loop(")
1282e31aef6aSopenharmony_ci            if self.environment.is_async:
1283e31aef6aSopenharmony_ci                self.write("auto_aiter(")
1284e31aef6aSopenharmony_ci            self.visit(node.iter, frame)
1285e31aef6aSopenharmony_ci            if self.environment.is_async:
1286e31aef6aSopenharmony_ci                self.write(")")
1287e31aef6aSopenharmony_ci            self.write(", loop)")
1288e31aef6aSopenharmony_ci            self.end_write(frame)
1289e31aef6aSopenharmony_ci
1290e31aef6aSopenharmony_ci        # at the end of the iteration, clear any assignments made in the
1291e31aef6aSopenharmony_ci        # loop from the top level
1292e31aef6aSopenharmony_ci        if self._assign_stack:
1293e31aef6aSopenharmony_ci            self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1294e31aef6aSopenharmony_ci
1295e31aef6aSopenharmony_ci    def visit_If(self, node: nodes.If, frame: Frame) -> None:
1296e31aef6aSopenharmony_ci        if_frame = frame.soft()
1297e31aef6aSopenharmony_ci        self.writeline("if ", node)
1298e31aef6aSopenharmony_ci        self.visit(node.test, if_frame)
1299e31aef6aSopenharmony_ci        self.write(":")
1300e31aef6aSopenharmony_ci        self.indent()
1301e31aef6aSopenharmony_ci        self.blockvisit(node.body, if_frame)
1302e31aef6aSopenharmony_ci        self.outdent()
1303e31aef6aSopenharmony_ci        for elif_ in node.elif_:
1304e31aef6aSopenharmony_ci            self.writeline("elif ", elif_)
1305e31aef6aSopenharmony_ci            self.visit(elif_.test, if_frame)
1306e31aef6aSopenharmony_ci            self.write(":")
1307e31aef6aSopenharmony_ci            self.indent()
1308e31aef6aSopenharmony_ci            self.blockvisit(elif_.body, if_frame)
1309e31aef6aSopenharmony_ci            self.outdent()
1310e31aef6aSopenharmony_ci        if node.else_:
1311e31aef6aSopenharmony_ci            self.writeline("else:")
1312e31aef6aSopenharmony_ci            self.indent()
1313e31aef6aSopenharmony_ci            self.blockvisit(node.else_, if_frame)
1314e31aef6aSopenharmony_ci            self.outdent()
1315e31aef6aSopenharmony_ci
1316e31aef6aSopenharmony_ci    def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1317e31aef6aSopenharmony_ci        macro_frame, macro_ref = self.macro_body(node, frame)
1318e31aef6aSopenharmony_ci        self.newline()
1319e31aef6aSopenharmony_ci        if frame.toplevel:
1320e31aef6aSopenharmony_ci            if not node.name.startswith("_"):
1321e31aef6aSopenharmony_ci                self.write(f"context.exported_vars.add({node.name!r})")
1322e31aef6aSopenharmony_ci            self.writeline(f"context.vars[{node.name!r}] = ")
1323e31aef6aSopenharmony_ci        self.write(f"{frame.symbols.ref(node.name)} = ")
1324e31aef6aSopenharmony_ci        self.macro_def(macro_ref, macro_frame)
1325e31aef6aSopenharmony_ci
1326e31aef6aSopenharmony_ci    def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1327e31aef6aSopenharmony_ci        call_frame, macro_ref = self.macro_body(node, frame)
1328e31aef6aSopenharmony_ci        self.writeline("caller = ")
1329e31aef6aSopenharmony_ci        self.macro_def(macro_ref, call_frame)
1330e31aef6aSopenharmony_ci        self.start_write(frame, node)
1331e31aef6aSopenharmony_ci        self.visit_Call(node.call, frame, forward_caller=True)
1332e31aef6aSopenharmony_ci        self.end_write(frame)
1333e31aef6aSopenharmony_ci
1334e31aef6aSopenharmony_ci    def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1335e31aef6aSopenharmony_ci        filter_frame = frame.inner()
1336e31aef6aSopenharmony_ci        filter_frame.symbols.analyze_node(node)
1337e31aef6aSopenharmony_ci        self.enter_frame(filter_frame)
1338e31aef6aSopenharmony_ci        self.buffer(filter_frame)
1339e31aef6aSopenharmony_ci        self.blockvisit(node.body, filter_frame)
1340e31aef6aSopenharmony_ci        self.start_write(frame, node)
1341e31aef6aSopenharmony_ci        self.visit_Filter(node.filter, filter_frame)
1342e31aef6aSopenharmony_ci        self.end_write(frame)
1343e31aef6aSopenharmony_ci        self.leave_frame(filter_frame)
1344e31aef6aSopenharmony_ci
1345e31aef6aSopenharmony_ci    def visit_With(self, node: nodes.With, frame: Frame) -> None:
1346e31aef6aSopenharmony_ci        with_frame = frame.inner()
1347e31aef6aSopenharmony_ci        with_frame.symbols.analyze_node(node)
1348e31aef6aSopenharmony_ci        self.enter_frame(with_frame)
1349e31aef6aSopenharmony_ci        for target, expr in zip(node.targets, node.values):
1350e31aef6aSopenharmony_ci            self.newline()
1351e31aef6aSopenharmony_ci            self.visit(target, with_frame)
1352e31aef6aSopenharmony_ci            self.write(" = ")
1353e31aef6aSopenharmony_ci            self.visit(expr, frame)
1354e31aef6aSopenharmony_ci        self.blockvisit(node.body, with_frame)
1355e31aef6aSopenharmony_ci        self.leave_frame(with_frame)
1356e31aef6aSopenharmony_ci
1357e31aef6aSopenharmony_ci    def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1358e31aef6aSopenharmony_ci        self.newline(node)
1359e31aef6aSopenharmony_ci        self.visit(node.node, frame)
1360e31aef6aSopenharmony_ci
1361e31aef6aSopenharmony_ci    class _FinalizeInfo(t.NamedTuple):
1362e31aef6aSopenharmony_ci        const: t.Optional[t.Callable[..., str]]
1363e31aef6aSopenharmony_ci        src: t.Optional[str]
1364e31aef6aSopenharmony_ci
1365e31aef6aSopenharmony_ci    @staticmethod
1366e31aef6aSopenharmony_ci    def _default_finalize(value: t.Any) -> t.Any:
1367e31aef6aSopenharmony_ci        """The default finalize function if the environment isn't
1368e31aef6aSopenharmony_ci        configured with one. Or, if the environment has one, this is
1369e31aef6aSopenharmony_ci        called on that function's output for constants.
1370e31aef6aSopenharmony_ci        """
1371e31aef6aSopenharmony_ci        return str(value)
1372e31aef6aSopenharmony_ci
1373e31aef6aSopenharmony_ci    _finalize: t.Optional[_FinalizeInfo] = None
1374e31aef6aSopenharmony_ci
1375e31aef6aSopenharmony_ci    def _make_finalize(self) -> _FinalizeInfo:
1376e31aef6aSopenharmony_ci        """Build the finalize function to be used on constants and at
1377e31aef6aSopenharmony_ci        runtime. Cached so it's only created once for all output nodes.
1378e31aef6aSopenharmony_ci
1379e31aef6aSopenharmony_ci        Returns a ``namedtuple`` with the following attributes:
1380e31aef6aSopenharmony_ci
1381e31aef6aSopenharmony_ci        ``const``
1382e31aef6aSopenharmony_ci            A function to finalize constant data at compile time.
1383e31aef6aSopenharmony_ci
1384e31aef6aSopenharmony_ci        ``src``
1385e31aef6aSopenharmony_ci            Source code to output around nodes to be evaluated at
1386e31aef6aSopenharmony_ci            runtime.
1387e31aef6aSopenharmony_ci        """
1388e31aef6aSopenharmony_ci        if self._finalize is not None:
1389e31aef6aSopenharmony_ci            return self._finalize
1390e31aef6aSopenharmony_ci
1391e31aef6aSopenharmony_ci        finalize: t.Optional[t.Callable[..., t.Any]]
1392e31aef6aSopenharmony_ci        finalize = default = self._default_finalize
1393e31aef6aSopenharmony_ci        src = None
1394e31aef6aSopenharmony_ci
1395e31aef6aSopenharmony_ci        if self.environment.finalize:
1396e31aef6aSopenharmony_ci            src = "environment.finalize("
1397e31aef6aSopenharmony_ci            env_finalize = self.environment.finalize
1398e31aef6aSopenharmony_ci            pass_arg = {
1399e31aef6aSopenharmony_ci                _PassArg.context: "context",
1400e31aef6aSopenharmony_ci                _PassArg.eval_context: "context.eval_ctx",
1401e31aef6aSopenharmony_ci                _PassArg.environment: "environment",
1402e31aef6aSopenharmony_ci            }.get(
1403e31aef6aSopenharmony_ci                _PassArg.from_obj(env_finalize)  # type: ignore
1404e31aef6aSopenharmony_ci            )
1405e31aef6aSopenharmony_ci            finalize = None
1406e31aef6aSopenharmony_ci
1407e31aef6aSopenharmony_ci            if pass_arg is None:
1408e31aef6aSopenharmony_ci
1409e31aef6aSopenharmony_ci                def finalize(value: t.Any) -> t.Any:  # noqa: F811
1410e31aef6aSopenharmony_ci                    return default(env_finalize(value))
1411e31aef6aSopenharmony_ci
1412e31aef6aSopenharmony_ci            else:
1413e31aef6aSopenharmony_ci                src = f"{src}{pass_arg}, "
1414e31aef6aSopenharmony_ci
1415e31aef6aSopenharmony_ci                if pass_arg == "environment":
1416e31aef6aSopenharmony_ci
1417e31aef6aSopenharmony_ci                    def finalize(value: t.Any) -> t.Any:  # noqa: F811
1418e31aef6aSopenharmony_ci                        return default(env_finalize(self.environment, value))
1419e31aef6aSopenharmony_ci
1420e31aef6aSopenharmony_ci        self._finalize = self._FinalizeInfo(finalize, src)
1421e31aef6aSopenharmony_ci        return self._finalize
1422e31aef6aSopenharmony_ci
1423e31aef6aSopenharmony_ci    def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
1424e31aef6aSopenharmony_ci        """Given a group of constant values converted from ``Output``
1425e31aef6aSopenharmony_ci        child nodes, produce a string to write to the template module
1426e31aef6aSopenharmony_ci        source.
1427e31aef6aSopenharmony_ci        """
1428e31aef6aSopenharmony_ci        return repr(concat(group))
1429e31aef6aSopenharmony_ci
1430e31aef6aSopenharmony_ci    def _output_child_to_const(
1431e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1432e31aef6aSopenharmony_ci    ) -> str:
1433e31aef6aSopenharmony_ci        """Try to optimize a child of an ``Output`` node by trying to
1434e31aef6aSopenharmony_ci        convert it to constant, finalized data at compile time.
1435e31aef6aSopenharmony_ci
1436e31aef6aSopenharmony_ci        If :exc:`Impossible` is raised, the node is not constant and
1437e31aef6aSopenharmony_ci        will be evaluated at runtime. Any other exception will also be
1438e31aef6aSopenharmony_ci        evaluated at runtime for easier debugging.
1439e31aef6aSopenharmony_ci        """
1440e31aef6aSopenharmony_ci        const = node.as_const(frame.eval_ctx)
1441e31aef6aSopenharmony_ci
1442e31aef6aSopenharmony_ci        if frame.eval_ctx.autoescape:
1443e31aef6aSopenharmony_ci            const = escape(const)
1444e31aef6aSopenharmony_ci
1445e31aef6aSopenharmony_ci        # Template data doesn't go through finalize.
1446e31aef6aSopenharmony_ci        if isinstance(node, nodes.TemplateData):
1447e31aef6aSopenharmony_ci            return str(const)
1448e31aef6aSopenharmony_ci
1449e31aef6aSopenharmony_ci        return finalize.const(const)  # type: ignore
1450e31aef6aSopenharmony_ci
1451e31aef6aSopenharmony_ci    def _output_child_pre(
1452e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1453e31aef6aSopenharmony_ci    ) -> None:
1454e31aef6aSopenharmony_ci        """Output extra source code before visiting a child of an
1455e31aef6aSopenharmony_ci        ``Output`` node.
1456e31aef6aSopenharmony_ci        """
1457e31aef6aSopenharmony_ci        if frame.eval_ctx.volatile:
1458e31aef6aSopenharmony_ci            self.write("(escape if context.eval_ctx.autoescape else str)(")
1459e31aef6aSopenharmony_ci        elif frame.eval_ctx.autoescape:
1460e31aef6aSopenharmony_ci            self.write("escape(")
1461e31aef6aSopenharmony_ci        else:
1462e31aef6aSopenharmony_ci            self.write("str(")
1463e31aef6aSopenharmony_ci
1464e31aef6aSopenharmony_ci        if finalize.src is not None:
1465e31aef6aSopenharmony_ci            self.write(finalize.src)
1466e31aef6aSopenharmony_ci
1467e31aef6aSopenharmony_ci    def _output_child_post(
1468e31aef6aSopenharmony_ci        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1469e31aef6aSopenharmony_ci    ) -> None:
1470e31aef6aSopenharmony_ci        """Output extra source code after visiting a child of an
1471e31aef6aSopenharmony_ci        ``Output`` node.
1472e31aef6aSopenharmony_ci        """
1473e31aef6aSopenharmony_ci        self.write(")")
1474e31aef6aSopenharmony_ci
1475e31aef6aSopenharmony_ci        if finalize.src is not None:
1476e31aef6aSopenharmony_ci            self.write(")")
1477e31aef6aSopenharmony_ci
1478e31aef6aSopenharmony_ci    def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1479e31aef6aSopenharmony_ci        # If an extends is active, don't render outside a block.
1480e31aef6aSopenharmony_ci        if frame.require_output_check:
1481e31aef6aSopenharmony_ci            # A top-level extends is known to exist at compile time.
1482e31aef6aSopenharmony_ci            if self.has_known_extends:
1483e31aef6aSopenharmony_ci                return
1484e31aef6aSopenharmony_ci
1485e31aef6aSopenharmony_ci            self.writeline("if parent_template is None:")
1486e31aef6aSopenharmony_ci            self.indent()
1487e31aef6aSopenharmony_ci
1488e31aef6aSopenharmony_ci        finalize = self._make_finalize()
1489e31aef6aSopenharmony_ci        body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1490e31aef6aSopenharmony_ci
1491e31aef6aSopenharmony_ci        # Evaluate constants at compile time if possible. Each item in
1492e31aef6aSopenharmony_ci        # body will be either a list of static data or a node to be
1493e31aef6aSopenharmony_ci        # evaluated at runtime.
1494e31aef6aSopenharmony_ci        for child in node.nodes:
1495e31aef6aSopenharmony_ci            try:
1496e31aef6aSopenharmony_ci                if not (
1497e31aef6aSopenharmony_ci                    # If the finalize function requires runtime context,
1498e31aef6aSopenharmony_ci                    # constants can't be evaluated at compile time.
1499e31aef6aSopenharmony_ci                    finalize.const
1500e31aef6aSopenharmony_ci                    # Unless it's basic template data that won't be
1501e31aef6aSopenharmony_ci                    # finalized anyway.
1502e31aef6aSopenharmony_ci                    or isinstance(child, nodes.TemplateData)
1503e31aef6aSopenharmony_ci                ):
1504e31aef6aSopenharmony_ci                    raise nodes.Impossible()
1505e31aef6aSopenharmony_ci
1506e31aef6aSopenharmony_ci                const = self._output_child_to_const(child, frame, finalize)
1507e31aef6aSopenharmony_ci            except (nodes.Impossible, Exception):
1508e31aef6aSopenharmony_ci                # The node was not constant and needs to be evaluated at
1509e31aef6aSopenharmony_ci                # runtime. Or another error was raised, which is easier
1510e31aef6aSopenharmony_ci                # to debug at runtime.
1511e31aef6aSopenharmony_ci                body.append(child)
1512e31aef6aSopenharmony_ci                continue
1513e31aef6aSopenharmony_ci
1514e31aef6aSopenharmony_ci            if body and isinstance(body[-1], list):
1515e31aef6aSopenharmony_ci                body[-1].append(const)
1516e31aef6aSopenharmony_ci            else:
1517e31aef6aSopenharmony_ci                body.append([const])
1518e31aef6aSopenharmony_ci
1519e31aef6aSopenharmony_ci        if frame.buffer is not None:
1520e31aef6aSopenharmony_ci            if len(body) == 1:
1521e31aef6aSopenharmony_ci                self.writeline(f"{frame.buffer}.append(")
1522e31aef6aSopenharmony_ci            else:
1523e31aef6aSopenharmony_ci                self.writeline(f"{frame.buffer}.extend((")
1524e31aef6aSopenharmony_ci
1525e31aef6aSopenharmony_ci            self.indent()
1526e31aef6aSopenharmony_ci
1527e31aef6aSopenharmony_ci        for item in body:
1528e31aef6aSopenharmony_ci            if isinstance(item, list):
1529e31aef6aSopenharmony_ci                # A group of constant data to join and output.
1530e31aef6aSopenharmony_ci                val = self._output_const_repr(item)
1531e31aef6aSopenharmony_ci
1532e31aef6aSopenharmony_ci                if frame.buffer is None:
1533e31aef6aSopenharmony_ci                    self.writeline("yield " + val)
1534e31aef6aSopenharmony_ci                else:
1535e31aef6aSopenharmony_ci                    self.writeline(val + ",")
1536e31aef6aSopenharmony_ci            else:
1537e31aef6aSopenharmony_ci                if frame.buffer is None:
1538e31aef6aSopenharmony_ci                    self.writeline("yield ", item)
1539e31aef6aSopenharmony_ci                else:
1540e31aef6aSopenharmony_ci                    self.newline(item)
1541e31aef6aSopenharmony_ci
1542e31aef6aSopenharmony_ci                # A node to be evaluated at runtime.
1543e31aef6aSopenharmony_ci                self._output_child_pre(item, frame, finalize)
1544e31aef6aSopenharmony_ci                self.visit(item, frame)
1545e31aef6aSopenharmony_ci                self._output_child_post(item, frame, finalize)
1546e31aef6aSopenharmony_ci
1547e31aef6aSopenharmony_ci                if frame.buffer is not None:
1548e31aef6aSopenharmony_ci                    self.write(",")
1549e31aef6aSopenharmony_ci
1550e31aef6aSopenharmony_ci        if frame.buffer is not None:
1551e31aef6aSopenharmony_ci            self.outdent()
1552e31aef6aSopenharmony_ci            self.writeline(")" if len(body) == 1 else "))")
1553e31aef6aSopenharmony_ci
1554e31aef6aSopenharmony_ci        if frame.require_output_check:
1555e31aef6aSopenharmony_ci            self.outdent()
1556e31aef6aSopenharmony_ci
1557e31aef6aSopenharmony_ci    def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1558e31aef6aSopenharmony_ci        self.push_assign_tracking()
1559e31aef6aSopenharmony_ci        self.newline(node)
1560e31aef6aSopenharmony_ci        self.visit(node.target, frame)
1561e31aef6aSopenharmony_ci        self.write(" = ")
1562e31aef6aSopenharmony_ci        self.visit(node.node, frame)
1563e31aef6aSopenharmony_ci        self.pop_assign_tracking(frame)
1564e31aef6aSopenharmony_ci
1565e31aef6aSopenharmony_ci    def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1566e31aef6aSopenharmony_ci        self.push_assign_tracking()
1567e31aef6aSopenharmony_ci        block_frame = frame.inner()
1568e31aef6aSopenharmony_ci        # This is a special case.  Since a set block always captures we
1569e31aef6aSopenharmony_ci        # will disable output checks.  This way one can use set blocks
1570e31aef6aSopenharmony_ci        # toplevel even in extended templates.
1571e31aef6aSopenharmony_ci        block_frame.require_output_check = False
1572e31aef6aSopenharmony_ci        block_frame.symbols.analyze_node(node)
1573e31aef6aSopenharmony_ci        self.enter_frame(block_frame)
1574e31aef6aSopenharmony_ci        self.buffer(block_frame)
1575e31aef6aSopenharmony_ci        self.blockvisit(node.body, block_frame)
1576e31aef6aSopenharmony_ci        self.newline(node)
1577e31aef6aSopenharmony_ci        self.visit(node.target, frame)
1578e31aef6aSopenharmony_ci        self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1579e31aef6aSopenharmony_ci        if node.filter is not None:
1580e31aef6aSopenharmony_ci            self.visit_Filter(node.filter, block_frame)
1581e31aef6aSopenharmony_ci        else:
1582e31aef6aSopenharmony_ci            self.write(f"concat({block_frame.buffer})")
1583e31aef6aSopenharmony_ci        self.write(")")
1584e31aef6aSopenharmony_ci        self.pop_assign_tracking(frame)
1585e31aef6aSopenharmony_ci        self.leave_frame(block_frame)
1586e31aef6aSopenharmony_ci
1587e31aef6aSopenharmony_ci    # -- Expression Visitors
1588e31aef6aSopenharmony_ci
1589e31aef6aSopenharmony_ci    def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1590e31aef6aSopenharmony_ci        if node.ctx == "store" and (
1591e31aef6aSopenharmony_ci            frame.toplevel or frame.loop_frame or frame.block_frame
1592e31aef6aSopenharmony_ci        ):
1593e31aef6aSopenharmony_ci            if self._assign_stack:
1594e31aef6aSopenharmony_ci                self._assign_stack[-1].add(node.name)
1595e31aef6aSopenharmony_ci        ref = frame.symbols.ref(node.name)
1596e31aef6aSopenharmony_ci
1597e31aef6aSopenharmony_ci        # If we are looking up a variable we might have to deal with the
1598e31aef6aSopenharmony_ci        # case where it's undefined.  We can skip that case if the load
1599e31aef6aSopenharmony_ci        # instruction indicates a parameter which are always defined.
1600e31aef6aSopenharmony_ci        if node.ctx == "load":
1601e31aef6aSopenharmony_ci            load = frame.symbols.find_load(ref)
1602e31aef6aSopenharmony_ci            if not (
1603e31aef6aSopenharmony_ci                load is not None
1604e31aef6aSopenharmony_ci                and load[0] == VAR_LOAD_PARAMETER
1605e31aef6aSopenharmony_ci                and not self.parameter_is_undeclared(ref)
1606e31aef6aSopenharmony_ci            ):
1607e31aef6aSopenharmony_ci                self.write(
1608e31aef6aSopenharmony_ci                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1609e31aef6aSopenharmony_ci                )
1610e31aef6aSopenharmony_ci                return
1611e31aef6aSopenharmony_ci
1612e31aef6aSopenharmony_ci        self.write(ref)
1613e31aef6aSopenharmony_ci
1614e31aef6aSopenharmony_ci    def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1615e31aef6aSopenharmony_ci        # NSRefs can only be used to store values; since they use the normal
1616e31aef6aSopenharmony_ci        # `foo.bar` notation they will be parsed as a normal attribute access
1617e31aef6aSopenharmony_ci        # when used anywhere but in a `set` context
1618e31aef6aSopenharmony_ci        ref = frame.symbols.ref(node.name)
1619e31aef6aSopenharmony_ci        self.writeline(f"if not isinstance({ref}, Namespace):")
1620e31aef6aSopenharmony_ci        self.indent()
1621e31aef6aSopenharmony_ci        self.writeline(
1622e31aef6aSopenharmony_ci            "raise TemplateRuntimeError"
1623e31aef6aSopenharmony_ci            '("cannot assign attribute on non-namespace object")'
1624e31aef6aSopenharmony_ci        )
1625e31aef6aSopenharmony_ci        self.outdent()
1626e31aef6aSopenharmony_ci        self.writeline(f"{ref}[{node.attr!r}]")
1627e31aef6aSopenharmony_ci
1628e31aef6aSopenharmony_ci    def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1629e31aef6aSopenharmony_ci        val = node.as_const(frame.eval_ctx)
1630e31aef6aSopenharmony_ci        if isinstance(val, float):
1631e31aef6aSopenharmony_ci            self.write(str(val))
1632e31aef6aSopenharmony_ci        else:
1633e31aef6aSopenharmony_ci            self.write(repr(val))
1634e31aef6aSopenharmony_ci
1635e31aef6aSopenharmony_ci    def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1636e31aef6aSopenharmony_ci        try:
1637e31aef6aSopenharmony_ci            self.write(repr(node.as_const(frame.eval_ctx)))
1638e31aef6aSopenharmony_ci        except nodes.Impossible:
1639e31aef6aSopenharmony_ci            self.write(
1640e31aef6aSopenharmony_ci                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1641e31aef6aSopenharmony_ci            )
1642e31aef6aSopenharmony_ci
1643e31aef6aSopenharmony_ci    def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1644e31aef6aSopenharmony_ci        self.write("(")
1645e31aef6aSopenharmony_ci        idx = -1
1646e31aef6aSopenharmony_ci        for idx, item in enumerate(node.items):
1647e31aef6aSopenharmony_ci            if idx:
1648e31aef6aSopenharmony_ci                self.write(", ")
1649e31aef6aSopenharmony_ci            self.visit(item, frame)
1650e31aef6aSopenharmony_ci        self.write(",)" if idx == 0 else ")")
1651e31aef6aSopenharmony_ci
1652e31aef6aSopenharmony_ci    def visit_List(self, node: nodes.List, frame: Frame) -> None:
1653e31aef6aSopenharmony_ci        self.write("[")
1654e31aef6aSopenharmony_ci        for idx, item in enumerate(node.items):
1655e31aef6aSopenharmony_ci            if idx:
1656e31aef6aSopenharmony_ci                self.write(", ")
1657e31aef6aSopenharmony_ci            self.visit(item, frame)
1658e31aef6aSopenharmony_ci        self.write("]")
1659e31aef6aSopenharmony_ci
1660e31aef6aSopenharmony_ci    def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1661e31aef6aSopenharmony_ci        self.write("{")
1662e31aef6aSopenharmony_ci        for idx, item in enumerate(node.items):
1663e31aef6aSopenharmony_ci            if idx:
1664e31aef6aSopenharmony_ci                self.write(", ")
1665e31aef6aSopenharmony_ci            self.visit(item.key, frame)
1666e31aef6aSopenharmony_ci            self.write(": ")
1667e31aef6aSopenharmony_ci            self.visit(item.value, frame)
1668e31aef6aSopenharmony_ci        self.write("}")
1669e31aef6aSopenharmony_ci
1670e31aef6aSopenharmony_ci    visit_Add = _make_binop("+")
1671e31aef6aSopenharmony_ci    visit_Sub = _make_binop("-")
1672e31aef6aSopenharmony_ci    visit_Mul = _make_binop("*")
1673e31aef6aSopenharmony_ci    visit_Div = _make_binop("/")
1674e31aef6aSopenharmony_ci    visit_FloorDiv = _make_binop("//")
1675e31aef6aSopenharmony_ci    visit_Pow = _make_binop("**")
1676e31aef6aSopenharmony_ci    visit_Mod = _make_binop("%")
1677e31aef6aSopenharmony_ci    visit_And = _make_binop("and")
1678e31aef6aSopenharmony_ci    visit_Or = _make_binop("or")
1679e31aef6aSopenharmony_ci    visit_Pos = _make_unop("+")
1680e31aef6aSopenharmony_ci    visit_Neg = _make_unop("-")
1681e31aef6aSopenharmony_ci    visit_Not = _make_unop("not ")
1682e31aef6aSopenharmony_ci
1683e31aef6aSopenharmony_ci    @optimizeconst
1684e31aef6aSopenharmony_ci    def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1685e31aef6aSopenharmony_ci        if frame.eval_ctx.volatile:
1686e31aef6aSopenharmony_ci            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1687e31aef6aSopenharmony_ci        elif frame.eval_ctx.autoescape:
1688e31aef6aSopenharmony_ci            func_name = "markup_join"
1689e31aef6aSopenharmony_ci        else:
1690e31aef6aSopenharmony_ci            func_name = "str_join"
1691e31aef6aSopenharmony_ci        self.write(f"{func_name}((")
1692e31aef6aSopenharmony_ci        for arg in node.nodes:
1693e31aef6aSopenharmony_ci            self.visit(arg, frame)
1694e31aef6aSopenharmony_ci            self.write(", ")
1695e31aef6aSopenharmony_ci        self.write("))")
1696e31aef6aSopenharmony_ci
1697e31aef6aSopenharmony_ci    @optimizeconst
1698e31aef6aSopenharmony_ci    def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1699e31aef6aSopenharmony_ci        self.write("(")
1700e31aef6aSopenharmony_ci        self.visit(node.expr, frame)
1701e31aef6aSopenharmony_ci        for op in node.ops:
1702e31aef6aSopenharmony_ci            self.visit(op, frame)
1703e31aef6aSopenharmony_ci        self.write(")")
1704e31aef6aSopenharmony_ci
1705e31aef6aSopenharmony_ci    def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1706e31aef6aSopenharmony_ci        self.write(f" {operators[node.op]} ")
1707e31aef6aSopenharmony_ci        self.visit(node.expr, frame)
1708e31aef6aSopenharmony_ci
1709e31aef6aSopenharmony_ci    @optimizeconst
1710e31aef6aSopenharmony_ci    def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1711e31aef6aSopenharmony_ci        if self.environment.is_async:
1712e31aef6aSopenharmony_ci            self.write("(await auto_await(")
1713e31aef6aSopenharmony_ci
1714e31aef6aSopenharmony_ci        self.write("environment.getattr(")
1715e31aef6aSopenharmony_ci        self.visit(node.node, frame)
1716e31aef6aSopenharmony_ci        self.write(f", {node.attr!r})")
1717e31aef6aSopenharmony_ci
1718e31aef6aSopenharmony_ci        if self.environment.is_async:
1719e31aef6aSopenharmony_ci            self.write("))")
1720e31aef6aSopenharmony_ci
1721e31aef6aSopenharmony_ci    @optimizeconst
1722e31aef6aSopenharmony_ci    def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1723e31aef6aSopenharmony_ci        # slices bypass the environment getitem method.
1724e31aef6aSopenharmony_ci        if isinstance(node.arg, nodes.Slice):
1725e31aef6aSopenharmony_ci            self.visit(node.node, frame)
1726e31aef6aSopenharmony_ci            self.write("[")
1727e31aef6aSopenharmony_ci            self.visit(node.arg, frame)
1728e31aef6aSopenharmony_ci            self.write("]")
1729e31aef6aSopenharmony_ci        else:
1730e31aef6aSopenharmony_ci            if self.environment.is_async:
1731e31aef6aSopenharmony_ci                self.write("(await auto_await(")
1732e31aef6aSopenharmony_ci
1733e31aef6aSopenharmony_ci            self.write("environment.getitem(")
1734e31aef6aSopenharmony_ci            self.visit(node.node, frame)
1735e31aef6aSopenharmony_ci            self.write(", ")
1736e31aef6aSopenharmony_ci            self.visit(node.arg, frame)
1737e31aef6aSopenharmony_ci            self.write(")")
1738e31aef6aSopenharmony_ci
1739e31aef6aSopenharmony_ci            if self.environment.is_async:
1740e31aef6aSopenharmony_ci                self.write("))")
1741e31aef6aSopenharmony_ci
1742e31aef6aSopenharmony_ci    def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1743e31aef6aSopenharmony_ci        if node.start is not None:
1744e31aef6aSopenharmony_ci            self.visit(node.start, frame)
1745e31aef6aSopenharmony_ci        self.write(":")
1746e31aef6aSopenharmony_ci        if node.stop is not None:
1747e31aef6aSopenharmony_ci            self.visit(node.stop, frame)
1748e31aef6aSopenharmony_ci        if node.step is not None:
1749e31aef6aSopenharmony_ci            self.write(":")
1750e31aef6aSopenharmony_ci            self.visit(node.step, frame)
1751e31aef6aSopenharmony_ci
1752e31aef6aSopenharmony_ci    @contextmanager
1753e31aef6aSopenharmony_ci    def _filter_test_common(
1754e31aef6aSopenharmony_ci        self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
1755e31aef6aSopenharmony_ci    ) -> t.Iterator[None]:
1756e31aef6aSopenharmony_ci        if self.environment.is_async:
1757e31aef6aSopenharmony_ci            self.write("(await auto_await(")
1758e31aef6aSopenharmony_ci
1759e31aef6aSopenharmony_ci        if is_filter:
1760e31aef6aSopenharmony_ci            self.write(f"{self.filters[node.name]}(")
1761e31aef6aSopenharmony_ci            func = self.environment.filters.get(node.name)
1762e31aef6aSopenharmony_ci        else:
1763e31aef6aSopenharmony_ci            self.write(f"{self.tests[node.name]}(")
1764e31aef6aSopenharmony_ci            func = self.environment.tests.get(node.name)
1765e31aef6aSopenharmony_ci
1766e31aef6aSopenharmony_ci        # When inside an If or CondExpr frame, allow the filter to be
1767e31aef6aSopenharmony_ci        # undefined at compile time and only raise an error if it's
1768e31aef6aSopenharmony_ci        # actually called at runtime. See pull_dependencies.
1769e31aef6aSopenharmony_ci        if func is None and not frame.soft_frame:
1770e31aef6aSopenharmony_ci            type_name = "filter" if is_filter else "test"
1771e31aef6aSopenharmony_ci            self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
1772e31aef6aSopenharmony_ci
1773e31aef6aSopenharmony_ci        pass_arg = {
1774e31aef6aSopenharmony_ci            _PassArg.context: "context",
1775e31aef6aSopenharmony_ci            _PassArg.eval_context: "context.eval_ctx",
1776e31aef6aSopenharmony_ci            _PassArg.environment: "environment",
1777e31aef6aSopenharmony_ci        }.get(
1778e31aef6aSopenharmony_ci            _PassArg.from_obj(func)  # type: ignore
1779e31aef6aSopenharmony_ci        )
1780e31aef6aSopenharmony_ci
1781e31aef6aSopenharmony_ci        if pass_arg is not None:
1782e31aef6aSopenharmony_ci            self.write(f"{pass_arg}, ")
1783e31aef6aSopenharmony_ci
1784e31aef6aSopenharmony_ci        # Back to the visitor function to handle visiting the target of
1785e31aef6aSopenharmony_ci        # the filter or test.
1786e31aef6aSopenharmony_ci        yield
1787e31aef6aSopenharmony_ci
1788e31aef6aSopenharmony_ci        self.signature(node, frame)
1789e31aef6aSopenharmony_ci        self.write(")")
1790e31aef6aSopenharmony_ci
1791e31aef6aSopenharmony_ci        if self.environment.is_async:
1792e31aef6aSopenharmony_ci            self.write("))")
1793e31aef6aSopenharmony_ci
1794e31aef6aSopenharmony_ci    @optimizeconst
1795e31aef6aSopenharmony_ci    def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1796e31aef6aSopenharmony_ci        with self._filter_test_common(node, frame, True):
1797e31aef6aSopenharmony_ci            # if the filter node is None we are inside a filter block
1798e31aef6aSopenharmony_ci            # and want to write to the current buffer
1799e31aef6aSopenharmony_ci            if node.node is not None:
1800e31aef6aSopenharmony_ci                self.visit(node.node, frame)
1801e31aef6aSopenharmony_ci            elif frame.eval_ctx.volatile:
1802e31aef6aSopenharmony_ci                self.write(
1803e31aef6aSopenharmony_ci                    f"(Markup(concat({frame.buffer}))"
1804e31aef6aSopenharmony_ci                    f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1805e31aef6aSopenharmony_ci                )
1806e31aef6aSopenharmony_ci            elif frame.eval_ctx.autoescape:
1807e31aef6aSopenharmony_ci                self.write(f"Markup(concat({frame.buffer}))")
1808e31aef6aSopenharmony_ci            else:
1809e31aef6aSopenharmony_ci                self.write(f"concat({frame.buffer})")
1810e31aef6aSopenharmony_ci
1811e31aef6aSopenharmony_ci    @optimizeconst
1812e31aef6aSopenharmony_ci    def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1813e31aef6aSopenharmony_ci        with self._filter_test_common(node, frame, False):
1814e31aef6aSopenharmony_ci            self.visit(node.node, frame)
1815e31aef6aSopenharmony_ci
1816e31aef6aSopenharmony_ci    @optimizeconst
1817e31aef6aSopenharmony_ci    def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1818e31aef6aSopenharmony_ci        frame = frame.soft()
1819e31aef6aSopenharmony_ci
1820e31aef6aSopenharmony_ci        def write_expr2() -> None:
1821e31aef6aSopenharmony_ci            if node.expr2 is not None:
1822e31aef6aSopenharmony_ci                self.visit(node.expr2, frame)
1823e31aef6aSopenharmony_ci                return
1824e31aef6aSopenharmony_ci
1825e31aef6aSopenharmony_ci            self.write(
1826e31aef6aSopenharmony_ci                f'cond_expr_undefined("the inline if-expression on'
1827e31aef6aSopenharmony_ci                f" {self.position(node)} evaluated to false and no else"
1828e31aef6aSopenharmony_ci                f' section was defined.")'
1829e31aef6aSopenharmony_ci            )
1830e31aef6aSopenharmony_ci
1831e31aef6aSopenharmony_ci        self.write("(")
1832e31aef6aSopenharmony_ci        self.visit(node.expr1, frame)
1833e31aef6aSopenharmony_ci        self.write(" if ")
1834e31aef6aSopenharmony_ci        self.visit(node.test, frame)
1835e31aef6aSopenharmony_ci        self.write(" else ")
1836e31aef6aSopenharmony_ci        write_expr2()
1837e31aef6aSopenharmony_ci        self.write(")")
1838e31aef6aSopenharmony_ci
1839e31aef6aSopenharmony_ci    @optimizeconst
1840e31aef6aSopenharmony_ci    def visit_Call(
1841e31aef6aSopenharmony_ci        self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1842e31aef6aSopenharmony_ci    ) -> None:
1843e31aef6aSopenharmony_ci        if self.environment.is_async:
1844e31aef6aSopenharmony_ci            self.write("(await auto_await(")
1845e31aef6aSopenharmony_ci        if self.environment.sandboxed:
1846e31aef6aSopenharmony_ci            self.write("environment.call(context, ")
1847e31aef6aSopenharmony_ci        else:
1848e31aef6aSopenharmony_ci            self.write("context.call(")
1849e31aef6aSopenharmony_ci        self.visit(node.node, frame)
1850e31aef6aSopenharmony_ci        extra_kwargs = {"caller": "caller"} if forward_caller else None
1851e31aef6aSopenharmony_ci        loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1852e31aef6aSopenharmony_ci        block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1853e31aef6aSopenharmony_ci        if extra_kwargs:
1854e31aef6aSopenharmony_ci            extra_kwargs.update(loop_kwargs, **block_kwargs)
1855e31aef6aSopenharmony_ci        elif loop_kwargs or block_kwargs:
1856e31aef6aSopenharmony_ci            extra_kwargs = dict(loop_kwargs, **block_kwargs)
1857e31aef6aSopenharmony_ci        self.signature(node, frame, extra_kwargs)
1858e31aef6aSopenharmony_ci        self.write(")")
1859e31aef6aSopenharmony_ci        if self.environment.is_async:
1860e31aef6aSopenharmony_ci            self.write("))")
1861e31aef6aSopenharmony_ci
1862e31aef6aSopenharmony_ci    def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1863e31aef6aSopenharmony_ci        self.write(node.key + "=")
1864e31aef6aSopenharmony_ci        self.visit(node.value, frame)
1865e31aef6aSopenharmony_ci
1866e31aef6aSopenharmony_ci    # -- Unused nodes for extensions
1867e31aef6aSopenharmony_ci
1868e31aef6aSopenharmony_ci    def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1869e31aef6aSopenharmony_ci        self.write("Markup(")
1870e31aef6aSopenharmony_ci        self.visit(node.expr, frame)
1871e31aef6aSopenharmony_ci        self.write(")")
1872e31aef6aSopenharmony_ci
1873e31aef6aSopenharmony_ci    def visit_MarkSafeIfAutoescape(
1874e31aef6aSopenharmony_ci        self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1875e31aef6aSopenharmony_ci    ) -> None:
1876e31aef6aSopenharmony_ci        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1877e31aef6aSopenharmony_ci        self.visit(node.expr, frame)
1878e31aef6aSopenharmony_ci        self.write(")")
1879e31aef6aSopenharmony_ci
1880e31aef6aSopenharmony_ci    def visit_EnvironmentAttribute(
1881e31aef6aSopenharmony_ci        self, node: nodes.EnvironmentAttribute, frame: Frame
1882e31aef6aSopenharmony_ci    ) -> None:
1883e31aef6aSopenharmony_ci        self.write("environment." + node.name)
1884e31aef6aSopenharmony_ci
1885e31aef6aSopenharmony_ci    def visit_ExtensionAttribute(
1886e31aef6aSopenharmony_ci        self, node: nodes.ExtensionAttribute, frame: Frame
1887e31aef6aSopenharmony_ci    ) -> None:
1888e31aef6aSopenharmony_ci        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
1889e31aef6aSopenharmony_ci
1890e31aef6aSopenharmony_ci    def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1891e31aef6aSopenharmony_ci        self.write(self.import_aliases[node.importname])
1892e31aef6aSopenharmony_ci
1893e31aef6aSopenharmony_ci    def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1894e31aef6aSopenharmony_ci        self.write(node.name)
1895e31aef6aSopenharmony_ci
1896e31aef6aSopenharmony_ci    def visit_ContextReference(
1897e31aef6aSopenharmony_ci        self, node: nodes.ContextReference, frame: Frame
1898e31aef6aSopenharmony_ci    ) -> None:
1899e31aef6aSopenharmony_ci        self.write("context")
1900e31aef6aSopenharmony_ci
1901e31aef6aSopenharmony_ci    def visit_DerivedContextReference(
1902e31aef6aSopenharmony_ci        self, node: nodes.DerivedContextReference, frame: Frame
1903e31aef6aSopenharmony_ci    ) -> None:
1904e31aef6aSopenharmony_ci        self.write(self.derive_context(frame))
1905e31aef6aSopenharmony_ci
1906e31aef6aSopenharmony_ci    def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1907e31aef6aSopenharmony_ci        self.writeline("continue", node)
1908e31aef6aSopenharmony_ci
1909e31aef6aSopenharmony_ci    def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1910e31aef6aSopenharmony_ci        self.writeline("break", node)
1911e31aef6aSopenharmony_ci
1912e31aef6aSopenharmony_ci    def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1913e31aef6aSopenharmony_ci        scope_frame = frame.inner()
1914e31aef6aSopenharmony_ci        scope_frame.symbols.analyze_node(node)
1915e31aef6aSopenharmony_ci        self.enter_frame(scope_frame)
1916e31aef6aSopenharmony_ci        self.blockvisit(node.body, scope_frame)
1917e31aef6aSopenharmony_ci        self.leave_frame(scope_frame)
1918e31aef6aSopenharmony_ci
1919e31aef6aSopenharmony_ci    def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1920e31aef6aSopenharmony_ci        ctx = self.temporary_identifier()
1921e31aef6aSopenharmony_ci        self.writeline(f"{ctx} = {self.derive_context(frame)}")
1922e31aef6aSopenharmony_ci        self.writeline(f"{ctx}.vars = ")
1923e31aef6aSopenharmony_ci        self.visit(node.context, frame)
1924e31aef6aSopenharmony_ci        self.push_context_reference(ctx)
1925e31aef6aSopenharmony_ci
1926e31aef6aSopenharmony_ci        scope_frame = frame.inner(isolated=True)
1927e31aef6aSopenharmony_ci        scope_frame.symbols.analyze_node(node)
1928e31aef6aSopenharmony_ci        self.enter_frame(scope_frame)
1929e31aef6aSopenharmony_ci        self.blockvisit(node.body, scope_frame)
1930e31aef6aSopenharmony_ci        self.leave_frame(scope_frame)
1931e31aef6aSopenharmony_ci        self.pop_context_reference()
1932e31aef6aSopenharmony_ci
1933e31aef6aSopenharmony_ci    def visit_EvalContextModifier(
1934e31aef6aSopenharmony_ci        self, node: nodes.EvalContextModifier, frame: Frame
1935e31aef6aSopenharmony_ci    ) -> None:
1936e31aef6aSopenharmony_ci        for keyword in node.options:
1937e31aef6aSopenharmony_ci            self.writeline(f"context.eval_ctx.{keyword.key} = ")
1938e31aef6aSopenharmony_ci            self.visit(keyword.value, frame)
1939e31aef6aSopenharmony_ci            try:
1940e31aef6aSopenharmony_ci                val = keyword.value.as_const(frame.eval_ctx)
1941e31aef6aSopenharmony_ci            except nodes.Impossible:
1942e31aef6aSopenharmony_ci                frame.eval_ctx.volatile = True
1943e31aef6aSopenharmony_ci            else:
1944e31aef6aSopenharmony_ci                setattr(frame.eval_ctx, keyword.key, val)
1945e31aef6aSopenharmony_ci
1946e31aef6aSopenharmony_ci    def visit_ScopedEvalContextModifier(
1947e31aef6aSopenharmony_ci        self, node: nodes.ScopedEvalContextModifier, frame: Frame
1948e31aef6aSopenharmony_ci    ) -> None:
1949e31aef6aSopenharmony_ci        old_ctx_name = self.temporary_identifier()
1950e31aef6aSopenharmony_ci        saved_ctx = frame.eval_ctx.save()
1951e31aef6aSopenharmony_ci        self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1952e31aef6aSopenharmony_ci        self.visit_EvalContextModifier(node, frame)
1953e31aef6aSopenharmony_ci        for child in node.body:
1954e31aef6aSopenharmony_ci            self.visit(child, frame)
1955e31aef6aSopenharmony_ci        frame.eval_ctx.revert(saved_ctx)
1956e31aef6aSopenharmony_ci        self.writeline(f"context.eval_ctx.revert({old_ctx_name})")
1957