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
This commit is contained in:
Alexander Udalov
2015-06-26 19:12:51 +03:00
parent 1e7b96aec0
commit 110d6fa7f1
7 changed files with 48 additions and 36 deletions
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.builtins package org.jetbrains.kotlin.builtins
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import java.io.InputStream import java.io.InputStream
@@ -32,7 +32,7 @@ public class BuiltinsPackageFragment(
loadResource: (path: String) -> InputStream? loadResource: (path: String) -> InputStream?
) : DeserializedPackageFragment(fqName, storageManager, module, BuiltInsSerializedResourcePaths, loadResource) { ) : DeserializedPackageFragment(fqName, storageManager, module, BuiltInsSerializedResourcePaths, loadResource) {
protected override fun loadClassNames(fqName: FqName, packageProto: ProtoBuf.Package): Collection<Name> { protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection<Name> {
return packageProto.getExtension(BuiltInsProtoBuf.className)?.map { id -> nameResolver.getName(id) } ?: listOf() return packageProto.getExtension(BuiltInsProtoBuf.className)?.map { id -> nameResolver.getName(id) } ?: listOf()
} }
} }
@@ -35,12 +35,24 @@ public class ClassDeserializer(private val components: DeserializationComponents
val classData = key.classData ?: components.classDataFinder.findClassData(classId) ?: return null val classData = key.classData ?: components.classDataFinder.findClassData(classId) ?: return null
val outerContext = if (classId.isNestedClass()) { 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 { else {
val fragments = components.packageFragmentProvider.getPackageFragments(classId.getPackageFqName()) val fragments = components.packageFragmentProvider.getPackageFragments(classId.getPackageFqName())
assert(fragments.size() == 1) { "There should be exactly one package: $fragments, class id is $classId" } 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()) return DeserializedClassDescriptor(outerContext, classData.getClassProto(), classData.getNameResolver())
@@ -20,12 +20,11 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.SerializedResourcePaths import org.jetbrains.kotlin.serialization.SerializedResourcePaths
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope 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.StorageManager
import org.jetbrains.kotlin.storage.get
import java.io.InputStream import java.io.InputStream
import javax.inject.Inject import javax.inject.Inject
import kotlin.properties.Delegates import kotlin.properties.Delegates
@@ -50,16 +49,20 @@ public abstract class DeserializedPackageFragment(
this.components = components this.components = components
} }
private val memberScopeLazyValue: NotNullLazyValue<JetScope> = storageManager.createLazyValue { internal val deserializedMemberScope by storageManager.createLazyValue {
val packageStream = loadResourceSure(serializedResourcePaths.getPackageFilePath(fqName)) val packageStream = loadResourceSure(serializedResourcePaths.getPackageFilePath(fqName))
val packageProto = ProtoBuf.Package.parseFrom(packageStream, serializedResourcePaths.EXTENSION_REGISTRY) 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<Name> internal fun hasTopLevelClass(name: Name): Boolean {
return name in getMemberScope().classNames
}
protected abstract fun loadClassNames(packageProto: ProtoBuf.Package): Collection<Name>
protected fun loadResourceSure(path: String): InputStream = protected fun loadResourceSure(path: String): InputStream =
loadResource(path) ?: throw IllegalStateException("Resource not found in classpath: $path") loadResource(path) ?: throw IllegalStateException("Resource not found in classpath: $path")
} }
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.serialization.deserialization package org.jetbrains.kotlin.serialization.deserialization
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.ClassData import org.jetbrains.kotlin.serialization.ClassData
@@ -30,22 +29,14 @@ public open class ResourceLoadingClassDataFinder(
private val loadResource: (path: String) -> InputStream? private val loadResource: (path: String) -> InputStream?
) : ClassDataFinder { ) : ClassDataFinder {
override fun findClassData(classId: ClassId): ClassData? { 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 stream = loadResource(serializedResourcePaths.getClassMetadataPath(classId)) ?: return null
val classProto = ProtoBuf.Class.parseFrom(stream, serializedResourcePaths.EXTENSION_REGISTRY) return ClassData(
val nameResolver = packageFragment.nameResolver,
(packageFragment as? DeserializedPackageFragment ?: error("Not a deserialized package fragment: $packageFragment")).nameResolver ProtoBuf.Class.parseFrom(stream, serializedResourcePaths.EXTENSION_REGISTRY)
)
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)
} }
} }
@@ -139,6 +139,10 @@ public class DeserializedClassDescriptor(
return supertypes 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 toString() = "deserialized class ${getName().toString()}" // not using descriptor render to preserve laziness
override fun getSource() = SourceElement.NO_SOURCE override fun getSource() = SourceElement.NO_SOURCE
@@ -241,9 +245,9 @@ public class DeserializedClassDescriptor(
} }
private inner class NestedClassDescriptors { private inner class NestedClassDescriptors {
private val nestedClassNames = nestedClassNames() internal val nestedClassNames = nestedClassNames()
val findNestedClass = c.storageManager.createMemoizedFunctionWithNullableValues<Name, ClassDescriptor> { internal val findNestedClass = c.storageManager.createMemoizedFunctionWithNullableValues<Name, ClassDescriptor> {
name -> name ->
if (name in nestedClassNames) { if (name in nestedClassNames) {
c.components.deserializeClass(classId.createNestedClassId(name)) c.components.deserializeClass(classId.createNestedClassId(name))
@@ -16,16 +16,17 @@
package org.jetbrains.kotlin.serialization.deserialization.descriptors 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.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter 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.serialization.deserialization.NameResolver
import org.jetbrains.kotlin.storage.get
import org.jetbrains.kotlin.utils.addIfNotNull
public open class DeserializedPackageMemberScope( public open class DeserializedPackageMemberScope(
packageDescriptor: PackageFragmentDescriptor, packageDescriptor: PackageFragmentDescriptor,
@@ -36,7 +37,8 @@ public open class DeserializedPackageMemberScope(
) : DeserializedMemberScope(components.createContext(packageDescriptor, nameResolver), proto.getMemberList()) { ) : DeserializedMemberScope(components.createContext(packageDescriptor, nameResolver), proto.getMemberList()) {
private val packageFqName = packageDescriptor.fqName 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) override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean)
= computeDescriptors(kindFilter, nameFilter) = computeDescriptors(kindFilter, nameFilter)
@@ -44,7 +46,7 @@ public open class DeserializedPackageMemberScope(
override fun getClassDescriptor(name: Name) = c.components.deserializeClass(ClassId(packageFqName, name)) override fun getClassDescriptor(name: Name) = c.components.deserializeClass(ClassId(packageFqName, name))
override fun addClassDescriptors(result: MutableCollection<DeclarationDescriptor>, nameFilter: (Name) -> Boolean) { override fun addClassDescriptors(result: MutableCollection<DeclarationDescriptor>, nameFilter: (Name) -> Boolean) {
for (className in classNames()) { for (className in classNames) {
if (nameFilter(className)) { if (nameFilter(className)) {
result.addIfNotNull(getClassDescriptor(className)) result.addIfNotNull(getClassDescriptor(className))
} }
@@ -16,11 +16,11 @@
package org.jetbrains.kotlin.serialization.js package org.jetbrains.kotlin.serialization.js
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import java.io.InputStream import java.io.InputStream
@@ -31,7 +31,7 @@ public class KotlinJavascriptPackageFragment(
loadResource: (path: String) -> InputStream? loadResource: (path: String) -> InputStream?
) : DeserializedPackageFragment(fqName, storageManager, module, KotlinJavascriptSerializedResourcePaths, loadResource) { ) : DeserializedPackageFragment(fqName, storageManager, module, KotlinJavascriptSerializedResourcePaths, loadResource) {
protected override fun loadClassNames(fqName: FqName, packageProto: ProtoBuf.Package): Collection<Name> { protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection<Name> {
val classesStream = loadResourceSure(KotlinJavascriptSerializedResourcePaths.getClassesInPackageFilePath(fqName)) val classesStream = loadResourceSure(KotlinJavascriptSerializedResourcePaths.getClassesInPackageFilePath(fqName))
val classesProto = JsProtoBuf.Classes.parseFrom(classesStream, serializedResourcePaths.EXTENSION_REGISTRY) val classesProto = JsProtoBuf.Classes.parseFrom(classesStream, serializedResourcePaths.EXTENSION_REGISTRY)
return classesProto.getClassNameList()?.map { id -> nameResolver.getName(id) } ?: listOf() return classesProto.getClassNameList()?.map { id -> nameResolver.getName(id) } ?: listOf()