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 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 unknown_globals(self, scope): # collect scope's global use locations global_nodes = defaultdict(list) # {assembled: [node]} for id_, scopeVar in scope.globals().items(): for head_node in scopeVar.uses: var_top = treeutil.findVarRoot(head_node) full_name = (treeutil.assembleVariable(var_top))[0] global_nodes[full_name].append(head_node) # filter allowed globals # - from config global_nodes = dict([(key,nodes) for (key,nodes) in global_nodes.items() if key not in self.opts.allowed_globals]) # - from known classes and namespaces global_nodes = dict([(key,nodes) for (key,nodes) in global_nodes.items() if not extension_match_in(key, self.known_globals_bases,self.opts.class_namespaces)]) # known classes (classList + their namespaces) # - from built-ins new_keys = gs.globals_filter_by_builtins(global_nodes.keys()) global_nodes = dict([(key,nodes) for (key,nodes) in global_nodes.items() if key in new_keys]) # - with jshints for key, nodes in global_nodes.items(): global_nodes[key] = [node for node in nodes if not gs.ident_is_ignored(key, node)] # warn remaining for key, nodes in global_nodes.items(): for node in nodes: issue = warn("Unknown global symbol used: '%s'" % key, self.file_name, node) self.issues.append(issue)
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 unknown_globals(self, scope): # helper functions not_jsignored = inverse(gs.test_ident_is_jsignored) not_builtin = inverse(gs.test_ident_is_builtin()) not_libsymbol = inverse(curry3(gs.test_for_libsymbol, self.opts.class_namespaces)(self.known_globals_bases)) not_confsymbol = lambda node: globals_table[node] not in self.opts.allowed_globals def warn_appender(global_nodes): for node in global_nodes: issue = warn("Unknown global symbol used: '%s'" % globals_table[node], self.file_name, node) issue.name = globals_table[node] # @deprecated {3.0} to filter against #ignore later self.issues.append(issue) # ------------------------------ # collect scope's global use locations globals_table = {} # {node: assembled} for id_, scopeVar in scope.globals().items(): for head_node in scopeVar.uses: var_top = treeutil.findVarRoot(head_node) assembled = (treeutil.assembleVariable(var_top))[0] globals_table[head_node] = assembled # filter and add remains to warnings pipeline( globals_table.keys() , bind(filter, not_builtin) , bind(filter, not_jsignored) , bind(filter, not_libsymbol) , bind(filter, not_confsymbol) , warn_appender )
def unknown_globals(self, scope): # take advantage of Scope() objects for id_, scopeVar in scope.globals().items(): if id_ in self.opts.allowed_globals: continue elif id_ in lang.GLOBALS: # JS built-ins ('alert' etc.) continue else: # we want to be more specific than just the left-most symbol, # like "qx", so let's look at the var uses for var_node in scopeVar.uses: var_top = treeutil.findVarRoot(var_node) full_name = (treeutil.assembleVariable(var_top))[0] ok = False if extension_match_in(full_name, self.known_globals_bases, self.opts.class_namespaces): # known classes (classList + their namespaces) ok = True else: at_hints = get_at_hints(var_node) # check full_name against @ignore hints if at_hints: ok = ( self.is_name_ignore_filtered(full_name, at_hints) or self.is_name_lint_filtered(full_name, at_hints, "ignoreUndefined")) # /**deprecated*/ if not ok: issue = warn("Unknown global symbol used: '%s'" % full_name, self.file_name, var_node) self.issues.append(issue)
def unknown_globals(self, scope): # helper functions not_jsignored = inverse(gs.test_ident_is_jsignored) not_builtin = inverse(gs.test_ident_is_builtin()) not_libsymbol = inverse(curry3(gs.test_for_libsymbol, self.opts.class_namespaces)(self.known_globals_bases)) not_confsymbol = lambda node: globals_table[node] not in self.opts.allowed_globals def warn_appender(global_nodes): for node in global_nodes: issue = warn("Unknown global symbol used: '%s'" % globals_table[node], self.file_name, node) self.issues.append(issue) # ------------------------------ # collect scope's global use locations globals_table = {} # {node: assembled} for id_, scopeVar in scope.globals().items(): for head_node in scopeVar.uses: var_top = treeutil.findVarRoot(head_node) assembled = (treeutil.assembleVariable(var_top))[0] globals_table[head_node] = assembled # filter and add remains to warnings pipeline( globals_table.keys() , bind(filter, not_builtin) , bind(filter, not_jsignored) , bind(filter, not_libsymbol) , bind(filter, not_confsymbol) , warn_appender )
def function_unknown_globals(self, funcnode): # take advantage of Scope() objects scope = funcnode.scope for id_, scopeVar in scope.globals().items(): if id_ in self.opts.allowed_globals: continue elif id_ in lang.GLOBALS: # JS built-ins ('alert' etc.) continue else: # we want to be more specific than just the left-most symbol, # like "qx", so let's look at the var uses for var_node in scopeVar.uses: var_top = treeutil.findVarRoot(var_node) full_name = (treeutil.assembleVariable(var_top))[0] ok = False if extension_match_in( full_name, self.opts.library_classes + self.opts.class_namespaces ): # known classes (classList + their namespaces) ok = True else: at_hints = get_at_hints( var_node) # check full_name against @ignore hints if at_hints: ok = (self.is_name_ignore_filtered( full_name, at_hints) or self.is_name_lint_filtered( full_name, at_hints, "ignoreUndefined") ) # /**deprecated*/ if not ok: warn( "Unknown global symbol used: '%s'" % var_node.get("value"), self.file_name, var_node)
def unknown_globals(self, scope): # collect scope's global use locations global_nodes = defaultdict(list) # {assembled: [node]} for id_, scopeVar in scope.globals().items(): for head_node in scopeVar.uses: var_top = treeutil.findVarRoot(head_node) full_name = (treeutil.assembleVariable(var_top))[0] global_nodes[full_name].append(head_node) # filter allowed globals # - from config global_nodes = dict([(key, nodes) for (key, nodes) in global_nodes.items() if key not in self.opts.allowed_globals]) # - from known classes and namespaces global_nodes = dict([ (key, nodes) for (key, nodes) in global_nodes.items() if not extension_match_in( key, self.known_globals_bases, self.opts.class_namespaces) ]) # known classes (classList + their namespaces) # - from built-ins new_keys = gs.globals_filter_by_builtins(global_nodes.keys()) global_nodes = dict([(key, nodes) for (key, nodes) in global_nodes.items() if key in new_keys]) # - with jshints for key, nodes in global_nodes.items(): global_nodes[key] = [ node for node in nodes if not gs.ident_is_ignored(key, node) ] # warn remaining for key, nodes in global_nodes.items(): for node in nodes: issue = warn("Unknown global symbol used: '%s'" % key, self.file_name, node) self.issues.append(issue)
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 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 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 visit(self, scopeNode): # go through globals for scopeVar in scopeNode.globals(): # this might be as little as 'qx' for node in scopeVar.occurrences(): # assemble var var_node = treeutil.findVarRoot(node) var_name = var_node.toJS() # e.g. "qx.util.ResourceManager.getInstance" # lookup in globals_map known_global, replacement = ( self.global_map.longest_match(var_name)) # -> "qx.util.ResourceManager" # find node in tree dot_number = 0 # TODO uptimes = u'/'.join(['..'] * dot_number) source_node = treeutil.selectNode(node, uptimes) # construct replacement repl_node = treegenerator.parse(replacement) # could be "foo.bar" or "qx.$g['bNq']" # replace known symbol in tree source_node.parent.replaceChild(source_node, repl_node)
def test(node): var_root = treeutil.findVarRoot(node) name = treeutil.assembleVariable(var_root)[0] return bool(GlobalSymbolsCombinedPatt.search(name))
def assembleVar(node): assert node.isVar() var_root = treeutil.findVarRoot(node) return var_root.toJS(defaultOptions)
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
def test_ident_is_jsignored(node): var_root = treeutil.findVarRoot(node) name = treeutil.assembleVariable(var_root)[0] return name_is_jsignored(name, node)
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