diff --git a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java index 92af8158517..f962710f5d8 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -3,7 +3,6 @@ package org.jetbrains.jet.codegen; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; -import jet.typeinfo.TypeInfo; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -28,8 +27,6 @@ public class ClassCodegen { private final Codegens factory; private final JetTypeMapper typeMapper; - private static final Type TYPEINFO_TYPE = Type.getType(TypeInfo.class); - public ClassCodegen(Project project, Codegens factory, BindingContext bindingContext) { this.project = project; this.factory = factory; @@ -79,13 +76,14 @@ public class ClassCodegen { new String[] { "jet/JetObject", JetTypeMapper.jvmNameForInterface(descriptor) } ); - v.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); + int typeinfoStatic = descriptor.getTypeConstructor().getParameters().size() > 0 ? 0 : Opcodes.ACC_STATIC; + v.visitField(Opcodes.ACC_PRIVATE | typeinfoStatic, "$typeInfo", "Ljet/typeinfo/TypeInfo;", null, null); generateStaticInitializer(descriptor, v); generatePrimaryConstructor(aClass, v, kind); - generateGetTypeInfo(aClass, v); + generateGetTypeInfo(descriptor, v); generateClassBody(aClass, v, kind); @@ -193,6 +191,17 @@ public class ClassCodegen { frameMap.enter(parameter, argTypes[i].getSize()); } + int firstTypeParameter = -1; + int typeParamCount = classDescriptor.getTypeConstructor().getParameters().size(); + if (kind == OwnerKind.IMPLEMENTATION) { + if (typeParamCount > 0) { + firstTypeParameter = frameMap.enterTemp(); + for (int i = 1; i < typeParamCount; i++) { + frameMap.enterTemp(); + } + } + } + List specifiers = aClass.getDelegationSpecifiers(); if (specifiers.isEmpty() || !(specifiers.get(0) instanceof JetDelegatorToSuperCall)) { @@ -264,6 +273,10 @@ public class ClassCodegen { n++; } + if (firstTypeParameter > 0 && kind == OwnerKind.IMPLEMENTATION) { + generateTypeInfoInitializer(classDescriptor, firstTypeParameter, typeParamCount, iv); + } + generateInitializers(aClass, kind, codegen, iv); int curParam = 0; @@ -284,6 +297,25 @@ public class ClassCodegen { mv.visitEnd(); } + private void generateTypeInfoInitializer(ClassDescriptor classDescriptor, int firstTypeParameter, int typeParamCount, InstructionAdapter iv) { + iv.load(0, JetTypeMapper.TYPE_OBJECT); + iv.anew(JetTypeMapper.TYPE_TYPEINFO); + iv.dup(); + + iv.aconst(typeMapper.jvmType(classDescriptor, OwnerKind.INTERFACE)); + iv.iconst(typeParamCount); + iv.newarray(JetTypeMapper.TYPE_TYPEINFO); + + for (int i = 0; i < typeParamCount; i++) { + iv.dup(); + iv.iconst(i); + iv.load(firstTypeParameter + i, JetTypeMapper.TYPE_OBJECT); + iv.astore(JetTypeMapper.TYPE_OBJECT); + } + iv.invokespecial("jet/typeinfo/TypeInfo", "", "(Ljava/lang/Class;[Ljet/typeinfo/TypeInfo;)V"); + iv.putfield(typeMapper.jvmName(classDescriptor, OwnerKind.IMPLEMENTATION), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); + } + private void generateInitializers(JetClass aClass, OwnerKind kind, ExpressionCodegen codegen, InstructionAdapter iv) { for (JetDeclaration declaration : aClass.getDeclarations()) { if (declaration instanceof JetProperty) { @@ -360,15 +392,16 @@ public class ClassCodegen { } private void generateStaticInitializer(ClassDescriptor descriptor, ClassVisitor cv) { + if (descriptor.getTypeConstructor().getParameters().size() > 0) { + // we will have a dynamic type info field + return; + } final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "", "()V", null, null); mv.visitCode(); InstructionAdapter v = new InstructionAdapter(mv); - v.anew(TYPEINFO_TYPE); - v.dup(); - v.aconst(Type.getObjectType(JetTypeMapper.jvmNameForInterface(descriptor))); - v.invokespecial("jet/typeinfo/TypeInfo", "", "(Ljava/lang/Class;)V"); + newTypeInfo(v, Type.getObjectType(JetTypeMapper.jvmNameForInterface(descriptor))); v.putstatic(JetTypeMapper.jvmNameForImplementation(descriptor), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); mv.visitInsn(Opcodes.RETURN); @@ -377,7 +410,14 @@ public class ClassCodegen { mv.visitEnd(); } - private void generateGetTypeInfo(JetClass aClass, ClassVisitor cv) { + public static void newTypeInfo(InstructionAdapter v, Type asmType) { + v.anew(JetTypeMapper.TYPE_TYPEINFO); + v.dup(); + v.aconst(asmType); + v.invokespecial("jet/typeinfo/TypeInfo", "", "(Ljava/lang/Class;)V"); + } + + private void generateGetTypeInfo(ClassDescriptor descriptor, ClassVisitor cv) { final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "getTypeInfo", "()Ljet/typeinfo/TypeInfo;", @@ -385,9 +425,15 @@ public class ClassCodegen { null); mv.visitCode(); InstructionAdapter v = new InstructionAdapter(mv); - ClassDescriptor descriptor = bindingContext.getClassDescriptor(aClass); - v.getstatic(JetTypeMapper.jvmNameForImplementation(descriptor), "$typeInfo", "Ljet/typeinfo/TypeInfo;"); - v.areturn(TYPEINFO_TYPE); + String owner = JetTypeMapper.jvmNameForImplementation(descriptor); + 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;"); + } + v.areturn(JetTypeMapper.TYPE_TYPEINFO); mv.visitMaxs(0, 0); mv.visitEnd(); } diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 76dc4cfe4c6..736082b0022 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1147,6 +1147,13 @@ public class ExpressionCodegen extends JetVisitor { Method method = typeMapper.mapConstructorSignature((ConstructorDescriptor) constructorDescriptor, OwnerKind.IMPLEMENTATION); pushMethodArguments(expression, method); + + for (JetTypeReference typeArgumentReference : constructorType.getTypeArgumentsAsTypes()) { + JetType typeArgument = bindingContext.resolveTypeReference(typeArgumentReference); + // TODO is the makeNullable() call correct here? + ClassCodegen.newTypeInfo(v, typeMapper.mapType(TypeUtils.makeNullable(typeArgument))); + } + v.invokespecial(JetTypeMapper.jvmNameForImplementation(classDecl), "", method.getDescriptor()); myStack.push(StackValue.onStack(type)); return; @@ -1289,19 +1296,47 @@ public class ExpressionCodegen extends JetVisitor { } JetTypeReference typeReference = ((JetTypePattern) pattern).getTypeReference(); JetType jetType = bindingContext.resolveTypeReference(typeReference); - if (jetType.getArguments().size() > 0) { - throw new UnsupportedOperationException("don't know how to handle type arguments in is"); - } DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor(); if (!(descriptor instanceof ClassDescriptor)) { throw new UnsupportedOperationException("don't know how to handle non-class types in is"); } - gen(expression.getLeftHandSide(), OBJECT_TYPE); - Type type = typeMapper.jvmType((ClassDescriptor) descriptor, OwnerKind.INTERFACE); - v.instanceOf(type); + if (jetType.getArguments().size() > 0) { + newTypeInfo(jetType); + gen(expression.getLeftHandSide(), OBJECT_TYPE); + v.invokevirtual("jet/typeinfo/TypeInfo", "isInstance", "(Ljava/lang/Object;)Z"); + } + else { + gen(expression.getLeftHandSide(), OBJECT_TYPE); + Type type = typeMapper.jvmType((ClassDescriptor) descriptor, OwnerKind.INTERFACE); + v.instanceOf(type); + } myStack.push(StackValue.onStack(Type.BOOLEAN_TYPE)); } + private void newTypeInfo(JetType jetType) { + v.anew(JetTypeMapper.TYPE_TYPEINFO); + v.dup(); + + v.aconst(typeMapper.jvmType((ClassDescriptor) jetType.getConstructor().getDeclarationDescriptor(), OwnerKind.INTERFACE)); + List arguments = jetType.getArguments(); + if (arguments.size() > 0) { + v.iconst(arguments.size()); + v.newarray(JetTypeMapper.TYPE_TYPEINFO); + + for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) { + TypeProjection argument = arguments.get(i); + v.dup(); + v.iconst(i); + newTypeInfo(argument.getType()); + v.astore(JetTypeMapper.TYPE_OBJECT); + } + v.invokespecial("jet/typeinfo/TypeInfo", "", "(Ljava/lang/Class;[Ljet/typeinfo/TypeInfo;)V"); + } + else { + v.invokespecial("jet/typeinfo/TypeInfo", "", "(Ljava/lang/Class;)V"); + } + } + private static class CompilationException extends RuntimeException { } } diff --git a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java index a32aefb4913..260389ff6d7 100644 --- a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.codegen; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import jet.typeinfo.TypeInfo; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -12,6 +13,7 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; +import java.util.ArrayList; import java.util.List; /** @@ -19,6 +21,7 @@ import java.util.List; */ public class JetTypeMapper { static final Type TYPE_OBJECT = Type.getObjectType("java/lang/Object"); + static final Type TYPE_TYPEINFO = Type.getType(TypeInfo.class); private final JetStandardLibrary standardLibrary; private final BindingContext bindingContext; @@ -57,6 +60,9 @@ public class JetTypeMapper { } public Type jvmType(ClassDescriptor jetClass, OwnerKind kind) { + if (jetClass == standardLibrary.getString()) { + return Type.getType(String.class); + } return Type.getType("L" + jvmName(jetClass, kind) + ";"); } @@ -227,17 +233,21 @@ public class JetTypeMapper { public Method mapConstructorSignature(ConstructorDescriptor descriptor, OwnerKind kind) { boolean delegate = kind == OwnerKind.DELEGATING_IMPLEMENTATION; List parameters = descriptor.getUnsubstitutedValueParameters(); - int count = parameters.size(); - int first = delegate ? 1 : 0; - Type[] parameterTypes = new Type[count + first]; + List parameterTypes = new ArrayList(); + ClassDescriptor classDescriptor = descriptor.getContainingDeclaration(); if (delegate) { - parameterTypes[0] = jetInterfaceType(descriptor.getContainingDeclaration()); + parameterTypes.add(jetInterfaceType(classDescriptor)); + } + for (ValueParameterDescriptor parameter : parameters) { + parameterTypes.add(mapType(parameter.getOutType())); } - for (int i = 0; i < count; i++) { - parameterTypes[i + first] = mapType(parameters.get(i).getOutType()); + List typeParameters = classDescriptor.getTypeConstructor().getParameters(); + for (TypeParameterDescriptor typeParameter : typeParameters) { + parameterTypes.add(TYPE_TYPEINFO); } - return new Method("", Type.VOID_TYPE, parameterTypes); + + return new Method("", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()])); } static int getAccessModifiers(JetDeclaration p, int defaultFlags) { diff --git a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java index e34a917e9ad..2f591de0e6c 100644 --- a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java +++ b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java @@ -15,7 +15,8 @@ public class ErrorHandler { public static final ErrorHandler THROW_EXCEPTION = new ErrorHandler() { @Override public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) { - throw new IllegalStateException("Unresolved reference: " + referenceExpression.getText()); + throw new IllegalStateException("Unresolved reference: " + referenceExpression.getText() + + " at offset " + referenceExpression.getTextRange().getStartOffset()); } @Override diff --git a/idea/testData/codegen/typeInfo/isWithGenerics.jet b/idea/testData/codegen/typeInfo/isWithGenerics.jet new file mode 100644 index 00000000000..416799acd8a --- /dev/null +++ b/idea/testData/codegen/typeInfo/isWithGenerics.jet @@ -0,0 +1,7 @@ +class Wrapper() { +} + +fun foo() { + val wrapper = new Wrapper() + return wrapper is Wrapper +} diff --git a/idea/tests/org/jetbrains/jet/codegen/TypeInfoTest.java b/idea/tests/org/jetbrains/jet/codegen/TypeInfoTest.java index d4cbf33b702..d9f7a070b07 100644 --- a/idea/tests/org/jetbrains/jet/codegen/TypeInfoTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/TypeInfoTest.java @@ -45,6 +45,13 @@ public class TypeInfoTest extends CodegenTestCase { assertTrue((Boolean) foo.invoke(null, newRunnable())); } + public void testIsWithGenerics() throws Exception { + loadFile(); + System.out.println(generateToText()); + Method foo = generateFunction(); + assertFalse((Boolean) foo.invoke(null)); + } + private Runnable newRunnable() { return new Runnable() { @Override diff --git a/stdlib/src/jet/typeinfo/TypeInfo.java b/stdlib/src/jet/typeinfo/TypeInfo.java index 7a6b78f2c23..5f80f501fe2 100644 --- a/stdlib/src/jet/typeinfo/TypeInfo.java +++ b/stdlib/src/jet/typeinfo/TypeInfo.java @@ -2,29 +2,49 @@ package jet.typeinfo; import jet.JetObject; +import java.util.Arrays; + /** * @author abreslav */ public class TypeInfo implements JetObject { - private TypeInfo typeInfo; - private TypeInfo typeArgument; - private Class theClass; + private final Class theClass; + private final TypeInfo[] typeParameters; public TypeInfo(Class theClass) { this.theClass = theClass; + this.typeParameters = null; } - private TypeInfo(TypeInfo typeArgument) { - this.typeArgument = typeArgument; + public TypeInfo(Class theClass, TypeInfo[] typeParameters) { + this.theClass = theClass; + this.typeParameters = typeParameters; } public boolean isInstance(Object obj) { + if (obj instanceof JetObject) { + return isSubtypeOf(((JetObject) obj).getTypeInfo()); + } throw new UnsupportedOperationException(); // TODO } public boolean isSubtypeOf(TypeInfo other) { - throw new UnsupportedOperationException(); // TODO + if (!theClass.isAssignableFrom(other.theClass)) { + return false; + } + if (typeParameters != null) { + if (other.typeParameters == null || other.typeParameters.length != typeParameters.length) { + throw new IllegalArgumentException("inconsistent type infos for the same class"); + } + for (int i = 0; i < typeParameters.length; i++) { + // TODO handle variance here + if (!typeParameters [i].equals(other.typeParameters [i])) { + return false; + } + } + } + return true; } @Override @@ -35,4 +55,24 @@ public class TypeInfo implements JetObject { } return typeInfo; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeInfo typeInfo = (TypeInfo) o; + + if (!theClass.equals(typeInfo.theClass)) return false; + if (!Arrays.equals(typeParameters, typeInfo.typeParameters)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = theClass.hashCode(); + result = 31 * result + (typeParameters != null ? Arrays.hashCode(typeParameters) : 0); + return result; + } }