def optimizeConstruct(node, superClass, methodName, classDefNodes): patchCount = 0 # Need to run through all the nodes, to skip embedded qx.*.define(), # which will be treated separately # Handle Node # skip embedded qx.*.define() if node in classDefNodes: return 0 elif node.type == "variable" and node.hasParentContext("call/operand"): varName, complete = treeutil.assembleVariable(node) if not (complete and varName == "this.base"): return 0 call = node.parent.parent try: firstArgName = treeutil.selectNode(call, "params/1/identifier/@name") except tree.NodeAccessException: return 0 if firstArgName != "arguments": return 0 # "construct" if methodName == "construct": newCall = treeutil.compileString("%s.call()" % superClass) # "member" else: newCall = treeutil.compileString("%s.prototype.%s.call()" % (superClass, methodName)) newCall.replaceChild( newCall.getChild("params"), call.getChild("params")) # replace with old arglist treeutil.selectNode(newCall, "params/1/identifier").set( "name", "this") # arguments -> this call.parent.replaceChild(call, newCall) patchCount += 1 # Handle Children if node.hasChildren(): for child in node.children: patchCount += optimizeConstruct(child, superClass, methodName, classDefNodes) return patchCount
def _stringOptimizer(self, tree): # string optimization works over a list of statements, # so extract the <file>'s <statements> node statementsNode = tree.getChild("statements") stringMap = stringoptimizer.search(statementsNode) if len(stringMap) == 0: return tree stringList = stringoptimizer.sort(stringMap) stringoptimizer.replace(statementsNode, stringList) # TODO: Re-write this using treeutil.ensureClosureWrapper() # Build JS string fragments stringStart = "(function(){" stringReplacement = stringoptimizer.replacement(stringList) stringStop = "})();" # Compile wrapper node wrapperNode = treeutil.compileString(stringStart+stringReplacement+stringStop, self.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) # Add wrapper to now empty statements node statementsNode.addChild(wrapperNode) return tree
def _stringOptimizer(self, tree): # string optimization works over a list of statements, # so extract the <file>'s <statements> node statementsNode = tree.getChild("statements") stringMap = stringoptimizer.search(statementsNode) if len(stringMap) == 0: return tree stringList = stringoptimizer.sort(stringMap) stringoptimizer.replace(statementsNode, stringList) # Build JS string fragments stringStart = "(function(){" stringReplacement = stringoptimizer.replacement(stringList) stringStop = "})();" # Compile wrapper node wrapperNode = treeutil.compileString( stringStart + stringReplacement + stringStop, self.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) # Add wrapper to now empty statements node statementsNode.addChild(wrapperNode) return tree
def replace(node, stringList, var="$", verbose=False): if node.type == "constant" and node.get("constantType") == "string": if node.get("detail") == "singlequotes": quote = "'" elif node.get("detail") == "doublequotes": quote = '"' oldvalue = "%s%s%s" % (quote, node.get("value"), quote) for pos, item in enumerate(stringList): if item["value"] == oldvalue: newvalue = "%s[%s]" % ( var, pos) # this is only for output, and is bogus if verbose: poldvalue = oldvalue if isinstance(poldvalue, unicode): poldvalue = poldvalue.encode("utf-8") print " - Replace: %s => %s" % (poldvalue, newvalue) line = node.get("line") # generate identifier replacement_ident = treeutil.compileString("SSSS_%s" % pos) replacement_ident.set("line", node.get("line")) replacement_ident.set("column", node.get("column")) # replace node node.parent.replaceChild(node, replacement_ident) break if check(node, verbose): for child in node.children: replace(child, stringList, var, verbose)
def _stringOptimizeHelper(self, tree, id, variants): stringMap = stringoptimizer.search(tree) if len(stringMap) == 0: return stringList = stringoptimizer.sort(stringMap) stringoptimizer.replace(tree, stringList) # Build JS string fragments stringStart = "(function(){" stringReplacement = stringoptimizer.replacement(stringList) stringStop = "})();" # Compile wrapper node wrapperNode = treeutil.compileString(stringStart+stringReplacement+stringStop, id + "||stringopt") # Reorganize structure funcBody = wrapperNode.getChild("operand").getChild("group").getChild("function").getChild("body").getChild("block") if tree.hasChildren(): for child in copy.copy(tree.children): tree.removeChild(child) funcBody.addChild(child) # Add wrapper to tree tree.addChild(wrapperNode)
def replace(node, stringList, var="$", verbose=False): if node.type == "constant" and node.get("constantType") == "string": if node.get("detail") == "singlequotes": quote = "'" elif node.get("detail") == "doublequotes": quote = '"' oldvalue = "%s%s%s" % (quote, node.get("value"), quote) for pos,item in enumerate(stringList): if item["value"] == oldvalue: newvalue = "%s[%s]" % (var, pos) # this is only for output, and is bogus if verbose: poldvalue = oldvalue if isinstance(poldvalue, unicode): poldvalue = poldvalue.encode("utf-8") print " - Replace: %s => %s" % (poldvalue, newvalue) line = node.get("line") # generate identifier replacement_ident = treeutil.compileString("SSSS_%s" % pos) replacement_ident.set("line", node.get("line")) replacement_ident.set("column", node.get("column")) # replace node node.parent.replaceChild(node, replacement_ident) break if check(node, verbose): for child in node.children: replace(child, stringList, var, verbose)
def _optimizeStrings(tree, id): stringMap = stringoptimizer.search(tree) if len(stringMap) == 0: return stringList = stringoptimizer.sort(stringMap) stringoptimizer.replace(tree, stringList) # Build JS string fragments stringStart = "(function(){" stringReplacement = stringoptimizer.replacement(stringList) stringStop = "})();" # Compile wrapper node wrapperNode = treeutil.compileString( stringStart + stringReplacement + stringStop, id + "||stringopt") # Reorganize structure funcBody = wrapperNode.getChild("operand").getChild("group").getChild( "function").getChild("body").getChild("block") if tree.hasChildren(): for child in copy.copy(tree.children): tree.removeChild(child) funcBody.addChild(child) # Add wrapper to tree tree.addChild(wrapperNode)
def process(tree, id_): # refresh scopes to get a check-set tree = scopes.create_scopes(tree) check_set = tree.scope.all_var_names() check_set.update(lang.RESERVED.keys()) # assuming a <file> or <block> node statementsNode = tree.getChild("statements") # create a map for strings to var names stringMap = search(statementsNode, verbose=False) if len(stringMap) == 0: return tree # apply the vars #stringList = sort(stringMap) replace(statementsNode, stringMap, check_set) # create a 'var' decl for the string vars stringReplacement = replacement(stringMap) repl_tree = treeutil.compileString(stringReplacement, id_ + "||stringopt") # ensure a wrapping closure closure, closure_block = treeutil.ensureClosureWrapper(statementsNode.children) statementsNode.removeAllChildren() statementsNode.addChild(closure) # add 'var' decl to closure closure_block.addChild(repl_tree, 0) # 'var ...'; decl to front of statement list return tree
def process(tree, id_): # refresh scopes to get a check-set tree = scopes.create_scopes(tree) check_set = tree.scope.all_var_names() check_set.update(lang.RESERVED.keys()) # assuming a <file> or <block> node statementsNode = tree.getChild("statements") # 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, stringMap, check_set) # create a 'var' decl for the string vars stringReplacement = replacement(stringMap) repl_tree = treeutil.compileString(stringReplacement, id_ + "||stringopt") # ensure a wrapping closure closure, closure_block = treeutil.ensureClosureWrapper( statementsNode.children) statementsNode.removeAllChildren() statementsNode.addChild(closure) # add 'var' decl to closure closure_block.addChild(repl_tree, 0) # 'var ...'; decl to front of statement list return tree
def optimizeConstruct(node, superClass, methodName, classDefNodes): patchCount = 0 # Need to run through all the nodes, to skip embedded qx.*.define(), # which will be treated separately # Handle Node # skip embedded qx.*.define() if node in classDefNodes: return 0 elif node.type == "variable" and node.hasParentContext("call/operand"): varName, complete = treeutil.assembleVariable(node) if not (complete and varName == "this.base"): return 0 call = node.parent.parent try: firstArgName = treeutil.selectNode(call, "params/1/identifier/@name") except tree.NodeAccessException: return 0 if firstArgName != "arguments": return 0 # "construct" if methodName == "construct": newCall = treeutil.compileString("%s.call()" % superClass) # "member" else: newCall = treeutil.compileString("%s.prototype.%s.call()" % (superClass, methodName)) newCall.replaceChild(newCall.getChild("params"), call.getChild("params")) # replace with old arglist treeutil.selectNode(newCall, "params/1/identifier").set("name", "this") # arguments -> this call.parent.replaceChild(call, newCall) patchCount += 1 # Handle Children if node.hasChildren(): for child in node.children: patchCount += optimizeConstruct(child, superClass, methodName, classDefNodes) return patchCount
def replace(node, stringMap, check_set=set(), verbose=False): mapper = NameMapper(check_set) for cstring,value in stringMap.items(): var_name = mapper.mapper(cstring) value[0] = var_name # memoize var_name in stringMap for node in value[1]: repl_ident = treeutil.compileString(var_name) repl_ident.set("line", node.get("line")) repl_ident.set("column", node.get("column")) node.parent.replaceChild(node, repl_ident)
def replace(node, stringMap, check_set=set(), verbose=False): mapper = NameMapper(check_set) for cstring, value in stringMap.items(): var_name = mapper.mapper(cstring) value[0] = var_name # memoize var_name in stringMap for node in value[1]: repl_ident = treeutil.compileString(var_name) repl_ident.set("line", node.get("line")) repl_ident.set("column", node.get("column")) node.parent.replaceChild(node, repl_ident)
def patch(node): patchCount = 0 this_base_vars = treeutil.findVariable(node, "this.base") for var in this_base_vars: if var.parent.type == "operand" and var.parent.parent.type == "call": call = var.parent.parent try: firstArgName = treeutil.selectNode( call, "params/1/identifier/@name") except tree.NodeAccessException: continue if firstArgName != "arguments": continue newCall = treeutil.compileString( "arguments.callee.base.call(this)") newCall.replaceChild(newCall.getChild("params"), call.getChild("params")) treeutil.selectNode(newCall, "params/1/identifier").set("name", "this") call.parent.replaceChild(call, newCall) patchCount += 1 this_self_vars = treeutil.findVariable(node, "this.self") for var in this_self_vars: if var.parent.type == "operand" and var.parent.parent.type == "call": call = var.parent.parent try: firstArgName = treeutil.selectNode( call, "params/1/identifier/@name") except tree.NodeAccessException: continue if firstArgName != "arguments": continue newCall = treeutil.compileString("arguments.callee.self") call.parent.replaceChild(call, newCall) patchCount += 1 return patchCount
def patch(node): patchCount = 0 this_base_vars = treeutil.findVariable(node, "this.base") for var in this_base_vars: if var.parent.type == "operand" and var.parent.parent.type == "call": call = var.parent.parent try: firstArgName = treeutil.selectNode(call, "params/1/identifier/@name") except tree.NodeAccessException: continue if firstArgName != "arguments": continue newCall = treeutil.compileString("arguments.callee.base.call(this)") newCall.replaceChild(newCall.getChild("params"), call.getChild("params")) treeutil.selectNode(newCall, "params/1/identifier").set("name", "this") call.parent.replaceChild(call, newCall) patchCount += 1 this_self_vars = treeutil.findVariable(node, "this.self") for var in this_self_vars: if var.parent.type == "operand" and var.parent.parent.type == "call": call = var.parent.parent try: firstArgName = treeutil.selectNode(call, "params/1/identifier/@name") except tree.NodeAccessException: continue if firstArgName != "arguments": continue newCall = treeutil.compileString("arguments.callee.self") call.parent.replaceChild(call, newCall) patchCount += 1 return patchCount
def generateProperty(name, config, members, method): if not method in ["get", "is"]: return # print "Generate %s for %s" % (method, name) code = "function(){" if "inheritable" in config: code += 'if(this.$$inherit_' + name + '!==undefined)' code += 'return this.$$inherit_' + name + ';' code += 'else ' code += 'if(this.$$user_' + name + '!==undefined)' code += 'return this.$$user_' + name + ';' if "themeable" in config: code += 'else if(this.$$theme_' + name + '!==undefined)' code += 'return this.$$theme_' + name + ';' if not "init" in config and "deferredInit" in config: code += 'else if(this.$$init_' + name + '!==undefined)' code += 'return this.$$init_' + name + ';' code += 'else ' if "init" in config: code += 'return this.$$init_' + name + ';' else: code += 'return null;' code += "}" func = treeutil.compileString(code) pair = treeutil.createPair(method + name[0].upper() + name[1:], func) members.addChild(pair)
def generateProperty(name, config, members, method): if not method in ["get", "is"]: return # print "Generate %s for %s" % (method, name) code = "function(){" if "inheritable" in config: code += 'if(this.$$inherit_' + name + '!==undefined)' code += 'return this.$$inherit_' + name + ';' code += 'else ' code += 'if(this.$$user_' + name + '!==undefined)' code += 'return this.$$user_' + name + ';' if "themeable" in config: code += 'else if(this.$$theme_' + name + '!==undefined)' code += 'return this.$$theme_' + name + ';' if not "init" in config and "deferredInit" in config: code += 'else if(this.$$init_' + name + '!==undefined)' code += 'return this.$$init_' + name + ';' code += 'else ' if "init" in config: code += 'return this.$$init_' + name + ';' else: code += 'return null;' code += "}" func = treeutil.compileString(code) pair = treeutil.createPair(method + name[0].upper() + name[1:], func) members.addChild(pair)
def generateProperty(name, config, members, method): if not method in ["get", "is"]: return # print "Generate %s for %s" % (method, name) code = "function(){" if config.has_key("inheritable"): code += "if(this.$$inherit_" + name + "!==undefined)" code += "return this.$$inherit_" + name + ";" code += "else " code += "if(this.$$user_" + name + "!==undefined)" code += "return this.$$user_" + name + ";" if config.has_key("themeable"): code += "else if(this.$$theme_" + name + "!==undefined)" code += "return this.$$theme_" + name + ";" if not config.has_key("init") and config.has_key("deferredInit"): code += "else if(this.$$init_" + name + "!==undefined)" code += "return this.$$init_" + name + ";" code += "else " if config.has_key("init"): code += "return this.$$init_" + name + ";" else: code += "return null;" code += "}" func = treeutil.compileString(code) pair = treeutil.createPair(method + name[0].upper() + name[1:], func) members.addChild(pair)
def findClassForFeature(self, featureId, variants, classMaps): # get the method name clazzId = self.id if featureId == u'': # corner case: bare class reference outside "new ..." return clazzId, featureId # TODO: The next doesn't provide much, qx.Class.getInstance has no new dependencies # currently (aside from "new this", which I cannot relate back to 'construct' # ATM). Leave it in anyway, to not break bug#5660. #elif featureId == "getInstance": # corner case: singletons get this from qx.Class # clazzId = "qx.Class" elif featureId == "getInstance" and self.type == "singleton": featureId = "construct" elif featureId in ('call', 'apply'): # this might get overridden, oh well... clazzId = "Function" # TODO: getter/setter are also not lexically available! # handle .call() ?! if clazzId not in self._classesObj: # can't further process non-qooxdoo classes # TODO: maybe this should better use something like isInterestingIdentifier() # to invoke the same machinery for filtering references like in other places return None, None # early return if class id is finalized if clazzId != self.id: classObj = self._classesObj[clazzId] featureNode = self.getFeatureNode(featureId, variants) if featureNode: return clazzId, featureNode else: return None, None # now try this class if self.id in classMaps: classMap = classMaps[self.id] else: classMap = classMaps[self.id] = self.getClassMap (variants) featureNode = self.getFeatureNode(featureId, variants, classMap) if featureNode: return self.id, featureNode if featureId == 'construct': # constructor requested, but not supplied in class map # supply the default constructor featureNode = treeutil.compileString("function(){this.base(arguments);}", self.path) return self.id, featureNode # inspect inheritance/mixins parents = [] extendVal = classMap.get('extend', None) if extendVal: extendVal = treeutil.variableOrArrayNodeToArray(extendVal) parents.extend(extendVal) # this.base calls if featureId == "base": classId = parents[0] # first entry must be super-class if classId in self._classesObj: return self._classesObj[classId].findClassForFeature('construct', variants, classMaps) else: return None, None includeVal = classMap.get('include', None) if includeVal: # 'include' value according to Class spec. if includeVal.type in ('variable', 'array'): includeVal = treeutil.variableOrArrayNodeToArray(includeVal) # assume qx.core.Environment.filter() call else: filterMap = variantoptimizer.getFilterMap(includeVal, self.id) includeSymbols = [] for key, node in filterMap.items(): # only consider true or undefined #if key not in variants or (key in variants and bool(variants[key]): # map value has to be value/variable variable = node.children[0] assert variable.type == "variable" symbol, isComplete = treeutil.assembleVariable(variable) assert isComplete includeSymbols.append(symbol) includeVal = includeSymbols parents.extend(includeVal) # go through all ancestors for parClass in parents: if parClass not in self._classesObj: continue parClassObj = self._classesObj[parClass] rclass, keyval = parClassObj.findClassForFeature(featureId, variants, classMaps) if rclass: return rclass, keyval return None, None
def findClassForFeature(self, featureId, variants, classMaps): # get the method name clazzId = self.id if featureId == u'': # corner case: bare class reference outside "new ..." return clazzId, featureId # TODO: The next doesn't provide much, qx.Class.getInstance has no new dependencies # currently (aside from "new this", which I cannot relate back to 'construct' # ATM). Leave it in anyway, to not break bug#5660. #elif featureId == "getInstance": # corner case: singletons get this from qx.Class # clazzId = "qx.Class" elif featureId == "getInstance" and self.type == "singleton": featureId = "construct" elif featureId in ('call', 'apply'): # this might get overridden, oh well... clazzId = "Function" # TODO: getter/setter are also not lexically available! # handle .call() ?! if clazzId not in ClassesAll: # can't further process non-qooxdoo classes # TODO: maybe this should better use something like isInterestingIdentifier() # to invoke the same machinery for filtering references like in other places return None, None # early return if class id is finalized if clazzId != self.id: classObj = ClassesAll[clazzId] featureNode = self.getFeatureNode(featureId, variants) if featureNode: return clazzId, featureNode else: return None, None # now try this class if self.id in classMaps: classMap = classMaps[self.id] else: classMap = classMaps[self.id] = self.getClassMap (variants) featureNode = self.getFeatureNode(featureId, variants, classMap) if featureNode: return self.id, featureNode if featureId == 'construct': # constructor requested, but not supplied in class map # supply the default constructor featureNode = treeutil.compileString("function(){this.base(arguments);}", self.path) # the next is a hack to provide minimal scope info featureNode.set("treegenerator_tag", 1) featureNode = scopes.create_scopes(featureNode) return self.id, featureNode # inspect inheritance/mixins parents = [] extendVal = classMap.get('extend', None) if extendVal: extendVal = treeutil.variableOrArrayNodeToArray(extendVal) parents.extend(extendVal) # this.base calls if featureId == "base": classId = parents[0] # first entry must be super-class if classId in ClassesAll: return ClassesAll[classId].findClassForFeature('construct', variants, classMaps) else: return None, None includeVal = classMap.get('include', None) if includeVal: # 'include' value according to Class spec. if includeVal.type in NODE_VARIABLE_TYPES + ('array',): includeVal = treeutil.variableOrArrayNodeToArray(includeVal) # assume qx.core.Environment.filter() call else: filterMap = variantoptimizer.getFilterMap(includeVal, self.id) includeSymbols = [] for key, node in filterMap.items(): # only consider true or undefined #if key not in variants or (key in variants and bool(variants[key]): # map value has to be value/variable variable = node.children[0] assert variable.isVar() symbol, isComplete = treeutil.assembleVariable(variable) assert isComplete includeSymbols.append(symbol) includeVal = includeSymbols parents.extend(includeVal) # go through all ancestors for parClass in parents: if parClass not in ClassesAll: continue parClassObj = ClassesAll[parClass] rclass, keyval = parClassObj.findClassForFeature(featureId, variants, classMaps) if rclass: return rclass, keyval return None, None
newCall.replaceChild(newCall.getChild("params"), call.getChild("params")) # replace with old arglist treeutil.selectNode(newCall, "params/1/identifier").set("name", "this") # arguments -> this call.parent.replaceChild(call, newCall) patchCount += 1 # Handle Children if node.hasChildren(): for child in node.children: patchCount += optimizeConstruct(child, superClass, methodName, classDefNodes) return patchCount if __name__ == "__main__": cls = """qx.Class.define("qx.Car", { extend: qx.core.Object, construct : function() { this.base(arguments, "2") }, members : { foo : function() { return this.base(arguments) } } })""" node = treeutil.compileString(cls) patch(node) print node.toJavascript()
def findClassForFeature(self, featureId, variants, classMaps): # get the method name clazzId = self.id if featureId == u'': # corner case: bare class reference outside "new ..." return clazzId, featureId elif featureId == "getInstance": # corner case: singletons get this from qx.Class clazzId = "qx.Class" elif featureId in ('call', 'apply'): # this might get overridden, oh well... clazzId = "Function" # TODO: getter/setter are also not lexically available! # handle .call() ?! if clazzId not in self._classesObj: # can't further process non-qooxdoo classes # TODO: maybe this should better use something like isInterestingIdentifier() # to invoke the same machinery for filtering references like in other places return None, None # early return if class id is finalized if clazzId != self.id: classObj = self._classesObj[clazzId] featureNode = self.getFeatureNode(featureId, variants) if featureNode: return clazzId, featureNode else: return None, None # now try this class if self.id in classMaps: classMap = classMaps[self.id] else: classMap = classMaps[self.id] = self.getClassMap (variants) featureNode = self.getFeatureNode(featureId, variants, classMap) if featureNode: return self.id, featureNode if featureId == 'construct': # constructor requested, but not supplied in class map # supply the default constructor featureNode = treeutil.compileString("function(){this.base(arguments);}", self.path) return self.id, featureNode # inspect inheritance/mixins parents = [] extendVal = classMap.get('extend', None) if extendVal: extendVal = treeutil.variableOrArrayNodeToArray(extendVal) parents.extend(extendVal) # this.base calls if featureId == "base": classId = parents[0] # first entry must be super-class if classId in self._classesObj: return self._classesObj[classId].findClassForFeature('construct', variants, classMaps) else: return None, None includeVal = classMap.get('include', None) if includeVal: # 'include' value according to Class spec. if includeVal.type in ('variable', 'array'): includeVal = treeutil.variableOrArrayNodeToArray(includeVal) # assume qx.core.Variant.select() call else: _, branchMap = variantoptimizer.getSelectParams(includeVal) includeVal = set() for key in branchMap: # just pick up all possible include values includeVal.update(treeutil.variableOrArrayNodeToArray(branchMap[key])) includeVal = list(includeVal) parents.extend(includeVal) # go through all ancestors for parClass in parents: if parClass not in self._classesObj: continue parClassObj = self._classesObj[parClass] rclass, keyval = parClassObj.findClassForFeature(featureId, variants, classMaps) if rclass: return rclass, keyval return None, None
"name", "this") # arguments -> this call.parent.replaceChild(call, newCall) patchCount += 1 # Handle Children if node.hasChildren(): for child in node.children: patchCount += optimizeConstruct(child, superClass, methodName, classDefNodes) return patchCount if __name__ == "__main__": cls = """qx.Class.define("qx.Car", { extend: qx.core.Object, construct : function() { this.base(arguments, "2") }, members : { foo : function() { return this.base(arguments) } } })""" node = treeutil.compileString(cls) patch(node) print node.toJavascript()