From 110d6fa7f1031732d19c031280d8e39985d2d2ec Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 26 Jun 2015 19:12:51 +0300 Subject: [PATCH] Don't load irrelevant classes by accident, if resource happens to exist Check with the containing class or package first, don't load the class if the container doesn't know about it. This also makes the workaround for case-insensitive file systems unnecessary --- .../builtins/BuiltinsPackageFragment.kt | 4 ++-- .../deserialization/ClassDeserializer.kt | 16 +++++++++++-- .../DeserializedPackageFragment.kt | 17 ++++++++------ .../ResourceLoadingClassDataFinder.kt | 23 ++++++------------- .../DeserializedClassDescriptor.kt | 8 +++++-- .../DeserializedPackageMemberScope.kt | 12 ++++++---- .../js/KotlinJavascriptPackageFragment.kt | 4 ++-- 7 files changed, 48 insertions(+), 36 deletions(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltinsPackageFragment.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltinsPackageFragment.kt index 4d25cb09c4d..d745744f919 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltinsPackageFragment.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/BuiltinsPackageFragment.kt @@ -16,12 +16,12 @@ package org.jetbrains.kotlin.builtins -import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment import org.jetbrains.kotlin.storage.StorageManager import java.io.InputStream @@ -32,7 +32,7 @@ public class BuiltinsPackageFragment( loadResource: (path: String) -> InputStream? ) : DeserializedPackageFragment(fqName, storageManager, module, BuiltInsSerializedResourcePaths, loadResource) { - protected override fun loadClassNames(fqName: FqName, packageProto: ProtoBuf.Package): Collection { + protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection { return packageProto.getExtension(BuiltInsProtoBuf.className)?.map { id -> nameResolver.getName(id) } ?: listOf() } } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ClassDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ClassDeserializer.kt index f26af6d8b5b..22e680678a8 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ClassDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ClassDeserializer.kt @@ -35,12 +35,24 @@ public class ClassDeserializer(private val components: DeserializationComponents val classData = key.classData ?: components.classDataFinder.findClassData(classId) ?: return null val outerContext = if (classId.isNestedClass()) { - (deserializeClass(classId.getOuterClassId()) as? DeserializedClassDescriptor)?.c ?: return null + val outerClass = deserializeClass(classId.getOuterClassId()) as? DeserializedClassDescriptor ?: return null + + // Find the outer class first and check if he knows anything about the nested class we're looking for + if (!outerClass.hasNestedClass(classId.getShortClassName())) return null + + outerClass.c } else { val fragments = components.packageFragmentProvider.getPackageFragments(classId.getPackageFqName()) assert(fragments.size() == 1) { "There should be exactly one package: $fragments, class id is $classId" } - components.createContext(fragments.single(), classData.getNameResolver()) + + val fragment = fragments.single() + if (fragment is DeserializedPackageFragment) { + // Similarly, verify that the containing package has information about this class + if (!fragment.hasTopLevelClass(classId.getShortClassName())) return null + } + + components.createContext(fragment, classData.getNameResolver()) } return DeserializedClassDescriptor(outerContext, classData.getClassProto(), classData.getNameResolver()) diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedPackageFragment.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedPackageFragment.kt index 2dc100d084d..2f404139dd2 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedPackageFragment.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedPackageFragment.kt @@ -20,12 +20,11 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.SerializedResourcePaths import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope -import org.jetbrains.kotlin.storage.NotNullLazyValue import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.storage.get import java.io.InputStream import javax.inject.Inject import kotlin.properties.Delegates @@ -50,16 +49,20 @@ public abstract class DeserializedPackageFragment( this.components = components } - private val memberScopeLazyValue: NotNullLazyValue = storageManager.createLazyValue { + internal val deserializedMemberScope by storageManager.createLazyValue { val packageStream = loadResourceSure(serializedResourcePaths.getPackageFilePath(fqName)) val packageProto = ProtoBuf.Package.parseFrom(packageStream, serializedResourcePaths.EXTENSION_REGISTRY) - DeserializedPackageMemberScope(this, packageProto, nameResolver, components, classNames = { loadClassNames(fqName, packageProto) }) + DeserializedPackageMemberScope(this, packageProto, nameResolver, components, classNames = { loadClassNames(packageProto) }) } - override fun getMemberScope() = memberScopeLazyValue() + override fun getMemberScope() = deserializedMemberScope - protected abstract fun loadClassNames(fqName: FqName, packageProto: ProtoBuf.Package): Collection + internal fun hasTopLevelClass(name: Name): Boolean { + return name in getMemberScope().classNames + } + + protected abstract fun loadClassNames(packageProto: ProtoBuf.Package): Collection protected fun loadResourceSure(path: String): InputStream = loadResource(path) ?: throw IllegalStateException("Resource not found in classpath: $path") -} \ No newline at end of file +} diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ResourceLoadingClassDataFinder.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ResourceLoadingClassDataFinder.kt index d689f211a2c..03b86ee84d5 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ResourceLoadingClassDataFinder.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/ResourceLoadingClassDataFinder.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.serialization.deserialization -import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.serialization.ClassData @@ -30,22 +29,14 @@ public open class ResourceLoadingClassDataFinder( private val loadResource: (path: String) -> InputStream? ) : ClassDataFinder { override fun findClassData(classId: ClassId): ClassData? { - val packageFragment = packageFragmentProvider.getPackageFragments(classId.getPackageFqName()).singleOrNull() ?: return null + val packageFragment = packageFragmentProvider.getPackageFragments(classId.getPackageFqName()).singleOrNull() + as? DeserializedPackageFragment ?: return null val stream = loadResource(serializedResourcePaths.getClassMetadataPath(classId)) ?: return null - val classProto = ProtoBuf.Class.parseFrom(stream, serializedResourcePaths.EXTENSION_REGISTRY) - val nameResolver = - (packageFragment as? DeserializedPackageFragment ?: error("Not a deserialized package fragment: $packageFragment")).nameResolver - - val expectedShortName = classId.getShortClassName() - val actualShortName = nameResolver.getClassId(classProto.getFqName()).getShortClassName() - if (!actualShortName.isSpecial() && actualShortName != expectedShortName) { - // Workaround for case-insensitive file systems, - // otherwise we'd find "Collection" for "collection" etc - return null - } - - return ClassData(nameResolver, classProto) + return ClassData( + packageFragment.nameResolver, + ProtoBuf.Class.parseFrom(stream, serializedResourcePaths.EXTENSION_REGISTRY) + ) } -} \ No newline at end of file +} diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt index ae5ee0bfc77..1244c3f7e11 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt @@ -139,6 +139,10 @@ public class DeserializedClassDescriptor( return supertypes } + internal fun hasNestedClass(name: Name): Boolean { + return name in nestedClasses.nestedClassNames + } + override fun toString() = "deserialized class ${getName().toString()}" // not using descriptor render to preserve laziness override fun getSource() = SourceElement.NO_SOURCE @@ -241,9 +245,9 @@ public class DeserializedClassDescriptor( } private inner class NestedClassDescriptors { - private val nestedClassNames = nestedClassNames() + internal val nestedClassNames = nestedClassNames() - val findNestedClass = c.storageManager.createMemoizedFunctionWithNullableValues { + internal val findNestedClass = c.storageManager.createMemoizedFunctionWithNullableValues { name -> if (name in nestedClassNames) { c.components.deserializeClass(classId.createNestedClassId(name)) diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedPackageMemberScope.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedPackageMemberScope.kt index 9a98d7e8cd8..55c647defef 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedPackageMemberScope.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedPackageMemberScope.kt @@ -16,16 +16,17 @@ package org.jetbrains.kotlin.serialization.deserialization.descriptors -import org.jetbrains.kotlin.serialization.ProtoBuf -import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents import org.jetbrains.kotlin.serialization.deserialization.NameResolver +import org.jetbrains.kotlin.storage.get +import org.jetbrains.kotlin.utils.addIfNotNull public open class DeserializedPackageMemberScope( packageDescriptor: PackageFragmentDescriptor, @@ -36,7 +37,8 @@ public open class DeserializedPackageMemberScope( ) : DeserializedMemberScope(components.createContext(packageDescriptor, nameResolver), proto.getMemberList()) { private val packageFqName = packageDescriptor.fqName - private val classNames = c.storageManager.createLazyValue(classNames) + + internal val classNames by c.storageManager.createLazyValue { classNames().toSet() } override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean) = computeDescriptors(kindFilter, nameFilter) @@ -44,7 +46,7 @@ public open class DeserializedPackageMemberScope( override fun getClassDescriptor(name: Name) = c.components.deserializeClass(ClassId(packageFqName, name)) override fun addClassDescriptors(result: MutableCollection, nameFilter: (Name) -> Boolean) { - for (className in classNames()) { + for (className in classNames) { if (nameFilter(className)) { result.addIfNotNull(getClassDescriptor(className)) } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt index 9f71c3f853e..8c619cd1772 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptPackageFragment.kt @@ -16,11 +16,11 @@ package org.jetbrains.kotlin.serialization.js -import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment import org.jetbrains.kotlin.storage.StorageManager import java.io.InputStream @@ -31,7 +31,7 @@ public class KotlinJavascriptPackageFragment( loadResource: (path: String) -> InputStream? ) : DeserializedPackageFragment(fqName, storageManager, module, KotlinJavascriptSerializedResourcePaths, loadResource) { - protected override fun loadClassNames(fqName: FqName, packageProto: ProtoBuf.Package): Collection { + protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection { val classesStream = loadResourceSure(KotlinJavascriptSerializedResourcePaths.getClassesInPackageFilePath(fqName)) val classesProto = JsProtoBuf.Classes.parseFrom(classesStream, serializedResourcePaths.EXTENSION_REGISTRY) return classesProto.getClassNameList()?.map { id -> nameResolver.getName(id) } ?: listOf()