Source code for jasy.script.optimize.LocalVariables
#
# Jasy - Web Tooling Framework
# Copyright 2010-2012 Zynga Inc.
# Copyright 2013-2014 Sebastian Werner
#
import string
import jasy.script.tokenize.Lang
#
# Public API
#
[docs]class Error(Exception):
def __init__(self, name, line):
self.__name = name
self.__line = line
def __str__(self):
return "Unallowed private field access to %s at line %s!" % (self.__name, self.__line)
[docs]def optimize(node):
"""
Node to optimize with the global variables to ignore as names
"""
blocked = set(node.scope.shared.keys())
blocked.update(node.scope.modified)
__patch(node, blocked)
#
# Implementation
#
def __baseEncode(num, alphabet=string.ascii_letters):
if (num == 0):
return alphabet[0]
arr = []
base = len(alphabet)
while num:
rem = num % base
num = num // base
arr.append(alphabet[rem])
arr.reverse()
return "".join(arr)
def __patch(node, blocked=None, enable=False, translate=None):
# Start with first level scopes (global scope should not be affected)
if node.type == "script" and hasattr(node, "parent"):
enable = True
#
# GENERATE TRANSLATION TABLE
#
if enable:
scope = getattr(node, "scope", None)
if scope:
declared = scope.declared
params = scope.params
if declared or params:
usedRepl = set()
if not translate:
translate = {}
else:
# copy only the interesting ones from the shared set
newTranslate = {}
for name in scope.shared:
if name in translate:
newTranslate[name] = translate[name]
usedRepl.add(translate[name])
translate = newTranslate
# Merge in usage data into declaration map to have
# the possibilities to sort translation priority to
# the usage number. Pretty cool.
names = set()
if params:
names.update(params)
if declared:
names.update(declared)
# We have to sort the set() before to support both Python 3.2 and
# Python 3.3 with identical results.
namesSorted = list(reversed(sorted(sorted(names), key=lambda x: scope.accessed[x] if x in scope.accessed else 0)))
# Extend translation map by new replacements for locally
# declared variables. Automatically ignores keywords. Only
# blocks usage of replacements where the original variable from
# outer scope is used. This way variable names may be re-used more
# often than in the original code.
pos = 0
for name in namesSorted:
while True:
repl = __baseEncode(pos)
pos += 1
if not repl in usedRepl and not repl in jasy.script.tokenize.Lang.keywords and not repl in blocked:
break
# print("Translate: %s => %s" % (name, repl))
translate[name] = repl
#
# APPLY TRANSLATION
#
if translate:
# Update param names in outer function block
if node.type == "script" and hasattr(node, "parent"):
function = node.parent
if function.type == "function" and hasattr(function, "params"):
for identifier in function.params:
if identifier.value in translate:
identifier.value = translate[identifier.value]
# Update names of exception objects
elif node.type == "exception" and node.value in translate:
node.value = translate[node.value]
# Update function name
elif node.type == "function" and hasattr(node, "name") and node.name in translate:
node.name = translate[node.name]
# Update identifiers
elif node.type == "identifier":
# Ignore param blocks from inner functions
if node.parent.type == "list" and getattr(node.parent, "rel", None) == "params":
pass
# Ignore keyword in property initialization names
elif node.parent.type == "property_init" and node.parent[0] == node:
pass
# Update all identifiers which are
# a) not part of a dot operator
# b) first in a dot operator
elif node.parent.type != "dot" or node.parent.index(node) == 0:
if node.value in translate:
node.value = translate[node.value]
# Update declarations (as part of a var statement)
elif node.type == "declaration":
varName = getattr(node, "name", None)
if varName != None:
if varName in translate:
node.name = varName = translate[varName]
else:
# JS 1.7 Destructing Expression
for identifier in node.names:
if identifier.value in translate:
identifier.value = translate[identifier.value]
#
# PROCESS CHILDREN
#
for child in node:
# None children are allowed sometimes e.g. during array_init like [1,2,,,7,8]
if child != None:
__patch(child, blocked, enable, translate)