From 0dfef593c833e8fcee16d4c15f2bd2d01c4edf02 Mon Sep 17 00:00:00 2001 From: Alex Tkachman Date: Mon, 19 Sep 2011 09:12:24 +0300 Subject: [PATCH] work in progress on type info --- .../codegen/ImplementationBodyCodegen.java | 12 +- .../jetbrains/jet/codegen/SignatureUtil.java | 77 +++++ stdlib/src/jet/typeinfo/JetSignature.java | 15 + stdlib/src/jet/typeinfo/TypeInfo.java | 303 ++++++++++++++++-- stdlib/src/jet/typeinfo/TypeInfoPattern.java | 100 ++++++ .../src/jet/typeinfo/TypeInfoProjection.java | 2 + 6 files changed, 482 insertions(+), 27 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/SignatureUtil.java create mode 100644 stdlib/src/jet/typeinfo/JetSignature.java create mode 100644 stdlib/src/jet/typeinfo/TypeInfoPattern.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index aead6e36f70..88346225fc3 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -10,12 +10,8 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeProjection; -import org.jetbrains.jet.lang.types.Variance; import org.jetbrains.jet.lexer.JetTokens; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; +import org.objectweb.asm.*; import org.objectweb.asm.commons.InstructionAdapter; import org.objectweb.asm.commons.Method; @@ -101,6 +97,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { interfaces.toArray(new String[interfaces.size()]) ); v.visitSource(myClass.getContainingFile().getName(), null); + + if(myClass instanceof JetClass) { + AnnotationVisitor annotationVisitor = v.visitAnnotation("Ljet/typeinfo/JetSignature;", true); + annotationVisitor.visit("value", SignatureUtil.classToSignature((JetClass)myClass, state.getBindingContext(), state.getTypeMapper())); + annotationVisitor.visitEnd(); + } } private String jvmName() { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/SignatureUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/SignatureUtil.java new file mode 100644 index 00000000000..0bddcbbb7ec --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/SignatureUtil.java @@ -0,0 +1,77 @@ +package org.jetbrains.jet.codegen; + +import jet.typeinfo.TypeInfoVariance; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.psi.JetDelegationSpecifier; +import org.jetbrains.jet.lang.psi.JetTypeParameter; +import org.jetbrains.jet.lang.psi.JetTypeReference; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeProjection; +import org.jetbrains.jet.lang.types.Variance; +import org.objectweb.asm.Type; + +import java.util.List; + +/** + * @author alex.tkachman + */ +public class SignatureUtil { + public static String classToSignature(JetClass type, BindingContext bindingContext, JetTypeMapper typeMapper) { + StringBuilder sb = new StringBuilder(); + genTypeParams(type, sb); + List delegationSpecifiers = type.getDelegationSpecifiers(); + for (JetDelegationSpecifier specifier : delegationSpecifiers) { + JetTypeReference typeReference = specifier.getTypeReference(); + JetType jetType = bindingContext.get(BindingContext.TYPE, typeReference); + genJetType(sb, jetType, typeMapper); + } + return sb.toString(); + } + + private static void genJetType(StringBuilder sb, JetType jetType, JetTypeMapper typeMapper) { + DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor(); + if(descriptor instanceof ClassDescriptor) { + JetType defaultType = ((ClassDescriptor) descriptor).getDefaultType(); + org.objectweb.asm.Type type = typeMapper.mapType(defaultType, OwnerKind.IMPLEMENTATION); + if(JetTypeMapper.isPrimitive(type)) { + type = JetTypeMapper.boxType(type); + } + sb.append(type.getDescriptor()); + } + else { + sb.append("T").append(descriptor.getName()).append(";"); + } + if(!jetType.getArguments().isEmpty()) { + sb.append("<"); + for (TypeProjection typeProjection : jetType.getArguments()) { + if(typeProjection.getProjectionKind() == Variance.IN_VARIANCE) + sb.append("in "); + if(typeProjection.getProjectionKind() == Variance.OUT_VARIANCE) + sb.append("out "); + genJetType(sb, typeProjection.getType(), typeMapper); + } + sb.append(">"); + } + if(jetType.isNullable()) + sb.append("?"); + } + + private static void genTypeParams(JetClass type, StringBuilder sb) { + List parameters = type.getTypeParameterList().getParameters(); + for(JetTypeParameter param : parameters) { + sb.append("T"); + Variance variance = param.getVariance(); + if(variance == Variance.IN_VARIANCE) + sb.append("in "); + else if(variance == Variance.OUT_VARIANCE) + sb.append("out "); + sb.append(param.getName()); + sb.append(";"); + } + } +} diff --git a/stdlib/src/jet/typeinfo/JetSignature.java b/stdlib/src/jet/typeinfo/JetSignature.java new file mode 100644 index 00000000000..ab2455903c2 --- /dev/null +++ b/stdlib/src/jet/typeinfo/JetSignature.java @@ -0,0 +1,15 @@ +package jet.typeinfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.TypeVariable; +import java.util.*; + +/** + * @author alex.tkachman + */ +@Retention(RetentionPolicy.RUNTIME) +@interface JetSignature { + String value(); + +} diff --git a/stdlib/src/jet/typeinfo/TypeInfo.java b/stdlib/src/jet/typeinfo/TypeInfo.java index cd97f0f8547..08c9df501b2 100644 --- a/stdlib/src/jet/typeinfo/TypeInfo.java +++ b/stdlib/src/jet/typeinfo/TypeInfo.java @@ -4,7 +4,9 @@ import jet.JetObject; import jet.Tuple0; import java.lang.reflect.Field; -import java.util.Arrays; +import java.lang.reflect.TypeVariable; +import java.util.*; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author abreslav @@ -12,6 +14,8 @@ import java.util.Arrays; * @author alex.tkachman */ public abstract class TypeInfo implements JetObject { + private final static TypeInfoProjection[] EMPTY = new TypeInfoProjection[0]; + public static final TypeInfo BYTE_TYPE_INFO = getTypeInfo(Byte.class, false); public static final TypeInfo SHORT_TYPE_INFO = getTypeInfo(Short.class, false); public static final TypeInfo INT_TYPE_INFO = getTypeInfo(Integer.class, false); @@ -35,20 +39,20 @@ public abstract class TypeInfo implements JetObject { public static final TypeInfo NULLABLE_TUPLE0_TYPE_INFO = getTypeInfo(Tuple0.class, true); private TypeInfo typeInfo; - private final Class theClass; + private final Signature signature; private final boolean nullable; private final TypeInfoProjection[] projections; private TypeInfo(Class theClass, boolean nullable) { - this.theClass = theClass; - this.nullable = nullable; - this.projections = null; + this(theClass, nullable, EMPTY); } private TypeInfo(Class theClass, boolean nullable, TypeInfoProjection[] projections) { - this.theClass = theClass; + 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) { @@ -85,7 +89,7 @@ public abstract class TypeInfo implements JetObject { public final Object getClassObject() { try { - final Class implClass = theClass.getClassLoader().loadClass(theClass.getCanonicalName()); + final Class implClass = signature.klazz.getClassLoader().loadClass(signature.klazz.getCanonicalName()); final Field classobj = implClass.getField("$classobj"); return classobj.get(null); } catch (Exception e) { @@ -100,25 +104,23 @@ public abstract class TypeInfo implements JetObject { return ((JetObject) obj).getTypeInfo().isSubtypeOf(this); } - return theClass.isAssignableFrom(obj.getClass()); // TODO + return signature.klazz.isAssignableFrom(obj.getClass()); // TODO } public final boolean isSubtypeOf(TypeInfo superType) { if (nullable && !superType.nullable) { return false; } - if (!superType.theClass.isAssignableFrom(theClass)) { + if (!superType.signature.klazz.isAssignableFrom(signature.klazz)) { return false; } - if (projections != null) { - 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].equals(superType.projections[i])) { - 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].equals(superType.projections[i])) { + return false; } } return true; @@ -148,7 +150,7 @@ public abstract class TypeInfo implements JetObject { TypeInfo typeInfo = (TypeInfo) o; - if (!theClass.equals(typeInfo.theClass)) return false; + if (!signature.klazz.equals(typeInfo.signature.klazz)) return false; if (nullable != typeInfo.nullable) return false; if (!Arrays.equals(projections, typeInfo.projections)) return false; @@ -157,13 +159,13 @@ public abstract class TypeInfo implements JetObject { @Override public final int hashCode() { - return 31 * theClass.hashCode() + (projections != null ? Arrays.hashCode(projections) : 0); + return 31 * signature.klazz.hashCode() + Arrays.hashCode(projections); } @Override public final String toString() { - StringBuilder sb = new StringBuilder().append(theClass.getName()); - if (projections != null && projections.length != 0) { + 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(","); @@ -196,4 +198,261 @@ public abstract class TypeInfo implements JetObject { return this; } } + + public static class Signature { + final Class klazz; + final List variables; + final List superTypes; + + final HashMap superSignatures = new HashMap(); + + 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()); + } + } + } + } + } + + 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(); + + int cur; + final char [] string; + + Map variables; + + final ClassLoader classLoader; + + Parser(String string, ClassLoader classLoader) { + this.classLoader = classLoader; + this.string = string.toCharArray(); + } + + public static Signature parse(Class klass) { + lock.readLock().lock(); + Signature sig = map.get(klass); + lock.readLock().unlock(); + if (sig == null) { + lock.writeLock().lock(); + + sig = map.get(klass); + if (sig == null) { + sig = internalParse(klass); + map.put(klass, sig); + } + + lock.writeLock().unlock(); + } + return sig; + } + + private static Signature internalParse(Class klass) { + JetSignature annotation = (JetSignature) klass.getAnnotation(JetSignature.class); + if(annotation != null) { + String value = annotation.value(); + if(value != null) { + Parser parser = new Parser(value, klass.getClassLoader()); + return new Signature(klass, parser.parseVars(), parser.parseTypes()); + } + } + return getGenericSignature(klass); + } + + private static Signature getGenericSignature(Class klass) { + // todo complete impl + + TypeVariable[] typeParameters = klass.getTypeParameters(); + Map variables; + List vars; + if(typeParameters == null || typeParameters.length == 0) { + variables = Collections.emptyMap(); + vars = Collections.emptyList(); + } + else { + variables = new HashMap(); + 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)); + } + } + + List types = new LinkedList(); + java.lang.reflect.Type genericSuperclass = klass.getGenericSuperclass(); + return new Signature(klass, vars, types); + } + + public List parseVars() { + List list = null; + while(cur != string.length && string[cur] == 'T') { + if(list == null) + list = new LinkedList(); + 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; + } + + private Var parseVar() { + TypeInfoVariance variance = parseVariance(); + String name = parseName(); + return new Var(name, variance); + } + + private String parseName() { + int c = ++cur; // skip 'T' + while (string[c] != ';') { + c++; + } + String name = new String(string, cur, c - cur); + cur = c+1; + return name; + } + + private TypeInfoVariance parseVariance() { + if(string[cur] == 'i' && string[cur+1] == 'n' && string[cur+2] == ' ' ) { + cur += 3; + return TypeInfoVariance.IN_VARIANCE; + } + else if (string[cur] == 'o' && string[cur+1] == 'u' && string[cur+2] == 't' && string[cur+2] == ' ') { + cur += 4; + return TypeInfoVariance.OUT_VARIANCE; + } + else { + return TypeInfoVariance.INVARIANT; + } + } + + public List parseTypes() { + List types = null; + while(cur != string.length) { + if(types == null) { + types = new LinkedList(); + } + types.add(parseType()); + } + return types == null ? Collections.emptyList() : types; + } + + private Type parseType() { + switch (string[cur]) { + case 'L': + String name = parseName(); + Class aClass; + try { + aClass = classLoader.loadClass(name.replace('/','.')); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + List proj = null; + boolean nullable = false; + if(cur != string.length && string[cur] == '<') { + cur++; + while(string[cur] != '>') { + if(proj == null) + proj = new LinkedList(); + proj.add(parseProjection()); + } + cur++; + } + if(cur != string.length && string[cur] == '?') { + cur++; + nullable = true; + } + return new TypeReal(aClass, nullable, proj == null ? Collections.emptyList() : proj); + + case 'T': + return parseTypeVar(); + + default: + throw new IllegalStateException(new String(string)); + } + } + + private Type parseTypeVar() { + String name = parseName(); + boolean nullable = false; + if(string[cur] == '?') { + nullable = true; + cur++; + } + + return new TypeVar(nullable, variables.get(name)); + } + + private TypeProj parseProjection() { + TypeInfoVariance variance = parseVariance(); + Type type = parseType(); + return new TypeProj(variance, type); + } + } } diff --git a/stdlib/src/jet/typeinfo/TypeInfoPattern.java b/stdlib/src/jet/typeinfo/TypeInfoPattern.java new file mode 100644 index 00000000000..eff0ae819cc --- /dev/null +++ b/stdlib/src/jet/typeinfo/TypeInfoPattern.java @@ -0,0 +1,100 @@ +package jet.typeinfo; + +/** + * This class represents how type info of super class should be matched against type parameters of subclass. + * + * For each subclass we create such pattern for subclass itself and all it super classes either during + * static initialization(or maybe when needed). + * + * For each instance of parametrized type we keep it type info, which can be understood as binding of type parameter variables. + * + * @author alex.tkachman + */ +public interface TypeInfoPattern { + TypeInfo substitute(TypeInfo[] variables); + + class Var implements TypeInfoPattern { + public final int index; + + public Var(int index) { + this.index = index; + } + + @Override + public TypeInfo substitute(TypeInfo[] variables) { + return variables[index]; + } + } + + class Const implements TypeInfoPattern { + private final TypeInfo typeInfo; + + public Const(TypeInfo typeInfo) { + this.typeInfo = typeInfo; + } + + @Override + public TypeInfo substitute(TypeInfo[] variables) { + return typeInfo; + } + } + + class Pattern implements TypeInfoPattern { + public static final TypeInfoPatternProjection[] EMPTY = new TypeInfoPatternProjection[0]; + public final Class klazz; + public final boolean nullable; + public final TypeInfoPatternProjection [] patterns; + + public Pattern(Class klazz, boolean nullable, TypeInfoPatternProjection[] patterns) { + this.klazz = klazz; + this.nullable = nullable; + this.patterns = patterns; + } + + public Pattern(Class klazz, boolean nullable) { + this.klazz = klazz; + this.nullable = nullable; + this.patterns = EMPTY; + } + + @Override + public TypeInfo substitute(TypeInfo[] variables) { + return TypeInfo.getTypeInfo(klazz, nullable, TypeInfoPatternProjection.substitute(patterns, variables)); + } + } + + class TypeInfoPatternProjection { + public final TypeInfoVariance variance; + + public final TypeInfoPattern pattern; + + public TypeInfoPatternProjection(TypeInfoVariance variance, TypeInfoPattern pattern) { + this.variance = variance; + this.pattern = pattern; + } + + public TypeInfoProjection substitute(final TypeInfo[] variables) { + return new TypeInfoProjection() { + @Override + public TypeInfoVariance getVariance() { + return variance; + } + + @Override + public TypeInfo getType() { + return pattern.substitute(variables); + } + }; + } + + public static TypeInfoProjection[] substitute(TypeInfoPatternProjection[] patterns, TypeInfo[] variables) { + if(patterns.length == 0) + return TypeInfoProjection.EMPTY_ARRAY; + TypeInfoProjection [] projections = new TypeInfoProjection[patterns.length]; + for (int i = 0; i < projections.length; i++) { + projections[i] = patterns[i].substitute(variables); + } + return projections; + } + } +} diff --git a/stdlib/src/jet/typeinfo/TypeInfoProjection.java b/stdlib/src/jet/typeinfo/TypeInfoProjection.java index ad2553d6085..09d5fdc264c 100644 --- a/stdlib/src/jet/typeinfo/TypeInfoProjection.java +++ b/stdlib/src/jet/typeinfo/TypeInfoProjection.java @@ -4,6 +4,8 @@ package jet.typeinfo; * @author alex.tkachman */ public interface TypeInfoProjection { + TypeInfoProjection[] EMPTY_ARRAY = new TypeInfoProjection[0]; + TypeInfoVariance getVariance(); TypeInfo getType();