See More

package edu.rice.cs.drjava.model.repl; import edu.rice.cs.drjava.model.repl.types.*; //import edu.rice.cs.dynamicjava.symbol.type.*; //import koala.dynamicjava.tree.Expression; import edu.rice.cs.plt.tuple.Pair; import edu.rice.cs.plt.tuple.Option; import edu.rice.cs.plt.iter.IterUtil; import edu.rice.cs.plt.lambda.Thunk; import edu.rice.cs.plt.lambda.Lambda; import edu.rice.cs.plt.object.ObjectUtil; import java.io.Serializable; import static edu.rice.cs.plt.debug.DebugUtil.debug; /** A type system allows for variance in the typing rules of the system, while maintaining * a standard type checker. It separates the type checker from most of the details of type * implementations. For simplicity, it is also defined independently of most type-checker implementation * issues, such as syntax. To enforce these relationships, the type checker should minimize * references to specific subtypes of {@link Type}; and the type system should minimize references * to specific subtypes of {@link Expression}. */ public abstract class TypeSystem { public static final BooleanType BOOLEAN = new BooleanType(); public static final CharType CHAR = new CharType(); public static final ByteType BYTE = new ByteType(); public static final ShortType SHORT = new ShortType(); public static final IntType INT = new IntType(); public static final LongType LONG = new LongType(); public static final FloatType FLOAT = new FloatType(); public static final DoubleType DOUBLE = new DoubleType(); public static final NullType NULL = new NullType(); public static final VoidType VOID = new VoidType(); public static final TopType TOP = new TopType(); public static final BottomType BOTTOM = new BottomType(); public static final SimpleClassType OBJECT = new SimpleClassType(SymbolUtil.wrapClass(Object.class)); public static final SimpleClassType STRING = new SimpleClassType(SymbolUtil.wrapClass(String.class)); public static final SimpleClassType CLONEABLE = new SimpleClassType(SymbolUtil.wrapClass(Cloneable.class)); public static final SimpleClassType SERIALIZABLE = new SimpleClassType(SymbolUtil.wrapClass(Serializable.class)); public static final SimpleClassType THROWABLE = new SimpleClassType(SymbolUtil.wrapClass(Throwable.class)); public static final SimpleClassType EXCEPTION = new SimpleClassType(SymbolUtil.wrapClass(Exception.class)); public static final SimpleClassType RUNTIME_EXCEPTION = new SimpleClassType(SymbolUtil.wrapClass(RuntimeException.class)); public static final SimpleClassType BOOLEAN_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Boolean.class)); public static final SimpleClassType CHARACTER_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Character.class)); public static final SimpleClassType BYTE_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Byte.class)); public static final SimpleClassType SHORT_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Short.class)); public static final SimpleClassType INTEGER_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Integer.class)); public static final SimpleClassType LONG_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Long.class)); public static final SimpleClassType FLOAT_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Float.class)); public static final SimpleClassType DOUBLE_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Double.class)); public static final SimpleClassType VOID_CLASS = new SimpleClassType(SymbolUtil.wrapClass(Void.class)); protected static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; protected static final Iterable EMPTY_TYPE_ITERABLE = IterUtil.empty(); protected static final Iterable EMPTY_EXPRESSION_ITERABLE = IterUtil.empty(); protected static final Option NONE_TYPE_OPTION = Option.none(); public TypeWrapper wrap(Type t) { return (t == null) ? null : new TypeWrapper(t); } public Iterable wrap(Iterable extends Type> ts) { return (ts == null) ? null : IterUtil.map(ts, WRAP_TYPE); } private final Lambda WRAP_TYPE = new Lambda() { public TypeWrapper value(Type t) { return (t == null) ? null : new TypeWrapper(t); } }; public Option wrap(Option t) { if (t == null) return null; else return t.isSome() ? Option.some(new TypeWrapper(t.unwrap())) : Option.none(); } /** * A wrapper for types that provides an alternate {@code toString()} and {@code equals()} implementation: * {@code toString()} is defined in terms of {@link #userRepresentation}; {@code equals()} is defined * in terms of {@link #isEqual}. (Note that a corresponding {@code hashCode()} function is not implemented.) */ public class TypeWrapper { private Type _t; public TypeWrapper(Type t) { _t = t; } /** Produce a string using {@link #userRepresentation}. */ public String toString() { return typePrinter().print(_t); } /** Compare two TypeWrappers using {@link #isEqual}. */ public boolean equals(Object o) { if (this == o) { return true; } else if (!(o instanceof TypeWrapper)) { return false; } else { return isEqual(_t, ((TypeWrapper) o)._t); } } /** Throws an UnsupportedOperationException. */ public int hashCode() { throw new UnsupportedOperationException(); } } public abstract TypePrinter typePrinter(); /* Type Predicates */ /** Determine if {@code t} is a primitive. */ public abstract boolean isPrimitive(Type t); /** Determine if {@code t} is a reference. */ public abstract boolean isReference(Type t); /** Determine if {@code t} is an array. */ public abstract boolean isArray(Type t); /** Determine if the type is well-formed. */ public abstract boolean isWellFormed(Type t); /** * Determine if the type can be used in an enhanced for loop. {@code true} implies that an object of * type {@code t} has member {@code iterator()}, which returns a {@link java.util.Iterator}. */ public abstract boolean isIterable(Type t); /** * Determine if an object with type {@code t} is enumerable (and so can be used as the selector of a * {@code switch} statement) */ public abstract boolean isEnum(Type t); /** Determine if the type is available at runtime (via a {@link Class} object) */ public abstract boolean isReifiable(Type t); /** * Determine if there exist values whose most specific type is {@code t} (ignoring * constructor-accessibility issues). (Note that this implies that {@code t} is captured.) */ public abstract boolean isConcrete(Type t); /** Determine if {@code t} is valid in the {@code extends} clause of a class definition */ public abstract boolean isExtendable(Type t); /** Determine if {@code t} is valid in the {@code implements} clause of a class definition */ public abstract boolean isImplementable(Type t); /* Fundamental Type Relationships */ /** Determine if the given types may be treated as equal. This is recursive, transitive, and symmetric. */ public abstract boolean isEqual(Type t1, Type t2); /** * Determine if {@code subT} is a subtype of {@code superT}. This is a recursive * (in terms of {@link #isEqual}), transitive relation. */ public abstract boolean isSubtype(Type subT, Type superT); /** Whether two types are known to be disjoint. */ public abstract boolean isDisjoint(Type t1, Type t2); /** Determine if {@link #assign} would succeed given a non-constant expression of the given type */ public abstract boolean isAssignable(Type target, Type expT); /** Determine if {@link #assign} would succeed given a constant expression of the given type and value */ public abstract boolean isAssignable(Type target, Type expT, Object expValue); /** Determine if {@link #makePrimitive} would succeed given an expression of the given type */ public abstract boolean isPrimitiveConvertible(Type t); /** Determine if {@link #makeReference} would succeed given an expression of the given type */ public abstract boolean isReferenceConvertible(Type t); /** Compute a common supertype of the given list of types. */ public abstract Type join(Iterable extends Type> ts); /** Compute a common supertype of the given pair of types. */ public Type join(Type t1, Type t2) { return join(IterUtil.make(t1, t2)); } /** Compute a common subtype of the given list of types. */ public abstract Type meet(Iterable extends Type> ts); /** Compute a common supertype of the given pair of types. */ public Type meet(Type t1, Type t2) { return meet(IterUtil.make(t1, t2)); } /* Unary Operations on Types */ /** * Compute the capture of {@code t}. Capture eliminates wildcards in a {@link ParameterizedClassType} * and converts VarargArrayTypes to StandardArrayTypes. */ public abstract Type capture(Type t); /** * Compute the erased type of {@code t}. The result is guaranteed to be reifiable (according * to {@link #isReifiable}) and a supertype of {@code t}. */ public abstract Type erase(Type t); /** * Determine the class corresponding to the erasure of {@code t}. To prevent over-eager loading of * user-defined classes, computation of the result is delayed by wrapping it in a thunk. (A DJClass * return type would be incorrect, as there's no such thing (for example) as an array DJClass.) */ public abstract Thunk> erasedClass(Type t); /** * Determine the type of the class object associated with t (for example, (informally) * {@code classOf(Integer) = Class}). */ public abstract Type reflectionClassOf(Type t); /** * Determine the element type of the given array type. Assumes {@code t} is an array type (according to * {@link #isArray}). */ public abstract Type arrayElementType(Type t); /** Get the type of the object, if any, that dynamically encloses instances of {@code t}. */ public abstract Option dynamicallyEnclosingType(Type t); /* Class Type Operations */ /** Create a {@link SimpleClassType} or {@link RawClassType} corresponding to the given class. */ public abstract ClassType makeClassType(DJClass c); /** * Create a {@link SimpleClassType}, {@link RawClassType}, or {@link ParameterizedClassType} * corresponding to the given class with given type arguments. If {@code args} is nonempty, * the result must be a {@code ParameterizedClassType} (or an error must occur). * * @param c The class to be instantiated * @param args The type arguments for {@code c} * @throws InvalidTypeArgumentException If the arguments do not correspond to the formal parameters of * {@code c} (bounds are not checked, so the result may not be * well-formed). */ public abstract ClassType makeClassType(DJClass c, Iterable extends Type> args) throws InvalidTypeArgumentException; /* Conversions on Expressions */ /** * Convert the expression to a primitive. The result is guaranteed to have a primitive type as its * TYPE property (according to {@link #isPrimitive}). * * @param e A typed expression * @return A typed expression equivalent to {@code e} that has a primitive type * @throws UnsupportedConversionException If the expression cannot be converted to a primitive */ public abstract Expression makePrimitive(Expression e) throws UnsupportedConversionException; /** * Convert the expression to a reference. The result is guaranteed to have a reference type as its * TYPE property (according to {@link #isReference}). * * @param e A typed expression * @return A typed expression equivalent to {@code e} that has a reference type * @throws UnsupportedConversionException If the expression cannot be converted to a reference */ public abstract Expression makeReference(Expression e) throws UnsupportedConversionException; /** * Perform unary numeric promotion on an expression. * * @param e A typed expression with a primitive type * @return A typed expression equivalent to {@code e} with the promoted type * @throws UnsupportedConversionException If the expression cannot be used for numeric promotion */ public abstract Expression unaryPromote(Expression e) throws UnsupportedConversionException; /** * Perform binary numeric promotion on a pair of expressions. The resulting pair of expressions * are guaranteed to have the same type. * * @param e1 A typed expression with a primitive type * @param e2 A typed expression with a primitive type * @return Two typed expressions equivalent to {@code e1} and {@code e2} with the promoted type * @throws UnsupportedConversionException If either expression cannot be used for numeric promotion */ public abstract Pair binaryPromote(Expression e1, Expression e2) throws UnsupportedConversionException; /** * Perform a join (as defined for the ? : operator) on a pair of expressions. The resulting pair * of expressions are guaranteed to have the same type. That type may contain uncaptured wildcards. * * @param e1 A typed expression * @param e2 A typed expression * @return Two typed expressions equivalent to {@code e1} and {@code e2} with the joined type * @throws UnsupportedConversionException If the two types are incompatible. */ public abstract Pair mergeConditional(Expression e1, Expression e2) throws UnsupportedConversionException; /** * Perform a cast on the given expression. Any necessary conversions are performed. If necessary, * the {@code CHECKED_TYPE} and {@code CONVERTED_TYPE} properties are set on the result. * * @return An expression equivalent to {@code e}, wrapped in any necessary conversions * @throws UnsupportedConversionException If the cast is to an incompatible type. */ public abstract Expression cast(Type target, Expression e) throws UnsupportedConversionException; /** * Prepare the given expression for assignment, wrapping it in any necessary conversions. * * @return An expression equivalent to {@code e}, wrapped in any necessary conversions * @throws UnsupportedConversionException If assignment to the given type is incorrect. */ public abstract Expression assign(Type target, Expression e) throws UnsupportedConversionException; /* Member lookup operations */ /** * Lookup the constructor corresponding the the given invocation. * @param t The type of the object to be constructed. * @param typeArgs The type arguments for the constructor's type parameters. * @param args A list of typed expressions corresponding to the constructor's parameters. * @param expected The type expected in the invocation's calling context, if any. * @return A {@link ConstructorInvocation} object representing the matched constructor. * @throws InvalidTypeArgumentException If the type arguments are invalid (for example, a primitive type). * @throws UnmatchedLookupException If 0 or more than 1 constructor matches the given arguments and type * arguments. */ // Must produce a reasonable value when looking up a constructor in an interface (for anonymous classes) public abstract ConstructorInvocation lookupConstructor(Type t, Iterable extends Type> typeArgs, Iterable extends Expression> args, Option expected, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; public abstract boolean containsMethod(Type t, String name, Access.Module accessModule); public abstract boolean containsStaticMethod(Type t, String name, Access.Module accessModule); /** * Lookup the method corresponding the the given invocation. * @param object A typed expression representing the object whose method is to be invoked. * @param name The name of the method. * @param typeArgs The type arguments for the method's type parameters. * @param args A list of typed expressions corresponding to the method's parameters. * @param expected The type expected in the invocation's calling context, if any. * @return An {@link ObjectMethodInvocation} object representing the matched method. * @throws InvalidTypeArgumentException If the type arguments are invalid (for example, a primitive type). * @throws UnmatchedLookupException If 0 or more than 1 method matches the given name, arguments, and type * arguments. */ public abstract ObjectMethodInvocation lookupMethod(Expression object, String name, Iterable extends Type> typeArgs, Iterable extends Expression> args, Option expected, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; /** * Lookup the static method corresponding the the given invocation. * @param t The type in which to search for a static method. * @param name The name of the method. * @param typeArgs The type arguments for the method's type parameters. * @param args A list of typed expressions corresponding to the method's parameters. * @param expected The type expected in the invocation's calling context, if any. * @return A {@link StaticMethodInvocation} object representing the matched method. * @throws InvalidTypeArgumentException If the type arguments are invalid (for example, a primitive type). * @throws UnmatchedLookupException If 0 or more than 1 method matches the given name, arguments, and type * arguments. */ public abstract StaticMethodInvocation lookupStaticMethod(Type t, String name, Iterable extends Type> typeArgs, Iterable extends Expression> args, Option expected, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; public abstract boolean containsField(Type t, String name, Access.Module accessModule); public abstract boolean containsStaticField(Type t, String name, Access.Module accessModule); /** * Lookup the field with the given name in the given object. * @param object A typed expression representing the object whose field is to be accessed. * @param name The name of the field. * @return An {@link ObjectFieldReference} object representing the matched field. * @throws UnmatchedLookupException If 0 or more than 1 field matches the given name. */ public abstract ObjectFieldReference lookupField(Expression object, String name, Access.Module accessModule) throws UnmatchedLookupException; /** * Lookup the static field with the given name. * @param t The type in which to search for a static field. * @param name The name of the field. * @return A {@link StaticFieldReference} object representing the matched field. * @throws UnmatchedLookupException If 0 or more than 1 field matches the given name. */ public abstract StaticFieldReference lookupStaticField(Type t, String name, Access.Module accessModule) throws UnmatchedLookupException; public abstract boolean containsClass(Type t, String name, Access.Module accessModule); public abstract boolean containsStaticClass(Type t, String name, Access.Module accessModule); /** * Lookup the class with the given name in the given object. * @param object A typed expression representing the object whose class is to be accessed. * @param name The name of the class. * @param typeArgs The type arguments for the class * @return A type representing the named class. * @throws InvalidTypeArgumentException If the type arguments are invalid or do not correspond to the * class's formal parameters (bounds are not checked, so the result * may not be well-formed). * @throws UnmatchedLookupException If 0 or more than 1 class matches the given name. */ public abstract ClassType lookupClass(Expression object, String name, Iterable extends Type> typeArgs, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; /** * Lookup the class with the given name in the given type. * @param t The type in which to search for a static class. * @param name The name of the class. * @param typeArgs The type arguments for the class * @return A type representing the named class. * @throws InvalidTypeArgumentException If the type arguments are invalid or do not correspond to the * class's formal parameters (bounds are not checked, so the * result may not be well-formed). * @throws UnmatchedLookupException If 0 or more than 1 class matches the given name. */ public abstract ClassType lookupClass(Type t, String name, Iterable extends Type> typeArgs, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; /** * Lookup the static class with the given name. * @param t The type in which to search for a static class. * @param name The name of the class. * @param typeArgs The type arguments for the class * @return A type representing the named class. * @throws InvalidTypeArgumentException If the type arguments are invalid or do not correspond to the * class's formal parameters (bounds are not checked, so the result * may not be well-formed). * @throws UnmatchedLookupException If 0 or more than 1 class matches the given name. */ public abstract ClassType lookupStaticClass(Type t, String name, Iterable extends Type> typeArgs, Access.Module accessModule) throws InvalidTypeArgumentException, UnmatchedLookupException; public static interface TypePrinter { /** Produce a string representing the type */ public String print(Type t); /** Produce a string representing the list of types */ public String print(Iterable extends Type> ts); /** Produce a string representing the signature of the function */ public String print(Function f); } /** Abstraction of the result of a method or constructor lookup */ public static abstract class FunctionInvocation { private final Iterable extends Type> _typeArgs; private final Iterable extends Expression> _args; private final Iterable extends Type> _thrown; protected FunctionInvocation(Iterable extends Type> typeArgs, Iterable extends Expression> args, Iterable extends Type> thrown) { _typeArgs = typeArgs; _args = args; _thrown = thrown; } /** @return The (possible inferred) type arguments used in the invocation */ public Iterable extends Type> typeArgs() { return _typeArgs; } /** * @return The arguments, wrapped in any necessary promotions so that each expression has the same * type as its corresponding formal parameter. */ public Iterable extends Expression> args() { return _args; } /** @return The declared thrown types of the invocation */ public Iterable extends Type> thrown() { return _thrown; } } /** The result of a constructor lookup */ public static class ConstructorInvocation extends FunctionInvocation { private final DJConstructor _constructor; public ConstructorInvocation(DJConstructor constructor, Iterable extends Type> typeArgs, Iterable extends Expression> args, Iterable extends Type> thrown) { super(typeArgs, args, thrown); _constructor = constructor; } /** @return The reflection object corresponding to the invoked constructor */ public DJConstructor constructor() { return _constructor; } } /** Abstraction of the result of a static or non-static method lookup */ public static abstract class MethodInvocation extends FunctionInvocation { private final DJMethod _method; private final Type _returnType; protected MethodInvocation(DJMethod method, Type returnType, Iterable extends Type> typeArgs, Iterable extends Expression> args, Iterable extends Type> thrown) { super(typeArgs, args, thrown); _method = method; _returnType = returnType; } /** @return The reflection object corresponding to the invoked method */ public DJMethod method() { return _method; } /** @return The return type of the invocation (before capture) */ public Type returnType() { return _returnType; } } /** The result of a non-static method lookup */ public static class ObjectMethodInvocation extends MethodInvocation { private final Expression _object; public ObjectMethodInvocation(DJMethod method, Type returnType, Expression object, Iterable extends Type> typeArgs, Iterable extends Expression> args, Iterable extends Type> thrown) { super(method, returnType, typeArgs, args, thrown); _object = object; } /** * @return The object whose method is invoked, wrapped in any necessary promotions so that * the type is the type whose declared member is the matched method. */ public Expression object() { return _object; } } /** The result of a static method lookup */ public static class StaticMethodInvocation extends MethodInvocation { public StaticMethodInvocation(DJMethod method, Type returnType, Iterable extends Type> typeArgs, Iterable extends Expression> args, Iterable extends Type> thrown) { super(method, returnType, typeArgs, args, thrown); } } /** Abstraction of the result of a static or non-static field lookup */ public static abstract class FieldReference { private final DJField _field; private final Type _type; protected FieldReference(DJField field, Type type) { _field = field; _type = type; } /** @return The reflection object corresponding to the accessed field */ public DJField field() { return _field; } /** @return The return type of the access (before capture) */ public Type type() { return _type; } public boolean equals(Object that) { if (this == that) { return true; } else if (!(that instanceof FieldReference)) { return false; } else { FieldReference r = (FieldReference) that; return _field.equals(r._field) && _type.equals(r._type); } } public int hashCode() { return ObjectUtil.hash(FieldReference.class, _field, _type); } } /** The result of a non-static field lookup */ public static class ObjectFieldReference extends FieldReference { private final Expression _object; public ObjectFieldReference(DJField field, Type type, Expression object) { super(field, type); _object = object; } /** * @return The object whose field is accessed, wrapped in any necessary promotions so that * the type is the type whose declared member is the matched field. */ public Expression object() { return _object; } } /** The result of a static field lookup */ public static class StaticFieldReference extends FieldReference { public StaticFieldReference(DJField field, Type type) { super(field, type); } } public static class TypeSystemException extends Exception { } public static class InvalidTypeArgumentException extends TypeSystemException { } public static class UnsupportedConversionException extends TypeSystemException { } public static class UnmatchedLookupException extends TypeSystemException { private final int _matches; public UnmatchedLookupException(int matches) { _matches = matches; } public int matches() { return _matches; } } /** A function lookup that failed because none of the given candidates matched the provided arguments. */ public static class UnmatchedFunctionLookupException extends UnmatchedLookupException { private final Iterable extends Function> _candidates; public UnmatchedFunctionLookupException(Iterable extends Function> candidates) { super(0); _candidates = candidates; } public Iterable extends Function> candidates() { return _candidates; } } /** A function lookup that failed because all of the given candidates equally matched the provided arguments. */ public static class AmbiguousFunctionLookupException extends UnmatchedLookupException { private final Iterable extends Function> _candidates; public AmbiguousFunctionLookupException(Iterable extends Function> candidates) { super(IterUtil.sizeOf(candidates)); _candidates = candidates; } public Iterable extends Function> candidates() { return _candidates; } } }