| Index: grit/node/base.py
|
| diff --git a/grit/node/base.py b/grit/node/base.py
|
| index 375524050878f1c44a1fc31e7d3f154018e763cb..dff6a9b4e6aedb841c67d6e2c5a12a9f4e26a190 100644
|
| --- a/grit/node/base.py
|
| +++ b/grit/node/base.py
|
| @@ -6,7 +6,7 @@
|
| '''Base types for nodes in a GRIT resource tree.
|
| '''
|
|
|
| -import collections
|
| +import ast
|
| import os
|
| import sys
|
| import types
|
| @@ -28,11 +28,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 +443,61 @@ 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')
|
| + or 'bsd' in sys.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)
|
|
|
|
|