def depsItem_from_node(self, node): scope = node.scope # some initializations (might get refined later) depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = scope.is_load_time depsItem.needsRecursion = False depsItem.isCall = False depsItem.node = node var_root = treeutil.findVarRoot(node) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if treeutil.isCallOperand(var_root): # it's a function call or new op. depsItem.isCall = True # interesting when following transitive deps # .name, .attribute assembled = (treeutil.assembleVariable(node))[0] #className, classAttribute = self._splitQxClass(assembled) className = gs.test_for_libsymbol(assembled, ClassesAll, []) # TODO: no namespaces!? if not className: is_lib_class = False className = assembled classAttribute = '' else: is_lib_class = True if len(assembled) > len(className): classAttribute = assembled[len(className)+1:] else: classAttribute = '' # we allow self-references, to be able to track method dependencies within the same class assembled_parts = assembled.split('.') if assembled_parts[0] == 'this': className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] elif scope.is_defer and assembled_parts[0] in DEFER_ARGS: className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] if is_lib_class and not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(var_root): classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) if (is_lib_class and scope.is_load_time and (treeutil.isCallOperand(var_root) or treeutil.isNEWoperand(var_root))): depsItem.needsRecursion = True return depsItem
def depsItem_from_node(self, node): scope = node.scope # some initializations (might get refined later) depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = scope.is_load_time depsItem.needsRecursion = False depsItem.isCall = False depsItem.node = node is_lib_class = False var_root = treeutil.findVarRoot(node) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if treeutil.isCallOperand(var_root): # it's a function call or new op. depsItem.isCall = True # interesting when following transitive deps # .name, .attribute assembled = (treeutil.assembleVariable(node))[0] className, classAttribute = self._splitQxClass(assembled) if not className: if "." in assembled: className, classAttribute = assembled.split('.')[:2] else: className = assembled else: is_lib_class = True # we allow self-references, to be able to track method dependencies within the same class if self.is_this(className): if className.find('.')>-1: classAttribute = className.split('.')[1] className = self.id is_lib_class = True elif scope.is_defer and className in DEFER_ARGS: className = self.id is_lib_class = True if is_lib_class and not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(var_root): classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) #if self.followCallDeps(var_root, self.id, className, isLoadTime): #if self.id=='qx.bom.element.Style' and depsItem.attribute=='__detectVendorProperties': # import pydb; pydb.debugger() if (is_lib_class and scope.is_load_time and (treeutil.isCallOperand(var_root) or treeutil.isNEWoperand(var_root))): depsItem.needsRecursion = True return depsItem
def hasFollowContext(node): pchn = node.getParentChain() pchain = "/".join(pchn) return ( pchain.endswith("call/operand") # it's a function call or treeutil.isNEWoperand(node) # it's a 'new' operation )
def qualify_deps_item(self, node, isLoadTime, inDefer, depsItem=None): if not depsItem: depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = isLoadTime depsItem.needsRecursion = False depsItem.isCall = False var_root = treeutil.findVarRoot( node ) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if var_root.hasParentContext("call/operand"): # it's a function call depsItem.isCall = True # interesting when following transitive deps # .name assembled = (treeutil.assembleVariable(node))[0] _, className, classAttribute = self._isInterestingReference( assembled, var_root, self.id, inDefer) # postcond: # - className != '' must always be true, as we know it is an interesting reference # - might be a known qooxdoo class, or an unknown class (use 'className in self._classes') # - if assembled contained ".", classAttribute will contain approx. non-class part if className: # we allow self-references, to be able to track method dependencies within the same class if className == 'this': className = self.id elif inDefer and className in DEFER_ARGS: className = self.id if not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(node): classAttribute = 'construct' # Can't do the next; it's catching too many occurrences of 'getInstance' that have # nothing to do with the singleton 'getInstance' method (just grep in the framework) #elif classAttribute == 'getInstance': # erase 'getInstance' and introduce 'construct' dependency # classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) if self.followCallDeps(var_root, self.id, className, isLoadTime): depsItem.needsRecursion = True if depsItem.name: return depsItem else: return None
def qualify_deps_item(self, node, isLoadTime, inDefer, depsItem=None): if not depsItem: depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = isLoadTime depsItem.needsRecursion = False depsItem.isCall = False var_root = treeutil.findVarRoot(node) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if var_root.hasParentContext("call/operand"): # it's a function call depsItem.isCall = True # interesting when following transitive deps # .name assembled = (treeutil.assembleVariable(node))[0] _, className, classAttribute = self._isInterestingReference(assembled, var_root, self.id, inDefer) # postcond: # - className != '' must always be true, as we know it is an interesting reference # - might be a known qooxdoo class, or an unknown class (use 'className in self._classes') # - if assembled contained ".", classAttribute will contain approx. non-class part if className: # we allow self-references, to be able to track method dependencies within the same class if className == 'this': className = self.id elif inDefer and className in DEFER_ARGS: className = self.id if not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(node): classAttribute = 'construct' # Can't do the next; it's catching too many occurrences of 'getInstance' that have # nothing to do with the singleton 'getInstance' method (just grep in the framework) #elif classAttribute == 'getInstance': # erase 'getInstance' and introduce 'construct' dependency # classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) if self.followCallDeps(var_root, self.id, className, isLoadTime): depsItem.needsRecursion = True if depsItem.name: return depsItem else: return None
def isUnknownClass(self, assembled, node, fileId): # check name in 'new ...' position if (treeutil.isNEWoperand(node) # check name in "'extend' : ..." position or (node.hasParentContext("keyvalue/*") and node.parent.parent.get('key') == 'extend')): # skip built-in classes (Error, document, RegExp, ...) if (assembled in lang.BUILTIN + ['clazz'] or re.match(r'this\b', assembled)): return False # skip scoped vars - expensive, therefore last test elif self._isScopedVar(assembled, node, fileId): return False else: return True return False
def checkNodeContext(node): ## # Getting the longest left-most dotaccessor in the expression <node> # is in, e.g. the 'a.b' in 'a.b().c.d[0].e'. # # The idea is to only treat 'a.b' as interesting, and ignore all # other vars in such an expression. So the check is to see whether # <node> represents 'a.b'. # # To get this we # (a) use the leftmostChild of <node> ("downwards") # (If <node> represents 'a.b', this should be 'a', but would # be 'c' if in 'c.d'). The leftmostChild is also used # elsewhere, so it is passed in as a param. # (b) from that search upwards the tree to get the highest enclosing # dotaccessor node. # If this is identical to <node>, <node> is interesting. # def is_leftmost_and_highest_pure_dotaccessor(node, leftmost_child): res = False # as _isInterestingReference is run on *any* var node while # traversing the tree intermediate occurrences var nodes like # in 'a.b().c[d].e' are run through it as well; but it is enough to treat # the longest left-most expression, so we restrict ourself to the "head" var # expression like 'a.b' here, and skip other occurrences (like 'c', 'd' # and 'e' in the example) #myFirst = node.getFirstChild(mandatory=False, ignoreComments=True) #if not treeutil.checkFirstChainChild(myFirst): # see if myFirst is the first identifier in a chain # context = '' # get the top-most dotaccessor of this identifier/constant(?) ##localTop = treeutil.findVarRoot(leftmostChild) # testing for the 'a.b' in 'a.b().c[d].e'; bare 'a' in 'a' is also caught ##if localTop != node: ## context = '' # ---------------------------------------------------- # assumption: not is_right_dot_operand(node), so # we're on the 'left' axis of the (left-leaning) expression var_root = treeutil.findVarRoot(leftmost_child) # get the highest pur dotaccessor above it res = var_root == node return res def is_right_dot_operand(node): return node.parent.type == "dotaccessor" and node.parent.getChild(1) == node # ------------------------------------------------------------------ context = 'interesting' # every context is interesting, maybe we get more specific or reset to '' # don't treat label references if node.parent.type in ("break", "continue"): context = '' # skip right dot operands (the 'b' in 'a.b') elif is_right_dot_operand(node): context = '' else: leftmost_child = treeutil.findLeftmostChild(node) # get left-most child # skip inner parts of a complex dotaccessor chain ('.c' in 'a.b().c.d[3].f') if not is_leftmost_and_highest_pure_dotaccessor(node, leftmost_child): context = '' # '/^\s*$/.test(value)' or '[].push' or '{}.toString' elif leftmost_child.type in ("constant", "array", "map"): context = '' # check name in 'new ...' position elif treeutil.isNEWoperand(node): 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 _analyzeClassDepsNode_1(self, node, depsList, inLoadContext, inDefer=False): if node.isVar(): if node.dep: depsList.append(node.dep) return assembled = (treeutil.assembleVariable(node))[0] # treat dependencies in defer as requires deferNode = self.checkDeferNode(assembled, node) if deferNode != None: self._analyzeClassDepsNode(deferNode, depsList, inLoadContext=True, inDefer=True) (context, className, classAttribute) = self._isInterestingReference(assembled, node, self.id, inDefer) # postcond: # - if className != '' it is an interesting reference # - might be a known qooxdoo class, or an unknown class (use 'className in self._classes') # - if assembled contained ".", classAttribute will contain approx. non-class part if className: # we allow self-references, to be able to track method dependencies within the same class if className == 'this': className = self.id elif inDefer and className in DEFER_ARGS: className = self.id if not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(node): classAttribute = 'construct' # Can't do the next; it's catching too many occurrences of 'getInstance' that have # nothing to do with the singleton 'getInstance' method (just grep in the framework) #elif classAttribute == 'getInstance': # erase 'getInstance' and introduce 'construct' dependency # classAttribute = 'construct' line = node.get('line',0) depsItem = DependencyItem(className, classAttribute, self.id, line if line else -1, inLoadContext) #print "-- adding: %s (%s:%s)" % (className, treeutil.getFileFromSyntaxItem(node), node.get('line',False)) if node.hasParentContext("call/operand"): # it's a function call depsItem.isCall = True # interesting when following transitive deps # Adding all items to list; let caller sort things out depsList.append(depsItem) node.dep = depsItem # Mark items that need recursive analysis of their dependencies (bug#1455) if self.followCallDeps(node, self.id, className, inLoadContext): depsItem.needsRecursion = True # check e.g. qx.core.Environment.get("runtime.name") elif node.type == "constant" and node.hasParentContext("call/arguments"): if node.dep: depsList.append(node.dep) return callnode = treeutil.selectNode(node, "../..") if variantoptimizer.isEnvironmentCall(callnode): className, classAttribute = self.getClassNameFromEnvKey(node.get("value", "")) if className: depsItem = DependencyItem(className, classAttribute, self.id, node.get('line', -1), inLoadContext) depsItem.isCall = True # treat as if actual call, to collect recursive deps if inLoadContext: depsItem.needsRecursion = True depsList.append(depsItem) node.dep = depsItem elif node.type == "body" and node.parent.type == "function": if (node.parent.hasParentContext("call/operand") or node.parent.hasParentContext("call/operand/group")): # if the function is immediately called, it's still load context (if that's what it was before) pass else: inLoadContext = False if node.hasChildren(): for child in node.children: self._analyzeClassDepsNode(child, depsList, inLoadContext, inDefer) return
def depsItem_from_node(self, node): scope = node.scope # some initializations (might get refined later) depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = scope.is_load_time depsItem.needsRecursion = False depsItem.isCall = False depsItem.node = node var_root = treeutil.findVarRoot(node) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if treeutil.isCallOperand(var_root): # it's a function call or new op. depsItem.isCall = True # interesting when following transitive deps # .name, .attribute assembled = (treeutil.assembleVariable(node))[0] className = gs.test_for_libsymbol(assembled, ClassesAll, []) # TODO: no namespaces!? if not className: is_lib_class = False className = assembled classAttribute = '' else: is_lib_class = True if len(assembled) > len(className): classAttribute = assembled[len(className)+1:] dotidx = classAttribute.find(".") # see if classAttribute is chained too if dotidx > -1: classAttribute = classAttribute[:dotidx] # only use the first component else: classAttribute = '' # we allow self-references, to be able to track method dependencies within the same class assembled_parts = assembled.split('.') if assembled_parts[0] == 'this': className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] elif scope.is_defer and assembled_parts[0] in DEFER_ARGS: className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] if is_lib_class and not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(var_root): classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) if (is_lib_class and scope.is_load_time and (treeutil.isCallOperand(var_root) or treeutil.isNEWoperand(var_root))): depsItem.needsRecursion = True return depsItem
def checkNodeContext(node): ## # Getting the longest left-most dotaccessor in the expression <node> # is in, e.g. the 'a.b' in 'a.b().c.d[0].e'. # # The idea is to only treat 'a.b' as interesting, and ignore all # other vars in such an expression. So the check is to see whether # <node> represents 'a.b'. # # To get this we # (a) use the leftmostChild of <node> ("downwards") # (If <node> represents 'a.b', this should be 'a', but would # be 'c' if in 'c.d'). The leftmostChild is also used # elsewhere, so it is passed in as a param. # (b) from that search upwards the tree to get the highest enclosing # dotaccessor node. # If this is identical to <node>, <node> is interesting. # def is_leftmost_and_highest_pure_dotaccessor(node, leftmost_child): res = False # as _isInterestingReference is run on *any* var node while # traversing the tree intermediate occurrences var nodes like # in 'a.b().c[d].e' are run through it as well; but it is enough to treat # the longest left-most expression, so we restrict ourself to the "head" var # expression like 'a.b' here, and skip other occurrences (like 'c', 'd' # and 'e' in the example) #myFirst = node.getFirstChild(mandatory=False, ignoreComments=True) #if not treeutil.checkFirstChainChild(myFirst): # see if myFirst is the first identifier in a chain # context = '' # get the top-most dotaccessor of this identifier/constant(?) ##localTop = treeutil.findVarRoot(leftmostChild) # testing for the 'a.b' in 'a.b().c[d].e'; bare 'a' in 'a' is also caught ##if localTop != node: ## context = '' # ---------------------------------------------------- # assumption: not is_right_dot_operand(node), so # we're on the 'left' axis of the (left-leaning) expression var_root = treeutil.findVarRoot( leftmost_child) # get the highest pur dotaccessor above it res = var_root == node return res def is_right_dot_operand(node): return node.parent.type == "dotaccessor" and node.parent.getChild( 1) == node # ------------------------------------------------------------------ context = 'interesting' # every context is interesting, maybe we get more specific or reset to '' # don't treat label references if node.parent.type in ("break", "continue"): context = '' # skip right dot operands (the 'b' in 'a.b') elif is_right_dot_operand(node): context = '' else: leftmost_child = treeutil.findLeftmostChild( node) # get left-most child # skip inner parts of a complex dotaccessor chain ('.c' in 'a.b().c.d[3].f') if not is_leftmost_and_highest_pure_dotaccessor( node, leftmost_child): context = '' # '/^\s*$/.test(value)' or '[].push' or '{}.toString' elif leftmost_child.type in ("constant", "array", "map"): context = '' # check name in 'new ...' position elif treeutil.isNEWoperand(node): 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 _analyzeClassDepsNode_1(self, node, depsList, inLoadContext, inDefer=False): if node.isVar(): if node.dep: depsList.append(node.dep) return assembled = (treeutil.assembleVariable(node))[0] # treat dependencies in defer as requires deferNode = self.checkDeferNode(assembled, node) if deferNode != None: self._analyzeClassDepsNode(deferNode, depsList, inLoadContext=True, inDefer=True) (context, className, classAttribute) = self._isInterestingReference( assembled, node, self.id, inDefer) # postcond: # - if className != '' it is an interesting reference # - might be a known qooxdoo class, or an unknown class (use 'className in self._classes') # - if assembled contained ".", classAttribute will contain approx. non-class part if className: # we allow self-references, to be able to track method dependencies within the same class if className == 'this': className = self.id elif inDefer and className in DEFER_ARGS: className = self.id if not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(node): classAttribute = 'construct' # Can't do the next; it's catching too many occurrences of 'getInstance' that have # nothing to do with the singleton 'getInstance' method (just grep in the framework) #elif classAttribute == 'getInstance': # erase 'getInstance' and introduce 'construct' dependency # classAttribute = 'construct' line = node.get('line', 0) depsItem = DependencyItem(className, classAttribute, self.id, line if line else -1, inLoadContext) #print "-- adding: %s (%s:%s)" % (className, treeutil.getFileFromSyntaxItem(node), node.get('line',False)) if node.hasParentContext( "call/operand"): # it's a function call depsItem.isCall = True # interesting when following transitive deps # Adding all items to list; let caller sort things out depsList.append(depsItem) node.dep = depsItem # Mark items that need recursive analysis of their dependencies (bug#1455) if self.followCallDeps(node, self.id, className, inLoadContext): depsItem.needsRecursion = True # check e.g. qx.core.Environment.get("runtime.name") elif node.type == "constant" and node.hasParentContext( "call/arguments"): if node.dep: depsList.append(node.dep) return callnode = treeutil.selectNode(node, "../..") if variantoptimizer.isEnvironmentCall(callnode): className, classAttribute = self.getClassNameFromEnvKey( node.get("value", "")) if className: depsItem = DependencyItem(className, classAttribute, self.id, node.get('line', -1), inLoadContext) depsItem.isCall = True # treat as if actual call, to collect recursive deps if inLoadContext: depsItem.needsRecursion = True depsList.append(depsItem) node.dep = depsItem elif node.type == "body" and node.parent.type == "function": if (node.parent.hasParentContext("call/operand") or node.parent.hasParentContext("call/operand/group")): # if the function is immediately called, it's still load context (if that's what it was before) pass else: inLoadContext = False if node.hasChildren(): for child in node.children: self._analyzeClassDepsNode(child, depsList, inLoadContext, inDefer) return
def depsItem_from_node(self, node): scope = node.scope # some initializations (might get refined later) depsItem = DependencyItem('', '', '') depsItem.name = '' depsItem.attribute = '' depsItem.requestor = self.id depsItem.line = node.get("line", -1) depsItem.isLoadDep = scope.is_load_time depsItem.needsRecursion = False depsItem.isCall = False depsItem.node = node is_lib_class = False var_root = treeutil.findVarRoot( node ) # various of the tests need the var (dot) root, rather than the head symbol (like 'qx') # .isCall if treeutil.isCallOperand(var_root): # it's a function call or new op. depsItem.isCall = True # interesting when following transitive deps # .name, .attribute assembled = (treeutil.assembleVariable(node))[0] className, classAttribute = self._splitQxClass(assembled) assembled_parts = assembled.split('.') if not className: if "." in assembled: className = '.'.join(assembled_parts[:-1]) classAttribute = assembled_parts[-1] #className, classAttribute = assembled.split('.')[:2] else: className = assembled else: is_lib_class = True # we allow self-references, to be able to track method dependencies within the same class if assembled_parts[0] == 'this': className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] elif scope.is_defer and assembled_parts[0] in DEFER_ARGS: className = self.id is_lib_class = True if '.' in assembled: classAttribute = assembled_parts[1] if is_lib_class and not classAttribute: # see if we have to provide 'construct' if treeutil.isNEWoperand(var_root): classAttribute = 'construct' depsItem.name = className depsItem.attribute = classAttribute # .needsRecursion # Mark items that need recursive analysis of their dependencies (bug#1455) #if self.followCallDeps(var_root, self.id, className, isLoadTime): #if self.id=='qx.bom.element.Style' and depsItem.attribute=='__detectVendorProperties': # import pydb; pydb.debugger() if (is_lib_class and scope.is_load_time and (treeutil.isCallOperand(var_root) or treeutil.isNEWoperand(var_root))): depsItem.needsRecursion = True return depsItem