Do not use .kotlin_module files in reflection

Previously, we used a pretty roundabout way to load a MemberScope from a
single file facade represented by KPackageImpl, which involved going
through ModuleDescriptor, PackageFragmentProvider, PackagePartProvider
etc. The only advantage of this approach was that it sort of works
similarly as in the compiler, however mutable state in
RuntimePackagePartProvider and the fact that .kotlin_module files were
required for this to work diminished this advantage.

In this change, we load MemberScope from a KPackageImpl pretty much
directly, by using the existing method
`DeserializedDescriptorResolver.createKotlinPackagePartScope` and
caching the result in the new component PackagePartScopeCache.

 #KT-30344 Fixed
This commit is contained in:
Alexander Udalov
2019-03-15 19:01:25 +01:00
parent 7aa75ab89b
commit cd6c88fa2c
6 changed files with 75 additions and 155 deletions
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.reflect.jvm.internal.components
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.findKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import java.util.concurrent.ConcurrentHashMap
class PackagePartScopeCache(private val resolver: DeserializedDescriptorResolver, private val kotlinClassFinder: ReflectKotlinClassFinder) {
private val cache = ConcurrentHashMap<ClassId, MemberScope>()
fun getPackagePartScope(fileClass: ReflectKotlinClass): MemberScope = cache.getOrPut(fileClass.classId) {
val fqName = fileClass.classId.packageFqName
val parts =
if (fileClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS)
fileClass.classHeader.multifilePartNames.mapNotNull { partName ->
val classId = ClassId.topLevel(JvmClassName.byInternalName(partName).fqNameForTopLevelClassMaybeWithDollars)
kotlinClassFinder.findKotlinClass(classId)
}
else listOf(fileClass)
val packageFragment = EmptyPackageFragmentDescriptor(resolver.components.moduleDescriptor, fqName)
val scopes = parts.mapNotNull { part ->
resolver.createKotlinPackagePartScope(packageFragment, part)
}.toList()
ChainedMemberScope.create("package $fqName ($fileClass)", scopes)
}
}
@@ -36,10 +36,7 @@ import org.jetbrains.kotlin.load.java.lazy.JavaResolverSettings
import org.jetbrains.kotlin.load.java.lazy.LazyJavaPackageFragmentProvider
import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
import org.jetbrains.kotlin.load.kotlin.BinaryClassAnnotationAndConstantLoaderImpl
import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
import org.jetbrains.kotlin.load.kotlin.JavaClassDataFinder
import org.jetbrains.kotlin.load.kotlin.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.serialization.deserialization.ContractDeserializer
@@ -50,7 +47,7 @@ import org.jetbrains.kotlin.utils.Jsr305State
class RuntimeModuleData private constructor(
val deserialization: DeserializationComponents,
val packagePartProvider: RuntimePackagePartProvider
val packagePartScopeCache: PackagePartScopeCache
) {
val module: ModuleDescriptor get() = deserialization.moduleDescriptor
@@ -64,25 +61,23 @@ class RuntimeModuleData private constructor(
val reflectKotlinClassFinder = ReflectKotlinClassFinder(classLoader)
val deserializedDescriptorResolver = DeserializedDescriptorResolver()
val singleModuleClassResolver = SingleModuleClassResolver()
val runtimePackagePartProvider = RuntimePackagePartProvider(classLoader)
val javaResolverCache = JavaResolverCache.EMPTY
val notFoundClasses = NotFoundClasses(storageManager, module)
val annotationTypeQualifierResolver = AnnotationTypeQualifierResolver(storageManager, Jsr305State.DISABLED)
val globalJavaResolverContext = JavaResolverComponents(
val javaResolverComponents = JavaResolverComponents(
storageManager, ReflectJavaClassFinder(classLoader), reflectKotlinClassFinder, deserializedDescriptorResolver,
SignaturePropagator.DO_NOTHING, RuntimeErrorReporter, javaResolverCache,
SignaturePropagator.DO_NOTHING, RuntimeErrorReporter, JavaResolverCache.EMPTY,
JavaPropertyInitializerEvaluator.DoNothing, SamConversionResolver.Empty, RuntimeSourceElementFactory,
singleModuleClassResolver, runtimePackagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module,
singleModuleClassResolver, PackagePartProvider.Empty, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module,
ReflectionTypes(module, notFoundClasses), annotationTypeQualifierResolver,
SignatureEnhancement(annotationTypeQualifierResolver, Jsr305State.DISABLED),
JavaClassesTracker.Default, JavaResolverSettings.Default
)
val lazyJavaPackageFragmentProvider = LazyJavaPackageFragmentProvider(globalJavaResolverContext)
val lazyJavaPackageFragmentProvider = LazyJavaPackageFragmentProvider(javaResolverComponents)
builtIns.initialize(module, isAdditionalBuiltInsFeatureSupported = true)
val javaDescriptorResolver = JavaDescriptorResolver(lazyJavaPackageFragmentProvider, javaResolverCache)
val javaDescriptorResolver = JavaDescriptorResolver(lazyJavaPackageFragmentProvider, JavaResolverCache.EMPTY)
val javaClassDataFinder = JavaClassDataFinder(reflectKotlinClassFinder, deserializedDescriptorResolver)
val binaryClassAnnotationAndConstantLoader = BinaryClassAnnotationAndConstantLoaderImpl(
module, notFoundClasses, storageManager, reflectKotlinClassFinder
@@ -103,7 +98,10 @@ class RuntimeModuleData private constructor(
module.setDependencies(module)
module.initialize(CompositePackageFragmentProvider(listOf(javaDescriptorResolver.packageFragmentProvider, builtinsProvider)))
return RuntimeModuleData(deserializationComponentsForJava.components, runtimePackagePartProvider)
return RuntimeModuleData(
deserializationComponentsForJava.components,
PackagePartScopeCache(deserializedDescriptorResolver, reflectKotlinClassFinder)
)
}
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2010-2015 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 kotlin.reflect.jvm.internal.components
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
import org.jetbrains.kotlin.load.kotlin.loadModuleMapping
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
import java.io.IOException
import java.util.*
class RuntimePackagePartProvider(private val classLoader: ClassLoader) : PackagePartProvider {
// Names of modules which were registered with registerModule
private val visitedModules = hashSetOf<String>()
// Package FQ name -> list of JVM internal names of package parts in that package across all registered modules
private val packageParts = hashMapOf<String, LinkedHashSet<String>>()
@Synchronized
fun registerModule(moduleName: String) {
if (!visitedModules.add(moduleName)) return
val resourcePath = "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}"
val resources = try {
classLoader.getResources(resourcePath)
} catch (e: IOException) {
EmptyEnumeration
}
for (resource in resources) {
try {
resource.openStream()?.use { stream ->
val mapping = ModuleMapping.loadModuleMapping(
stream.readBytes(), resourcePath, DeserializationConfiguration.Default
) { version ->
throw UnsupportedOperationException(
"Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is $version, " +
"expected version is ${JvmMetadataVersion.INSTANCE}. Please update Kotlin to the latest version"
)
}
for ((packageFqName, parts) in mapping.packageFqName2Parts) {
packageParts.getOrPut(packageFqName) { linkedSetOf() }.addAll(parts.parts)
}
}
} catch (e: UnsupportedOperationException) {
throw e
} catch (e: Exception) {
// TODO: do not swallow this exception?
}
}
}
@Synchronized
override fun findPackageParts(packageFqName: String): List<String> =
packageParts[packageFqName]?.toList().orEmpty()
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> {
// TODO: load annotations from resource files
return emptyList()
}
private object EmptyEnumeration : Enumeration<Nothing> {
override fun hasMoreElements(): Boolean = false
override fun nextElement(): Nothing = throw NoSuchElementException()
}
}
@@ -78,7 +78,7 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
val classLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), ForTestCompileRuntime.runtimeAndReflectJarClassLoader())
val actual = createReflectedPackageView(classLoader, KotlinTestUtils.TEST_MODULE_NAME)
val actual = createReflectedPackageView(classLoader)
val comparatorConfiguration = Configuration(
/* checkPrimaryConstructors = */ fileName.endsWith(".kt"),
@@ -135,9 +135,8 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
}
}
private fun createReflectedPackageView(classLoader: URLClassLoader, moduleName: String): SyntheticPackageViewForTest {
private fun createReflectedPackageView(classLoader: URLClassLoader): SyntheticPackageViewForTest {
val moduleData = RuntimeModuleData.create(classLoader)
moduleData.packagePartProvider.registerModule(moduleName)
val module = moduleData.module
val generatedPackageDir = File(tmpdir, LoadDescriptorUtil.TEST_PACKAGE_FQNAME.pathSegments().single().asString())
@@ -153,10 +152,7 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
val header = binaryClass?.classHeader
if (header?.kind == KotlinClassHeader.Kind.FILE_FACADE || header?.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS) {
val packageView = module.getPackage(LoadDescriptorUtil.TEST_PACKAGE_FQNAME)
if (!packageScopes.contains(packageView.memberScope)) {
packageScopes.add(packageView.memberScope)
}
packageScopes.add(moduleData.packagePartScopeCache.getPackagePartScope(binaryClass))
} else if (header == null || header.kind == KotlinClassHeader.Kind.CLASS) {
// Either a normal Kotlin class or a Java class
val classId = klass.classId