Support serialization/deserialization of local classes

Most of these changes are aimed to support and correctly transform ClassId for
local classes, no actual bytes are written or read at the moment (see next
commits). See the comment on ClassId for clarification.

A hack in DescriptorSerializer which erased anonymous types to Any (which had
been breaking the consistency between descriptors resolved from sources and
from serialized information) is now gone
This commit is contained in:
Alexander Udalov
2015-02-13 23:28:15 +03:00
parent add9bb34cb
commit 0d05fca838
14 changed files with 216 additions and 176 deletions
+1
View File
@@ -35,6 +35,7 @@ message QualifiedNameTable {
enum Kind {
CLASS = 0;
PACKAGE = 1;
LOCAL = 2;
}
}
@@ -29,7 +29,8 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
public class DescriptorSerializer {
@@ -45,7 +46,7 @@ public class DescriptorSerializer {
@NotNull
public static DescriptorSerializer createTopLevel(@NotNull SerializerExtension extension) {
return new DescriptorSerializer(new StringTable(), new Interner<TypeParameterDescriptor>(), extension);
return new DescriptorSerializer(new StringTable(extension), new Interner<TypeParameterDescriptor>(), extension);
}
@NotNull
@@ -210,42 +211,14 @@ public class DescriptorSerializer {
builder.addValueParameter(local.valueParameter(valueParameterDescriptor));
}
builder.setReturnType(local.type(getSerializableReturnType(descriptor.getReturnType())));
//noinspection ConstantConditions
builder.setReturnType(local.type(descriptor.getReturnType()));
extension.serializeCallable(descriptor, builder, stringTable);
return builder;
}
@NotNull
private static JetType getSerializableReturnType(@NotNull JetType type) {
return isSerializableType(type) ? type : KotlinBuiltIns.getInstance().getAnyType();
}
/**
* @return true iff this type can be serialized. Types which correspond to type parameters, top-level classes, inner classes, and
* generic classes with serializable arguments are serializable. For other types (local classes, inner of local, etc.) it may be
* problematical to construct a FQ name for serialization
*/
private static boolean isSerializableType(@NotNull JetType type) {
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof TypeParameterDescriptor) {
return true;
}
else if (descriptor instanceof ClassDescriptor) {
for (TypeProjection projection : type.getArguments()) {
if (!isSerializableType(projection.getType())) {
return false;
}
}
return isTopLevelOrInnerClass((ClassDescriptor) descriptor);
}
else {
throw new IllegalStateException("Unknown type constructor: " + type);
}
}
private static int getAccessorFlags(@NotNull PropertyAccessorDescriptor accessor) {
return Flags.getAccessorFlags(
hasAnnotations(accessor),
@@ -855,6 +855,10 @@ public final class ProtoBuf {
* <code>PACKAGE = 1;</code>
*/
PACKAGE(1, 1),
/**
* <code>LOCAL = 2;</code>
*/
LOCAL(2, 2),
;
/**
@@ -865,6 +869,10 @@ public final class ProtoBuf {
* <code>PACKAGE = 1;</code>
*/
public static final int PACKAGE_VALUE = 1;
/**
* <code>LOCAL = 2;</code>
*/
public static final int LOCAL_VALUE = 2;
public final int getNumber() { return value; }
@@ -873,6 +881,7 @@ public final class ProtoBuf {
switch (value) {
case 0: return CLASS;
case 1: return PACKAGE;
case 2: return LOCAL;
default: return null;
}
}
@@ -52,4 +52,9 @@ public abstract class SerializerExtension {
@NotNull StringTable stringTable
) {
}
@NotNull
public String getLocalClassName(@NotNull ClassDescriptor descriptor) {
throw new UnsupportedOperationException("Local classes are unsupported: " + descriptor);
}
}
@@ -60,6 +60,11 @@ public class StringTable {
private final Interner<String> strings = new Interner<String>();
private final Interner<FqNameProto> qualifiedNames = new Interner<FqNameProto>();
private final SerializerExtension extension;
public StringTable(@NotNull SerializerExtension extension) {
this.extension = extension;
}
@NotNull
public List<String> getStrings() {
@@ -89,23 +94,33 @@ public class StringTable {
if (descriptor instanceof ClassDescriptor) {
builder.setKind(QualifiedName.Kind.CLASS);
}
builder.setShortName(getSimpleNameIndex(descriptor.getName()));
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
int shortName;
if (containingDeclaration instanceof PackageFragmentDescriptor) {
shortName = getSimpleNameIndex(descriptor.getName());
PackageFragmentDescriptor fragment = (PackageFragmentDescriptor) containingDeclaration;
if (!fragment.getFqName().isRoot()) {
builder.setParentQualifiedName(getFqNameIndex(fragment.getFqName()));
}
}
else if (containingDeclaration instanceof ClassDescriptor) {
shortName = getSimpleNameIndex(descriptor.getName());
ClassDescriptor outerClass = (ClassDescriptor) containingDeclaration;
builder.setParentQualifiedName(getFqNameIndex(outerClass));
}
else {
throw new IllegalStateException("FQ names are only stored for top-level or inner classes: " + descriptor);
if (descriptor instanceof ClassDescriptor) {
builder.setKind(QualifiedName.Kind.LOCAL);
shortName = getStringIndex(extension.getLocalClassName((ClassDescriptor) descriptor));
}
else {
throw new IllegalStateException("Package container should be a package: " + descriptor);
}
}
builder.setShortName(shortName);
return qualifiedNames.intern(new FqNameProto(builder));
}
@@ -17,13 +17,14 @@
package org.jetbrains.kotlin.serialization.deserialization;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.serialization.ProtoBuf;
import java.util.LinkedList;
import static org.jetbrains.kotlin.serialization.ProtoBuf.QualifiedNameTable.QualifiedName;
public class NameResolver {
@@ -61,42 +62,30 @@ public class NameResolver {
@NotNull
public ClassId getClassId(int index) {
QualifiedName fqNameProto = qualifiedNames.getQualifiedName(index);
assert fqNameProto.getKind() == ProtoBuf.QualifiedNameTable.QualifiedName.Kind.CLASS : "Not a class fqName: " + fqNameProto.getKind();
LinkedList<String> packageFqName = new LinkedList<String>();
LinkedList<String> relativeClassName = new LinkedList<String>();
boolean local = false;
StringBuilder relativeClassName = new StringBuilder();
QualifiedName packageFqNameProto = renderFqName(relativeClassName, fqNameProto, QualifiedName.Kind.CLASS);
FqName packageFqName;
if (packageFqNameProto != null) {
StringBuilder sb = new StringBuilder();
QualifiedName mustBeNull = renderFqName(sb, packageFqNameProto, QualifiedName.Kind.PACKAGE);
assert mustBeNull == null : "Prefix of an fqName must be all of kind PACKAGE";
packageFqName = new FqName(sb.toString());
}
else {
packageFqName = FqName.ROOT;
}
return new ClassId(packageFqName, new FqNameUnsafe(relativeClassName.toString()));
}
@Nullable
private QualifiedName renderFqName(StringBuilder sb, QualifiedName fqNameProto, QualifiedName.Kind kind) {
QualifiedName result = null;
if (fqNameProto.hasParentQualifiedName()) {
QualifiedName parentProto = qualifiedNames.getQualifiedName(fqNameProto.getParentQualifiedName());
if (kind == null || parentProto.getKind() == kind) {
result = renderFqName(sb, parentProto, kind);
sb.append(".");
}
else {
result = parentProto;
while (index != -1) {
QualifiedName proto = qualifiedNames.getQualifiedName(index);
String shortName = strings.getString(proto.getShortName());
switch (proto.getKind()) {
case CLASS:
relativeClassName.addFirst(shortName);
break;
case PACKAGE:
packageFqName.addFirst(shortName);
break;
case LOCAL:
relativeClassName.addFirst(shortName);
local = true;
break;
}
index = proto.getParentQualifiedName();
}
sb.append(strings.getString(fqNameProto.getShortName()));
return result;
return new ClassId(FqName.fromSegments(packageFqName), FqNameUnsafe.fromSegments(relativeClassName), local);
}
@NotNull
@@ -90,8 +90,14 @@ public class TypeDeserializer(
typeParameterDescriptors().get(proto.getId())?.getTypeConstructor() ?:
parent?.typeParameterTypeConstructor(proto)
private fun computeClassDescriptor(fqNameIndex: Int): ClassDescriptor? =
c.components.moduleDescriptor.findClassAcrossModuleDependencies(c.nameResolver.getClassId(fqNameIndex))
private fun computeClassDescriptor(fqNameIndex: Int): ClassDescriptor? {
val id = c.nameResolver.getClassId(fqNameIndex)
if (id.isLocal()) {
// Local classes can't be found in scopes
return c.components.deserializeClass(id)
}
return c.components.moduleDescriptor.findClassAcrossModuleDependencies(id)
}
fun typeArguments(protos: List<ProtoBuf.Type.Argument>): List<TypeProjection> =
protos.map { proto ->