Fix several bugs in serialization of inner generic classes

- Interner was working incorrectly with parents
- nested classes were serialized in codegen out of any context

 #KT-5660 Fixed
This commit is contained in:
Alexander Udalov
2014-11-24 16:07:40 +03:00
parent 36c65e4264
commit 1f68c94ce6
14 changed files with 205 additions and 25 deletions
@@ -37,16 +37,35 @@ public class DescriptorSerializer {
private final Interner<TypeParameterDescriptor> typeParameters;
private final SerializerExtension extension;
public DescriptorSerializer(@NotNull SerializerExtension extension) {
this(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
}
private DescriptorSerializer(NameTable nameTable, Interner<TypeParameterDescriptor> typeParameters, SerializerExtension extension) {
this.nameTable = nameTable;
this.typeParameters = typeParameters;
this.extension = extension;
}
@NotNull
public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
return new DescriptorSerializer(new NameTable(), new Interner<TypeParameterDescriptor>(), extension);
}
@NotNull
public static DescriptorSerializer create(@NotNull ClassDescriptor descriptor, @NotNull SerializerExtension extension) {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
DescriptorSerializer parentSerializer =
container instanceof ClassDescriptor
? create((ClassDescriptor) container, extension)
: createTopLevel(extension);
// Calculate type parameter ids for the outer class beforehand, as it would've had happened if we were always
// serializing outer classes before nested classes.
// Otherwise our interner can get wrong ids because we may serialize classes in any order.
DescriptorSerializer serializer = parentSerializer.createChildSerializer();
for (TypeParameterDescriptor typeParameter : descriptor.getTypeConstructor().getParameters()) {
serializer.typeParameters.intern(typeParameter);
}
return serializer;
}
private DescriptorSerializer createChildSerializer() {
return new DescriptorSerializer(nameTable, new Interner<TypeParameterDescriptor>(typeParameters), extension);
}
@@ -68,16 +87,14 @@ public class DescriptorSerializer {
builder.setFqName(getClassId(classDescriptor));
DescriptorSerializer local = createChildSerializer();
for (TypeParameterDescriptor typeParameterDescriptor : classDescriptor.getTypeConstructor().getParameters()) {
builder.addTypeParameter(local.typeParameter(typeParameterDescriptor));
builder.addTypeParameter(typeParameter(typeParameterDescriptor));
}
if (!KotlinBuiltIns.getInstance().isSpecialClassWithNoSupertypes(classDescriptor)) {
// Special classes (Any, Nothing) have no supertypes
for (JetType supertype : classDescriptor.getTypeConstructor().getSupertypes()) {
builder.addSupertype(local.type(supertype));
builder.addSupertype(type(supertype));
}
}
@@ -88,7 +105,7 @@ public class DescriptorSerializer {
}
else {
ProtoBuf.Class.PrimaryConstructor.Builder constructorBuilder = ProtoBuf.Class.PrimaryConstructor.newBuilder();
constructorBuilder.setData(local.callableProto(primaryConstructor));
constructorBuilder.setData(callableProto(primaryConstructor));
builder.setPrimaryConstructor(constructorBuilder);
}
}
@@ -99,7 +116,7 @@ public class DescriptorSerializer {
if (descriptor instanceof CallableMemberDescriptor) {
CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor;
if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
builder.addMember(local.callableProto(member));
builder.addMember(callableProto(member));
}
}
@@ -32,7 +32,7 @@ public final class Interner<T> {
public Interner(Interner<T> parent, @NotNull TObjectHashingStrategy<T> hashing) {
this.parent = parent;
this.firstIndex = parent == null ? 0 : parent.all.size();
this.firstIndex = parent != null ? parent.all.size() + parent.firstIndex : 0;
this.interned = new TObjectIntHashMap<T>(hashing);
}
@@ -40,30 +40,39 @@ public final class Interner<T> {
this(null, hashing);
}
@SuppressWarnings("unchecked")
public Interner(@Nullable Interner<T> parent) {
//noinspection unchecked
this(parent, TObjectHashingStrategy.CANONICAL);
}
public Interner() {
//noinspection unchecked
this((Interner) null);
this((Interner<T>) null);
}
public int intern(@NotNull T obj) {
assert parent == null || parent.all.size() == firstIndex : "Parent changed in parallel with child: indexes will be wrong";
if (parent != null && parent.interned.contains(obj)) {
return parent.intern(obj);
private int find(@NotNull T obj) {
assert parent == null || parent.all.size() + parent.firstIndex == firstIndex :
"Parent changed in parallel with child: indexes will be wrong";
if (parent != null) {
int index = parent.find(obj);
if (index >= 0) return index;
}
if (interned.contains(obj)) {
return interned.get(obj);
}
int index = firstIndex + interned.size();
return -1;
}
public int intern(@NotNull T obj) {
int index = find(obj);
if (index >= 0) return index;
index = firstIndex + interned.size();
interned.put(obj, index);
all.add(obj);
return index;
}
@NotNull
public List<T> getAllInternedObjects() {
return all;
}