Class TypeSystem


  • public final class TypeSystem
    extends Object
    Root context object for type analysis. Type systems own a global SymbolResolver, which creates and caches external symbols. Methods of this class promote symbols to types, and compose types together. TypeOps and TypeConversion have some more operations on types.

    Some special types are presented as constant fields, eg OBJECT or NULL_TYPE. These are always comparable by reference. Note that the primitive wrapper types are not exposed as constants here, but can be accessed by using the box method on some primitive constant.

    The lifetime of a type system is the analysis: it is shared by all compilation units. TODO this is hacked together by comparing the ClassLoader, but this should be in the language instance

    Nodes have a reference to the type system they were created for: JavaNode.getTypeSystem().

    • Field Detail

      • NULL_TYPE

        public final JTypeMirror NULL_TYPE
        The bottom type of the reference type system. This is named the null type in the JLS and is not denotable in Java programs.

        This implementation uses this as the type of the 'null' literal.

        The null type has no symbol.

      • BOOLEAN

        public final JPrimitiveType BOOLEAN
        Primitive type boolean.
      • NO_TYPE

        public final JTypeMirror NO_TYPE
        A constant to represent the normal absence of a type. The primitive void.class represents that type, and this is the return type of a void method.

        Note that the type of the class literal void.class is Class<java.lang.Void>, not NO_TYPE.

        java.lang.Void is represented by BOXED_VOID. Note that BOXED_VOID.unbox() != NO_TYPE, NO_TYPE.box() != BOXED_VOID.

        NO_TYPE.isPrimitive() returns false, even though void.class.isPrimitive() returns true.

      • UNRESOLVED_METHOD

        public final JMethodSig UNRESOLVED_METHOD
        Sentinel value for an unresolved method. This type corresponds to a method declaration in the type UNKNOWN, returning UNKNOWN.
      • UNBOUNDED_WILD

        public final JWildcardType UNBOUNDED_WILD
        The unbounded wildcard, "?".
      • CLONEABLE

        public final JClassType CLONEABLE
        The interface Cloneable. This is included because it is a supertype of array types.
      • SERIALIZABLE

        public final JClassType SERIALIZABLE
        The interface Serializable. This is included because it is a supertype of array types.
      • BOXED_VOID

        public final JClassType BOXED_VOID
        This is the boxed type of Void.class, not to be confused with void.class, which in this framework is represented by NO_TYPE.

        Note that BOXED_VOID.unbox() != NO_TYPE, NO_TYPE.box() != BOXED_VOID.

    • Constructor Detail

      • TypeSystem

        public TypeSystem​(Function<TypeSystem,​? extends SymbolResolver> symResolverMaker)
        Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
        Parameters:
        symResolverMaker - A function that creates a new symbol resolver, which will be owned by the new type system. Because of cyclic dependencies, the new type system is leaked before its initialization completes, so fields of the type system are unusable at that time. The resolver is used to create some shared types: OBJECT, CLONEABLE, SERIALIZABLE, BOXED_VOID.
    • Method Detail

      • usingClassLoaderClasspath

        public static TypeSystem usingClassLoaderClasspath​(ClassLoader bootstrapResourceLoader)
        Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
        Parameters:
        bootstrapResourceLoader - Classloader used to resolve class files to populate the fields of the new type system
      • usingClasspath

        public static TypeSystem usingClasspath​(net.sourceforge.pmd.lang.java.symbols.internal.asm.Classpath bootstrapResourceLoader)
        Builds a new type system. Its public fields will be initialized with fresh types, unrelated to other types.
        Parameters:
        bootstrapResourceLoader - Classpath used to resolve class files to populate the fields of the new type system
      • bootstrapResolver

        public SymbolResolver bootstrapResolver()
        Returns the bootstrap symbol resolver. Concrete analysis passes may decorate this with different resolvers.
      • getClassSymbol

        public @Nullable JClassSymbol getClassSymbol​(@Nullable Class<?> clazz)
        Returns the class symbol for the given reflected class. This asks the classloader of this type system. Returns null if the parameter is null, or the class is not available in the analysis classpath.
        Parameters:
        clazz - Class
      • getClassSymbol

        public @Nullable JClassSymbol getClassSymbol​(String binaryName)
        Returns a symbol for the binary name. Returns null if the name is null or the symbol is not found on the classpath. The class must not be an array.
        Parameters:
        binaryName - Binary name
        Returns:
        A symbol, or null
        Throws:
        IllegalArgumentException - if the argument is not a binary name
      • getClassSymbolFromCanonicalName

        public @Nullable JClassSymbol getClassSymbolFromCanonicalName​(String canonicalName)
        Returns a symbol for the canonical name. Returns null if the name is null or the symbol is not found on the classpath. The class must not be an array.

        Canonical names separate nested classes with . (periods) instead of $ (dollars) as the JVM does. Users usually use canonical names, but lookup is much more costly.

        Parameters:
        canonicalName - Canonical name
        Returns:
        A symbol, or null
        Throws:
        IllegalArgumentException - if the argument is not a binary name
      • getModuleSymbol

        public @Nullable JModuleSymbol getModuleSymbol​(String moduleName)
        Since:
        7.5.0
      • typeOf

        public JTypeMirror typeOf​(@Nullable JTypeDeclSymbol symbol,
                                  boolean isErased)
        Returns a type mirror for the given symbol. If the symbol declares type parameters, then the resulting type is raw (differs from the behaviour of declaration(JClassSymbol)), meaning all its supertypes are erased.

        If the symbol is a type parameter, returns a JTypeVar.

        If the symbol is a JClassSymbol, then:

        • If it represents a primitive type, the corresponding JPrimitiveType is returned (one of INT, CHAR, etc.).
        • If it represents an array type, a new JArrayType is returned. The component type will be built with a recursive call.
        • If it represents a class or interface type, a JClassType is returned.
          • If the parameter isErased is true, and if the symbol declares type parameters, then it will be a raw type. This means, which means all its generic supertypes are erased.
          • Otherwise, the generic supertypes are preserved. In particular, if the symbol declares type parameters itself, then it will be a generic type declaration.
          If the symbol is a non-static member of another class, then the given type's enclosing type is created, applying the above rules about erasure recursively. A type is either completely erased, or completely parameterized.
        Parameters:
        symbol - Symbol for the type declaration
        isErased - Whether the type should be consider erased, if it represents a class or interface type. This does not erase type variables, or array types for that matter.
        Returns:
        A type, or null if the symbol is null
      • declaration

        public JTypeMirror declaration​(@Nullable JClassSymbol klass)
        Like typeOf(JTypeDeclSymbol, boolean), defaulting the erased parameter to false. If the symbol is not generic, the returned symbol is not actually a generic type declaration.
        Parameters:
        klass - Symbol
        Returns:
        An erased class type
      • parameterise

        public @NonNull JTypeMirror parameterise​(@NonNull JClassSymbol klass,
                                                 @NonNull List<? extends JTypeMirror> typeArgs)
        Produce a parameterized type with the given symbol and type arguments. The type argument list must match the declared formal type parameters in length. Non-generic symbols are accepted by this method, provided the argument list is empty. If the symbol is unresolved, any type argument list is accepted.

        This method is equivalent to rawType(klass).withTypeArguments(typeArgs), but that code would require a cast.

        Parameters:
        klass - A symbol
        typeArgs - List of type arguments
        Throws:
        IllegalArgumentException - see JClassType.withTypeArguments(List)
      • arrayType

        public JTypeMirror arrayType​(@NonNull JTypeMirror element,
                                     int numDimensions)
        Creates a new array type from an arbitrary element type.
        
         arrayType(T, 0)          = T
         arrayType(T, 1)          = T[]
         arrayType(T, 3)          = T[][][]
         arrayType(T[], 2)        = T[][][]
         
        Parameters:
        element - Element type
        numDimensions - Number of dimensions
        Returns:
        A new array type
        Throws:
        IllegalArgumentException - If numDimensions is negative
        IllegalArgumentException - If the element type is a JWildcardType, the null type, or void.
        NullPointerException - If the element type is null
      • wildcard

        public JWildcardType wildcard​(boolean isUpperBound,
                                      @NonNull JTypeMirror bound)
        Builds a wildcard type with a single bound.
        
        
         wildcard(true, T)      = ? extends T
         wildcard(false, T)     = ? super T
         wildcard(true, OBJECT) = ?
        
         
        Parameters:
        isUpperBound - If true, this is an "extends" wildcard, otherwise a "super"
        bound - Bound of the wildcard
        Returns:
        A wildcard
        Throws:
        NullPointerException - If the bound is null
        IllegalArgumentException - If the bound is a primitive type, or a wildcard type
        IllegalArgumentException - If the bound is OBJECT and this is a lower-bounded wildcard (? super Object)
      • lub

        public JTypeMirror lub​(Collection<? extends JTypeMirror> types)
        The least upper bound, or "lub", of a set of reference types is a shared supertype that is more specific than any other shared supertype (that is, no other shared supertype is a subtype of the least upper bound).
        Throws:
        IllegalArgumentException - If types is empty
        NullPointerException - If types is null
      • glb

        public JTypeMirror glb​(Collection<? extends JTypeMirror> types)
        Returns the greatest lower bound of the given set of types. This is defined in JLS§5.1.10 (Capture Conversion):
         glb(V1,...,Vm) = V1 & ... & Vm
         glb(V) = V
         

        This may alter the components, so that:

        • No intersection type is a component: ((A & B) & C) = (A & (B & C)) = (A & B & C)
        • No two types in the intersection are subtypes of one another (the intersection is minimal): A <: B => (A & B) = A, in particular, (A & A) = A
        • The intersection has a single component that is a class, array, or type variable. If all components are interfaces, then that component is OBJECT.
        • If several components are arrays, then their components are intersected: A[] & B[] = (A & B)[]

        If after these transformations, only a single component remains, then that is the returned type. Otherwise a JIntersectionType is created. Note that the intersection may be unsatisfiable (eg A[] & Runnable), but we don't attempt to minimize this to NULL_TYPE. Similarly, we do not attempt to minimize valid intersections. For instance List<?> & Collection<Number> can technically be minimized to List<Number>, but doing this requires inference of a fitting parameterization in general, which is complex, and not necessary in the internal tasks where intersection types are useful. In fact intersection types are precisely useful because they are simple to build.

        See also JLS§4.9 (Intersection types).

        Throws:
        IllegalArgumentException - If some component is not a class, interface, array, or type variable
        IllegalArgumentException - If there is more than one minimal class or array type
        IllegalArgumentException - If types is empty
        NullPointerException - If types is null
      • logStats

        public void logStats()
        Called at the end of the analysis to log statistics about the loaded types.