def _analyzeClassDepsNode_1(self, node, depsList, inLoadContext, inDefer=False): if node.type in ("file", "function", "catch"): top_scope = node.scope else: top_scope = scopes.find_enclosing(node) # get enclosing scope of node # import pydb; pydb.debugger() for scope in top_scope.scope_iterator(): # walk through this and all nested scopes for global_name, scopeVar in scope.globals().items(): # get the global symbols { sym_name: ScopeVar } for node in scopeVar.uses: # create a depsItem for all its uses depsItem = self.qualify_deps_item(node, scope.is_load_time, scope.is_defer) depsList.append(depsItem) # and qualify them # Augment with feature dependencies introduces with qx.core.Environment.get("...") calls for envCall in variantoptimizer.findVariantNodes(node): className, classAttribute = self.getClassNameFromEnvKey( envCall.getChild("arguments").children[0].get("value", "") ) if className: depsItem = DependencyItem(className, classAttribute, self.id, envCall.get("line", -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps # .inLoadContext # get 'qx' node of 'qx.core.Environment....' call_operand = envCall.getChild("operand").children[0] qx_idnode = treeutil.findFirstChainChild(call_operand) scope = qx_idnode.scope inLoadContext = scope.is_load_time # get its scope's .is_load_time depsItem.isLoadDep = inLoadContext if inLoadContext: depsItem.needsRecursion = True depsList.append(depsItem) return
def getCombinedDeps(self, variants, config, stripSelfReferences=True, projectClassNames=True, genProxy=None, force=False, tree=None): # init lists loadFinal = [] runFinal = [] # add static dependencies if genProxy == None: static, cached = self.dependencies(variants, force, tree=tree) else: static, cached = genProxy.dependencies(self.id, self.path, variants) loadFinal.extend(static["load"]) runFinal.extend(static["run"]) # fix self-references if stripSelfReferences: loadFinal = [x for x in loadFinal if x.name != self.id] runFinal = [x for x in runFinal if x.name != self.id] # collapse multiple occurrences of the same class if projectClassNames: loads = loadFinal loadFinal = [] for dep in loads: if dep.name not in (x.name for x in loadFinal): loadFinal.append(dep) runs = runFinal runFinal = [] for dep in runs: if dep.name not in (x.name for x in runFinal): runFinal.append(dep) # add config dependencies crequire = config.get("require", {}) if self.id in crequire: loadFinal.extend( DependencyItem(x, '', "|config|") for x in crequire[self.id]) cuse = config.get("use", {}) if self.id in cuse: runFinal.extend( DependencyItem(x, '', "|config|") for x in cuse[self.id]) # result dict deps = { "load": loadFinal, "run": runFinal, "ignore": static['ignore'], } return deps, cached
def depsItems_from_Json(self, deps_json): result = {"run": [], "load": [], "ignore": []} for category in ("run", "load"): for classId in deps_json[category]: if any([classId.startswith(x) for x in ("/resource/", "/translation/", "/locale/")]): continue # sorting out resource, locale and msgid dependencies depsItem = DependencyItem(classId, "", "|dependency.json|") depsItem.isLoadDep = category == "load" result[category].append(depsItem) return result
def depsItems_from_Json(self, deps_json): result = {'run':[], 'load':[], 'ignore':[]} for category in ('run', 'load'): for classId in deps_json[category]: if any([classId.startswith(x) for x in ('/resource/', '/translation/', '/locale/')]): continue # sorting out resource, locale and msgid dependencies depsItem = DependencyItem(classId, '', '|dependency.json|') depsItem.isLoadDep = category == 'load' result[category].append(depsItem) return result
def dependencies_from_comphints(self, node): load, run = [], [] # Read meta data meta = self.getHints() metaLoad = meta.get("loadtimeDeps", []) metaRun = meta.get("runtimeDeps", []) metaOptional = meta.get("optionalDeps", []) metaIgnore = meta.get("ignoreDeps", []) metaIgnore.extend(metaOptional) all_feature_checks = [False, False] # load_feature_checks, run_feature_checks # regexify globs in metaignore metaIgnore = map(HintArgument, metaIgnore) # Turn strings into DependencyItems() for target, metaHint in ((load, metaLoad), (run, metaRun)): for key in metaHint: # add all feature checks if requested if key == "feature-checks": all_feature_checks[bool(metaHint == metaRun)] = True target.extend( self.depsItems_from_envchecks(-1, metaHint == metaLoad)) # turn an entry into a DependencyItem elif isinstance(key, types.StringTypes): sig = key.split('#', 1) className = sig[0] attrName = sig[1] if len(sig) > 1 else '' target.append( DependencyItem(className, attrName, self.id, "|hints|")) # Add JSDoc Hints for hint in node.hint.iterator(): for target, hintKey in ((load, 'require'), (run, 'use')): if hintKey in hint.hints: for hintArg in hint.hints[hintKey][None]: # add all feature checks if requested if hintArg == "feature-checks": all_feature_checks[bool(hintKey == 'use')] = True target.extend( self.depsItems_from_envchecks( hint.node.get('line', -1), metaHint == metaLoad)) # turn the HintArgument into a DependencyItem else: sig = hintArg.source.split('#', 1) className = sig[0] attrName = sig[1] if len(sig) > 1 else '' target.append( DependencyItem(className, attrName, self.id, hint.node.get('line', -1))) return load, run, metaIgnore, all_feature_checks
def getCombinedDeps(self, classesAll_, variants, config, stripSelfReferences=True, projectClassNames=True, genProxy=None, force=False, tree=None): # init lists global ClassesAll ClassesAll = classesAll_ # TODO: this is a quick hack, to not have to pass classesAll_ around as param loadFinal = [] runFinal = [] # add static dependencies if genProxy == None: static, cached = self.dependencies (variants, force, tree=tree) else: static, cached = genProxy.dependencies(self.id, self.path, variants) loadFinal.extend(static["load"]) runFinal.extend(static["run"]) # fix self-references if stripSelfReferences: loadFinal = [x for x in loadFinal if x.name != self.id] runFinal = [x for x in runFinal if x.name != self.id] # collapse multiple occurrences of the same class if projectClassNames: def dedup(deps): out = [] seen = set() for dep in deps: name = dep.name if name in seen: continue seen.add(name) out.append(dep) return out loadFinal = dedup(loadFinal) runFinal = dedup(runFinal) # add config dependencies crequire = config.get("require", {}) if self.id in crequire: loadFinal.extend(DependencyItem(x, '', "|config|") for x in crequire[self.id]) cuse = config.get("use", {}) if self.id in cuse: runFinal.extend(DependencyItem(x, '', "|config|") for x in cuse[self.id]) # result dict deps = { "load" : loadFinal, "run" : runFinal, "ignore" : static['ignore'], } return deps, cached
def _analyzeClassDepsNode_2(self, node, depsList, inLoadContext, inDefer=False): if node.type in ('file', 'function', 'catch'): top_scope = node.scope else: top_scope = scopes.find_enclosing( node) # get enclosing scope of node for scope in top_scope.scope_iterator( ): # walk through this and all nested scopes for global_name, scopeVar in scope.globals().items( ): # get the global symbols { sym_name: ScopeVar } for var_node in scopeVar.uses: # create a depsItem for all its uses if treeutil.hasAncestor( var_node, node ): # var_node is not disconnected through optimization depsItem = self.qualify_deps_item( var_node, scope.is_load_time, scope.is_defer) # as this also does filtering if depsItem: depsList.append(depsItem) # and qualify them #if depsItem.name == "qx.log.appender.Console": # import pydb; pydb.debugger() # Augment with feature dependencies introduces with qx.core.Environment.get("...") calls for env_operand in variantoptimizer.findVariantNodes(node): call_node = env_operand.parent.parent env_key = call_node.getChild("arguments").children[0].get( "value", "") className, classAttribute = self.getClassNameFromEnvKey(env_key) if className: #print className depsItem = DependencyItem(className, classAttribute, self.id, env_operand.get('line', -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps # .inLoadContext # get 'qx' node of 'qx.core.Environment....' qx_idnode = treeutil.findFirstChainChild(env_operand) scope = qx_idnode.scope inLoadContext = scope.is_load_time # get its scope's .is_load_time depsItem.isLoadDep = inLoadContext if inLoadContext: depsItem.needsRecursion = True depsList.append(depsItem) return
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 getAllEnvChecks(self, nodeline, inLoadContext): result = [] envmappings = self.context['envchecksmap'] for key in envmappings: clsname, clsattribute = self.getClassNameFromEnvKey(key) result.append(DependencyItem(clsname, clsattribute, self.id, nodeline, inLoadContext)) return result
def depsItems_from_envchecks(self, nodeline, inLoadContext): # Without qx.core.Environment._checksMap: # --------------------------------------- result = [] qcEnvClass = ClassesAll['qx.core.Environment'] for key in qcEnvClass.envKeyProviderIndex: clsname = qcEnvClass.classNameFromEnvKeyByIndex(key) result.append(DependencyItem(clsname, "", self.id, nodeline, inLoadContext)) return result
def depsItem_from_node(self, full_name, var_node): # attribute (e.g. method) attribute = '' # requestor (class id of the current tree) requestor = self.file_name # line (where requested) line = var_node.get("line",False) or -1 # is it a static load-time dependency? isLoadDep = self.is_static_loaddep(var_node) depsItem = DependencyItem(full_name, attribute, requestor, line, isLoadDep) # is the var a call operand var_root = treeutil.findChainRoot(var_node) depsItem.isCall = var_root.hasParentContext("call/operand") # depsItem.needsRecursion can only be set in transitive analysis return depsItem
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 depsItems_from_envchecks(self, nodeline, inLoadContext): result = [] qcEnvClass = ClassesAll['qx.core.Environment'] for key in qcEnvClass.checksMap: clsname, clsattribute = qcEnvClass.classNameFromEnvKey(key) result.append( DependencyItem(clsname, clsattribute, self.id, nodeline, inLoadContext)) return result
def dependencies_from_envcalls(self, node): depsList = [] qcEnvClass = ClassesAll['qx.core.Environment'] for env_operand in variantoptimizer.findVariantNodes(node): call_node = env_operand.parent.parent env_key = call_node.getChild("arguments").children[0].get("value", "") className, classAttribute = qcEnvClass.classNameFromEnvKey(env_key) if className and className in ClassesAll: #print className depsItem = DependencyItem(className, classAttribute, self.id, env_operand.get('line', -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps # .inLoadContext qx_idnode = treeutil.findFirstChainChild(env_operand) # 'qx' node of 'qx.core.Environment....' scope = qx_idnode.scope depsItem.isLoadDep = scope.is_load_time if depsItem.isLoadDep: depsItem.needsRecursion = True depsList.append(depsItem) return depsList
def depsItem_from_node(self, full_name, var_node): # attribute (e.g. method) attribute = '' # requestor (class id of the current tree) requestor = self.file_name # line (where requested) line = var_node.get("line", False) or -1 # is it a static load-time dependency? isLoadDep = self.is_static_loaddep(var_node) depsItem = DependencyItem(full_name, attribute, requestor, line, isLoadDep) # is the var a call operand var_root = treeutil.findChainRoot(var_node) depsItem.isCall = var_root.hasParentContext("call/operand") # depsItem.needsRecursion can only be set in transitive analysis return depsItem
def buildShallowDeps(tree=None): load, run = [], [] ignore = [DependencyItem(x, '', "|DefaultIgnoredNamesDynamic|") for x in self.defaultIgnoredNamesDynamic] console.debug("Getting shallow deps of: %s" % self.id) console.indent() # Read source tree data if not tree: if variantSet: # a filled variantSet map means that "variants" optimization is wanted tree = self.optimize(None, ["variants"], variantSet) else: tree = self.tree() # Get deps from compiler hints if not hasattr(tree, 'hint'): tree = jshints.create_hints_tree(tree) # this will be used by some of the subsequent method calls load_hints, run_hints, ignore_hints, all_feature_checks = self.dependencies_from_comphints(tree) # ignore_hints=[HintArgument] load.extend(load_hints) run.extend(run_hints) load_feature_checks = all_feature_checks[0] run_feature_checks = all_feature_checks[1] # Analyze tree treeDeps = [] # will be filled by _analyzeClassDepsNode self._analyzeClassDepsNode(tree, treeDeps, inLoadContext=True) # Filter lexical deps through ignore_hints load1, run1, ignore1 = self.filter_symbols_by_comphints(treeDeps, ignore_hints) # load and run are being filtered, ignore contains the actually filtered depsItems # integrate into existing lists load_hint_names = [str(x) for x in load_hints] for dep in load1: if str(dep) in load_hint_names and not load_feature_checks: console.warn("%s: @require(%s) is auto-detected" % (self.id, dep)) load.append(dep) run_hint_names = [str(x) for x in run_hints] for dep in run1: if str(dep) in run_hint_names and not run_feature_checks: console.warn("%s: @use(%s) is auto-detected" % (self.id, dep)) run.append(dep) ignore.extend(ignore1) console.outdent() # Build data structure deps = { "load" : load, "run" : run, "ignore" : ignore, } return deps
def dependencies_from_envcalls(self, node): depsList = [] if 'qx.core.Environment' not in ClassesAll: self.context['console'].warn("No qx.core.Environment available to extract feature keys from") return depsList qcEnvClass = ClassesAll['qx.core.Environment'] for env_operand in variantoptimizer.findVariantNodes(node): call_node = env_operand.parent.parent env_key = call_node.getChild("arguments").children[0].get("value", "") # Without qx.core.Environment._checksMap: # --------------------------------------- className = qcEnvClass.classNameFromEnvKeyByIndex(env_key) if className and className in ClassesAll: #print className depsItem = DependencyItem(className, "", self.id, env_operand.get('line', -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps depsItem.node = call_node # .inLoadContext qx_idnode = treeutil.findFirstChainChild(env_operand) # 'qx' node of 'qx.core.Environment....' scope = qx_idnode.scope depsItem.isLoadDep = scope.is_load_time if depsItem.isLoadDep: depsItem.needsRecursion = True depsList.append(depsItem) # With qx.core.Environment._checksMap: # ------------------------------------ # className, classAttribute = qcEnvClass.classNameFromEnvKey(env_key) # if className and className in ClassesAll: # #print className # depsItem = DependencyItem(className, classAttribute, self.id, env_operand.get('line', -1)) # depsItem.isCall = True # treat as if actual call, to collect recursive deps # depsItem.node = call_node # # .inLoadContext # qx_idnode = treeutil.findFirstChainChild(env_operand) # 'qx' node of 'qx.core.Environment....' # scope = qx_idnode.scope # depsItem.isLoadDep = scope.is_load_time # if depsItem.isLoadDep: # depsItem.needsRecursion = True # depsList.append(depsItem) return depsList
def dependencies_from_envcalls(self, node): depsList = [] if 'qx.core.Environment' not in ClassesAll: self.context['console'].warn( "No qx.core.Environment available to extract feature keys from" ) return depsList qcEnvClass = ClassesAll['qx.core.Environment'] for env_operand in variantoptimizer.findVariantNodes(node): call_node = env_operand.parent.parent env_key = call_node.getChild("arguments").children[0].get( "value", "") className, classAttribute = qcEnvClass.classNameFromEnvKey(env_key) if className and className in ClassesAll: #print className depsItem = DependencyItem(className, classAttribute, self.id, env_operand.get('line', -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps depsItem.node = call_node # .inLoadContext qx_idnode = treeutil.findFirstChainChild( env_operand) # 'qx' node of 'qx.core.Environment....' scope = qx_idnode.scope depsItem.isLoadDep = scope.is_load_time if depsItem.isLoadDep: depsItem.needsRecursion = True depsList.append(depsItem) return depsList
def _analyzeClassDepsNode_2(self, node, depsList, inLoadContext, inDefer=False): if node.type in ('file', 'function', 'catch'): top_scope = node.scope else: top_scope = scopes.find_enclosing(node) # get enclosing scope of node for scope in top_scope.scope_iterator(): # walk through this and all nested scopes for global_name, scopeVar in scope.globals().items(): # get the global symbols { sym_name: ScopeVar } for var_node in scopeVar.uses: # create a depsItem for all its uses if treeutil.hasAncestor(var_node, node): # var_node is not disconnected through optimization depsItem = self.qualify_deps_item(var_node, scope.is_load_time, scope.is_defer) # as this also does filtering if depsItem: depsList.append(depsItem) # and qualify them #if depsItem.name == "qx.log.appender.Console": # import pydb; pydb.debugger() # Augment with feature dependencies introduces with qx.core.Environment.get("...") calls for env_operand in variantoptimizer.findVariantNodes(node): call_node = env_operand.parent.parent env_key = call_node.getChild("arguments").children[0].get("value", "") className, classAttribute = self.getClassNameFromEnvKey(env_key) if className: #print className depsItem = DependencyItem(className, classAttribute, self.id, env_operand.get('line', -1)) depsItem.isCall = True # treat as if actual call, to collect recursive deps # .inLoadContext # get 'qx' node of 'qx.core.Environment....' qx_idnode = treeutil.findFirstChainChild(env_operand) scope = qx_idnode.scope inLoadContext = scope.is_load_time # get its scope's .is_load_time depsItem.isLoadDep = inLoadContext if inLoadContext: depsItem.needsRecursion = True depsList.append(depsItem) 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
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 buildShallowDeps(tree=None): load = [] run = [] ignore = [DependencyItem(x, '', "|DefaultIgnoredNamesDynamic|") for x in self.defaultIgnoredNamesDynamic] console.debug("Analyzing tree: %s" % self.id) console.indent() # Read meta data meta = self.getHints() metaLoad = meta.get("loadtimeDeps", []) metaRun = meta.get("runtimeDeps" , []) metaOptional = meta.get("optionalDeps", []) metaIgnore = meta.get("ignoreDeps" , []) metaIgnore.extend(metaOptional) # regexify globs in metaignore metaIgnore = map(MetaIgnore, metaIgnore) # Turn strings into DependencyItems() for target,metaHint in ((load,metaLoad), (run,metaRun), (ignore,metaIgnore)): for key in metaHint: # add all feature checks if requested if key == "feature-checks" and metaHint in (metaLoad, metaRun): target.extend(self.getAllEnvChecks(-1, metaHint==metaLoad)) # turn an entry into a DependencyItem elif isinstance(key, types.StringTypes): sig = key.split('#',1) className = sig[0] attrName = sig[1] if len(sig)>1 else '' target.append(DependencyItem(className, attrName, self.id, "|hints|")) # Read source tree data if not tree: if variantSet: # a filled variantSet map means that "variants" optimization is wanted tree = self.optimize(None, ["variants"], variantSet) else: tree = self.tree() # analyze tree treeDeps = [] # will be filled by _analyzeClassDepsNode self._analyzeClassDepsNode(tree, treeDeps, inLoadContext=True) # Process source tree data for dep in treeDeps: if dep.isLoadDep: if "auto-require" not in metaIgnore: item = dep.name if item in metaIgnore: pass elif item in metaLoad: console.warn("%s: #require(%s) is auto-detected" % (self.id, item)) else: # adding all items to list (the second might have needsRecursion) load.append(dep) else: # runDep if "auto-use" not in metaIgnore: item = dep.name if item in metaIgnore: pass #elif item in (x.name for x in load): # pass elif item in metaRun: console.warn("%s: #use(%s) is auto-detected" % (self.id, item)) else: # adding all items to list (to comply with the 'load' deps) run.append(dep) console.outdent() # Build data structure deps = { "load" : load, "run" : run, "ignore" : ignore, } return deps
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
# because all classes are included like an explicit include. for classId in excludeWithDeps: result.remove(classId) # Calculate dependencies else: #self._console.info(" ", feed=False) # Multiple loop over class list calculation processedEnvironment = False result = [] # reset any previous results for this iteration resultNames = [] # calculate class list recursively for item in includeWithDeps: depsItem = DependencyItem(item, '', '|config|') # calculate dependencies and add required classes classlistFromClassRecursive(depsItem, excludeWithDeps, variants, result, warn_deps, [], allowBlockLoaddeps) #classlistFromClassIterative(depsItem, excludeWithDeps, variants, result, warn_deps, [], allowBlockLoaddeps) self._console.dotclear() if self._console.getLevel() is "info": #self._console.nl() pass # extract names of depsItems result = [x.name for x in result] # warn about unknown references # add the list of name spaces of the selected classes
def getTransitiveDepsR(dependencyItem, variantString, totalDeps): # We don't add the in-param to the global result classId = dependencyItem.name methodId = dependencyItem.attribute function_pruned = False cacheId = "methoddeps-%r-%r-%r" % (classId, methodId, variantString) # The bad thing here is that 'variantString' contains environment setting # that normally have no influence on the dependencies (like # 'qx.Application'). So cached deps are rejected for no reason (ex. # building the demos of Demobrowser). But I cannot easily apply # variant-projection here, as it only proves that the current class is # independent of a specific environement key; but its recursive deps could # well be. Fix: Get the shallow deps of the current method from cache, and then get the # trans. deps of those items. They then could appy the same reasoning. if not force: # Check cache cachedDeps, _ = cache.read(cacheId) # no use to put this into a file, due to transitive dependencies to other files if cachedDeps != None: console.debug("using cached result") #print "\nusing cached result for", classId, methodId return cachedDeps # Need to calculate deps console.dot(1) # Check known class if classId not in ClassesAll: console.debug("Skipping unknown class of dependency: %s#%s (%s:%d)" % (classId, methodId, dependencyItem.requestor, dependencyItem.line)) return set() # Check other class elif classId != self.id: classObj = ClassesAll[classId] otherdeps = classObj.getTransitiveDeps(dependencyItem, variants, classMaps, totalDeps, force) return otherdeps # Check own hierarchy defClassId, attribNode = self.findClassForFeature(methodId, variants, classMaps) # lookup error if not defClassId or defClassId not in ClassesAll: console.debug("Skipping unknown definition of dependency: %s#%s (%s:%d)" % (classId, methodId, dependencyItem.requestor, dependencyItem.line)) return set() defDepsItem = DependencyItem(defClassId, methodId, classId) if dependencyItem.isCall: defDepsItem.isCall = True # if the dep is an inherited method being called, pursue the parent method as call localDeps = set() # inherited feature if defClassId != classId: self.resultAdd(defDepsItem, localDeps) defClass = ClassesAll[defClassId] otherdeps = defClass.getTransitiveDeps(defDepsItem, variants, classMaps, totalDeps, force) localDeps.update(otherdeps) return localDeps # Process own deps console.debug("%s#%s dependencies:" % (classId, methodId)) console.indent() if isinstance(attribNode, Node): if (attribNode.getChild("function", False) # is it a function(){..} value? and not dependencyItem.isCall # and the reference was no call ): function_pruned = True pass # don't lift those deps else: # Get the method's immediate deps # TODO: is this the right API?! depslist = [] if attribNode.type == 'value': attribNode = attribNode.children[0] self._analyzeClassDepsNode(attribNode, depslist, inLoadContext=False) console.debug( "shallow dependencies: %r" % (depslist,)) # This depends on attribNode belonging to current class my_ignores = self.getHints("ignoreDeps") + self.getHints("optionalDeps") my_ignores = map(HintArgument, my_ignores) for depsItem in depslist: if depsItem in totalDeps: continue if depsItem.name in my_ignores: continue if self.resultAdd(depsItem, localDeps): totalDeps = totalDeps.union(localDeps) # Recurse dependencies downstreamDeps = getTransitiveDepsR(depsItem, variants, totalDeps) localDeps.update(downstreamDeps) # Cache update # --- i cannot cache currently, if the deps of a function are pruned # when the function is passed as a ref, rather than called (s. above # around 'attribNode.getChild("function",...)') if not function_pruned: cache.write(cacheId, localDeps, memory=True, writeToFile=False) console.outdent() return localDeps
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 buildShallowDeps(tree=None): load = [] run = [] ignore = [ DependencyItem(x, '', "|DefaultIgnoredNamesDynamic|") for x in self.defaultIgnoredNamesDynamic ] console.debug("Analyzing tree: %s" % self.id) console.indent() # Read meta data meta = self.getHints() metaLoad = meta.get("loadtimeDeps", []) metaRun = meta.get("runtimeDeps", []) metaOptional = meta.get("optionalDeps", []) metaIgnore = meta.get("ignoreDeps", []) metaIgnore.extend(metaOptional) # regexify globs in metaignore metaIgnore = map(MetaIgnore, metaIgnore) # Turn strings into DependencyItems() for target, metaHint in ((load, metaLoad), (run, metaRun), (ignore, metaIgnore)): for key in metaHint: # add all feature checks if requested if key == "feature-checks" and metaHint in (metaLoad, metaRun): target.extend( self.getAllEnvChecks(-1, metaHint == metaLoad)) # turn an entry into a DependencyItem elif isinstance(key, types.StringTypes): sig = key.split('#', 1) className = sig[0] attrName = sig[1] if len(sig) > 1 else '' target.append( DependencyItem(className, attrName, self.id, "|hints|")) # Read source tree data if not tree: if variantSet: # a filled variantSet map means that "variants" optimization is wanted tree = self.optimize(None, ["variants"], variantSet) else: tree = self.tree() # do lint checking here, as we have a classList ("ClassesAll") to check globals against if True: # construct parse-level lint options opts = lint.defaultOptions() opts.library_classes = ClassesAll.keys() opts.class_namespaces = ClassList.namespaces_from_classnames( opts.library_classes) # some sensible settings (deviating from defaultOptions) opts.ignore_no_loop_block = True opts.ignore_reference_fields = True opts.ignore_undeclared_privates = True opts.ignore_unused_variables = True # override from config jobConf = Context.jobconf for option, value in jobConf.get("lint-check", {}).items(): setattr(opts, option.replace("-", "_"), value) lint.lint_check(tree, self.id, opts) # analyze tree treeDeps = [] # will be filled by _analyzeClassDepsNode self._analyzeClassDepsNode(tree, treeDeps, inLoadContext=True) # Process source tree data for dep in treeDeps: if dep.isLoadDep: if "auto-require" not in metaIgnore: item = dep.name if item in metaIgnore: pass elif item in metaLoad: console.warn("%s: #require(%s) is auto-detected" % (self.id, item)) else: # adding all items to list (the second might have needsRecursion) load.append(dep) else: # runDep if "auto-use" not in metaIgnore: item = dep.name if item in metaIgnore: pass #elif item in (x.name for x in load): # pass elif item in metaRun: console.warn("%s: #use(%s) is auto-detected" % (self.id, item)) else: # adding all items to list (to comply with the 'load' deps) run.append(dep) console.outdent() # Build data structure deps = { "load": load, "run": run, "ignore": ignore, } return deps
def _analyzeClassDepsNode(self, node, depsList, inLoadContext, inDefer=False): if node.type == "variable": 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 node.hasParentContext("instantiation/*/*/operand"): # 'new ...' position 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 = DependencyItem(className, classAttribute, self.id, node.get('line', -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) # 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/params"): 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 depsList.append(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, 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:] 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 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 getCombinedDeps_NOTUSED(self, fileId, variants, buildType="", stripSelfReferences=True, projectClassNames=True, genProxy=None): # init lists loadFinal = [] runFinal = [] # add static dependencies classObj = self._classesObj[fileId] if genProxy == None: static, cached = classObj.dependencies (variants) else: static, cached = genProxy.dependencies(classObj.id, classObj.path, variants) loadFinal.extend(static["load"]) runFinal.extend(static["run"]) # fix self-references if stripSelfReferences: loadFinal = [x for x in loadFinal if x.name != fileId] runFinal = [x for x in runFinal if x.name != fileId] # collapse multiple occurrences of the same class if projectClassNames: loads = loadFinal loadFinal = [] for dep in loads: if dep.name not in (x.name for x in loadFinal): loadFinal.append(dep) runs = runFinal runFinal = [] for dep in runs: if dep.name not in (x.name for x in runFinal): runFinal.append(dep) # TODO: this should be removed, as it cannot happen anymore (source is not variant-optimized) # fix dependency to classes that get removed with variant optimization #variantSelectClasses = ("qx.core.Environment",) #if len(variants) and (classObj.id not in variantSelectClasses): # depsUnOpt, _ = classObj.dependencies({}) # get unopt deps # # this might incur extra generation if unoptimized deps # # haven't computed before for this fileId # for depItem in depsUnOpt["load"]: # if depItem.name in variantSelectClasses and depItem.name not in [x.name for x in loadFinal]: # loadFinal.append(depItem) # for depItem in depsUnOpt["run"]: # if depItem.name in variantSelectClasses and depItem.name not in [x.name for x in runFinal]: # runFinal.append(depItem) # add config dependencies if fileId in self._require: loadFinal.extend(DependencyItem(x, '', "|config|") for x in self._require[fileId]) if fileId in self._use: runFinal.extend(DependencyItem(x, '', "|config|") for x in self._use[fileId]) # result dict deps = { "load" : loadFinal, "run" : runFinal, "ignore" : static['ignore'], } return deps, cached
def getTransitiveDepsR(dependencyItem, variantString, totalDeps): # We don't add the in-param to the global result classId = dependencyItem.name methodId = dependencyItem.attribute function_pruned = False cacheId = "methoddeps-%r-%r-%r" % (classId, methodId, variantString) # The bad thing here is that 'variantString' contains environment setting # that normally have no influence on the dependencies (like # 'qx.Application'). So cached deps are rejected for no reason (ex. # building the demos of Demobrowser). But I cannot easily apply # variant-projection here, as it only proves that the current class is # independent of a specific environement key; but its recursive deps could # well be. Fix: Get the shallow deps of the current method from cache, and then get the # trans. deps of those items. They then could appy the same reasoning. if not force: # Check cache cachedDeps, _ = cache.read(cacheId) # no use to put this into a file, due to transitive dependencies to other files if cachedDeps != None: console.debug("using cached result") #print "\nusing cached result for", classId, methodId return cachedDeps # Need to calculate deps console.dot("_") # Check known class if classId not in self._classesObj: console.debug("Skipping unknown class of dependency: %s#%s (%s:%d)" % (classId, methodId, dependencyItem.requestor, dependencyItem.line)) return set() # Check other class elif classId != self.id: classObj = self._classesObj[classId] otherdeps = classObj.getTransitiveDeps(dependencyItem, variants, classMaps, totalDeps, force) return otherdeps # Check own hierarchy defClassId, attribNode = self.findClassForFeature(methodId, variants, classMaps) # lookup error if not defClassId or defClassId not in self._classesObj: console.debug("Skipping unknown definition of dependency: %s#%s (%s:%d)" % (classId, methodId, dependencyItem.requestor, dependencyItem.line)) return set() defDepsItem = DependencyItem(defClassId, methodId, classId) if dependencyItem.isCall: defDepsItem.isCall = True # if the dep is an inherited method being called, pursue the parent method as call localDeps = set() # inherited feature if defClassId != classId: self.resultAdd(defDepsItem, localDeps) defClass = self._classesObj[defClassId] otherdeps = defClass.getTransitiveDeps(defDepsItem, variants, classMaps, totalDeps, force) localDeps.update(otherdeps) return localDeps # Process own deps console.debug("%s#%s dependencies:" % (classId, methodId)) console.indent() if isinstance(attribNode, Node): if (attribNode.getChild("function", False) # is it a function(){..} value? and not dependencyItem.isCall # and the reference was no call ): function_pruned = True pass # don't lift those deps else: # Get the method's immediate deps # TODO: is this the right API?! depslist = [] self._analyzeClassDepsNode(attribNode, depslist, inLoadContext=False) console.debug( "shallow dependencies: %r" % (depslist,)) # This depends on attribNode belonging to current class my_ignores = self.getHints("ignoreDeps") + self.getHints("optionalDeps") my_ignores = map(MetaIgnore, my_ignores) for depsItem in depslist: if depsItem in totalDeps: continue if depsItem.name in my_ignores: continue if self.resultAdd(depsItem, localDeps): # Recurse dependencies downstreamDeps = getTransitiveDepsR(depsItem, variants, totalDeps.union(localDeps)) localDeps.update(downstreamDeps) # Cache update # --- i cannot cache currently, if the deps of a function are pruned # when the function is passed as a ref, rather than called (s. above # around 'attribNode.getChild("function",...)') if not function_pruned: cache.write(cacheId, localDeps, memory=True, writeToFile=False) console.outdent() return localDeps
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