def checkAll(): def findVariables(rootNode): variables = [] for node in treeutil.nodeIterator(rootNode, ["assignment", "call"]): if node.type == "assignment": variables.append(node.getChild("left")) elif node.type == "call": variables.append(node.getChild("operand")) return variables variables = findVariables(classMap["members"]) if "construct" in classMap: variables.extend(findVariables(classMap["construct"])) for node in variables: this = treeutil.selectNode(node, "variable/identifier[1]/@name") if this != "this": continue field = treeutil.selectNode(node, "variable/identifier[2]/@name") if field is None: continue if field[0] != "_": continue elif field[1] == "_": prot = "private" else: prot = "protected" if prot == "protected": self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field) elif not field in restricted: self.log(node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))
def patch(tree): qxnode = treeutil.findQxDefine(tree) processBlock( treeutil.selectNode(qxnode, "params/map/keyvalue[@key='statics']/value/map")) processBlock( treeutil.selectNode(qxnode, "params/map/keyvalue[@key='members']/value/map"))
def checkRequiredBlocks(self): for node in treeutil.nodeIterator(self.tree, "loop"): block = treeutil.selectNode(node, "statement/block") if not block: self.log(node, "The statement of loops and conditions should be enclosed by a block in braces '{}'") for node in treeutil.nodeIterator(self.tree, "elseStatement"): block = treeutil.selectNode(node, "block") if not block: block = treeutil.selectNode(node, "loop[@loopType='IF']") if not block: self.log(node, "The statement of loops and conditions should be enclosed by a block in braces '{}'")
def isEnvironmentCall(callNode): if callNode.type != "call": return False operandNode = treeutil.selectNode(callNode, "operand") environNodes = treeutil.findVariablePrefix(operandNode, "qx.core.Environment") if len(environNodes) != 1: return False environMethod = treeutil.selectNode(environNodes[0], "identifier[4]/@name") if environMethod in InterestingEnvMethods: return True return False
def checkFields(self): define = treeutil.findQxDefine(self.tree) if not define: return classMapNode = treeutil.selectNode(define, "params/2") if classMapNode is None: return classMap = treeutil.mapNodeToMap(classMapNode) if not classMap.has_key("members"): return members = treeutil.mapNodeToMap(classMap["members"].children[0]) restricted = [key for key in members if key.startswith("_")] assignNodes = [ node for node in nodeIterator(classMap["members"], "assignment") ] if classMap.has_key("construct"): for node in nodeIterator(classMap["construct"], "assignment"): assignNodes.append(node) for node in assignNodes: this = treeutil.selectNode(node, "left/variable/identifier[1]/@name") if this != "this": continue field = treeutil.selectNode(node, "left/variable/identifier[2]/@name") if field is None: continue if field[0] != "_": continue elif field[1] == "_": prot = "private" else: prot = "protected" if prot == "protected": self.log( node, "Protected data field '%s'. Protected fields are deprecated. Better use private fileds in combination with getter and setter methods." % field) elif not field in restricted: self.log( node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))
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 _variantsFromTree(self, node): console = self.context['console'] #config = self.context['jobconf'] - TODO: this can yield job 'apiconf::build-resources' when running 'apiconf::build-data'!? config = Context.jobconf warn_non_literal_keys = "non-literal-keys" not in config.get( "config-warnings/environment", []) classvariants = set() for variantNode in variantoptimizer.findVariantNodes(node): firstParam = treeutil.selectNode(variantNode, "../../params/1") if firstParam: if treeutil.isStringLiteral(firstParam): classvariants.add(firstParam.get("value")) elif firstParam.isVar(): if warn_non_literal_keys: console.warn( "qx.core.Environment call with non-literal key (%s:%s)" % (self.id, variantNode.get("line", False))) elif firstParam.type == "map": # e.g. .filter() method mapMap = treeutil.mapNodeToMap(firstParam) classvariants.update(mapMap.keys()) else: console.warn( "qx.core.Environment call with alien first argument (%s:%s)" % (self.id, variantNode.get("line", False))) return classvariants
def patch(tree, id, feature_names): # get class map qxDefine = treeutil.findQxDefine(tree) classMap = treeutil.getClassMap(qxDefine) # go through features in 'members' and 'statics' for section in ("statics", "members"): if section in classMap: for key, node in classMap[section].items(): # skip registered keys if key in feature_names: continue # skip non-function attribs meth = None if node.type == 'function': meth = node else: meth = treeutil.selectNode(node, "function") if not meth: continue # prune else: parent = node.parent assert parent.type == "keyvalue" #print "pruning: %s#%s" % (id, key) parent.parent.removeChild( parent) # remove the entire key from the map
def patch(tree, id, feature_names): # get class map qxDefine = treeutil.findQxDefine(tree) classMap = treeutil.getClassMap(qxDefine) # go through features in 'members' and 'statics' for section in ("statics", "members"): if section in classMap: for key, node in classMap[section].items(): # skip registered keys if key in feature_names: continue # skip non-function attribs meth = None if node.type == 'function': meth = node else: meth = treeutil.selectNode(node, "function") if not meth: continue # prune else: parent = node.parent assert parent.type == "keyvalue" #print "pruning: %s#%s" % (id, key) parent.parent.removeChild(parent) # remove the entire key from the map
def _variantsFromTree(self, node): console = self.context["console"] config = self.context["jobconf"] warn_non_literal_keys = "non-literal-keys" not in config.get("config-warnings/environment", []) classvariants = set() for variantNode in variantoptimizer.findVariantNodes(node): firstParam = treeutil.selectNode(variantNode, "../../params/1") if firstParam: if treeutil.isStringLiteral(firstParam): classvariants.add(firstParam.get("value")) elif firstParam.type == "variable": if warn_non_literal_keys: console.warn( "qx.core.Environment call with non-literal key (%s:%s)" % (self.id, variantNode.get("line", False)) ) elif firstParam.type == "map": # e.g. .filter() method mapMap = treeutil.mapNodeToMap(firstParam) classvariants.update(mapMap.keys()) else: console.warn( "qx.core.Environment call with alien first argument (%s:%s)" % (self.id, variantNode.get("line", False)) ) return classvariants
def checkNodeContext(node): context = 'interesting' # every context is interesting, mybe we get more specific #context = '' # filter out the occurrences like 'c' in a.b().c myFirst = node.getFirstChild(mandatory=False, ignoreComments=True) if not treeutil.checkFirstChainChild(myFirst): # see if myFirst is the first identifier in a chain context = '' # filter out variable in lval position -- Nope! (qx.ui.form.ListItem.prototype.setValue = # function(..){...};) #elif (node.hasParentContext("assignment/left")): # context = '' # fitler out a.b[c] -- Nope! E.g. foo.ISO_8601_FORMAT might carry further dependencies # (like 'new qx.util.format.DateFormat("yyyy-MM-dd")') elif (treeutil.selectNode(node, "accessor")): context = 'accessor' # check name in 'new ...' position elif (node.hasParentContext("instantiation/*/*/operand")): context = 'new' # check name in call position elif (node.hasParentContext("call/operand")): context = 'call' # check name in "'extend' : ..." position elif (node.hasParentContext("keyvalue/*") and node.parent.parent.get('key') in ['extend']): #, 'include']): #print "-- found context: %s" % node.parent.parent.get('key') context = 'extend' return context
def _getExceptionVariables(self): identifier = treeutil.selectNode(self.node, "expression/variable/identifier") return [ VariableDefinition(identifier.get("name", None), identifier, False, self) ]
def search(node, variantMap, fileId_="", verb=False): if not variantMap: return False global verbose global fileId verbose = verb fileId = fileId_ modified = False variantNodes = findVariantNodes(node) for variantNode in variantNodes: variantMethod = variantNode.toJS(pp).rsplit('.', 1)[1] callNode = treeutil.selectNode(variantNode, "../..") if variantMethod in ["select"]: modified = processVariantSelect(callNode, variantMap) or modified elif variantMethod in ["get"]: modified = processVariantGet(callNode, variantMap) or modified elif variantMethod in ["filter"]: modified = processVariantFilter(callNode, variantMap) or modified # reduce decidable subtrees if modified: for cld in node.children[:]: new_cld = reducer.ast_reduce(cld) node.replaceChild(cld, new_cld) return modified
def checkUndefinedVariables(self, globals): # check whether this is a qooxdoo class and extract the top level namespace define = treeutil.findQxDefine(self.tree) if define: className = treeutil.selectNode(define, "params/1").get("value") globals.append(className.split(".")[0]) globalScope = self.script.getGlobalScope() for scope in self.script.iterScopes(): for use in scope.uses: if use.name in globals: continue if not use.definition: if self.isBadGlobal(use.name) and self._shouldPrintDeprecatedWarning(use.node, use.name): self.log(use.node, "Use of deprecated global identifier '%s'" % use.name) elif ( not self.isBadGlobal(use.name) and not self.isGoodGlobal(use.name) and self._shouldPrintUndefinedWarning(use.node, use.name) ): self.log(use.node, "Use of undefined or global identifier '%s'" % use.name) elif use.definition.scope == globalScope and self._shouldPrintUndefinedWarning(use.node, use.name): self.log(use.node, "Use of global identifier '%s'" % use.name)
def search(node, variantMap, fileId_="", verb=False): if not variantMap: return False global verbose global fileId verbose = verb fileId = fileId_ modified = False variantNodes = findVariantNodes(node) for variantNode in variantNodes: variantMethod = variantNode.toJS(pp).rsplit(".", 1)[1] callNode = treeutil.selectNode(variantNode, "../..") if variantMethod in ["select"]: modified = processVariantSelect(callNode, variantMap) or modified elif variantMethod in ["get"]: modified = processVariantGet(callNode, variantMap) or modified elif variantMethod in ["filter"]: modified = processVariantFilter(callNode, variantMap) or modified # reduce decidable subtrees if modified: for cld in node.children[:]: new_cld = reducer.ast_reduce(cld) node.replaceChild(cld, new_cld) return modified
def patch(tree, classObj, featureMap): # change this to 'False' if you want logging instead of function removal prune_dont_log = True feature_names = featureMap[classObj.id] # get class map qxDefine = treeutil.findQxDefine(tree) classMap = treeutil.getClassMap(qxDefine) # go through features in 'members' and 'statics' for section in ("statics", "members"): if section in classMap: for feature, node in classMap[section].items(): # skip registered and used keys if feature in feature_names and feature_names[feature].hasref(): continue else: parent = node.parent assert parent.type == "keyvalue" #print "static optimizing: %s#%s" % (classObj.id, feature) if prune_dont_log: # remove sub tree parent.parent.removeChild(parent) # remove the entire feature from the map # remove from featureMap if feature in feature_names: del featureMap[classObj.id][feature] # decrease the ref counts of the contained dependees decrementFromCode(classObj, node, featureMap) else: # OR: only add runtime logging to functions block = treeutil.selectNode(node, 'function/body/block/statements') if block: LogStmt = treegenerator.parse("console.warn('Static optimization would have removed: " + classObj.id + '#' + feature + "');") block.addChild(LogStmt, 0)
def visit(self, scopeNode): # go through globals for name, scopeVar in scopeNode.globals().iteritems(): # name might be as little as 'qx' for node in scopeVar.occurrences(): # assemble var var_node = treeutil.findVarRoot(node) var_name = var_node.toJS( None) # e.g. "qx.util.ResourceManager.getInstance" if var_name in lang.QX_CLASS_FACTORIES: # capture qx.*.define() calls succ, class_name, _ = treeutil.isQxDefine(var_node) if succ and class_name: self.new_qx_classes.append(class_name) # lookup in globals_map sKnown_global = self.globals_map.longest_match( var_name) # -> "qx.util.ResourceManager" if sKnown_global: sReplacement = self.globals_map[sKnown_global] # find node in tree dot_number = sKnown_global.count(".") uptimes = u'/'.join(['..'] * dot_number) source_node = treeutil.selectNode( node, uptimes) if uptimes else node # construct replacement repl_node = treegenerator.parse( sReplacement, expr=True) # could be "foo.bar" or "qx.$g['bNq']" # replace known symbol in tree source_node.parent.replaceChild(source_node, repl_node) # update scopeVar.occurrences??!! for child in scopeNode.children: self.visit(child)
def visit(self, scopeNode): # go through globals for name,scopeVar in scopeNode.globals().iteritems(): # name might be as little as 'qx' for node in scopeVar.occurrences(): # assemble var var_node = treeutil.findVarRoot(node) var_name = var_node.toJS(None) # e.g. "qx.util.ResourceManager.getInstance" if var_name in lang.QX_CLASS_FACTORIES: # capture qx.*.define() calls succ, class_name, _ = treeutil.isQxDefine(var_node) if succ and class_name: self.new_qx_classes.append(class_name) # lookup in globals_map sKnown_global = self.globals_map.longest_match(var_name) # -> "qx.util.ResourceManager" if sKnown_global: sReplacement = self.globals_map[sKnown_global] # find node in tree dot_number = sKnown_global.count(".") uptimes = u'/'.join(['..'] * dot_number) source_node = treeutil.selectNode(node, uptimes) if uptimes else node # construct replacement repl_node = treegenerator.parse(sReplacement, expr=True) # could be "foo.bar" or "qx.$g['bNq']" # replace known symbol in tree source_node.parent.replaceChild(source_node, repl_node) # update scopeVar.occurrences??!! for child in scopeNode.children: self.visit(child)
def checkRequiredBlocks(self): for node in treeutil.nodeIterator(self.tree, "loop"): block = treeutil.selectNode(node, "statement/block") if not block: self.log( node, "The statement of loops and conditions should be enclosed by a block in braces '{}'" ) for node in treeutil.nodeIterator(self.tree, "elseStatement"): block = treeutil.selectNode(node, "block") if not block: block = treeutil.selectNode(node, "loop[@loopType='IF']") if not block: self.log( node, "The statement of loops and conditions should be enclosed by a block in braces '{}'" )
def getClassVariantsFromTree(node, console): classvariants = set([]) # mostly taken from ecmascript.transform.optimizer.variantoptimizer variants = treeutil.findVariablePrefix(node, "qx.core.Variant") for variant in variants: if not variant.hasParentContext("call/operand"): continue variantMethod = treeutil.selectNode(variant, "identifier[4]/@name") if variantMethod not in ["select", "isSet", "compilerIsSet"]: continue firstParam = treeutil.selectNode(variant, "../../params/1") if firstParam and treeutil.isStringLiteral(firstParam): classvariants.add(firstParam.get("value")) else: console.warn("! qx.core.Variant call without literal argument") return classvariants
def findVariantNodes(node): for callnode in list( treeutil.nodeIterator(node, ["call"]) ): # enforce eagerness so nodes that are moved are still handled if isEnvironmentCall(callnode): yield treeutil.selectNode(callnode, "operand").getFirstChild() else: continue
def checkRequiredBlocks(self): for node in nodeIterator(self.tree, "loop"): block = treeutil.selectNode(node, "statement/block") if not block: self.log( node, "The statement of loops and conditions must be enclosed by a block in braces '{}'" )
def _getExceptionVariables(self): assert self.node.type == "catch" identifier = treeutil.selectNode(self.node, "params/identifier") assert identifier and identifier.type == "identifier", "Unable to retrieve 'catch' parameter" return [ VariableDefinition(identifier.get("value", ""), identifier, False, self) ]
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 checkDeferNode(self, assembled, node): deferNode = None if assembled in lang.QX_CLASS_FACTORIES: if node.hasParentContext("call/operand"): deferNode = treeutil.selectNode( node, "../../arguments/2/keyvalue[@key='defer']/value/function/body/block" ) return deferNode
def checkDeferNode(self, assembled, node): deferNode = None if assembled == "qx.Class.define" or assembled == "qx.Bootstrap.define" or assembled == "q.define": if node.hasParentContext("call/operand"): deferNode = treeutil.selectNode( node, "../../arguments/2/keyvalue[@key='defer']/value/function/body/block" ) return deferNode
def findVariantNodes(node): for callnode in list(treeutil.nodeIterator( node, ['call' ])): # enforce eagerness so nodes that are moved are still handled if isEnvironmentCall(callnode): yield treeutil.selectNode(callnode, "operand").getFirstChild() else: continue
def protectedIsLastVarChild(var): lastChild = var.getLastChild(ignoreComments=True) # like "this.a.b" -> b if lastChild.type != "identifier": # rules out this.a._prot[0] which isn't a call anyway return False name = treeutil.selectNode(lastChild, "@name") if name and protectedElement.match(name): return True else: return False
def environment_check_calls(self, node): for env_call in variantoptimizer.findVariantNodes(node): variantMethod = env_call.toJS(treegenerator.PackerFlags).rsplit('.',1)[1] callNode = treeutil.selectNode(env_call, "../..") if variantMethod in ["select"]: self.environment_check_select(callNode) elif variantMethod in ["get"]: self.environment_check_get(callNode) elif variantMethod in ["filter"]: self.environment_check_filter(callNode)
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 protectedIsLastVarChild(var): lastChild = var.getLastChild( ignoreComments=True) # like "this.a.b" -> b if lastChild.type != "identifier": # rules out this.a._prot[0] which isn't a call anyway return False name = treeutil.selectNode(lastChild, "@name") if name and protectedElement.match(name): return True else: return False
def _variantsFromTree(self, node): console = self.context['console'] classvariants = set([]) for variantNode in variantoptimizer.findVariantNodes(node): firstParam = treeutil.selectNode(variantNode, "../../params/1") if firstParam and treeutil.isStringLiteral(firstParam): classvariants.add(firstParam.get("value")) else: console.warn("qx.core.Environment call without literal argument (%s:%s)" % (self.id, variantNode.get("line", False))) return classvariants
def findVariantNodes(node): variantNodes = treeutil.findVariablePrefix(node, "qx.core.Environment") for variantNode in variantNodes: if not variantNode.hasParentContext("call/operand"): continue variantMethod = treeutil.selectNode(variantNode, "identifier[4]/@name") if variantMethod in InterestingEnvMethods: yield variantNode else: continue
def _getClassMap(self): define = treeutil.findQxDefine(self.tree) if not define: return {} classMapNode = treeutil.selectNode(define, "params/2") if classMapNode is None: return {} classMap = treeutil.mapNodeToMap(classMapNode) return classMap
def createEnvKeyProviderIndex(self, classesAll): envProviders = {} providingEnvClasses = [className for className in classesAll.keys() if className.startswith("qx.bom.client")] for className in providingEnvClasses: tree = classesAll[className].tree() for callnode in list(treeutil.nodeIterator(tree, ['call'])): if callnode.toJS(pp).startswith("qx.core.Environment.add"): envKey = treeutil.selectNode(callnode, "arguments/constant/@value") envProviders[envKey] = className return envProviders
def findVariantNodes(node): variantNodes = treeutil.findVariablePrefix(node, "qx.core.Variant") variantNodes.extend(treeutil.findVariablePrefix(node, "qx.core.Environment")) for variantNode in variantNodes: if not variantNode.hasParentContext("call/operand"): continue variantMethod = treeutil.selectNode(variantNode, "identifier[4]/@name") if variantMethod not in ["select", "isSet", "compilerIsSet", "get"]: continue else: yield variantNode
def checkAll(): def findVariables(rootNode): variables = [] for node in treeutil.nodeIterator(rootNode, ["assignment", "call"]): if node.type == "assignment": variables.append(node.getChild("left")) elif node.type == "call": variables.append(node.getChild("operand")) return variables variables = findVariables(classMap["members"]) if "construct" in classMap: variables.extend(findVariables(classMap["construct"])) for node in variables: this = treeutil.selectNode(node, "variable/identifier[1]/@name") if this != "this": continue field = treeutil.selectNode(node, "variable/identifier[2]/@name") if field is None: continue if field[0] != "_": continue elif field[1] == "_": prot = "private" else: prot = "protected" if prot == "protected": #self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field) pass # protected data fields are ok elif not field in restricted: self.log( node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))
def checkFields(self): define = treeutil.findQxDefine(self.tree) if not define: return classMapNode = treeutil.selectNode(define, "params/2") if classMapNode is None: return classMap = treeutil.mapNodeToMap(classMapNode) if not classMap.has_key("members"): return members = treeutil.mapNodeToMap(classMap["members"].children[0]) restricted = [key for key in members if key.startswith("_")] assignNodes = [node for node in treeutil.nodeIterator(classMap["members"], "assignment")] if classMap.has_key("construct"): for node in treeutil.nodeIterator(classMap["construct"], "assignment"): assignNodes.append(node) for node in assignNodes: this = treeutil.selectNode(node, "left/variable/identifier[1]/@name") if this != "this": continue field = treeutil.selectNode(node, "left/variable/identifier[2]/@name") if field is None: continue if field[0] != "_": continue elif field[1] == "_": prot = "private" else: prot = "protected" if prot == "protected": self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field) elif not field in restricted: self.log(node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))
def findVariantNodes(node): variantNodes = treeutil.findVariablePrefix(node, "qx.core.Variant") variantNodes.extend( treeutil.findVariablePrefix(node, "qx.core.Environment")) for variantNode in variantNodes: if not variantNode.hasParentContext("call/operand"): continue variantMethod = treeutil.selectNode(variantNode, "identifier[4]/@name") if variantMethod not in ["select", "isSet", "compilerIsSet", "get"]: continue else: yield variantNode
def checkDeferNode(self, assembled, node): deferNode = None if ( assembled == "qx.Class.define" or assembled == "qx.Bootstrap.define" or assembled == "q.define" or assembled == "qxWeb.define" ): if node.hasParentContext("call/operand"): deferNode = treeutil.selectNode( node, "../../arguments/2/keyvalue[@key='defer']/value/function/body/block" ) return deferNode
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 _variantsFromTree(self, node): console = self.context['console'] classvariants = set() for variantNode in variantoptimizer.findVariantNodes(node): firstParam = treeutil.selectNode(variantNode, "../../params/1") if firstParam: if treeutil.isStringLiteral(firstParam): classvariants.add(firstParam.get("value")) elif firstParam.type == "map": # e.g. .filter() method mapMap = treeutil.mapNodeToMap(firstParam) classvariants.update(mapMap.keys()) else: console.warn("qx.core.Environment call with alien first argument (%s:%s)" % (self.id, variantNode.get("line", False))) return classvariants
def isEnvironmentCall(callNode): if callNode.type != "call": return False operandNode = treeutil.selectNode(callNode, "operand") operand = operandNode.toJS(pp) environParts = operand.rsplit(".", 1) if len(environParts) != 2: return False elif environParts[0] not in InterestingEnvClasses: return False elif environParts[1] not in InterestingEnvMethods: return False else: return operand
def isEnvironmentCall(callNode): if callNode.type != "call": return False operandNode = treeutil.selectNode(callNode, "operand") operand = operandNode.toJS(pp) environParts = operand.rsplit('.', 1) if len(environParts) != 2: return False elif environParts[0] not in InterestingEnvClasses: return False elif environParts[1] not in InterestingEnvMethods: return False else: return operand
def extractChecksMap(self): tree = self.tree() checksMap = None for node in treeutil.nodeIterator(tree, "keyvalue"): if node.get("key", "") == "_checksMap": checksMap = node break assert checksMap assert checksMap.hasParentContext("keyvalue/value/map") # 'statics/_checksMap' checksMap = treeutil.selectNode(checksMap, "value/map") checksMap = treeutil.mapNodeToMap(checksMap) # stringify map values for key in checksMap: checksMap[key] = checksMap[key].children[0].get("value") return checksMap
def createEnvKeyProviderIndex(self, classesAll): envProviders = {} providingEnvClasses = [ className for className in classesAll.keys() if className.startswith("qx.bom.client") ] for className in providingEnvClasses: tree = classesAll[className].tree() for callnode in list(treeutil.nodeIterator(tree, ['call'])): if callnode.toJS(pp).startswith("qx.core.Environment.add"): envKey = treeutil.selectNode(callnode, "arguments/constant/@value") envProviders[envKey] = className return envProviders
def processVariantGet(callNode, variantMap): treeModified = False # Simple sanity checks params = callNode.getChild("params") if len(params.children) != 1: log( "Warning", "Expecting exactly one argument for qx.core.Environment.get. Ignoring this occurrence.", params) return treeModified firstParam = params.getChildByPosition(0) if not isStringLiteral(firstParam): # warning is currently covered in parsing code #log("Warning", "First argument must be a string literal! Ignoring this occurrence.", firstParam) return treeModified # skipping "relative" calls like "a.b.qx.core.Environment.get()" qxIdentifier = treeutil.selectNode(callNode, "operand/variable/identifier[1]") if not treeutil.checkFirstChainChild(qxIdentifier): log( "Warning", "Skipping relative qx.core.Environment.get call. Ignoring this occurrence ('%s')." % treeutil.findChainRoot(qxIdentifier).toJavascript()) return treeModified variantKey = firstParam.get("value") if variantKey in variantMap: confValue = variantMap[variantKey] else: return treeModified # Replace the .get() with its value resultNode = reduceCall(callNode, confValue) treeModified = True # Reduce any potential operations with literals (+3, =='hugo', ?a:b, ...) treeMod = True while treeMod: resultNode, treeMod = reduceOperation(resultNode) # Reduce a potential condition _ = reduceLoop(resultNode) return treeModified
def visit_call(self, node): #print "visiting", node.type if variantoptimizer.isEnvironmentCall(node): assert self.opts.envmappings key = treeutil.selectNode(node, "arguments/1") classname, methname = self.getClassNameFromEnvKey(key, self.opts.envmappings) if classname: depsItem = DependencyItem(classname, methname, self.file_name, node.get('line',0) or -1) depsItem.isCall = True # treat as if actual call, to collect recursive deps # check phase functor = node.getChild("operand") # get the "qx.core.Environment" symbol if self.is_static_loaddep(functor): depsItem.isLoadDep = True depsItem.needsRecursion = True self.deps.append(depsItem)
def usedVariablesIterator(node): # Switch on node context: # "function", "catch": if node.type in ["function", "catch"]: return # "catch": skip the identifier of catch clauses, e.g. the 'e' in 'catch(e)' # (it belongs to the catch scope) if node.parent and node.parent.type == "catch" and node == node.parent.children[ 0]: return # "for-in": treat variables used in for-in loops as used variables (why?) # (undeclared variables are handled by the normal "identifier" rule # further down) if (node.type == "first" and node.parent.type == "operation" and node.parent.get("operator") == "IN"): use = treeutil.selectNode(node, "var/definition/identifier") if use: name = use.get("value", False) yield (name, use) return # "identifier": if node.type == "identifier": isFirstChild = False isVariableMember = False if node.parent.parent.isVar( ): # (the old code added "accessor" for the types to check) isVariableMember = True isFirstChild = treeutil.checkFirstChainChild(node) # inside a variable only respect the first member if not isVariableMember or isFirstChild: name = node.get("value", False) if name: yield (name, node) # -- Recurse over children if node.children: for child in node.children: for (name, use) in Scope.usedVariablesIterator(child): yield (name, use) return
def collectCalls(node, calls): if node.hasChildren(): for child in node.children: collectCalls(child, calls) # looking out for classic function calls like # this.foo() or obj.hello(). Interesting here are # all methods which are called on "this". if node.type == "call": var = treeutil.selectNode(node, "operand/variable") if var: name = detectCallName(var) if name: if name in calls: calls[name].append(node) else: calls[name] = [node]
def _analyzeClassDepsNode(self, fileId, node, loadtime, runtime, inFunction): if node.type == "variable": assembled = (treeutil.assembleVariable(node))[0] # treat dependencies in defer as requires if assembled == "qx.Class.define" or assembled == "qx.Bootstrap.define": if node.parent.type == "operand" and node.parent.parent.type == "call": deferNode = treeutil.selectNode( node, "../../params/2/keyvalue[@key='defer']/value/function/body/block" ) if deferNode != None: self._analyzeClassDepsNode(fileId, deferNode, loadtime, runtime, False) # try to reduce to a class name assembledId = None if self._classes.has_key(assembled): assembledId = assembled elif "." in assembled: for entryId in self._classes: if assembled.startswith(entryId) and re.match( "%s\W" % entryId, assembled): assembledId = entryId break if assembledId and assembledId != fileId and self._classes.has_key( assembledId): if inFunction: target = runtime else: target = loadtime if not assembledId in target: target.append(assembledId) elif node.type == "body" and node.parent.type == "function": inFunction = True if node.hasChildren(): for child in node.children: self._analyzeClassDepsNode(fileId, child, loadtime, runtime, inFunction)
def processVariantGet(callNode, variantMap): treeModified = False # Simple sanity checks params = callNode.getChild("params") if len(params.children) != 1: log("Warning", "Expecting exactly one argument for qx.core.Environment.get. Ignoring this occurrence.", params) return treeModified firstParam = params.getChildByPosition(0) if not isStringLiteral(firstParam): log("Warning", "First argument must be a string literal! Ignoring this occurrence.", firstParam) return treeModified # skipping "relative" calls like "a.b.qx.core.Environment.get()" qxIdentifier = treeutil.selectNode(callNode, "operand/variable/identifier[1]") if not treeutil.checkFirstChainChild(qxIdentifier): log( "Warning", "Skipping relative qx.core.Environment.get call. Ignoring this occurrence ('%s')." % treeutil.findChainRoot(qxIdentifier).toJavascript(), ) return treeModified variantKey = firstParam.get("value") if variantKey in variantMap: confValue = variantMap[variantKey] else: return treeModified # Replace the .get() with its value resultNode = reduceCall(callNode, confValue) treeModified = True # Reduce any potential operations with literals (+3, =='hugo', ?a:b, ...) treeMod = True while treeMod: resultNode, treeMod = reduceOperation(resultNode) # Reduce a potential condition _ = reduceLoop(resultNode) return treeModified
def detectDeps(node, optionalDeps, loadtimeDeps, runtimeDeps, fileId, fileDb, inFunction): if node.type == "variable": if node.hasChildren: assembled = "" first = True for child in node.children: if child.type == "identifier": if not first: assembled += "." assembled += child.get("name") first = False if assembled != fileId and fileDb.has_key(assembled) and not assembled in optionalDeps: if inFunction: targetDeps = runtimeDeps else: targetDeps = loadtimeDeps if assembled in targetDeps: return targetDeps.append(assembled) else: assembled = "" break # treat dependencies in defer as requires if assembled == "qx.Class.define" or assembled == "qx.Bootstrap.define": if node.parent.type == "operand" and node.parent.parent.type == "call": deferNode = treeutil.selectNode( node, "../../params/2/keyvalue[@key='defer']/value/function/body/block" ) if deferNode != None: detectDeps(deferNode, optionalDeps, loadtimeDeps, runtimeDeps, fileId, fileDb, False) elif node.type == "body" and node.parent.type == "function": inFunction = True if node.hasChildren(): for child in node.children: detectDeps(child, optionalDeps, loadtimeDeps, runtimeDeps, fileId, fileDb, inFunction)