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 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 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 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 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 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 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 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 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 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 reduceOperation(literalNode): resultNode = None treeModified = False # can only reduce with constants if literalNode.type != "constant": return literalNode, False else: literalValue = constNodeToPyValue(literalNode) # check if we're in an operation ngParent = nextNongroupParent(literalNode) # could be "first", "second" etc. in ops if not ngParent or not ngParent.parent or ngParent.parent.type != "operation": return literalNode, False else: operationNode = ngParent.parent # get operator operator = operationNode.get("operator") # normalize expression noperationNode = normalizeExpression(operationNode) # re-gain knownn literal node for node in treeutil.nodeIterator(noperationNode, [literalNode.type]): if literalNode.attributes == node.attributes: nliteralNode = node break # equal, unequal if operator in ["EQ", "SHEQ", "NE", "SHNE"]: otherOperand, _ = getOtherOperand(noperationNode, nliteralNode) if otherOperand.type != "constant": return literalNode, False if operator in ["EQ", "SHEQ"]: cmpFcn = operators.eq elif operator in ["NE", "SHNE"]: cmpFcn = operators.ne operands = [literalValue] otherVal = constNodeToPyValue(otherOperand) operands.append(otherVal) result = cmpFcn(operands[0],operands[1]) resultNode = tree.Node("constant") resultNode.set("constantType","boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # order compares <, =<, ... elif operator in ["LT", "LE", "GT", "GE"]: otherOperand, otherPosition = getOtherOperand(noperationNode, nliteralNode) if otherOperand.type != "constant": return literalNode, False if operator == "LT": cmpFcn = operators.lt elif operator == "LE": cmpFcn = operators.le elif operator == "GT": cmpFcn = operators.gt elif operator == "GE": cmpFcn = operators.ge operands = {} operands[1 - otherPosition] = literalValue otherVal = constNodeToPyValue(otherOperand) operands[otherPosition] = otherVal result = cmpFcn(operands[0], operands[1]) resultNode = tree.Node("constant") resultNode.set("constantType","boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # logical ! (not) elif operator in ["NOT"]: result = not literalValue resultNode = tree.Node("constant") resultNode.set("constantType","boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # logical operators &&, || -- Currently disabled, s. bug#4856 elif False and operator in ["AND", "OR"]: result = None otherOperand, otherPosition = getOtherOperand(noperationNode, nliteralNode) if operator == "AND": #if otherPosition==1 and not literalValue: # short circuit # result = False #else: cmpFcn = (lambda x,y: x and y) elif operator == "OR": #if otherPosition==1 and literalValue: # short circuit # result = True #else: cmpFcn = (lambda x,y: x or y) if result == None: if otherOperand.type != "constant": return literalNode, False operands = {} operands[1 - otherPosition] = literalValue otherVal = constNodeToPyValue(otherOperand) operands[otherPosition] = otherVal result = cmpFcn(operands[0], operands[1]) resultNode = {literalValue:literalNode, otherVal:otherOperand}[result] # hook ?: operator elif operator in ["HOOK"]: if ngParent.type == "first": # optimize a literal condition if bool(literalValue): resultNode = treeutil.selectNode(noperationNode, "second/1", True) else: resultNode = treeutil.selectNode(noperationNode, "third/1", True) # unsupported operation else: pass if resultNode != None: #print "optimizing: operation" operationNode.parent.replaceChild(operationNode, resultNode) treeModified = True else: resultNode = literalNode treeModified = False return resultNode, treeModified
def reduceOperation(literalNode): resultNode = None treeModified = False # can only reduce with constants if literalNode.type != "constant": return literalNode, False else: literalValue = constNodeToPyValue(literalNode) # check if we're in an operation ngParent = nextNongroupParent( literalNode) # could be "first", "second" etc. in ops if not ngParent or not ngParent.parent or ngParent.parent.type != "operation": return literalNode, False else: operationNode = ngParent.parent # get operator operator = operationNode.get("operator") # normalize expression noperationNode = normalizeExpression(operationNode) # re-gain knownn literal node for node in treeutil.nodeIterator(noperationNode, [literalNode.type]): if literalNode.attributes == node.attributes: nliteralNode = node break # equal, unequal if operator in ["EQ", "SHEQ", "NE", "SHNE"]: otherOperand, _ = getOtherOperand(noperationNode, nliteralNode) if otherOperand.type != "constant": return literalNode, False if operator in ["EQ", "SHEQ"]: cmpFcn = operators.eq elif operator in ["NE", "SHNE"]: cmpFcn = operators.ne operands = [literalValue] otherVal = constNodeToPyValue(otherOperand) operands.append(otherVal) result = cmpFcn(operands[0], operands[1]) resultNode = tree.Node("constant") resultNode.set("constantType", "boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # order compares <, =<, ... elif operator in ["LT", "LE", "GT", "GE"]: otherOperand, otherPosition = getOtherOperand(noperationNode, nliteralNode) if otherOperand.type != "constant": return literalNode, False if operator == "LT": cmpFcn = operators.lt elif operator == "LE": cmpFcn = operators.le elif operator == "GT": cmpFcn = operators.gt elif operator == "GE": cmpFcn = operators.ge operands = {} operands[1 - otherPosition] = literalValue otherVal = constNodeToPyValue(otherOperand) operands[otherPosition] = otherVal result = cmpFcn(operands[0], operands[1]) resultNode = tree.Node("constant") resultNode.set("constantType", "boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # logical ! (not) elif operator in ["NOT"]: result = not literalValue resultNode = tree.Node("constant") resultNode.set("constantType", "boolean") resultNode.set("value", str(result).lower()) resultNode.set("line", noperationNode.get("line")) # logical operators &&, || -- Currently disabled, s. bug#4856 elif False and operator in ["AND", "OR"]: result = None otherOperand, otherPosition = getOtherOperand(noperationNode, nliteralNode) if operator == "AND": #if otherPosition==1 and not literalValue: # short circuit # result = False #else: cmpFcn = (lambda x, y: x and y) elif operator == "OR": #if otherPosition==1 and literalValue: # short circuit # result = True #else: cmpFcn = (lambda x, y: x or y) if result == None: if otherOperand.type != "constant": return literalNode, False operands = {} operands[1 - otherPosition] = literalValue otherVal = constNodeToPyValue(otherOperand) operands[otherPosition] = otherVal result = cmpFcn(operands[0], operands[1]) resultNode = { literalValue: literalNode, otherVal: otherOperand }[result] # hook ?: operator elif operator in ["HOOK"]: if ngParent.type == "first": # optimize a literal condition if bool(literalValue): resultNode = treeutil.selectNode(noperationNode, "second/1", True) else: resultNode = treeutil.selectNode(noperationNode, "third/1", True) # unsupported operation else: pass if resultNode != None: #print "optimizing: operation" operationNode.parent.replaceChild(operationNode, resultNode) treeModified = True else: resultNode = literalNode treeModified = False return resultNode, treeModified