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:
+27
-10
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user