work in progress on type info

This commit is contained in:
Alex Tkachman
2011-09-19 09:12:24 +03:00
parent fb4518fe04
commit 0dfef593c8
6 changed files with 482 additions and 27 deletions
@@ -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() {
@@ -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<JetDelegationSpecifier> 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<JetTypeParameter> 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(";");
}
}
}
+15
View File
@@ -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();
}
+281 -22
View File
@@ -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<T> implements JetObject {
private final static TypeInfoProjection[] EMPTY = new TypeInfoProjection[0];
public static final TypeInfo<Byte> BYTE_TYPE_INFO = getTypeInfo(Byte.class, false);
public static final TypeInfo<Short> SHORT_TYPE_INFO = getTypeInfo(Short.class, false);
public static final TypeInfo<Integer> INT_TYPE_INFO = getTypeInfo(Integer.class, false);
@@ -35,20 +39,20 @@ public abstract class TypeInfo<T> implements JetObject {
public static final TypeInfo<Tuple0> NULLABLE_TUPLE0_TYPE_INFO = getTypeInfo(Tuple0.class, true);
private TypeInfo<?> typeInfo;
private final Class<T> theClass;
private final Signature signature;
private final boolean nullable;
private final TypeInfoProjection[] projections;
private TypeInfo(Class<T> theClass, boolean nullable) {
this.theClass = theClass;
this.nullable = nullable;
this.projections = null;
this(theClass, nullable, EMPTY);
}
private TypeInfo(Class<T> 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 <T> TypeInfoProjection invariantProjection(final TypeInfo<T> typeInfo) {
@@ -85,7 +89,7 @@ public abstract class TypeInfo<T> 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<T> 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<T> 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<T> 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<T> implements JetObject {
return this;
}
}
public static class Signature {
final Class klazz;
final List<Var> variables;
final List<Type> superTypes;
final HashMap<Class,Signature> superSignatures = new HashMap<Class,Signature>();
public Signature(Class klazz, List<Var> variables, List<Type> 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<Class,Signature> 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<TypeProj> params;
public TypeReal(Class klazz, boolean nullable, List<TypeProj> params) {
super(nullable);
this.params = params;
this.klazz = klazz;
}
}
public static class Parser {
static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static final WeakHashMap<Class,Signature> map = new WeakHashMap<Class,Signature>();
int cur;
final char [] string;
Map<String,Integer> 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<String,Integer> variables;
List<Var> vars;
if(typeParameters == null || typeParameters.length == 0) {
variables = Collections.emptyMap();
vars = Collections.emptyList();
}
else {
variables = new HashMap<String, Integer>();
vars = new LinkedList<Var>();
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<Type> types = new LinkedList<Type>();
java.lang.reflect.Type genericSuperclass = klass.getGenericSuperclass();
return new Signature(klass, vars, types);
}
public List<Var> parseVars() {
List<Var> list = null;
while(cur != string.length && string[cur] == 'T') {
if(list == null)
list = new LinkedList<Var>();
list.add(parseVar());
}
List<Var> vars = list == null ? Collections.<Var>emptyList() : list;
if(vars.isEmpty())
variables = Collections.emptyMap();
else {
variables = new HashMap<String, Integer>();
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<Type> parseTypes() {
List<Type> types = null;
while(cur != string.length) {
if(types == null) {
types = new LinkedList<Type>();
}
types.add(parseType());
}
return types == null ? Collections.<Type>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<TypeProj> proj = null;
boolean nullable = false;
if(cur != string.length && string[cur] == '<') {
cur++;
while(string[cur] != '>') {
if(proj == null)
proj = new LinkedList<TypeProj>();
proj.add(parseProjection());
}
cur++;
}
if(cur != string.length && string[cur] == '?') {
cur++;
nullable = true;
}
return new TypeReal(aClass, nullable, proj == null ? Collections.<TypeProj>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);
}
}
}
@@ -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;
}
}
}
@@ -4,6 +4,8 @@ package jet.typeinfo;
* @author alex.tkachman
*/
public interface TypeInfoProjection {
TypeInfoProjection[] EMPTY_ARRAY = new TypeInfoProjection[0];
TypeInfoVariance getVariance();
TypeInfo getType();