diff options
Diffstat (limited to 'grit/node/base.py')
-rw-r--r-- | grit/node/base.py | 126 |
1 files changed, 65 insertions, 61 deletions
diff --git a/grit/node/base.py b/grit/node/base.py index 3755240..5ce037f 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,58 +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') - or 'bsd' in sys.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): @@ -517,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) |