Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | """ |
| 3 | jinja2.optimizer |
| 4 | ~~~~~~~~~~~~~~~~ |
| 5 | |
Armin Ronacher | 07a21ba | 2008-04-23 22:28:42 +0200 | [diff] [blame] | 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. |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 8 | |
Armin Ronacher | 07a21ba | 2008-04-23 22:28:42 +0200 | [diff] [blame] | 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. |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 13 | |
Armin Ronacher | 07a21ba | 2008-04-23 22:28:42 +0200 | [diff] [blame] | 14 | The solution would be a second syntax tree that has the scoping rules stored. |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 15 | |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 16 | :copyright: Copyright 2008 by Christoph Hack, Armin Ronacher. |
Armin Ronacher | d776437 | 2008-07-15 00:11:14 +0200 | [diff] [blame^] | 17 | :license: BSD. |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 18 | """ |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 19 | from jinja2 import nodes |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 20 | from jinja2.visitor import NodeTransformer |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 21 | |
| 22 | |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 23 | def optimize(node, environment): |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 24 | """The context hint can be used to perform an static optimization |
| 25 | based on the context given.""" |
| 26 | optimizer = Optimizer(environment) |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 27 | return optimizer.visit(node) |
Armin Ronacher | 81b8817 | 2008-04-09 00:40:05 +0200 | [diff] [blame] | 28 | |
| 29 | |
Christoph Hack | b40b880 | 2008-04-08 23:01:58 +0200 | [diff] [blame] | 30 | class Optimizer(NodeTransformer): |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 31 | |
Armin Ronacher | 81b8817 | 2008-04-09 00:40:05 +0200 | [diff] [blame] | 32 | def __init__(self, environment): |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 33 | self.environment = environment |
Christoph Hack | 9d99e47 | 2008-04-08 20:21:11 +0200 | [diff] [blame] | 34 | |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 35 | def visit_If(self, node): |
| 36 | """Eliminate dead code.""" |
Armin Ronacher | 6df604e | 2008-05-23 22:18:38 +0200 | [diff] [blame] | 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) |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 41 | try: |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 42 | val = self.visit(node.test).as_const() |
Armin Ronacher | 81b8817 | 2008-04-09 00:40:05 +0200 | [diff] [blame] | 43 | except nodes.Impossible: |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 44 | return self.generic_visit(node) |
Armin Ronacher | 81b8817 | 2008-04-09 00:40:05 +0200 | [diff] [blame] | 45 | if val: |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 46 | body = node.body |
| 47 | else: |
| 48 | body = node.else_ |
| 49 | result = [] |
| 50 | for node in body: |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 51 | result.extend(self.visit_list(node)) |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 52 | return result |
Armin Ronacher | 81b8817 | 2008-04-09 00:40:05 +0200 | [diff] [blame] | 53 | |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 54 | def fold(self, node): |
Armin Ronacher | 180a1bd | 2008-04-09 12:14:24 +0200 | [diff] [blame] | 55 | """Do constant folding.""" |
Armin Ronacher | 981cbf6 | 2008-05-13 09:12:27 +0200 | [diff] [blame] | 56 | node = self.generic_visit(node) |
Armin Ronacher | 180a1bd | 2008-04-09 12:14:24 +0200 | [diff] [blame] | 57 | try: |
Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 58 | return nodes.Const.from_untrusted(node.as_const(), |
Armin Ronacher | d55ab53 | 2008-04-09 16:13:39 +0200 | [diff] [blame] | 59 | lineno=node.lineno, |
| 60 | environment=self.environment) |
Armin Ronacher | 180a1bd | 2008-04-09 12:14:24 +0200 | [diff] [blame] | 61 | except nodes.Impossible: |
| 62 | return node |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 63 | |
Armin Ronacher | 180a1bd | 2008-04-09 12:14:24 +0200 | [diff] [blame] | 64 | visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \ |
| 65 | visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \ |
Armin Ronacher | 6dc6f29 | 2008-06-12 08:50:07 +0200 | [diff] [blame] | 66 | visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \ |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 67 | visit_Filter = visit_Test = visit_CondExpr = fold |
Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 68 | del fold |