(self, *args, translate:Callable)
return TopType()
Get the least upper bound (V, maximum) of the arguments.
Get the least upper bound (V, maximum) of the arguments.
Get the least upper bound ( V maximum ) of the arguments .
def _join(self, *args, translate:Callable):
        """
        Get the least upper bound (V, maximum) of the arguments.
        """

        if len(args) == 0:
            return BottomType()
        if len(args) == 1:
            return translate(args[0])
        if len(args) > 2:
            split = len(args) // 2
            first = self._join(*args[:split], translate=translate)
            second = self._join(*args[split:], translate=translate)
            return self._join(first, second, translate=translate)

        t1 = translate(args[0])
        t2 = translate(args[1])

        # Trivial cases
        if t1 == t2:
            return t1
        if isinstance(t1, TopType):
            return t1
        elif isinstance(t2, TopType):
            return t2
        if isinstance(t1, BottomType):
            return t2
        elif isinstance(t2, BottomType):
            return t1
        if isinstance(t1, TypeVariableReference) and not isinstance(t2, TypeVariableReference):
            return t1
        elif isinstance(t2, TypeVariableReference) and not isinstance(t1, TypeVariableReference):
            return t2

        # consult the graph
        t1_cls = self._abstract(t1)
        t2_cls = self._abstract(t2)

        if t1_cls in self._base_lattice and t2_cls in self._base_lattice:
            queue = [t1_cls]
            while queue:
                n = queue[0]
                queue = queue[1:]

                if networkx.has_path(self._base_lattice, n, t2_cls):
                    return self._concretize(n, t1, t2, self._join, translate)
                # go up
                queue.extend(self._base_lattice.predecessors(n))

        # handling Struct
        if t1_cls is Struct and t2_cls is Struct:
            fields = { }
            for offset in sorted(set(itertools.chain(t1.fields.keys(), t2.fields.keys()))):
                if offset in t1.fields and offset in t2.fields:
                    v = self._join(t1.fields[offset], t2.fields[offset], translate=translate)
                elif offset in t1.fields:
                    v = t1.fields[offset]
                elif offset in t2.fields:
                    v = t2.fields[offset]
                else:
                    raise Exception("Impossible")
                fields[offset] = v
            return Struct(fields=fields)

        # single element and single-element struct
        if issubclass(t2_cls, Int) and t1_cls is Struct:
            # swap them
            t1, t1_cls, t2, t2_cls = t2, t2_cls, t1, t1_cls
        if issubclass(t1_cls, Int) and t2_cls is Struct and len(t2.fields) == 1 and 0 in t2.fields:
            # e.g., char & struct {0: char}
            return Struct(fields={0: self._join(t1, t2.fields[0], translate=translate)})

        ptr_class = self._pointer_class()

        # Struct and Pointers
        if t1_cls is ptr_class and t2_cls is Struct:
            # swap them
            t1, t1_cls, t2, t2_cls = t2, t2_cls, t1, t1_cls
        if t1_cls is Struct and len(t1.fields) == 1 and 0 in t1.fields:
            if t1.fields[0].size == 8 and t2_cls is Pointer64:
                # they are equivalent
                # e.g., struct{0: int64}  ptr64(int8)
                # return t2 since t2 is more specific
                return t2
            elif t1.fields[0].size == 4 and t2_cls is Pointer32:
                return t2

        # import ipdb; ipdb.set_trace()
        return TopType()
def _join ( self , * args , translate : Callable ) : if len ( args ) == 0 : return BottomType ( ) if len ( args ) == 1 : return translate ( args [ 0 ] ) if len ( args ) > 2 : split = len ( args ) // 2 first = self . _join ( * args [ : split ] , translate = translate ) second = self . _join ( * args [ split : ] , translate = translate ) return self . _join ( first , second , translate = translate ) t1 = translate ( args [ 0 ] ) t2 = translate ( args [ 1 ] ) # Trivial cases if t1 == t2 : return t1 if isinstance ( t1 , TopType ) : return t1 elif isinstance ( t2 , TopType ) : return t2 if isinstance ( t1 , BottomType ) : return t2 elif isinstance ( t2 , BottomType ) : return t1 if isinstance ( t1 , TypeVariableReference ) and not isinstance ( t2 , TypeVariableReference ) : return t1 elif isinstance ( t2 , TypeVariableReference ) and not isinstance ( t1 , TypeVariableReference ) : return t2 # consult the graph t1_cls = self . _abstract ( t1 ) t2_cls = self . _abstract ( t2 ) if t1_cls in self . _base_lattice and t2_cls in self . _base_lattice : queue = [ t1_cls ] while queue : n = queue [ 0 ] queue = queue [ 1 : ] if networkx . has_path ( self . _base_lattice , n , t2_cls ) : return self . _concretize ( n , t1 , t2 , self . _join , translate ) # go up queue . extend ( self . _base_lattice . predecessors ( n ) ) # handling Struct if t1_cls is Struct and t2_cls is Struct : fields = { } for offset in sorted ( set ( itertools . chain ( t1 . fields . keys ( ) , t2 . fields . keys ( ) ) ) ) : if offset in t1 . fields and offset in t2 . fields : v = self . _join ( t1 . fields [ offset ] , t2 . fields [ offset ] , translate = translate ) elif offset in t1 . fields : v = t1 . fields [ offset ] elif offset in t2 . fields : v = t2 . fields [ offset ] else : raise Exception ( "Impossible" ) fields [ offset ] = v return Struct ( fields = fields ) # single element and single-element struct if issubclass ( t2_cls , Int ) and t1_cls is Struct : # swap them t1 , t1_cls , t2 , t2_cls = t2 , t2_cls , t1 , t1_cls if issubclass ( t1_cls , Int ) and t2_cls is Struct and len ( t2 . fields ) == 1 and 0 in t2 . fields : # e.g., char & struct {0: char} return Struct ( fields = { 0 : self . _join ( t1 , t2 . fields [ 0 ] , translate = translate ) } ) ptr_class = self . _pointer_class ( ) # Struct and Pointers if t1_cls is ptr_class and t2_cls is Struct : # swap them t1 , t1_cls , t2 , t2_cls = t2 , t2_cls , t1 , t1_cls if t1_cls is Struct and len ( t1 . fields ) == 1 and 0 in t1 . fields : if t1 . fields [ 0 ] . size == 8 and t2_cls is Pointer64 : # they are equivalent # e.g., struct{0: int64}  ptr64(int8) # return t2 since t2 is more specific return t2 elif t1 . fields [ 0 ] . size == 4 and t2_cls is Pointer32 : return t2 # import ipdb; ipdb.set_trace() return TopType ( )