/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * (C) Copyright IBM Corporation 2006-2010. */ package x10.rtt; import java.io.IOException; import java.lang.reflect.Array; import java.util.concurrent.ConcurrentHashMap; import x10.core.Any; import x10.serialization.SerializationConstants; import x10.serialization.X10JavaDeserializer; import x10.serialization.X10JavaSerializable; import x10.serialization.X10JavaSerializer; public class RuntimeType<T> implements Type<T>, X10JavaSerializable { private static final long serialVersionUID = 1L; public enum Variance {INVARIANT, COVARIANT, CONTRAVARIANT} private static final Variance[][] invariants = { null, new Variance[] {Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, new Variance[] {Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT,Variance.INVARIANT}, }; public static Variance[] INVARIANTS(int length) { assert length >= 1; if (length < invariants.length) { return invariants[length]; } Variance[] variances = new Variance[length]; java.util.Arrays.fill(variances, Variance.INVARIANT); return variances; } public Class<?> javaClass; private Variance[] variances; public Type<?>[] parents; // Just for allocation public RuntimeType() { } public RuntimeType(Class<?> javaClass) { this(javaClass, null, null); } protected RuntimeType(Class<?> javaClass, Variance[] variances) { this(javaClass, variances, null); } protected RuntimeType(Class<?> javaClass, Type<?>[] parents) { this(javaClass, null, parents); } protected RuntimeType(Class<?> javaClass, Variance[] variances, Type<?>[] parents) { this.javaClass = javaClass; this.variances = variances; this.parents = parents; } private static final boolean useCache = true; private static final ConcurrentHashMap<Class<?>, RuntimeType<?>> typeCache = new ConcurrentHashMap<Class<?>, RuntimeType<?>>(); public static <T> RuntimeType/*<T>*/ make(Class<?> javaClass) { if (useCache) { RuntimeType<?> type = typeCache.get(javaClass); if (type == null) { RuntimeType<?> type0 = Types.getRTTForKnownType(javaClass); if (type0 == null) { type0 = new RuntimeType<T>(javaClass, null, null); } type = typeCache.putIfAbsent(javaClass, type0); if (type == null) type = type0; } return (RuntimeType<T>) type; } else { RuntimeType<?> type = Types.getRTTForKnownType(javaClass); if (type == null) { type = new RuntimeType<T>(javaClass, null, null); } return (RuntimeType<T>) type; } } public static <T> RuntimeType/*<T>*/ make(Class<?> javaClass, Variance[] variances) { if (useCache) { RuntimeType<?> type = typeCache.get(javaClass); if (type == null) { RuntimeType<?> type0 = new RuntimeType<T>(javaClass, variances, null); type = typeCache.putIfAbsent(javaClass, type0); if (type == null) type = type0; } return (RuntimeType<T>) type; } else { return new RuntimeType<T>(javaClass, variances, null); } } public static <T> RuntimeType/*<T>*/ make(Class<?> javaClass, Type<?>[] parents) { if (useCache) { RuntimeType<?> type = typeCache.get(javaClass); if (type == null) { RuntimeType<?> type0 = new RuntimeType<T>(javaClass, null, parents); type = typeCache.putIfAbsent(javaClass, type0); if (type == null) type = type0; } return (RuntimeType<T>) type; } else { return new RuntimeType<T>(javaClass, null, parents); } } public static <T> RuntimeType/*<T>*/ make(Class<?> javaClass, Variance[] variances, Type<?>[] parents) { if (useCache) { RuntimeType<?> type = typeCache.get(javaClass); if (type == null) { RuntimeType<?> type0 = new RuntimeType<T>(javaClass, variances, parents); type = typeCache.putIfAbsent(javaClass, type0); if (type == null) type = type0; } return (RuntimeType<T>) type; } else { return new RuntimeType<T>(javaClass, variances, parents); } } public Class<?> getJavaClass() { return javaClass; } // not used // public Variance[] getVariances() { // return variances; // } private final Variance getVariance(int i) { return variances[i]; } private final int numParams() { return variances != null ? variances.length : 0; } public Type<?>[] getParents() { return parents; } @Override public String toString() { return typeName(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof RuntimeType<?>) { RuntimeType<?> rt = (RuntimeType<?>) o; if (!javaClass.equals(rt.javaClass)) { return false; } // N.B. for given javaClass, we assume variances and parents are unique. // Therefore we don't need to compare them. return true; } return false; } @Override public int hashCode() { return javaClass.hashCode(); } public boolean isAssignableTo(Type<?> superType) { if (this == superType) return true; if (superType == Types.ANY) return true; if (superType instanceof RuntimeType<?>) { RuntimeType<?> rt = (RuntimeType<?>) superType; if (rt.javaClass.isAssignableFrom(javaClass)) { return true; } } if (superType instanceof ParameterizedType) { ParameterizedType<?> pt = (ParameterizedType<?>) superType; if (pt.getRawType().isAssignableFrom(pt.getActualTypeArguments(), this, null)) { return true; } } return false; } public boolean hasZero() { return true; } public boolean isref() { return true; } public boolean isInstance(Object o) { if (o == null) {return false;} if (o.getClass() == javaClass) { return true; } return javaClass.isInstance(o); } private static final boolean subtypeTestForParam(Variance variance, Type<?> superParam, Type<?> subParam) { switch (variance) { case INVARIANT: return superParam.equals(subParam); case COVARIANT: return subParam.isAssignableTo(superParam); case CONTRAVARIANT: return superParam.isAssignableTo(subParam); } // assert false; // should never happen return true; } // o instanceof this and thisParams public final boolean isInstance(Object o, Type<?>... thisParams) { if (o == null) {return false;} Class<?> target = o.getClass(); if (target == javaClass || checkAnonymous(target)) { for (int i = 0, s = thisParams.length; i < s; i++) { Variance variance; Type<?> subParam; Type<?> thisParam; variance = getVariance(i); subParam = Types.getParam(o, i); thisParam = thisParams[i]; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} } return true; } else if (javaClass.isInstance(o)) { // i.e. type of o != This return checkParents(o, thisParams); } // not needed for Java primitives. not sure for String /* else if (o instanceof String || o instanceof Number) { // @NativeRep'ed type return checkParents(o, thisParams); } */ else { return false; } } private boolean checkAnonymous(Class<?> target) { if (!target.isAnonymousClass()) { return false; } if (target.getSuperclass() != java.lang.Object.class && target.getSuperclass() == javaClass) { return true; } if (target.getInterfaces().length == 1 && target.getInterfaces()[0] == javaClass) { return true; } return false; } private final boolean checkParents(Object o, Type<?>... params) { if (o instanceof Any) { Any any = (Any) o; RuntimeType<?> rtt = any.$getRTT(); // o.$RTT if (rtt == null) { return true; } return instantiateCheck(params, rtt, any); } if (o instanceof java.lang.String) { RuntimeType<?> rtt = Types.STRING; return instantiateCheck(params, rtt, o); } return false; } // TODO consolidate // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Object o), // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Any any) and // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Type<?>[] paramsRTT) // e.g. C[T1,T2]:Super[Int, T1] -> C[Int,Double]:Super[Int,Int] private final boolean instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Object o) { if (rtt.parents != null) { for (Type<?> t : rtt.parents) { if (javaClass.isAssignableFrom(t.getJavaClass())) { if (t instanceof ParameterizedType<?>) { ParameterizedType<?> pt = (ParameterizedType<?>) t; Type<?>[] origTypeArgumentsT = pt.getActualTypeArguments(); Type<?>[] resolvedTypeArgumentsT = new Type<?>[origTypeArgumentsT.length]; for (int i = 0; i < origTypeArgumentsT.length; i++) { if (origTypeArgumentsT[i] != null && origTypeArgumentsT[i] instanceof UnresolvedType) { int index = ((UnresolvedType) origTypeArgumentsT[i]).getIndex(); assert(index == -1); resolvedTypeArgumentsT[i] = rtt; } else { resolvedTypeArgumentsT[i] = origTypeArgumentsT[i]; } } if (isAssignableFrom(params, pt.getRawType(), resolvedTypeArgumentsT)) { return true; } } } } } return false; } // TODO consolidate // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Object o), // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Any any) and // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Type<?>[] paramsRTT) // e.g. C[T1,T2]:Super[Int, T1] -> C[Int,Double]:Super[Int,Int] private final boolean instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Any any) { if (rtt.parents != null) { for (Type<?> t : rtt.parents) { if (javaClass.isAssignableFrom(t.getJavaClass())) { if (t instanceof ParameterizedType<?>) { ParameterizedType<?> pt = (ParameterizedType<?>) t; Type<?>[] origTypeArgumentsT = pt.getActualTypeArguments(); Type<?>[] resolvedTypeArgumentsT = new Type<?>[origTypeArgumentsT.length]; for (int i = 0; i < origTypeArgumentsT.length; i++) { if (origTypeArgumentsT[i] != null && origTypeArgumentsT[i] instanceof UnresolvedType) { int index = ((UnresolvedType) origTypeArgumentsT[i]).getIndex(); resolvedTypeArgumentsT[i] = index == -1 ? rtt : any.$getParam(index); } else { resolvedTypeArgumentsT[i] = origTypeArgumentsT[i]; } } if (isAssignableFrom(params, pt.getRawType(), resolvedTypeArgumentsT)) { return true; } } if (t instanceof RuntimeType && equals(t)) { return true; } } } } return false; } // TODO consolidate // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Object o), // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Any any) and // instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Type<?>[] paramsRTT) // e.g. C[T1,T2]:Super[Int, T1] -> C[Int,Double]:Super[Int,Int] private final boolean instantiateCheck(Type<?>[] params, RuntimeType<?> rtt, Type<?>[] paramsRTT) { if (rtt.parents != null) { for (Type<?> t : rtt.parents) { if (javaClass.isAssignableFrom(t.getJavaClass())) { if (t instanceof ParameterizedType<?>) { ParameterizedType<?> pt = (ParameterizedType<?>) t; Type<?>[] origTypeArgumentsT = pt.getActualTypeArguments(); Type<?>[] resolvedTypeArgumentsT = new Type<?>[origTypeArgumentsT.length]; for (int i = 0; i < origTypeArgumentsT.length; i++) { if (origTypeArgumentsT[i] != null && origTypeArgumentsT[i] instanceof UnresolvedType) { int index = ((UnresolvedType) origTypeArgumentsT[i]).getIndex(); resolvedTypeArgumentsT[i] = index == -1 ? rtt : paramsRTT[index]; } else { resolvedTypeArgumentsT[i] = origTypeArgumentsT[i]; } } if (isAssignableFrom(params, pt.getRawType(), resolvedTypeArgumentsT)) { return true; } } if (t instanceof RuntimeType && equals(t)) { return true; } } } } return false; } // check "subType and subParams" <: "this and thisParams" final boolean isAssignableFrom(Type<?>[] thisParams, RuntimeType<?> subType, Type<?>[] subParams) { if (javaClass == subType.getJavaClass()) { if (thisParams != null) { for (int i = 0, s = thisParams.length; i < s; i ++) { Variance variance; Type<?> subParam; Type<?> thisParam; variance = getVariance(i); subParam = subParams[i]; thisParam = thisParams[i]; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} } } return true; } else if (javaClass.isAssignableFrom(subType.getJavaClass())) { return instantiateCheck(thisParams, subType, subParams); } else { return false; } } public Object makeArray(int dim0) { return Array.newInstance(javaClass, dim0); } public Object makeArray(int dim0, int dim1) { return Array.newInstance(javaClass, new int[] { dim0, dim1 }); } public Object makeArray(int dim0, int dim1, int dim2) { return Array.newInstance(javaClass, new int[] { dim0, dim1, dim2 }); } public Object makeArray(int dim0, int dim1, int dim2, int dim3) { return Array.newInstance(javaClass, new int[] { dim0, dim1, dim2, dim3 }); } public Object makeArray(int... dims) { return Array.newInstance(javaClass, dims); } public T getArray(Object array, int i) { return ((T[])array)[i]; } public void setArray(Object array, int i, T v) { ((T[])array)[i] = v; } public int arrayLength(Object array) { return ((T[])array).length; } private static final String X10_INTEROP_JAVA_ARRAY = "x10.interop.Java.array"; private static String typeName(Class<?> javaClass) { RuntimeType<?> rtt = null; if (javaClass.isArray()) { return X10_INTEROP_JAVA_ARRAY + "[" + typeName(javaClass.getComponentType()) + "]"; } else if ((rtt = Types.getRTTForKnownType(javaClass)) != null) { return rtt.typeName(); } else { return javaClass.getName(); } } public String typeName() { return typeName(javaClass); } protected final String typeNameForFun(Object o) { int numParams = numParams(); StringBuilder sb = new StringBuilder(); sb.append("("); int i; for (i = 0; i < numParams - 1; i++) { if (i != 0) sb.append(","); sb.append(((Any) o).$getParam(i).typeName()); } sb.append(")=>"); sb.append(((Any) o).$getParam(i).typeName()); return sb.toString(); } protected final String typeNameForVoidFun(Object o) { int numParams = numParams(); StringBuilder sb = new StringBuilder(); sb.append("("); if (numParams > 0) { for (int i = 0; i < numParams; i++) { if (i != 0) sb.append(","); sb.append(((Any) o).$getParam(i).typeName()); } } sb.append(")=>void"); return sb.toString(); } protected final String typeNameForOthers(Object o) { int numParams = numParams(); StringBuilder sb = new StringBuilder(); sb.append(typeName()); if (numParams > 0) { if (o instanceof Any) { sb.append("["); for (int i = 0; i < numParams; i++) { if (i != 0) sb.append(","); sb.append(Types.getParam(o, i).typeName()); } sb.append("]"); } } return sb.toString(); } // should be overridden by RTT of all function types public String typeName(Object o) { return typeNameForOthers(o); } // for shortcut public boolean isInstance(Object o, Type<?> thisParam0) { if (o == null) {return false;} Class<?> target = o.getClass(); if (target == javaClass || checkAnonymous(target)) { Variance variance; Type<?> subParam; Type<?> thisParam; variance = getVariance(0); subParam = Types.getParam(o, 0); thisParam = thisParam0; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} return true; } else if (javaClass.isInstance(o)) { return checkParents(o, thisParam0); } // not needed for Java primitives. not sure for String /* else if (o instanceof String || o instanceof Number) { // @NativeRep'ed type return checkParents(o, thisParam0); } */ else { return false; } } // for shortcut public final boolean isInstance(Object o, Type<?> thisParam0, Type<?> thisParam1) { if (o == null) {return false;} Class<?> target = o.getClass(); if (target == javaClass || checkAnonymous(target)) { Variance variance; Type<?> subParam; Type<?> thisParam; variance = getVariance(0); subParam = Types.getParam(o, 0); thisParam = thisParam0; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} variance = getVariance(1); subParam = Types.getParam(o, 1); thisParam = thisParam1; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} return true; } else if (javaClass.isInstance(o)) { return checkParents(o, thisParam0, thisParam1); } // not needed for Java primitives. not sure for String /* else if (o instanceof String || o instanceof Number) { // @NativeRep'ed type return checkParents(o, thisParam0, thisParam1); } */ else { return false; } } // for shortcut public final boolean isInstance(Object o, Type<?> thisParam0, Type<?> thisParam1, Type<?> thisParam2) { if (o == null) {return false;} Class<?> target = o.getClass(); if (target == javaClass || checkAnonymous(target)) { Variance variance; Type<?> subParam; Type<?> thisParam; variance = getVariance(0); subParam = Types.getParam(o, 0); thisParam = thisParam0; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} variance = getVariance(1); subParam = Types.getParam(o, 1); thisParam = thisParam1; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} variance = getVariance(2); subParam = Types.getParam(o, 2); thisParam = thisParam2; if (!subtypeTestForParam(variance, thisParam, subParam)) {return false;} return true; } else if (javaClass.isInstance(o)) { return checkParents(o, thisParam0, thisParam1, thisParam2); } // not needed for Java primitives. not sure for String /* else if (o instanceof String || o instanceof Number) { // @NativeRep'ed type return checkParents(o, thisParam0, thisParam1, thisParam2); } */ else { return false; } } public short $_get_serialization_id() { return SerializationConstants.NO_PREASSIGNED_ID; } public void $_serialize(X10JavaSerializer serializer) throws IOException { short sid = serializer.getSerializationId(javaClass, null); serializer.write(sid); } public static X10JavaSerializable $_deserializer(X10JavaDeserializer deserializer) throws IOException { RuntimeType rt = new RuntimeType(); int i = deserializer.record_reference(rt); X10JavaSerializable x10JavaSerializable = $_deserialize_body(rt, deserializer); if (rt != x10JavaSerializable) { deserializer.update_reference(i, x10JavaSerializable); } return x10JavaSerializable; } public static X10JavaSerializable $_deserialize_body(RuntimeType rt, X10JavaDeserializer deserializer) throws IOException { short classId = deserializer.readShort(); Class<?> clazz = deserializer.getClassForID(classId); rt.javaClass = clazz; return rt; } }