diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassId.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassId.java new file mode 100644 index 00000000000..c9df568a3f9 --- /dev/null +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassId.java @@ -0,0 +1,62 @@ +package org.jetbrains.jet.descriptors.serialization; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; + +public final class ClassId { + private final FqName packageFqName; + private final FqName relativeClassName; + + public ClassId(@NotNull FqName packageFqName, @NotNull FqName relativeClassName) { + this.packageFqName = packageFqName; + assert !relativeClassName.isRoot() : "Class name must not be root. " + packageFqName; + this.relativeClassName = relativeClassName; + } + + @NotNull + public FqName getPackageFqName() { + return packageFqName; + } + + @NotNull + public FqName getRelativeClassName() { + return relativeClassName; + } + + @NotNull + public ClassId createNestedClassId(@NotNull Name name) { + return new ClassId(getPackageFqName(), relativeClassName.child(name)); + } + + public FqName asSingleFqName() { + if (packageFqName.isRoot()) return relativeClassName; + return new FqName(packageFqName.getFqName() + "." + relativeClassName.getFqName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ClassId id = (ClassId) o; + + if (!packageFqName.equals(id.packageFqName)) return false; + if (!relativeClassName.equals(id.relativeClassName)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = packageFqName.hashCode(); + result = 31 * result + relativeClassName.hashCode(); + return result; + } + + @Override + public String toString() { + if (packageFqName.isRoot()) return "/" + relativeClassName; + return packageFqName.toString().replace('.', '/') + "/" + relativeClassName; + } +} diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassResolver.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassResolver.java index 29ec1090892..5a170bc2403 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassResolver.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassResolver.java @@ -19,18 +19,17 @@ package org.jetbrains.jet.descriptors.serialization; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.resolve.name.FqName; public interface ClassResolver { ClassResolver EMPTY = new ClassResolver() { @Nullable @Override - public ClassDescriptor findClass(@NotNull FqName fqName) { + public ClassDescriptor findClass(@NotNull ClassId classId) { return null; } }; @Nullable - ClassDescriptor findClass(@NotNull FqName fqName); + ClassDescriptor findClass(@NotNull ClassId classId); } diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/DescriptorSerializer.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/DescriptorSerializer.java index 2b61bfdf345..f9048f7c3f1 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/DescriptorSerializer.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/DescriptorSerializer.java @@ -23,6 +23,8 @@ import org.jetbrains.jet.lang.types.TypeConstructor; import org.jetbrains.jet.lang.types.TypeProjection; import org.jetbrains.jet.lang.types.Variance; +import java.util.Collection; + public class DescriptorSerializer { // TODO: flags @@ -72,18 +74,13 @@ public class DescriptorSerializer { builder.addSupertypes(local.type(supertype)); } - // TODO: nested classes - ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); if (primaryConstructor != null) { builder.setPrimaryConstructor(local.callableProto(primaryConstructor)); } // TODO: other constructors - // TODO: class object - for (DeclarationDescriptor descriptor : classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()) { - // TODO: other than functions if (descriptor instanceof CallableMemberDescriptor) { CallableMemberDescriptor member = (CallableMemberDescriptor) descriptor; if (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue; @@ -91,6 +88,23 @@ public class DescriptorSerializer { } } + Collection nestedClasses = classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors(); + for (DeclarationDescriptor descriptor : nestedClasses) { + ClassDescriptor nestedClass = (ClassDescriptor) descriptor; + int nameIndex = nameTable.getSimpleNameIndex(nestedClass.getName()); + if (nestedClass.getKind().isObject()) { + builder.addNestedObjectNames(nameIndex); + } + else { + builder.addNestedClassNames(nameIndex); + } + } + + if (classDescriptor.getClassObjectDescriptor() != null) { + // false is default + builder.setClassObjectPresent(true); + } + return builder; } diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NameResolver.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NameResolver.java index cfb1d683bb8..545d5e3b9ce 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NameResolver.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NameResolver.java @@ -22,6 +22,8 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; +import static org.jetbrains.jet.descriptors.serialization.ProtoBuf.QualifiedNameTable.*; + public class NameResolver { private final ProtoBuf.SimpleNameTable simpleNames; private final ProtoBuf.QualifiedNameTable qualifiedNames; @@ -51,26 +53,54 @@ public class NameResolver { return classDescriptors[fqNameIndex]; } - ProtoBuf.QualifiedNameTable.QualifiedName fqNameProto = qualifiedNames.getQualifiedNames(fqNameIndex); - assert fqNameProto.getKind() == ProtoBuf.QualifiedNameTable.QualifiedName.Kind.CLASS : "Not a class fqName: " + getFqName(fqNameIndex); + QualifiedName fqNameProto = qualifiedNames.getQualifiedNames(fqNameIndex); + assert fqNameProto.getKind() == QualifiedName.Kind.CLASS : "Not a class fqName: " + getClassId(fqNameIndex); - return classResolver.findClass(getFqName(fqNameIndex)); + + ClassId classId = getClassId(fqNameIndex); + return classResolver.findClass(classId); } @NotNull - public FqName getFqName(int index) { - ProtoBuf.QualifiedNameTable.QualifiedName fqNameProto = qualifiedNames.getQualifiedNames(index); - StringBuilder sb = renderFqName(new StringBuilder(), fqNameProto); - return new FqName(sb.toString()); + public ClassId getClassId(int index) { + QualifiedName fqNameProto = qualifiedNames.getQualifiedNames(index); + + 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 FqName(relativeClassName.toString())); } - private StringBuilder renderFqName(StringBuilder sb, ProtoBuf.QualifiedNameTable.QualifiedName fqNameProto) { + @Nullable + private QualifiedName renderFqName( + StringBuilder sb, + QualifiedName fqNameProto, + QualifiedName.Kind kind + ) { + QualifiedName result = null; if (fqNameProto.hasParentQualifiedName()) { - ProtoBuf.QualifiedNameTable.QualifiedName parentProto = qualifiedNames.getQualifiedNames(fqNameProto.getParentQualifiedName()); - renderFqName(sb, parentProto); - sb.append("."); + QualifiedName parentProto = qualifiedNames.getQualifiedNames(fqNameProto.getParentQualifiedName()); + if (kind == null || parentProto.getKind() == kind) { + result = renderFqName(sb, parentProto, kind); + sb.append("."); + } + else { + result = parentProto; + } } sb.append(simpleNames.getNames(fqNameProto.getShortName())); - return sb; + return result; } } diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NestedClassResolver.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NestedClassResolver.java new file mode 100644 index 00000000000..45d5f4e4bc2 --- /dev/null +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/NestedClassResolver.java @@ -0,0 +1,14 @@ +package org.jetbrains.jet.descriptors.serialization; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.resolve.name.Name; + +public interface NestedClassResolver { + @Nullable + ClassDescriptor resolveNestedClass(@NotNull ClassDescriptor outerClass, @NotNull Name name); + + @Nullable + ClassDescriptor resolveClassObject(@NotNull ClassDescriptor outerClass); +} diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/TypeDeserializer.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/TypeDeserializer.java index d87deeb8b5e..df9adf2e272 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/TypeDeserializer.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/TypeDeserializer.java @@ -119,7 +119,7 @@ public class TypeDeserializer { TypeConstructor typeConstructor = typeConstructor(constructorProto); if (typeConstructor == null) { String message = constructorProto.getKind() == ProtoBuf.Type.Constructor.Kind.CLASS - ? nameResolver.getFqName(id).asString() + ? nameResolver.getClassId(id).asSingleFqName().asString() : "Unknown type parameter " + id; typeConstructor = ErrorUtils.createErrorType(message).getConstructor(); } diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedClassDescriptor.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedClassDescriptor.java index 84d7d8f87c3..8162c187ee5 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedClassDescriptor.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedClassDescriptor.java @@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorBase; import org.jetbrains.jet.lang.descriptors.impl.ReceiverParameterDescriptorImpl; import org.jetbrains.jet.lang.resolve.OverrideResolver; import org.jetbrains.jet.lang.resolve.TraceUtil; +import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNullableImpl; import org.jetbrains.jet.lang.resolve.lazy.storage.NullableLazyValue; import org.jetbrains.jet.lang.resolve.lazy.storage.NullableLazyValueImpl; import org.jetbrains.jet.lang.resolve.name.Name; @@ -45,6 +46,12 @@ public class DeserializedClassDescriptor extends ClassDescriptorBase implements private final NullableLazyValue primaryConstructor; + private final NestedClassResolver nestedClassResolver; + private final NullableLazyValue classObjectDescriptor; + + private final NestedClassDescriptors nestedClasses; + private final NestedClassDescriptors nestedObjects; + private final Name name; private final DeclarationDescriptor containingDeclaration; private final DeserializedClassTypeConstructor typeConstructor; @@ -56,7 +63,7 @@ public class DeserializedClassDescriptor extends ClassDescriptorBase implements public DeserializedClassDescriptor( @NotNull DeclarationDescriptor containingDeclaration, @NotNull NameResolver nameResolver, - @NotNull ClassResolver classResolver, + @NotNull NestedClassResolver _nestedClassResolver, @NotNull ProtoBuf.Class classProto, @Nullable TypeDeserializer outerTypeDeserializer ) { @@ -82,6 +89,35 @@ public class DeserializedClassDescriptor extends ClassDescriptorBase implements return computePrimaryConstructor(); } }; + + this.nestedClassResolver = _nestedClassResolver; + this.classObjectDescriptor = new NullableLazyValueImpl() { + @Override + protected ClassDescriptor doCompute() { + return computeClassObjectDecriptor(); + } + }; + this.nestedClasses = new NestedClassDescriptors(stringSet(classProto.getNestedClassNamesList(), nameResolver)) { + @Override + protected ClassDescriptor resolveNestedClass(@NotNull Name name) { + return nestedClassResolver.resolveNestedClass(DeserializedClassDescriptor.this, name); + } + }; + this.nestedObjects = new NestedClassDescriptors(stringSet(classProto.getNestedObjectNamesList(), nameResolver)) { + @Override + protected ClassDescriptor resolveNestedClass(@NotNull Name name) { + return nestedClassResolver.resolveNestedClass(DeserializedClassDescriptor.this, name); + } + }; + } + + @NotNull + private static Set stringSet(@NotNull List nameIndices, @NotNull NameResolver nameResolver) { + Set result = new HashSet(nameIndices.size()); + for (Integer index : nameIndices) { + result.add(nameResolver.getName(index).getName()); + } + return result; } @NotNull @@ -180,10 +216,19 @@ public class DeserializedClassDescriptor extends ClassDescriptorBase implements return classObjectDescriptor == null ? null : classObjectDescriptor.getDefaultType(); } + @Nullable + private ClassDescriptor computeClassObjectDecriptor() { + if (!classProto.getClassObjectPresent()) { + return null; + } + + return nestedClassResolver.resolveClassObject(this); + } + @Nullable @Override public ClassDescriptor getClassObjectDescriptor() { - return null; // TODO + return classObjectDescriptor.compute(); } @NotNull @@ -314,12 +359,54 @@ public class DeserializedClassDescriptor extends ClassDescriptorBase implements @Nullable @Override protected ClassifierDescriptor getClassDescriptor(@NotNull Name name) { - return null; // TODO inner classes + return classDescriptor.nestedClasses.fun(name); } @Override protected void addAllClassDescriptors(@NotNull Collection result) { - // TODO inner classes + result.addAll(classDescriptor.nestedClasses.getAllDescriptors()); + } + + @Nullable + @Override + public ClassDescriptor getObjectDescriptor(@NotNull Name name) { + return classDescriptor.nestedObjects.fun(name); + } + + @NotNull + @Override + protected Collection computeAllObjectDescriptors() { + return classDescriptor.nestedObjects.getAllDescriptors(); + } + } + + private abstract static class NestedClassDescriptors extends MemoizedFunctionToNullableImpl { + private final Set declaredNames; + + public NestedClassDescriptors(@NotNull Set declaredNames) { + this.declaredNames = declaredNames; + } + + @Nullable + @Override + protected final ClassDescriptor doCompute(@NotNull Name name) { + if (!declaredNames.contains(name.getName())) return null; + + return resolveNestedClass(name); + } + + protected abstract ClassDescriptor resolveNestedClass(@NotNull Name name); + + @NotNull + public Collection getAllDescriptors() { + Collection result = new ArrayList(declaredNames.size()); + for (String name : declaredNames) { + ClassDescriptor descriptor = fun(Name.identifier(name)); + if (descriptor != null) { + result.add(descriptor); + } + } + return result; } } } diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedMemberScope.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedMemberScope.java index 1db334732db..a348ccbdd76 100644 --- a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedMemberScope.java +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/descriptors/DeserializedMemberScope.java @@ -54,6 +54,7 @@ public abstract class DeserializedMemberScope implements JetScope { private final MemoizedFunctionToNotNull> functions; private final MemoizedFunctionToNotNull> properties; private final NotNullLazyValue> allDescriptors; + private final NotNullLazyValue> objectDescriptors; public DeserializedMemberScope( @NotNull DeclarationDescriptor containingDeclaration, @@ -85,6 +86,13 @@ public abstract class DeserializedMemberScope implements JetScope { return computeAllDescriptors(); } }; + this.objectDescriptors = new NotNullLazyValueImpl>() { + @NotNull + @Override + protected Collection doCompute() { + return computeAllObjectDescriptors(); + } + }; } @NotNull @@ -164,14 +172,15 @@ public abstract class DeserializedMemberScope implements JetScope { @Nullable @Override - public ClassDescriptor getObjectDescriptor(@NotNull Name name) { - return null; // TODO - } + public abstract ClassDescriptor getObjectDescriptor(@NotNull Name name); + + @NotNull + protected abstract Collection computeAllObjectDescriptors(); @NotNull @Override public Collection getObjectDescriptors() { - return Collections.emptyList(); // TODO + return objectDescriptors.compute(); } @Nullable diff --git a/compiler/tests/org/jetbrains/jet/descriptors/serialization/AbstractDescriptorSerializationTest.java b/compiler/tests/org/jetbrains/jet/descriptors/serialization/AbstractDescriptorSerializationTest.java index 46951b34a3c..99ed8c51933 100644 --- a/compiler/tests/org/jetbrains/jet/descriptors/serialization/AbstractDescriptorSerializationTest.java +++ b/compiler/tests/org/jetbrains/jet/descriptors/serialization/AbstractDescriptorSerializationTest.java @@ -17,8 +17,10 @@ package org.jetbrains.jet.descriptors.serialization; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.protobuf.MessageLite; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.NullableFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.ConfigurationKind; @@ -35,6 +37,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap; +import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment; import org.jetbrains.jet.lang.resolve.lazy.LazyResolveTestUtil; import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNullable; @@ -45,7 +48,6 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.test.util.NamespaceComparator; import java.io.*; @@ -53,6 +55,8 @@ import java.util.*; public abstract class AbstractDescriptorSerializationTest extends KotlinTestWithEnvironment { + public static final Name TEST_PACKAGE_NAME = Name.identifier("test"); + @Override protected JetCoreEnvironment createEnvironment() { return createEnvironmentWithMockJdk(ConfigurationKind.JDK_ONLY); @@ -64,7 +68,7 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith JetTestUtils.createFile(ktFile.getName(), FileUtil.loadFile(ktFile), getProject()) ), getEnvironment()); - NamespaceDescriptor testNamespace = moduleDescriptor.getNamespace(FqName.topLevel(Name.identifier("test"))); + NamespaceDescriptor testNamespace = moduleDescriptor.getNamespace(FqName.topLevel(TEST_PACKAGE_NAME)); assert testNamespace != null; JavaDescriptorResolver javaDescriptorResolver = new InjectorForJavaDescriptorResolver( @@ -100,45 +104,61 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith } private static NamespaceDescriptor getDeserializedDescriptors( - final JavaDescriptorResolver javaDescriptorResolver, + JavaDescriptorResolver javaDescriptorResolver, List classes, List callables ) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - serialize(classes, callables, out); + ByteArrayOutputStream serializedCallables = new ByteArrayOutputStream(); + Map serializedClasses = Maps.newHashMap(); + serialize(classes, callables, serializedClasses, serializedCallables); - InputStream in = new ByteArrayInputStream(out.toByteArray()); + final Map classMetadata = Maps.newHashMap(); - ProtoBuf.SimpleNameTable simpleNames = ProtoBuf.SimpleNameTable.parseDelimitedFrom(in); - ProtoBuf.QualifiedNameTable qualifiedNames = ProtoBuf.QualifiedNameTable.parseDelimitedFrom(in); + for (Map.Entry entry : serializedClasses.entrySet()) { + ClassDescriptor classDescriptor = entry.getKey(); + byte[] bytes = entry.getValue(); - List classProtos = Lists.newArrayList(); - List callableProtos = Lists.newArrayList(); + ByteArrayInputStream in = new ByteArrayInputStream(bytes); - for (int i = 0; i < classes.size(); i++) { - ProtoBuf.Class proto = ProtoBuf.Class.parseDelimitedFrom(in); - classProtos.add(proto); - } + ProtoBuf.SimpleNameTable simpleNames = ProtoBuf.SimpleNameTable.parseDelimitedFrom(in); + ProtoBuf.QualifiedNameTable qualifiedNames = ProtoBuf.QualifiedNameTable.parseDelimitedFrom(in); + ProtoBuf.Class proto = ProtoBuf.Class.parseFrom(in); - for (int i = 0; i < callables.size(); i++) { - ProtoBuf.Callable proto = ProtoBuf.Callable.parseDelimitedFrom(in); - callableProtos.add(proto); + classMetadata.put(getClassNameString(classDescriptor), new ClassMetadata(simpleNames, qualifiedNames, proto)); } NamespaceDescriptorImpl namespace = createTestNamespace(); ClassResolver javaClassResolver = new JavaClassResolver(javaDescriptorResolver); ClassResolverImpl classResolver = new ClassResolverImpl( - javaClassResolver, namespace, simpleNames, qualifiedNames, classProtos + javaClassResolver, namespace, + new NullableFunction() { + @Nullable + @Override + public ClassMetadata fun(String fqName) { + return classMetadata.get(fqName); + } + } ); - for (ProtoBuf.Class proto : classProtos) { - FqName fqName = new FqName("test." + classResolver.getNameResolver().getName(proto.getName())); - ClassDescriptor descriptor = classResolver.findClass(fqName); - assert descriptor != null : "Class not loaded: " + fqName; + for (ClassDescriptor classDescriptor : classes) { + ClassId classId = new ClassId(DescriptorUtils.getFQName(classDescriptor.getContainingDeclaration()).toSafe(), + FqName.topLevel(classDescriptor.getName())); + ClassDescriptor descriptor = classResolver.findClass(classId); + assert descriptor != null : "Class not loaded: " + classId; namespace.getMemberScope().addClassifierDescriptor(descriptor); } + ByteArrayInputStream in = new ByteArrayInputStream(serializedCallables.toByteArray()); + ProtoBuf.SimpleNameTable simpleNames = ProtoBuf.SimpleNameTable.parseDelimitedFrom(in); + ProtoBuf.QualifiedNameTable qualifiedNames = ProtoBuf.QualifiedNameTable.parseDelimitedFrom(in); + + List callableProtos = Lists.newArrayList(); + for (int i = 0; i < callables.size(); i++) { + ProtoBuf.Callable proto = ProtoBuf.Callable.parseDelimitedFrom(in); + callableProtos.add(proto); + } + DescriptorDeserializer deserializer = new DescriptorDeserializer((DescriptorDeserializer) null, namespace, new NameResolver(simpleNames, qualifiedNames, classResolver)); for (ProtoBuf.Callable proto : callableProtos) { @@ -158,6 +178,13 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith return namespace; } + private static String getClassNameString(ClassDescriptor classDescriptor) { + if (classDescriptor.getKind() == ClassKind.CLASS_OBJECT) { + return getClassNameString((ClassDescriptor) classDescriptor.getContainingDeclaration()) + "." + JvmAbi.CLASS_OBJECT_CLASS_NAME; + } + return DescriptorUtils.getFQName(classDescriptor).getFqName(); + } + private static NamespaceDescriptorImpl createTestNamespace() { ModuleDescriptorImpl module = new ModuleDescriptorImpl(Name.special(""), JavaBridgeConfiguration.ALL_JAVA_IMPORTS, JavaToKotlinClassMap.getInstance()); @@ -165,30 +192,66 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith new NamespaceDescriptorImpl(module, Collections.emptyList(), JetPsiUtil.ROOT_NAMESPACE_NAME); module.setRootNamespace(rootNamespace); NamespaceDescriptorImpl test = - new NamespaceDescriptorImpl(rootNamespace, Collections.emptyList(), Name.identifier("test")); + new NamespaceDescriptorImpl(rootNamespace, Collections.emptyList(), TEST_PACKAGE_NAME); test.initialize(new WritableScopeImpl(JetScope.EMPTY, test, RedeclarationHandler.DO_NOTHING, "members of test namespace")); return test; } public static void serialize( - List classes, List callables, @NotNull OutputStream out + List classes, List callables, + Map serializedClasses, OutputStream serializedCallables ) throws IOException { + + serializeClasses(classes, serializedClasses); + DescriptorSerializer descriptorSerializer = new DescriptorSerializer(); - List messages = Lists.newArrayList(); - for (ClassDescriptor classDescriptor : classes) { - messages.add(descriptorSerializer.classProto(classDescriptor).build()); - - } - for (CallableMemberDescriptor callable : callables) { messages.add(descriptorSerializer.callableProto(callable).build()); } - NameSerializationUtil.serializeNameTable(out, descriptorSerializer.getNameTable()); + NameSerializationUtil.serializeNameTable(serializedCallables, descriptorSerializer.getNameTable()); for (MessageLite message : messages) { - message.writeDelimitedTo(out); + message.writeDelimitedTo(serializedCallables); + } + } + + private static void serializeClasses(Collection classes, Map serializedClasses) throws IOException { + for (ClassDescriptor classDescriptor : classes) { + DescriptorSerializer descriptorSerializer = new DescriptorSerializer(); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + ProtoBuf.Class classProto = descriptorSerializer.classProto(classDescriptor).build(); + + NameSerializationUtil.serializeNameTable(bytes, descriptorSerializer.getNameTable()); + classProto.writeTo(bytes); + + serializedClasses.put(classDescriptor, bytes.toByteArray()); + + //noinspection unchecked + serializeClasses((Collection) classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors(), serializedClasses); + + ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor(); + if (classObjectDescriptor != null) { + serializeClasses(Collections.singletonList(classObjectDescriptor), serializedClasses); + } + } + } + + private static class ClassMetadata { + private final ProtoBuf.SimpleNameTable simpleNames; + private final ProtoBuf.QualifiedNameTable qualifiedNames; + private final ProtoBuf.Class classProto; + + private ClassMetadata( + ProtoBuf.SimpleNameTable simpleNames, + ProtoBuf.QualifiedNameTable qualifiedNames, + ProtoBuf.Class classProto + ) { + this.simpleNames = simpleNames; + this.qualifiedNames = qualifiedNames; + this.classProto = classProto; } } @@ -196,68 +259,64 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith private final ClassResolver parentResolver; private final DeclarationDescriptor parentForClasses; - private final FqName parentFqName; - private final NameResolver nameResolver; - private final Map classProtos; + private final NullableFunction classMetadata; - private final MemoizedFunctionToNullable classes; + private final MemoizedFunctionToNullable classes; public ClassResolverImpl( @NotNull ClassResolver parentResolver, @NotNull DeclarationDescriptor parentForClasses, - @NotNull ProtoBuf.SimpleNameTable simpleNames, - @NotNull ProtoBuf.QualifiedNameTable qualifiedNames, - @NotNull List classProtos + @NotNull NullableFunction classMetadata ) { this.parentResolver = parentResolver; this.parentForClasses = parentForClasses; - this.parentFqName = DescriptorUtils.getFQName(parentForClasses).toSafe(); - this.nameResolver = new NameResolver(simpleNames, qualifiedNames, this); - this.classProtos = toMap(classProtos); + this.classMetadata = classMetadata; - this.classes = new MemoizedFunctionToNullableImpl() { + this.classes = new MemoizedFunctionToNullableImpl() { @Nullable @Override - protected ClassDescriptor doCompute(@NotNull FqName fqName) { - return resolveClass(fqName); + protected ClassDescriptor doCompute(@NotNull ClassId classId) { + return resolveClass(ClassResolverImpl.this.parentForClasses, classId); } }; } - @NotNull - private Map toMap(@NotNull List classProtos) { - Map map = new HashMap(classProtos.size()); - for (ProtoBuf.Class classProto : classProtos) { - map.put(nameResolver.getName(classProto.getName()), classProto); - } - return map; - } - - @NotNull - public NameResolver getNameResolver() { - return nameResolver; - } - @Nullable - private ClassDescriptor resolveClass(@NotNull FqName fqName) { - if (!parentFqName.equals(fqName.parent())) { - return parentResolver.findClass(fqName); + private ClassDescriptor resolveClass( + @NotNull DeclarationDescriptor containingDeclaration, + @NotNull final ClassId classId + ) { + FqName fqName = classId.asSingleFqName(); + + ClassMetadata classMetadata = this.classMetadata.fun(fqName.getFqName()); + if (classMetadata == null) { + return parentResolver.findClass(classId); } - ProtoBuf.Class classProto = classProtos.get(fqName.shortName()); - if (classProto == null) { - return parentResolver.findClass(fqName); - } + NestedClassResolver nestedClassResolver = new NestedClassResolver() { + @Nullable + @Override + public ClassDescriptor resolveNestedClass(@NotNull ClassDescriptor outerClass, @NotNull Name name) { + return resolveClass(outerClass, classId.createNestedClassId(name)); + } - return new DeserializedClassDescriptor(parentForClasses, nameResolver, this, classProto, null); + @Nullable + @Override + public ClassDescriptor resolveClassObject(@NotNull ClassDescriptor outerClass) { + return resolveClass(outerClass, classId.createNestedClassId(Name.identifier(JvmAbi.CLASS_OBJECT_CLASS_NAME))); + } + }; + + NameResolver nameResolver = new NameResolver(classMetadata.simpleNames, classMetadata.qualifiedNames, this); + return new DeserializedClassDescriptor(containingDeclaration, nameResolver, nestedClassResolver, classMetadata.classProto, null); } @Nullable @Override - public ClassDescriptor findClass(@NotNull FqName fqName) { - return classes.fun(fqName); + public ClassDescriptor findClass(@NotNull ClassId classId) { + return classes.fun(classId); } } @@ -270,24 +329,31 @@ public abstract class AbstractDescriptorSerializationTest extends KotlinTestWith @Nullable @Override - public ClassDescriptor findClass(@NotNull FqName fqName) { - ClassDescriptor classDescriptor = javaDescriptorResolver.resolveClass(fqName); - if (classDescriptor != null) { - return classDescriptor; + public ClassDescriptor findClass(@NotNull ClassId classId) { + ClassDescriptor javaClassDescriptor = javaDescriptorResolver.resolveClass(classId.asSingleFqName()); + if (javaClassDescriptor != null) { + return javaClassDescriptor; } - FqName current = fqName; - while (!current.isRoot()) { - NamespaceDescriptor namespace = getNamespace(current); - if (namespace != null) { - ClassDescriptor classifier = (ClassDescriptor) namespace.getMemberScope().getClassifier(current.shortName()); - if (classifier == null) { - throw new IllegalStateException("Class not found: " + fqName); - } - return classifier; + NamespaceDescriptor packageDescriptor = getNamespace(classId.getPackageFqName()); + if (packageDescriptor == null) { + throw new IllegalStateException("Java package not found: " + classId.getPackageFqName() + " for " + classId); + } + + JetScope scope = packageDescriptor.getMemberScope(); + for (Iterator iterator = classId.getRelativeClassName().pathSegments().iterator(); iterator.hasNext(); ) { + Name name = iterator.next(); + ClassifierDescriptor classifier = scope.getClassifier(name); + if (classifier == null) { + throw new IllegalStateException("Class not found: " + classId); } - current = current.parent(); + ClassDescriptor classDescriptor = (ClassDescriptor) classifier; + if (!iterator.hasNext()) { + return classDescriptor; + } + scope = classDescriptor.getUnsubstitutedInnerClassesScope(); } - throw new IllegalStateException("Class not found: " + fqName); + + throw new IllegalStateException("Class not found: " + classId); } @Nullable