Skip to content

Commit

Permalink
based stringoptimizer on treeutil.ensureClosureWrapper
Browse files Browse the repository at this point in the history
- stringoptimizer and globalsoptimizer now play well together, sharing a single
  closure wrapper for their purposes
- also tentatively disabled stringoptimizer.check(), which blocked string optimization
  in certain occurrences, but seemed superfluous
- shuffled around with <file> and <statements> wrappers around trees
- some reorg in ensureClosureWrapper
- tentatively removed nulling childNode.parent in tree.removeChild; this seems
  somewhat treacherous, because if you e.g. addChild() some existing nodes
  to some other, and then call removeAllChildren() on the previous parent, the
  children that have been added to the other node (and had their .parent been
  updated to the new parent), will get a .parent=None, effectively breaking
  the link to their new parent
  • Loading branch information
thron7 committed Feb 22, 2013
1 parent c86cef7 commit d42b633
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 37 deletions.
6 changes: 5 additions & 1 deletion tool/pylib/ecmascript/frontend/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ def addChild(self, childNode, index = None):
def removeChild(self, childNode):
if self.children:
self.children.remove(childNode)
childNode.parent = None
#childNode.parent = None

def removeAllChildren(self):
for child in self.children[:]:
self.children.remove(child)

def replaceChild(self, oldChild, newChild):
if self.children:
Expand Down
17 changes: 11 additions & 6 deletions tool/pylib/ecmascript/frontend/treeutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,19 +807,24 @@ def isKeyValue(node):
# <nodes> - [node1, node2, ...]
# nodeX is assumed to start with a <file> node
#
wrapper_statements_path = "statements/call/operand/group/function/body/block/statements"
def has_closure_wrapper(node):
return selectNode(node, wrapper_statements_path) # if this succeeds, there is a top-level wrapper
wrapper_statements_path = "operand/group/function/body/block/statements"

def is_closure_wrapped(node):
return (node.type == "call"
and selectNode(node, wrapper_statements_path)) # if this succeeds, there is a top-level wrapper

def ensureClosureWrapper(nodes):
if not nodes:
return None, None
# check if we have a closure wrapper already
if len(nodes) == 1:
closure_statements = has_closure_wrapper(nodes[0])
closure_statements = is_closure_wrapped(nodes[0])
if closure_statements:
return nodes[0], closure_statements
new_tree = treegenerator.createFileTree_from_string("(function(){})();")
# create a closure wrapper and attach argument nodes
new_tree = treegenerator.parse("(function(){})();").getChild("call")
closure_statements = selectNode(new_tree, wrapper_statements_path)
for node in nodes:
for node in nodes[:]:
closure_statements.addChild(node)
return new_tree, closure_statements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ def visit(self, scopeNode):
self.visit(child)

def propagate_new_globals(node, new_symbols, globals_map):
# assuming a container <node>, like <file> or <block>
# make sure there is a wrapping closure
node, closure_block = treeutil.ensureClosureWrapper([node])
stmtsNode = node.getChild("statements")
new_node, closure_block = treeutil.ensureClosureWrapper(stmtsNode.children)
stmtsNode.removeAllChildren()
stmtsNode.addChild(new_node)

# add new statements at end
for new_symbol in new_symbols:
Expand Down
55 changes: 26 additions & 29 deletions tool/pylib/ecmascript/transform/optimizer/stringoptimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def search_loop(node, stringMap={}, verbose=False):
else:
stringMap[value] = 1

if check(node, verbose):
#if check(node, verbose):
if 1:
for child in node.children:
search_loop(child, stringMap, verbose)

Expand All @@ -75,16 +76,18 @@ def check(node, verbose=True):
cu = node
nx = cu.getChild("operand", False)

if nx != None:
cu = nx
if nx.isVar():

all_ = cu.getAllChildrenOfType("identifier")
if nx != None:
cu = nx

for ch in all_:
if ch.get("value", '') in ["debug", "info", "warn", "error", "fatal", "Error", "alert"]:
if verbose:
print " - Ignore output statement at line: %s" % ch.get("line")
return False
all_ = cu.getAllChildrenOfType("identifier")

for ch in all_:
if ch.get("value", '') in ["debug", "info", "warn", "error", "fatal", "Error", "alert"]:
if verbose:
print " - Ignore output statement at line: %s" % ch.get("line")
return False

# Try to find all constant assignments (ns.UPPER = string)
elif node.type == "assignment":
Expand Down Expand Up @@ -145,7 +148,8 @@ def replace(node, stringList, var="$", verbose=False):
node.parent.replaceChild(node, replacement_ident)
break

if check(node, verbose):
#if check(node, verbose):
if 1:
for child in node.children:
replace(child, stringList, var, verbose)

Expand All @@ -167,36 +171,29 @@ def replacement(stringList):
# Interface function.
#
def stringOptimizer(tree, id_):
# string optimization works over a list of statements,
# so extract the <file>'s <statements> node
# assuming a <file> or <block> node
statementsNode = tree.getChild("statements")
stringMap = search(statementsNode)

# create a map for strings to var names
stringMap = search(statementsNode)
if len(stringMap) == 0:
return tree

# apply the vars
stringList = sort(stringMap)
replace(statementsNode, stringList)

# TODO: Re-write this using treeutil.ensureClosureWrapper()
# Build JS string fragments
stringStart = "(function(){"
# create a 'var' decl for the string vars
stringReplacement = replacement(stringList)
stringStop = "})();"

# Compile wrapper node
wrapperNode = treeutil.compileString(stringStart+stringReplacement+stringStop, id_ + "||stringopt")
repl_tree = treeutil.compileString(stringReplacement, id_ + "||stringopt")

# Reorganize structure
funcStatements = (wrapperNode.getChild("operand").getChild("group").getChild("function")
.getChild("body").getChild("block").getChild("statements"))
if statementsNode.hasChildren():
for child in statementsNode.children[:]:
statementsNode.removeChild(child)
funcStatements.addChild(child)
# ensure a wrapping closure
closure, closure_block = treeutil.ensureClosureWrapper(statementsNode.children)
statementsNode.removeAllChildren()
statementsNode.addChild(closure)

# Add wrapper to now empty statements node
statementsNode.addChild(wrapperNode)
# add 'var' decl to closure
closure_block.addChild(repl_tree, 0) # 'var ...'; decl to front of statement list

return tree

Expand Down

0 comments on commit d42b633

Please sign in to comment.