diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/AbstractClassResolver.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/AbstractClassResolver.java new file mode 100644 index 00000000000..3c0782edfa3 --- /dev/null +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/AbstractClassResolver.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.descriptors.serialization; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull; +import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNullImpl; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; + +public abstract class AbstractClassResolver implements ClassResolver { + + private final NameResolver nameResolver; + private final NestedClassResolver nestedClassResolver; + private final MemoizedFunctionToNotNull findClass; + + public AbstractClassResolver(@NotNull NameResolver nameResolver) { + this.nameResolver = nameResolver; + + this.nestedClassResolver = new NestedClassResolver() { + @Nullable + @Override + public ClassDescriptor resolveNestedClass(@NotNull ClassDescriptor outerClass, @NotNull Name name) { + return findClass(getClassId(outerClass).createNestedClassId(name)); + } + + @Nullable + @Override + public ClassDescriptor resolveClassObject(@NotNull ClassDescriptor outerClass) { + return findClass(getClassId(outerClass).createNestedClassId(getClassObjectName(outerClass))); + } + }; + + this.findClass = new MemoizedFunctionToNotNullImpl() { + @NotNull + @Override + protected ClassDescriptor doCompute(ClassId classId) { + ProtoBuf.Class classProto = getClassProto(classId); + assert classProto != null : "No class found: " + classId; + DeclarationDescriptor owner = + classId.isTopLevelClass() ? getPackage(classId.getPackageFqName()) : findClass(classId.getOuterClassId()); + assert owner != null : "No owner found for " + classId; + ClassDescriptor classDescriptor = new DeserializedClassDescriptor( + owner, AbstractClassResolver.this.nameResolver, AbstractClassResolver.this, nestedClassResolver, classProto, null + ); + classDescriptorCreated(classDescriptor); + return classDescriptor; + } + }; + } + + @Nullable + @Override + public ClassDescriptor findClass(@NotNull ClassId classId) { + return findClass.fun(classId); + } + + @Nullable + protected abstract ProtoBuf.Class getClassProto(@NotNull ClassId classId); + + @NotNull + protected abstract DeclarationDescriptor getPackage(@NotNull FqName fqName); + + @NotNull + protected abstract ClassId getClassId(@NotNull ClassDescriptor classDescriptor); + + @NotNull + protected abstract Name getClassObjectName(@NotNull ClassDescriptor outerClass); + + protected abstract void classDescriptorCreated(@NotNull ClassDescriptor classDescriptor); +} diff --git a/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassSerializationUtil.java b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassSerializationUtil.java new file mode 100644 index 00000000000..c4abd20b7df --- /dev/null +++ b/compiler/frontend/serialization/src/org/jetbrains/jet/descriptors/serialization/ClassSerializationUtil.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.descriptors.serialization; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; + +import java.util.Collection; + +public class ClassSerializationUtil { + public interface Sink { + void writeClass(@NotNull ClassDescriptor classDescriptor, @NotNull ProtoBuf.Class classProto); + } + + public static void serializeClass(@NotNull ClassDescriptor classDescriptor, @NotNull DescriptorSerializer serializer, @NotNull Sink sink) { + ProtoBuf.Class classProto = serializer.classProto(classDescriptor).build(); + sink.writeClass(classDescriptor, classProto); + + serializeClasses(classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors(), serializer, sink); + + serializeClasses(classDescriptor.getUnsubstitutedInnerClassesScope().getObjectDescriptors(), serializer, sink); + + ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor(); + if (classObjectDescriptor != null) { + serializeClass(classObjectDescriptor, serializer, sink); + } + } + + public static void serializeClasses( + @NotNull Collection descriptors, + @NotNull DescriptorSerializer serializer, + @NotNull Sink sink + ) { + for (DeclarationDescriptor descriptor : descriptors) { + if (descriptor instanceof ClassDescriptor) { + ClassDescriptor nestedClass = (ClassDescriptor) descriptor; + serializeClass(nestedClass, serializer, sink); + } + } + } + + @NotNull + public static ClassId getClassId(@NotNull ClassDescriptor classDescriptor, @NotNull NameTable.Namer namer) { + DeclarationDescriptor containingDeclaration = classDescriptor.getContainingDeclaration(); + if (containingDeclaration instanceof NamespaceDescriptor) { + NamespaceDescriptor namespaceDescriptor = (NamespaceDescriptor) containingDeclaration; + return new ClassId(getPackageFqName(namespaceDescriptor, namer), FqNameUnsafe.topLevel(namer.getClassName(classDescriptor))); + } + // it must be a nested class + ClassDescriptor outer = (ClassDescriptor) containingDeclaration; + ClassId outerId = getClassId(outer, namer); + return outerId.createNestedClassId(namer.getClassName(classDescriptor)); + } + + @NotNull + public static FqName getPackageFqName(@NotNull NamespaceDescriptor namespaceDescriptor, @NotNull NameTable.Namer namer) { + if (DescriptorUtils.isRootNamespace(namespaceDescriptor)) return FqName.ROOT; + + NamespaceDescriptor parent = (NamespaceDescriptor) namespaceDescriptor.getContainingDeclaration(); + return getPackageFqName(parent, namer) + .child(namer.getPackageName(namespaceDescriptor)); + } +} diff --git a/compiler/tests/org/jetbrains/jet/descriptors/serialization/BuiltinsDeserializationTest.java b/compiler/tests/org/jetbrains/jet/descriptors/serialization/BuiltinsDeserializationTest.java new file mode 100644 index 00000000000..777bfa17701 --- /dev/null +++ b/compiler/tests/org/jetbrains/jet/descriptors/serialization/BuiltinsDeserializationTest.java @@ -0,0 +1,221 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.descriptors.serialization; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.ConfigurationKind; +import org.jetbrains.jet.TestJdkKind; +import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; +import org.jetbrains.jet.jvm.compiler.ExpectedLoadErrorsUtil; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorImpl; +import org.jetbrains.jet.lang.psi.JetPsiUtil; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.java.JavaBridgeConfiguration; +import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap; +import org.jetbrains.jet.lang.resolve.lazy.KotlinTestWithEnvironment; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; +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.renderer.DescriptorRenderer; +import org.jetbrains.jet.renderer.DescriptorRendererBuilder; +import org.jetbrains.jet.test.util.NamespaceComparator; + +import java.util.*; + +public class BuiltinsDeserializationTest extends KotlinTestWithEnvironment { + + private static final Name CLASS_OBJECT_NAME = Name.special(""); + + @Override + protected JetCoreEnvironment createEnvironment() { + return createEnvironmentWithJdk(ConfigurationKind.JDK_AND_ANNOTATIONS, TestJdkKind.FULL_JDK); + } + + public void testBuiltIns() throws Exception { + Collection allDescriptors = KotlinBuiltIns.getInstance().getBuiltInsScope().getAllDescriptors(); + NamespaceDescriptorImpl actualNamespace = getDeserializedDescriptorsAsNamespace(allDescriptors); + + NamespaceComparator.Configuration configuration = NamespaceComparator.RECURSIVE.withRenderer( + new DescriptorRendererBuilder() + .setWithDefinedIn(false) + .setExcludedAnnotationClasses(Arrays.asList(new FqName(ExpectedLoadErrorsUtil.ANNOTATION_CLASS_NAME))) + .setOverrideRenderingPolicy(DescriptorRenderer.OverrideRenderingPolicy.RENDER_OPEN_OVERRIDE) + .setVerbose(true) + .setAlwaysRenderAny(true) + .setPrettyFunctionTypes(false) + .build() + ); + NamespaceComparator.validateAndCompareNamespaces(KotlinBuiltIns.getInstance().getBuiltInsPackage(), actualNamespace, configuration, null); + } + + private static NamespaceDescriptorImpl getDeserializedDescriptorsAsNamespace(Collection allDescriptors) { + DescriptorSerializer serializer = new DescriptorSerializer(NameTable.Namer.DEFAULT); + + final Map classProtos = serializeClasses(serializer, allDescriptors); + + List callableProtos = serializeCallables(serializer, allDescriptors); + + final NamespaceDescriptorImpl actualNamespace = createTestNamespace(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.shortName().asString()); + + NameResolver nameResolver = NameSerializationUtil.createNameResolver(serializer.getNameTable()); + + ClassResolver classResolver = new AbstractClassResolver(nameResolver) { + + @NotNull + @Override + protected DeclarationDescriptor getPackage(@NotNull FqName fqName) { + assert fqName.equals(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) : "Unsupported package: " + fqName; + return actualNamespace; + } + + @NotNull + @Override + protected ClassId getClassId(@NotNull ClassDescriptor classDescriptor) { + return BuiltinsDeserializationTest.getClassId(classDescriptor); + } + + @NotNull + @Override + protected Name getClassObjectName(@NotNull ClassDescriptor outerClass) { + return CLASS_OBJECT_NAME; + } + + @Nullable + @Override + protected ProtoBuf.Class getClassProto(@NotNull ClassId classId) { + return classProtos.get(classId); + } + + @Override + protected void classDescriptorCreated(@NotNull ClassDescriptor classDescriptor) { + if (!DescriptorUtils.isTopLevelDeclaration(classDescriptor)) return; + switch (classDescriptor.getKind()) { + case CLASS: + case TRAIT: + case ENUM_CLASS: + case ANNOTATION_CLASS: + actualNamespace.getMemberScope().addClassifierDescriptor(classDescriptor); + break; + case OBJECT: + actualNamespace.getMemberScope().addObjectDescriptor(classDescriptor); + break; + case ENUM_ENTRY: + assert false : "Enum entry appears to be a top-level declaration: " + classDescriptor; + case CLASS_OBJECT: + assert false : "Call object appears to be a top-level declaration: " + classDescriptor; + } + } + }; + + // Make the lazy loader create classes + for (ClassId classId : classProtos.keySet()) { + classResolver.findClass(classId); + } + + deserializeCallables(callableProtos, actualNamespace, nameResolver, classResolver); + + actualNamespace.getMemberScope().changeLockLevel(WritableScope.LockLevel.READING); + return actualNamespace; + } + + private static Map serializeClasses( + DescriptorSerializer serializer, + Collection allDescriptors + ) { + final Map classProtos = Maps.newHashMap(); + ClassSerializationUtil.serializeClasses(allDescriptors, serializer, new ClassSerializationUtil.Sink() { + @Override + public void writeClass( + @NotNull ClassDescriptor classDescriptor, @NotNull ProtoBuf.Class classProto + ) { + classProtos.put(getClassId(classDescriptor), classProto); + } + }); + return classProtos; + } + + private static List serializeCallables( + DescriptorSerializer serializer, + Collection allDescriptors + ) { + List callableProtos = Lists.newArrayList(); + for (DeclarationDescriptor descriptor : allDescriptors) { + if (descriptor instanceof CallableMemberDescriptor) { + CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) descriptor; + callableProtos.add(serializer.callableProto(callableMemberDescriptor).build()); + } + } + return callableProtos; + } + + private static void deserializeCallables( + List callableProtos, + NamespaceDescriptorImpl actualNamespace, + NameResolver nameResolver, + ClassResolver classResolver + ) { + DescriptorDeserializer descriptorDeserializer = DescriptorDeserializer.create(actualNamespace, nameResolver, classResolver); + for (ProtoBuf.Callable callableProto : callableProtos) { + CallableMemberDescriptor callableMemberDescriptor = descriptorDeserializer.loadCallable(callableProto); + if (callableMemberDescriptor instanceof PropertyDescriptor) { + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) callableMemberDescriptor; + actualNamespace.getMemberScope().addPropertyDescriptor(propertyDescriptor); + } + else if (callableMemberDescriptor instanceof FunctionDescriptor) { + FunctionDescriptor functionDescriptor = (FunctionDescriptor) callableMemberDescriptor; + actualNamespace.getMemberScope().addFunctionDescriptor(functionDescriptor); + } + } + } + + private static ClassId getClassId(ClassDescriptor classDescriptor) { + return ClassSerializationUtil.getClassId(classDescriptor, new NameTable.Namer() { + @NotNull + @Override + public Name getClassName(@NotNull ClassDescriptor classDescriptor) { + return classDescriptor.getKind() == ClassKind.CLASS_OBJECT ? CLASS_OBJECT_NAME : classDescriptor.getName(); + } + + @NotNull + @Override + public Name getPackageName(@NotNull NamespaceDescriptor namespaceDescriptor) { + return namespaceDescriptor.getName(); + } + }); + } + + private static NamespaceDescriptorImpl createTestNamespace(String testPackageName) { + ModuleDescriptorImpl module = new ModuleDescriptorImpl(Name.special(""), JavaBridgeConfiguration.ALL_JAVA_IMPORTS, + JavaToKotlinClassMap.getInstance()); + NamespaceDescriptorImpl rootNamespace = + new NamespaceDescriptorImpl(module, Collections.emptyList(), JetPsiUtil.ROOT_NAMESPACE_NAME); + module.setRootNamespace(rootNamespace); + NamespaceDescriptorImpl test = + new NamespaceDescriptorImpl(rootNamespace, Collections.emptyList(), Name.identifier(testPackageName)); + test.initialize(new WritableScopeImpl(JetScope.EMPTY, test, RedeclarationHandler.DO_NOTHING, "members of test namespace")); + return test; + } +}