Index: grit/node/base.py |
diff --git a/grit/node/base.py b/grit/node/base.py |
index 824532c62fe1846d446dd40060a7faa2c2035692..5ce037f3f26c76b795a92e87eb1e6df3f445eb8a 100644 |
--- a/grit/node/base.py |
+++ b/grit/node/base.py |
@@ -6,9 +6,8 @@ |
'''Base types for nodes in a GRIT resource tree. |
''' |
-import collections |
+import ast |
import os |
-import sys |
import types |
from xml.sax import saxutils |
@@ -28,11 +27,12 @@ class Node(object): |
# Default nodes to not whitelist skipped |
_whitelist_marked_as_skip = False |
- # A class-static cache to memoize EvaluateExpression(). |
- # It has a 2 level nested dict structure. The outer dict has keys |
- # of tuples which define the environment in which the expression |
- # will be evaluated. The inner dict is map of expr->result. |
- eval_expr_cache = collections.defaultdict(dict) |
+ # A class-static cache to speed up EvaluateExpression(). |
+ # Keys are expressions (e.g. 'is_ios and lang == "fr"'). Values are tuples |
+ # (code, variables_in_expr) where code is the compiled expression and can be |
+ # directly eval'd, and variables_in_expr is the list of variable and method |
+ # names used in the expression (e.g. ['is_ios', 'lang']). |
+ eval_expr_cache = {} |
def __init__(self): |
self.children = [] # A list of child elements |
@@ -442,59 +442,62 @@ class Node(object): |
return [] |
@classmethod |
- def GetPlatformAssertion(cls, target_platform): |
- '''If the platform is a specific well-known platform, this returns |
- the is_xyz string representing that platform (e.g. is_linux), |
- otherwise the empty string. |
- ''' |
- platform = '' |
- if target_platform == 'darwin': |
- platform = 'is_macosx' |
- elif target_platform.startswith('linux'): |
- platform = 'is_linux' |
- elif target_platform in ('cygwin', 'win32'): |
- platform = 'is_win' |
- elif target_platform in ('android', 'ios'): |
- platform = 'is_%s' % target_platform |
- return platform |
- |
- @classmethod |
- def EvaluateExpression(cls, expr, defs, target_platform, extra_variables=None): |
+ def EvaluateExpression(cls, expr, defs, target_platform, extra_variables={}): |
'''Worker for EvaluateCondition (below) and conditions in XTB files.''' |
- cache_dict = cls.eval_expr_cache[ |
- (tuple(defs.iteritems()), target_platform, extra_variables)] |
- if expr in cache_dict: |
- return cache_dict[expr] |
- def pp_ifdef(symbol): |
- return symbol in defs |
- def pp_if(symbol): |
- return defs.get(symbol, False) |
- variable_map = { |
- 'defs' : defs, |
- 'os': target_platform, |
- |
- # One of these is_xyz assertions gets set to True in the line |
- # following this initializer block. |
- 'is_linux': False, |
- 'is_macosx': False, |
- 'is_win': False, |
- 'is_android': False, |
- 'is_ios': False, |
- |
- # is_posix is not mutually exclusive of the others and gets |
- # set here, not below. |
- 'is_posix': (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5', |
- 'android', 'ios') |
- or 'bsd' in target_platform), |
- |
- 'pp_ifdef' : pp_ifdef, |
- 'pp_if' : pp_if, |
- } |
- variable_map[Node.GetPlatformAssertion(target_platform)] = True |
+ if expr in cls.eval_expr_cache: |
+ code, variables_in_expr = cls.eval_expr_cache[expr] |
+ else: |
+ # Get a list of all variable and method names used in the expression. |
+ syntax_tree = ast.parse(expr, mode='eval') |
+ variables_in_expr = [node.id for node in ast.walk(syntax_tree) if |
+ isinstance(node, ast.Name) and node.id not in ('True', 'False')] |
+ code = compile(syntax_tree, filename='<string>', mode='eval') |
+ cls.eval_expr_cache[expr] = code, variables_in_expr |
+ |
+ # Set values only for variables that are needed to eval the expression. |
+ variable_map = {} |
+ for name in variables_in_expr: |
+ if name == 'os': |
+ value = target_platform |
+ elif name == 'defs': |
+ value = defs |
+ |
+ elif name == 'is_linux': |
+ value = target_platform.startswith('linux') |
+ elif name == 'is_macosx': |
+ value = target_platform == 'darwin' |
+ elif name == 'is_win': |
+ value = target_platform in ('cygwin', 'win32') |
+ elif name == 'is_android': |
+ value = target_platform == 'android' |
+ elif name == 'is_ios': |
+ value = target_platform == 'ios' |
+ elif name == 'is_posix': |
+ value = (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5', |
+ 'android', 'ios') |
+ or 'bsd' in target_platform) |
+ |
+ elif name == 'pp_ifdef': |
+ def pp_ifdef(symbol): |
+ return symbol in defs |
+ value = pp_ifdef |
+ elif name == 'pp_if': |
+ def pp_if(symbol): |
+ return defs.get(symbol, False) |
+ value = pp_if |
+ |
+ elif name in defs: |
+ value = defs[name] |
+ elif name in extra_variables: |
+ value = extra_variables[name] |
+ else: |
+ # Undefined variables default to False. |
+ value = False |
- if extra_variables: |
- variable_map.update(extra_variables) |
- eval_result = cache_dict[expr] = eval(expr, {}, variable_map) |
+ variable_map[name] = value |
+ |
+ eval_result = eval(code, {}, variable_map) |
+ assert isinstance(eval_result, bool) |
return eval_result |
def EvaluateCondition(self, expr): |
@@ -518,10 +521,10 @@ class Node(object): |
context = getattr(root, 'output_context', '') |
defs = getattr(root, 'defines', {}) |
target_platform = getattr(root, 'target_platform', '') |
- extra_variables = ( |
- ('lang', lang), |
- ('context', context), |
- ) |
+ extra_variables = { |
+ 'lang': lang, |
+ 'context': context, |
+ } |
return Node.EvaluateExpression( |
expr, defs, target_platform, extra_variables) |