Esempio n. 1
0
    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
         )
Esempio n. 4
0
 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
         )
Esempio n. 5
0
    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
Esempio n. 7
0
    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
Esempio n. 10
0
    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
Esempio n. 11
0
        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
Esempio n. 12
0
    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
Esempio n. 13
0
    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