'is' works with generic types correctly (initial implementation of reified generics)

This commit is contained in:
Dmitry Jemerov
2011-05-13 18:06:25 +02:00
parent 5711927522
commit 26790f1346
7 changed files with 179 additions and 33 deletions
@@ -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<JetDelegationSpecifier> 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", "<init>", "(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,
"<clinit>", "()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", "<init>", "(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", "<init>", "(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();
}
@@ -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), "<init>", 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<TypeProjection> 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", "<init>", "(Ljava/lang/Class;[Ljet/typeinfo/TypeInfo;)V");
}
else {
v.invokespecial("jet/typeinfo/TypeInfo", "<init>", "(Ljava/lang/Class;)V");
}
}
private static class CompilationException extends RuntimeException {
}
}
@@ -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<ValueParameterDescriptor> parameters = descriptor.getUnsubstitutedValueParameters();
int count = parameters.size();
int first = delegate ? 1 : 0;
Type[] parameterTypes = new Type[count + first];
List<Type> parameterTypes = new ArrayList<Type>();
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<TypeParameterDescriptor> typeParameters = classDescriptor.getTypeConstructor().getParameters();
for (TypeParameterDescriptor typeParameter : typeParameters) {
parameterTypes.add(TYPE_TYPEINFO);
}
return new Method("<init>", Type.VOID_TYPE, parameterTypes);
return new Method("<init>", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()]));
}
static int getAccessModifiers(JetDeclaration p, int defaultFlags) {
@@ -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
@@ -0,0 +1,7 @@
class Wrapper<T>() {
}
fun foo() {
val wrapper = new Wrapper<Int>()
return wrapper is Wrapper<String>
}
@@ -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
+46 -6
View File
@@ -2,29 +2,49 @@ package jet.typeinfo;
import jet.JetObject;
import java.util.Arrays;
/**
* @author abreslav
*/
public class TypeInfo<T> implements JetObject {
private TypeInfo<?> typeInfo;
private TypeInfo<?> typeArgument;
private Class<T> theClass;
private final Class<T> theClass;
private final TypeInfo[] typeParameters;
public TypeInfo(Class<T> theClass) {
this.theClass = theClass;
this.typeParameters = null;
}
private TypeInfo(TypeInfo<?> typeArgument) {
this.typeArgument = typeArgument;
public TypeInfo(Class<T> 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<T> 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;
}
}