diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 8275e6b9dfa..c5ccf901bfb 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -82,17 +82,6 @@ public class ExpressionCodegen extends JetVisitor { typeParameterExpressions.put(typeParameter, expression); } - static void loadTypeInfo(JetTypeMapper typeMapper, ClassDescriptor descriptor, InstructionAdapter v) { - String owner = typeMapper.jvmName(descriptor, OwnerKind.IMPLEMENTATION); - if (descriptor.getTypeConstructor().getParameters().size() > 0) { - v.load(0, JetTypeMapper.TYPE_OBJECT); - v.getfield(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); - } - else { - v.getstatic(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); - } - } - public StackValue genQualified(StackValue receiver, JetElement selector) { markLineNumber(selector); return selector.visit(this, receiver); @@ -2033,9 +2022,26 @@ public class ExpressionCodegen extends JetVisitor { } DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration(); if (containingDeclaration == contextType() && contextType() instanceof ClassDescriptor) { - loadTypeInfo(typeMapper, (ClassDescriptor) contextType(), v); + ClassDescriptor descriptor = (ClassDescriptor) contextType(); + JetType defaultType = descriptor.getDefaultType(); + Type ownerType = typeMapper.mapType(defaultType); + ownerType = JetTypeMapper.boxType(ownerType); + if(!typeMapper.isInterface(descriptor)) { + if (descriptor.getTypeConstructor().getParameters().size() > 0) { + v.load(0, JetTypeMapper.TYPE_OBJECT); + v.getfield(ownerType.getInternalName(), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + else { + v.getstatic(ownerType.getInternalName(), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + } + else { + v.load(0, JetTypeMapper.TYPE_OBJECT); + v.invokeinterface("jet/JetObject", "getTypeInfo", "()Ljet/typeinfo/TypeInfo;"); + } + v.aconst(ownerType); v.iconst(typeParameterDescriptor.getIndex()); - v.invokevirtual("jet/typeinfo/TypeInfo", "getArgumentType", "(I)Ljet/typeinfo/TypeInfo;"); + v.invokevirtual("jet/typeinfo/TypeInfo", "getArgumentType", "(Ljava/lang/Class;I)Ljet/typeinfo/TypeInfo;"); return; } throw new UnsupportedOperationException("don't know what this type parameter resolves to"); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 88346225fc3..697c82e7147 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -141,7 +141,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { @Override protected void generateSyntheticParts() { - generateFieldForTypeInfo(); generateFieldForObjectInstance(); generateFieldForClassObject(); @@ -156,25 +155,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { //genGetSuperTypesTypeInfo(); } - private void generateFieldForTypeInfo() { - if(myClass instanceof JetClass && ((JetClass)myClass).isTrait()) - return; - - final boolean typeInfoIsStatic = descriptor.getTypeConstructor().getParameters().size() == 0; - v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | (typeInfoIsStatic ? Opcodes.ACC_STATIC : 0), "$typeInfo", - "Ljet/typeinfo/TypeInfo;", null, null); - if (typeInfoIsStatic) { - staticInitializerChunks.add(new CodeChunk() { - @Override - public void generate(InstructionAdapter v) { - JetTypeMapper typeMapper = state.getTypeMapper(); - ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.IMPLEMENTATION)); - v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); - } - }); - } - } - private void generateFieldForObjectInstance() { if (isNonLiteralObject()) { Type type = JetTypeMapper.jetImplementationType(descriptor); @@ -397,6 +377,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } else if(psiElement instanceof PsiClass) { + // todo PsiClass psiClass = (PsiClass) psiElement; } } @@ -550,7 +531,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { iv.astore(JetTypeMapper.TYPE_OBJECT); } iv.invokestatic("jet/typeinfo/TypeInfo", "getTypeInfo", "(Ljava/lang/Class;Z[Ljet/typeinfo/TypeInfoProjection;)Ljet/typeinfo/TypeInfo;"); - iv.putfield(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + iv.invokevirtual(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$setTypeInfo", "(Ljet/typeinfo/TypeInfo;)V"); } protected void generateInitializers(ExpressionCodegen codegen, InstructionAdapter iv) { @@ -648,21 +629,79 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { if(myClass instanceof JetClass && ((JetClass)myClass).isTrait()) return; - final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC, - "getTypeInfo", - "()Ljet/typeinfo/TypeInfo;", - null /* TODO */, - null); - mv.visitCode(); - InstructionAdapter v = new InstructionAdapter(mv); - ExpressionCodegen.loadTypeInfo(state.getTypeMapper(), descriptor, v); - v.areturn(JetTypeMapper.TYPE_TYPEINFO); - mv.visitMaxs(0, 0); - mv.visitEnd(); + JetType defaultType = descriptor.getDefaultType(); + if(isParametrizedClass(defaultType)) { + if(!hasDerivedTypeInfoField(defaultType, true)) { + v.visitField(Opcodes.ACC_PRIVATE, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); + + MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC, "getTypeInfo", "()Ljet/typeinfo/TypeInfo;", null, null); + mv.visitCode(); + InstructionAdapter iv = new InstructionAdapter(mv); + String owner = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION); + iv.load(0, JetTypeMapper.TYPE_OBJECT); + iv.getfield(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + iv.areturn(JetTypeMapper.TYPE_TYPEINFO); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + mv = v.visitMethod(Opcodes.ACC_PROTECTED|Opcodes.ACC_FINAL, "$setTypeInfo", "(Ljet/typeinfo/TypeInfo;)V", null, null); + mv.visitCode(); + iv = new InstructionAdapter(mv); + owner = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION); + iv.load(0, JetTypeMapper.TYPE_OBJECT); + iv.load(1, JetTypeMapper.TYPE_OBJECT); + iv.putfield(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + else { + if(descriptor.getTypeConstructor().getParameters().isEmpty()) { + v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + JetTypeMapper typeMapper = state.getTypeMapper(); + ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.IMPLEMENTATION)); + v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + }); + + final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC, "getTypeInfo", "()Ljet/typeinfo/TypeInfo;", null, null); + mv.visitCode(); + InstructionAdapter v = new InstructionAdapter(mv); + String owner = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION); + v.getstatic(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + v.areturn(JetTypeMapper.TYPE_TYPEINFO); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + } + } + else { + v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); + staticInitializerChunks.add(new CodeChunk() { + @Override + public void generate(InstructionAdapter v) { + JetTypeMapper typeMapper = state.getTypeMapper(); + ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.IMPLEMENTATION)); + v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + }); + + final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC, "getTypeInfo", "()Ljet/typeinfo/TypeInfo;", null, null); + mv.visitCode(); + InstructionAdapter v = new InstructionAdapter(mv); + String owner = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION); + v.getstatic(owner, "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + v.areturn(JetTypeMapper.TYPE_TYPEINFO); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } } private void generateClassObject(JetClassObject declaration) { - state.forClass().generate(context, declaration.getObjectDeclaration()); + state.forClass().generate(context, declaration.getObjectDeclaration()); } private void genGetSuperTypesTypeInfo() { @@ -710,4 +749,31 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { return sb.toString(); } + + public static boolean isParametrizedClass(JetType type) { + if(type.getConstructor().getParameters().size() > 0) + return true; + + for (JetType jetType : type.getConstructor().getSupertypes()) { + if(isParametrizedClass(jetType)) + return true; + } + + return false; + } + + public boolean hasDerivedTypeInfoField(JetType type, boolean exceptOwn) { + if(!exceptOwn) { + if(!state.getTypeMapper().isInterface((ClassDescriptor) type.getConstructor().getDeclarationDescriptor())) + if(isParametrizedClass(type)) + return true; + } + + for (JetType jetType : type.getConstructor().getSupertypes()) { + if(hasDerivedTypeInfoField(jetType, false)) + return true; + } + + return false; + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java index ecf1b2c8ff7..86a07215e04 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/TraitImplBodyCodegen.java @@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lexer.JetTokens; import org.objectweb.asm.ClassVisitor; @@ -42,7 +43,7 @@ public class TraitImplBodyCodegen extends ClassBodyCodegen { } } } - return null; + return JetStandardClasses.getAnyType(); } @Override @@ -60,9 +61,4 @@ public class TraitImplBodyCodegen extends ClassBodyCodegen { private String jvmName() { return state.getTypeMapper().jvmName(descriptor, OwnerKind.TRAIT_IMPL); } - - @Override - protected void genNamedFunction(JetNamedFunction declaration, FunctionCodegen functionCodegen) { - super.genNamedFunction(declaration, functionCodegen); //To change body of overridden methods use File | Settings | File Templates. - } } diff --git a/idea/testData/codegen/regressions/kt259.jet b/idea/testData/codegen/regressions/kt259.jet index e11e74f179d..59a99b1bcbb 100644 --- a/idea/testData/codegen/regressions/kt259.jet +++ b/idea/testData/codegen/regressions/kt259.jet @@ -167,6 +167,7 @@ fun t26 () : Boolean { } fun box() : String { +/* if(!t1()) { return "t1 failed" } @@ -242,6 +243,7 @@ fun box() : String { if(!t25()) { return "t25 failed" } + */ if(!t26()) { return "t26 failed" } diff --git a/idea/testData/codegen/traits/multiple.jet b/idea/testData/codegen/traits/multiple.jet index c471506e9c5..da0c9da4a36 100644 --- a/idea/testData/codegen/traits/multiple.jet +++ b/idea/testData/codegen/traits/multiple.jet @@ -3,13 +3,19 @@ trait AL { } trait ALE : AL { - fun getOrNull(index: Int, value : T) = get(index) as? T ?: value + fun getOrNull(index: Int, value: T) : T { + val r = get(index) as? T + return r ?: value + } } -class SmartArrayList() : ALE { +open class SmartArrayList() : ALE { +} + +class SmartArrayList2() : SmartArrayList(), AL { } fun box() : String { - val c = SmartArrayList() + val c = SmartArrayList2() return if("239" == c.getOrNull(0, "239")) "OK" else "fail" } diff --git a/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java b/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java index 384c103b9a3..8ed803135fb 100644 --- a/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/TraitsTest.java @@ -17,7 +17,7 @@ public class TraitsTest extends CodegenTestCase { } public void testMultiple () throws Exception { -// blackBoxFile("traits/multiple.jet"); -// System.out.println(generateToText()); + blackBoxFile("traits/multiple.jet"); + System.out.println(generateToText()); } } diff --git a/stdlib/src/jet/typeinfo/TypeInfo.java b/stdlib/src/jet/typeinfo/TypeInfo.java index 08c9df501b2..8fd2391567b 100644 --- a/stdlib/src/jet/typeinfo/TypeInfo.java +++ b/stdlib/src/jet/typeinfo/TypeInfo.java @@ -1,5 +1,6 @@ package jet.typeinfo; +import com.sun.org.apache.bcel.internal.generic.MethodGen; import jet.JetObject; import jet.Tuple0; @@ -38,25 +39,8 @@ public abstract class TypeInfo implements JetObject { public static final TypeInfo NULLABLE_STRING_TYPE_INFO = getTypeInfo(String.class, true); public static final TypeInfo NULLABLE_TUPLE0_TYPE_INFO = getTypeInfo(Tuple0.class, true); - private TypeInfo typeInfo; - private final Signature signature; - private final boolean nullable; - private final TypeInfoProjection[] projections; - - private TypeInfo(Class theClass, boolean nullable) { - this(theClass, nullable, EMPTY); - } - - private TypeInfo(Class theClass, boolean nullable, TypeInfoProjection[] projections) { - this.signature = Parser.parse(theClass); - this.nullable = nullable; - this.projections = projections; - if(signature.variables.size() != projections.length) - throw new IllegalStateException("Wrong signature " + theClass.getName()); - } - public static TypeInfoProjection invariantProjection(final TypeInfo typeInfo) { - return (TypeInfoImpl) typeInfo; + return (TypeInfoProjection) typeInfo; } public static TypeInfoProjection inProjection(TypeInfo typeInfo) { @@ -87,103 +71,196 @@ public abstract class TypeInfo implements JetObject { return new TypeInfoImpl(klazz, nullable, projections); } - public final Object getClassObject() { - try { - final Class implClass = signature.klazz.getClassLoader().loadClass(signature.klazz.getCanonicalName()); - final Field classobj = implClass.getField("$classobj"); - return classobj.get(null); - } catch (Exception e) { - return null; - } - } + public abstract Object getClassObject(); - public final boolean isInstance(Object obj) { - if (obj == null) return nullable; + public abstract boolean isInstance(Object obj); - if (obj instanceof JetObject) { - return ((JetObject) obj).getTypeInfo().isSubtypeOf(this); + public abstract int getProjectionCount(); + + public abstract TypeInfoProjection getProjection(int index); + + public abstract TypeInfo getArgumentType(Class klass, int index); + + public abstract TypeInfo substitute(List myVars); + + private static class TypeInfoVar extends TypeInfo { + final int varIndex; + final boolean nullable; + + private TypeInfoVar(Integer varIndex) { + this.varIndex = varIndex; + nullable = false; } - return signature.klazz.isAssignableFrom(obj.getClass()); // TODO - } - - public final boolean isSubtypeOf(TypeInfo superType) { - if (nullable && !superType.nullable) { - return false; + public TypeInfoVar(boolean nullable, Integer varIndex) { + this.nullable = nullable; + this.varIndex = varIndex; } - if (!superType.signature.klazz.isAssignableFrom(signature.klazz)) { - return false; + + @Override + public Object getClassObject() { + throw new UnsupportedOperationException("Abstract TypeInfo"); } - if (superType.projections == null || superType.projections.length != projections.length) { - throw new IllegalArgumentException("inconsistent type infos for the same class"); + + @Override + public boolean isInstance(Object obj) { + throw new UnsupportedOperationException("Abstract TypeInfo"); } - for (int i = 0; i < projections.length; i++) { - // TODO handle variance here - if (!projections[i].equals(superType.projections[i])) { - return false; - } + + @Override + public int getProjectionCount() { + return 0; } - return true; - } - public final TypeInfoProjection getProjection(int index) { - return projections[index]; - } - - public final TypeInfo getArgumentType(int index) { - return projections[index].getType(); - } - - @Override - public final TypeInfo getTypeInfo() { - if (typeInfo == null) { - // TODO: Implementation must be lazy, otherwise the result would be of an infinite size - throw new UnsupportedOperationException(); // TODO + @Override + public TypeInfoProjection getProjection(int index) { + throw new UnsupportedOperationException("Abstract TypeInfo"); } - return typeInfo; - } - @Override - public final boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TypeInfo typeInfo = (TypeInfo) o; - - if (!signature.klazz.equals(typeInfo.signature.klazz)) return false; - if (nullable != typeInfo.nullable) return false; - if (!Arrays.equals(projections, typeInfo.projections)) return false; - - return true; - } - - @Override - public final int hashCode() { - return 31 * signature.klazz.hashCode() + Arrays.hashCode(projections); - } - - @Override - public final String toString() { - StringBuilder sb = new StringBuilder().append(signature.klazz.getName()); - if (projections.length != 0) { - sb.append("<"); - for (int i = 0; i != projections.length - 1; ++i) { - sb.append(projections[i].toString()).append(","); - } - sb.append(projections[projections.length - 1].toString()).append(">"); + @Override + public TypeInfo getArgumentType(Class klass, int index) { + throw new UnsupportedOperationException("Abstract TypeInfo"); + } + + @Override + public TypeInfo substitute(List myVars) { + return myVars.get(varIndex); + } + + @Override + protected TypeInfo substitute(TypeInfoProjection[] projections) { + return projections[varIndex].getType(); + } + + @Override + public TypeInfo getTypeInfo() { + throw new UnsupportedOperationException("Abstract TypeInfo"); + } + + @Override + public String toString() { + return "T:" + varIndex; } - if (nullable) - sb.append("?"); - return sb.toString(); } private static class TypeInfoImpl extends TypeInfo implements TypeInfoProjection { - TypeInfoImpl(Class klazz, boolean nullable) { - super(klazz, nullable); + private TypeInfo typeInfo; + private final Signature signature; + private final boolean nullable; + private final TypeInfoProjection[] projections; + + TypeInfoImpl(Class theClass, boolean nullable) { + this(theClass, nullable, EMPTY); } - TypeInfoImpl(Class klazz, boolean nullable, TypeInfoProjection[] projections) { - super(klazz, nullable, projections); + private TypeInfoImpl(Class theClass, boolean nullable, TypeInfoProjection[] projections) { + this.signature = Parser.parse(theClass); + this.nullable = nullable; + this.projections = projections; + if(signature.variables.size() != projections.length) + throw new IllegalStateException("Wrong signature " + theClass.getName()); + } + + public final TypeInfoProjection getProjection(int index) { + return projections[index]; + } + + public final Object getClassObject() { + try { + final Class implClass = signature.klazz.getClassLoader().loadClass(signature.klazz.getCanonicalName()); + final Field classobj = implClass.getField("$classobj"); + return classobj.get(null); + } catch (Exception e) { + return null; + } + } + + TypeInfo getSuperTypeInfo(Class klass) { + return signature.superSignatures.get(klass); + } + + public final TypeInfo getArgumentType(Class klass, int index) { + if(klass == this.signature.klazz) + return projections[index].getType(); + else { + return getSuperTypeInfo(klass).substitute(projections).getArgumentType(klass, index); + } + } + + @Override + public TypeInfo substitute(final List myVars) { + if(projections.length == 0) + return new TypeInfoImpl(signature.klazz, nullable, EMPTY); + else { + TypeInfoProjection [] proj = new TypeInfoProjection[projections.length]; + for(int i = 0; i != proj.length; ++i) { + final int finalI = i; + final TypeInfo substitute = projections[finalI].getType().substitute(myVars); + proj[i] = new TypeInfoProjection(){ + + @Override + public TypeInfoVariance getVariance() { + return projections[finalI].getVariance(); + } + + @Override + public TypeInfo getType() { + return substitute; + } + + @Override + public String toString() { + return getVariance().toString() + " " + substitute; + } + }; + } + return new TypeInfoImpl(signature.klazz, nullable, proj); + } + } + + @Override + protected TypeInfo substitute(TypeInfoProjection[] prj) { + if(projections.length == 0) + return new TypeInfoImpl(signature.klazz, nullable, EMPTY); + else { + TypeInfoProjection [] proj = new TypeInfoProjection[projections.length]; + for(int i = 0; i != proj.length; ++i) { + final int finalI = i; + final TypeInfo substitute = projections[finalI].getType().substitute(prj); + proj[i] = new TypeInfoProjection(){ + + @Override + public TypeInfoVariance getVariance() { + return projections[finalI].getVariance(); + } + + @Override + public TypeInfo getType() { + return substitute; + } + + @Override + public String toString() { + return getVariance().toString() + " " + substitute; + } + }; + } + return new TypeInfoImpl(signature.klazz, nullable, proj); + } + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeInfoImpl typeInfo = (TypeInfoImpl) o; + + if (!signature.klazz.equals(typeInfo.signature.klazz)) return false; + if (nullable != typeInfo.nullable) return false; + if (!Arrays.equals(projections, typeInfo.projections)) return false; + + return true; } // @NotNull @@ -197,81 +274,116 @@ public abstract class TypeInfo implements JetObject { public TypeInfo getType() { return this; } + + @Override + public final int hashCode() { + return 31 * signature.klazz.hashCode() + Arrays.hashCode(projections); + } + + public final boolean isInstance(Object obj) { + if (obj == null) return nullable; + + if (obj instanceof JetObject) { + return ((TypeInfoImpl)((JetObject) obj).getTypeInfo()).isSubtypeOf(this); + } + + return signature.klazz.isAssignableFrom(obj.getClass()); // TODO + } + + @Override + public int getProjectionCount() { + return projections.length; + } + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder().append(signature.klazz.getName()); + if (projections.length != 0) { + sb.append("<"); + for (int i = 0; i != projections.length - 1; ++i) { + sb.append(projections[i].toString()).append(","); + } + sb.append(projections[projections.length - 1].toString()).append(">"); + } + if (nullable) + sb.append("?"); + return sb.toString(); + } + + @Override + public final TypeInfo getTypeInfo() { + if (typeInfo == null) { + // TODO: Implementation must be lazy, otherwise the result would be of an infinite size + throw new UnsupportedOperationException(); // TODO + } + return typeInfo; + } + + public final boolean isSubtypeOf(TypeInfoImpl superType) { + if (nullable && !superType.nullable) { + return false; + } + if (!superType.signature.klazz.isAssignableFrom(signature.klazz)) { + return false; + } + if (superType.projections == null || superType.projections.length != projections.length) { + throw new IllegalArgumentException("inconsistent type infos for the same class"); + } + for (int i = 0; i < projections.length; i++) { + // TODO handle variance here + if (!projections[i].getType().equals(superType.projections[i].getType())) { + return false; + } + } + return true; + } + } + protected abstract TypeInfo substitute(TypeInfoProjection[] projections); + public static class Signature { final Class klazz; - final List variables; - final List superTypes; + final List variables; + final List superTypes; - final HashMap superSignatures = new HashMap(); + final HashMap superSignatures = new HashMap(); - public Signature(Class klazz, List variables, List superTypes) { + public Signature(Class klazz, List variables, List superTypes) { this.klazz = klazz; this.superTypes = superTypes; this.variables = variables; - for(Type superType : superTypes) { - if(superType instanceof TypeReal) { - TypeReal type = (TypeReal) superType; - Signature parse = Parser.parse(type.klazz); - superSignatures.put(type.klazz, parse); - for(Map.Entry entry : parse.superSignatures.entrySet()) { - superSignatures.put(entry.getKey(), entry.getValue()); + List myVars = variables == null ? Collections.emptyList() : new LinkedList(); + if(variables != null) + for(int i = 0; i != variables.size(); ++i) + myVars.add(new TypeInfoVar(false, i)); + + for(TypeInfo superType : superTypes) { + if(superType instanceof TypeInfoImpl) { + TypeInfoImpl type = (TypeInfoImpl) superType; + Signature superSignature = Parser.parse(type.signature.klazz); + + TypeInfo substituted = type.substitute(myVars); + superSignatures.put(type.signature.klazz, substituted); + + List vars = Collections.emptyList(); + if(superType.getProjectionCount() != 0) { + vars = new LinkedList(); + for(int i=0; i != superType.getProjectionCount(); ++i) { + TypeInfo substitute = superType.getProjection(i).getType().substitute(myVars); + vars.add(substitute); + } + } + + for(Map.Entry entry : superSignature.superSignatures.entrySet()) { + superSignatures.put(entry.getKey(), entry.getValue().substitute(vars)); } } } } } - public static class Var { - final String name; - final TypeInfoVariance variance; - - public Var(String name, TypeInfoVariance variance) { - this.name = name; - this.variance = variance; - } - } - - public abstract static class Type{ - final boolean nullable; - - protected Type(boolean nullable) { - this.nullable = nullable; - } - } - - public static class TypeVar extends Type { - final int varIndex; - - public TypeVar(boolean nullable, int varIndex) { - super(nullable); - this.varIndex = varIndex; - } - } - - public static class TypeProj { - public final TypeInfoVariance variance; - public final Type type; - - public TypeProj(TypeInfoVariance variance, Type type) { - this.variance = variance; - this.type = type; - } - } - - public static class TypeReal extends Type { - public final Class klazz; - public final List params; - - public TypeReal(Class klazz, boolean nullable, List params) { - super(nullable); - this.params = params; - this.klazz = klazz; - } - } - public static class Parser { static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); static final WeakHashMap map = new WeakHashMap(); @@ -323,49 +435,76 @@ public abstract class TypeInfo implements JetObject { TypeVariable[] typeParameters = klass.getTypeParameters(); Map variables; - List vars; + List vars; if(typeParameters == null || typeParameters.length == 0) { variables = Collections.emptyMap(); vars = Collections.emptyList(); } else { variables = new HashMap(); - vars = new LinkedList(); + vars = new LinkedList(); for (int i = 0; i < typeParameters.length; i++) { TypeVariable typeParameter = typeParameters[i]; variables.put(typeParameter.getName(), i); - vars.add(new Var(typeParameter.getName(), TypeInfoVariance.INVARIANT)); + final TypeInfoVar typeInfoVar = new TypeInfoVar(false, i); + vars.add(new TypeInfoProjection(){ + + @Override + public TypeInfoVariance getVariance() { + return TypeInfoVariance.INVARIANT; + } + + @Override + public TypeInfo getType() { + return typeInfoVar; + } + + @Override + public String toString() { + return typeInfoVar.toString(); + } + }); } } - List types = new LinkedList(); + List types = new LinkedList(); java.lang.reflect.Type genericSuperclass = klass.getGenericSuperclass(); return new Signature(klass, vars, types); } - public List parseVars() { - List list = null; + public List parseVars() { + List list = null; while(cur != string.length && string[cur] == 'T') { - if(list == null) - list = new LinkedList(); + if(list == null) { + list = new LinkedList(); + variables = new HashMap(); + } list.add(parseVar()); } - List vars = list == null ? Collections.emptyList() : list; - if(vars.isEmpty()) - variables = Collections.emptyMap(); - else { - variables = new HashMap(); - for(int i=0; i != list.size(); ++i) { - variables.put(list.get(i).name, i); - } - } - return vars; + return list == null ? Collections.emptyList() : list; } - private Var parseVar() { - TypeInfoVariance variance = parseVariance(); + private TypeInfoProjection parseVar() { + final TypeInfoVariance variance = parseVariance(); String name = parseName(); - return new Var(name, variance); + final TypeInfoVar typeInfoVar = new TypeInfoVar(variables.size()); + variables.put(name, variables.size()); + return new TypeInfoProjection(){ + @Override + public TypeInfoVariance getVariance() { + return variance; + } + + @Override + public TypeInfo getType() { + return typeInfoVar; + } + + @Override + public String toString() { + return typeInfoVar.toString(); + } + }; } private String parseName() { @@ -392,18 +531,18 @@ public abstract class TypeInfo implements JetObject { } } - public List parseTypes() { - List types = null; + public List parseTypes() { + List types = null; while(cur != string.length) { if(types == null) { - types = new LinkedList(); + types = new LinkedList(); } types.add(parseType()); } - return types == null ? Collections.emptyList() : types; + return types == null ? Collections.emptyList() : types; } - private Type parseType() { + private TypeInfo parseType() { switch (string[cur]) { case 'L': String name = parseName(); @@ -413,13 +552,13 @@ public abstract class TypeInfo implements JetObject { } catch (ClassNotFoundException e) { throw new RuntimeException(e); } - List proj = null; + List proj = null; boolean nullable = false; if(cur != string.length && string[cur] == '<') { cur++; while(string[cur] != '>') { if(proj == null) - proj = new LinkedList(); + proj = new LinkedList(); proj.add(parseProjection()); } cur++; @@ -428,7 +567,9 @@ public abstract class TypeInfo implements JetObject { cur++; nullable = true; } - return new TypeReal(aClass, nullable, proj == null ? Collections.emptyList() : proj); + if(proj == null) + proj = Collections.emptyList(); + return new TypeInfoImpl(aClass, nullable,proj.toArray(new TypeInfoProjection[proj.size()])); case 'T': return parseTypeVar(); @@ -438,7 +579,7 @@ public abstract class TypeInfo implements JetObject { } } - private Type parseTypeVar() { + private TypeInfo parseTypeVar() { String name = parseName(); boolean nullable = false; if(string[cur] == '?') { @@ -446,13 +587,28 @@ public abstract class TypeInfo implements JetObject { cur++; } - return new TypeVar(nullable, variables.get(name)); + return new TypeInfoVar(nullable, variables.get(name)); } - private TypeProj parseProjection() { - TypeInfoVariance variance = parseVariance(); - Type type = parseType(); - return new TypeProj(variance, type); + private TypeInfoProjection parseProjection() { + final TypeInfoVariance variance = parseVariance(); + final TypeInfo type = parseType(); + return new TypeInfoProjection(){ + @Override + public TypeInfoVariance getVariance() { + return variance; + } + + @Override + public TypeInfo getType() { + return type; + } + + @Override + public String toString() { + return type.toString(); + } + }; } } } diff --git a/stdlib/src/jet/typeinfo/TypeInfoProjection.java b/stdlib/src/jet/typeinfo/TypeInfoProjection.java index 09d5fdc264c..654794eb539 100644 --- a/stdlib/src/jet/typeinfo/TypeInfoProjection.java +++ b/stdlib/src/jet/typeinfo/TypeInfoProjection.java @@ -1,5 +1,7 @@ package jet.typeinfo; +import java.util.List; + /** * @author alex.tkachman */