Home | History | Annotate | Download | only in jinja2
      1 # -*- coding: utf-8 -*-
      2 """
      3     jinja2.optimizer
      4     ~~~~~~~~~~~~~~~~
      5 
      6     The jinja optimizer is currently trying to constant fold a few expressions
      7     and modify the AST in place so that it should be easier to evaluate it.
      8 
      9     Because the AST does not contain all the scoping information and the
     10     compiler has to find that out, we cannot do all the optimizations we
     11     want.  For example loop unrolling doesn't work because unrolled loops would
     12     have a different scoping.
     13 
     14     The solution would be a second syntax tree that has the scoping rules stored.
     15 
     16     :copyright: (c) 2010 by the Jinja Team.
     17     :license: BSD.
     18 """
     19 from jinja2 import nodes
     20 from jinja2.visitor import NodeTransformer
     21 
     22 
     23 def optimize(node, environment):
     24     """The context hint can be used to perform an static optimization
     25     based on the context given."""
     26     optimizer = Optimizer(environment)
     27     return optimizer.visit(node)
     28 
     29 
     30 class Optimizer(NodeTransformer):
     31 
     32     def __init__(self, environment):
     33         self.environment = environment
     34 
     35     def visit_If(self, node):
     36         """Eliminate dead code."""
     37         # do not optimize ifs that have a block inside so that it doesn't
     38         # break super().
     39         if node.find(nodes.Block) is not None:
     40             return self.generic_visit(node)
     41         try:
     42             val = self.visit(node.test).as_const()
     43         except nodes.Impossible:
     44             return self.generic_visit(node)
     45         if val:
     46             body = node.body
     47         else:
     48             body = node.else_
     49         result = []
     50         for node in body:
     51             result.extend(self.visit_list(node))
     52         return result
     53 
     54     def fold(self, node):
     55         """Do constant folding."""
     56         node = self.generic_visit(node)
     57         try:
     58             return nodes.Const.from_untrusted(node.as_const(),
     59                                               lineno=node.lineno,
     60                                               environment=self.environment)
     61         except nodes.Impossible:
     62             return node
     63 
     64     visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
     65     visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
     66     visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
     67     visit_Filter = visit_Test = visit_CondExpr = fold
     68     del fold
     69