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
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<Name> {
protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection<Name> {
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 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())
@@ -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<JetScope> = 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<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 =
loadResource(path) ?: throw IllegalStateException("Resource not found in classpath: $path")
}
}
@@ -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)
)
}
}
}
@@ -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<Name, ClassDescriptor> {
internal val findNestedClass = c.storageManager.createMemoizedFunctionWithNullableValues<Name, ClassDescriptor> {
name ->
if (name in nestedClassNames) {
c.components.deserializeClass(classId.createNestedClassId(name))
@@ -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<DeclarationDescriptor>, nameFilter: (Name) -> Boolean) {
for (className in classNames()) {
for (className in classNames) {
if (nameFilter(className)) {
result.addIfNotNull(getClassDescriptor(className))
}
@@ -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<Name> {
protected override fun loadClassNames(packageProto: ProtoBuf.Package): Collection<Name> {
val classesStream = loadResourceSure(KotlinJavascriptSerializedResourcePaths.getClassesInPackageFilePath(fqName))
val classesProto = JsProtoBuf.Classes.parseFrom(classesStream, serializedResourcePaths.EXTENSION_REGISTRY)
return classesProto.getClassNameList()?.map { id -> nameResolver.getName(id) } ?: listOf()