From 12b48f86e7244d56c8e794748a42644be2bdbfed Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 22 Feb 2017 14:13:17 +0300 Subject: [PATCH] Do not load error classes in ReflectionTypes Previously ReflectionTypes.find returned an error class in case a class is not found in the module dependencies. The problem with this approach is that each call site should call ErrorUtils.isError on the result and report an error if needed, in order to stop this type from reaching the codegen, which can't handle error types. Now we create a MockClassDescriptor instance instead. It's not an error class, so it'll be handled correctly in the codegen. Also its scope is empty and errors are reported on any non-trivial usage (see MissingDependencyClassChecker), so this approach is not worse than error classes #KT-16484 Fixed --- .../checkers/JvmReflectionAPICallChecker.kt | 7 +- .../AbstractReflectionApiCallChecker.kt | 9 ++- .../DoubleColonExpressionResolver.kt | 13 +--- .../psi2ir/generators/GeneratorContext.kt | 6 +- compiler/testData/cli/jvm/noStdlib.kt | 1 + compiler/testData/cli/jvm/noStdlib.out | 10 +-- .../load/kotlin/reflect/RuntimeModuleData.kt | 5 +- .../kotlin/builtins/ReflectionTypes.kt | 66 +++++++------------ .../diagnostics/JsReflectionAPICallChecker.kt | 10 ++- 9 files changed, 61 insertions(+), 66 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt index bc8add4324a..602f2f61a19 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH +import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.getValue @@ -30,7 +31,11 @@ import org.jetbrains.kotlin.storage.getValue * If there's no Kotlin reflection implementation found in the classpath, checks that there are no usages * of reflection API which will fail at runtime. */ -class JvmReflectionAPICallChecker(private val module: ModuleDescriptor, storageManager: StorageManager) : AbstractReflectionApiCallChecker(module, storageManager) { +class JvmReflectionAPICallChecker( + private val module: ModuleDescriptor, + notFoundClasses: NotFoundClasses, + storageManager: StorageManager +) : AbstractReflectionApiCallChecker(module, notFoundClasses, storageManager) { override val isWholeReflectionApiAvailable by storageManager.createLazyValue { module.findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) != null } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AbstractReflectionApiCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AbstractReflectionApiCallChecker.kt index a8f6d13e569..a756845aed3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AbstractReflectionApiCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/AbstractReflectionApiCallChecker.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.getValue import org.jetbrains.kotlin.util.OperatorNameConventions @@ -34,12 +35,16 @@ private val ANY_MEMBER_NAMES = setOf("equals", "hashCode", "toString") /** * Checks that there are no usages of reflection API which will fail at runtime. */ -abstract class AbstractReflectionApiCallChecker(private val module: ModuleDescriptor, storageManager: StorageManager) : CallChecker { +abstract class AbstractReflectionApiCallChecker( + private val module: ModuleDescriptor, + private val notFoundClasses: NotFoundClasses, + storageManager: StorageManager +) : CallChecker { protected abstract val isWholeReflectionApiAvailable: Boolean protected abstract fun report(element: PsiElement, context: CallCheckerContext) private val kPropertyClasses by storageManager.createLazyValue { - val reflectionTypes = ReflectionTypes(module) + val reflectionTypes = ReflectionTypes(module, notFoundClasses) setOf(reflectionTypes.kProperty0, reflectionTypes.kProperty1, reflectionTypes.kProperty2) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt index 931f728a726..05057a34711 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt @@ -120,9 +120,6 @@ class DoubleColonExpressionResolver( val variance = if (result is DoubleColonLHS.Expression && !result.isObjectQualifier) Variance.OUT_VARIANCE else Variance.INVARIANT val kClassType = reflectionTypes.getKClassType(Annotations.EMPTY, type, variance) - if (kClassType.isError) { - c.trace.report(MISSING_DEPENDENCY_CLASS.on(expression.receiverExpression!!, KotlinBuiltIns.FQ_NAMES.kClass.toSafe())) - } return dataFlowAnalyzer.checkType(createTypeInfo(kClassType, dataFlowInfo), expression, c) } } @@ -535,9 +532,6 @@ class DoubleColonExpressionResolver( checkReferenceIsToAllowedMember(descriptor, context.trace, expression) val type = createKCallableTypeForReference(descriptor, lhs, reflectionTypes, context.scope.ownerDescriptor) ?: return null - if (type.isError) { - context.trace.report(MISSING_DEPENDENCY_CLASS.on(expression, KotlinBuiltIns.FQ_NAMES.kCallable.toSafe())) - } when (descriptor) { is FunctionDescriptor -> bindFunctionReference(expression, type, context) @@ -580,13 +574,10 @@ class DoubleColonExpressionResolver( /* isCoroutine = */ false ) - val parameterTypes = if (type.isError) emptyList() else type.arguments.dropLast(1) - val returnType = if (type.isError) null else type.arguments.last().type - functionDescriptor.initialize( null, null, emptyList(), - createValueParametersForInvokeInFunctionType(functionDescriptor, parameterTypes), - returnType, + createValueParametersForInvokeInFunctionType(functionDescriptor, type.arguments.dropLast(1)), + type.arguments.last().type, Modality.FINAL, Visibilities.PUBLIC ) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt index 898a5071ad2..a0ccfdc7011 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt @@ -23,6 +23,8 @@ import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.PsiSourceManager import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses +import org.jetbrains.kotlin.storage.LockBasedStorageManager class GeneratorContext( val configuration: Psi2IrConfiguration, @@ -31,6 +33,6 @@ class GeneratorContext( ) : IrGeneratorContext(IrBuiltIns(moduleDescriptor.builtIns)) { val sourceManager = PsiSourceManager() - val reflectionTypes = ReflectionTypes(moduleDescriptor) + // TODO: inject a correct StorageManager instance, or store NotFoundClasses inside ModuleDescriptor + val reflectionTypes = ReflectionTypes(moduleDescriptor, NotFoundClasses(LockBasedStorageManager.NO_LOCKS, moduleDescriptor)) } - diff --git a/compiler/testData/cli/jvm/noStdlib.kt b/compiler/testData/cli/jvm/noStdlib.kt index 73c9c89cddf..1fd608749a4 100644 --- a/compiler/testData/cli/jvm/noStdlib.kt +++ b/compiler/testData/cli/jvm/noStdlib.kt @@ -2,6 +2,7 @@ import kotlin.reflect.* fun foo() { String::class.primaryConstructor + Void::class ::foo.isExternal listOf(42) } diff --git a/compiler/testData/cli/jvm/noStdlib.out b/compiler/testData/cli/jvm/noStdlib.out index dc50f3644d1..e38779c55ed 100644 --- a/compiler/testData/cli/jvm/noStdlib.out +++ b/compiler/testData/cli/jvm/noStdlib.out @@ -1,13 +1,13 @@ compiler/testData/cli/jvm/noStdlib.kt:1:8: error: unresolved reference: kotlin import kotlin.reflect.* ^ -compiler/testData/cli/jvm/noStdlib.kt:4:5: error: cannot access class 'kotlin.reflect.KClass'. Check your module classpath for missing or conflicting dependencies +compiler/testData/cli/jvm/noStdlib.kt:4:19: error: unresolved reference: primaryConstructor String::class.primaryConstructor - ^ -compiler/testData/cli/jvm/noStdlib.kt:5:5: error: cannot access class 'kotlin.reflect.KCallable'. Check your module classpath for missing or conflicting dependencies + ^ +compiler/testData/cli/jvm/noStdlib.kt:6:11: error: unresolved reference: isExternal ::foo.isExternal - ^ -compiler/testData/cli/jvm/noStdlib.kt:6:5: error: unresolved reference: listOf + ^ +compiler/testData/cli/jvm/noStdlib.kt:7:5: error: unresolved reference: listOf listOf(42) ^ COMPILATION_ERROR diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/RuntimeModuleData.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/RuntimeModuleData.kt index f4b28e64e37..92cd8f0ff90 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/RuntimeModuleData.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/kotlin/reflect/RuntimeModuleData.kt @@ -55,11 +55,13 @@ class RuntimeModuleData private constructor( val singleModuleClassResolver = SingleModuleClassResolver() val runtimePackagePartProvider = RuntimePackagePartProvider(classLoader) val javaResolverCache = JavaResolverCache.EMPTY + val notFoundClasses = NotFoundClasses(storageManager, module) val globalJavaResolverContext = JavaResolverComponents( storageManager, ReflectJavaClassFinder(classLoader), reflectKotlinClassFinder, deserializedDescriptorResolver, ExternalAnnotationResolver.EMPTY, SignaturePropagator.DO_NOTHING, RuntimeErrorReporter, javaResolverCache, JavaPropertyInitializerEvaluator.DoNothing, SamConversionResolver, RuntimeSourceElementFactory, singleModuleClassResolver, - runtimePackagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module, ReflectionTypes(module) + runtimePackagePartProvider, SupertypeLoopChecker.EMPTY, LookupTracker.DO_NOTHING, module, + ReflectionTypes(module, notFoundClasses) ) val lazyJavaPackageFragmentProvider = LazyJavaPackageFragmentProvider(globalJavaResolverContext) @@ -68,7 +70,6 @@ class RuntimeModuleData private constructor( val javaDescriptorResolver = JavaDescriptorResolver(lazyJavaPackageFragmentProvider, javaResolverCache) val javaClassDataFinder = JavaClassDataFinder(reflectKotlinClassFinder, deserializedDescriptorResolver) - val notFoundClasses = NotFoundClasses(storageManager, module) val binaryClassAnnotationAndConstantLoader = BinaryClassAnnotationAndConstantLoaderImpl( module, notFoundClasses, storageManager, reflectKotlinClassFinder ) diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt index 302fe997d4e..c0127e1b83e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt @@ -21,11 +21,13 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import org.jetbrains.kotlin.types.* import java.util.* @@ -33,41 +35,34 @@ import kotlin.reflect.KProperty val KOTLIN_REFLECT_FQ_NAME = FqName("kotlin.reflect") -class ReflectionTypes(module: ModuleDescriptor) { +class ReflectionTypes(module: ModuleDescriptor, private val notFoundClasses: NotFoundClasses) { private val kotlinReflectScope: MemberScope by lazy(LazyThreadSafetyMode.PUBLICATION) { module.getPackage(KOTLIN_REFLECT_FQ_NAME).memberScope } - fun find(className: String): ClassDescriptor { + private fun find(className: String, numberOfTypeParameters: Int): ClassDescriptor { val name = Name.identifier(className) return kotlinReflectScope.getContributedClassifier(name, NoLookupLocation.FROM_REFLECTION) as? ClassDescriptor - ?: ErrorUtils.createErrorClass(KOTLIN_REFLECT_FQ_NAME.child(name).asString()) + ?: notFoundClasses.getClass(ClassId(KOTLIN_REFLECT_FQ_NAME, name), listOf(numberOfTypeParameters)) } - private object ClassLookup { + private class ClassLookup(val numberOfTypeParameters: Int) { operator fun getValue(types: ReflectionTypes, property: KProperty<*>): ClassDescriptor { - return types.find(property.name.capitalize()) + return types.find(property.name.capitalize(), numberOfTypeParameters) } } - fun getKFunction(n: Int): ClassDescriptor = find("KFunction$n") + fun getKFunction(n: Int): ClassDescriptor = find("KFunction$n", n + 1) - val kClass: ClassDescriptor by ClassLookup - val kProperty0: ClassDescriptor by ClassLookup - val kProperty1: ClassDescriptor by ClassLookup - val kProperty2: ClassDescriptor by ClassLookup - val kMutableProperty0: ClassDescriptor by ClassLookup - val kMutableProperty1: ClassDescriptor by ClassLookup + val kClass: ClassDescriptor by ClassLookup(1) + val kProperty0: ClassDescriptor by ClassLookup(1) + val kProperty1: ClassDescriptor by ClassLookup(2) + val kProperty2: ClassDescriptor by ClassLookup(3) + val kMutableProperty0: ClassDescriptor by ClassLookup(1) + val kMutableProperty1: ClassDescriptor by ClassLookup(2) - fun getKClassType(annotations: Annotations, type: KotlinType, variance: Variance): KotlinType { - val descriptor = kClass - if (ErrorUtils.isError(descriptor)) { - return descriptor.defaultType - } - - val arguments = listOf(TypeProjectionImpl(variance, type)) - return KotlinTypeFactory.simpleNotNullType(annotations, descriptor, arguments) - } + fun getKClassType(annotations: Annotations, type: KotlinType, variance: Variance): KotlinType = + KotlinTypeFactory.simpleNotNullType(annotations, kClass, listOf(TypeProjectionImpl(variance, type))) fun getKFunctionType( annotations: Annotations, @@ -78,31 +73,20 @@ class ReflectionTypes(module: ModuleDescriptor) { builtIns: KotlinBuiltIns ): KotlinType { val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, parameterNames, returnType, builtIns) - val classDescriptor = getKFunction(arguments.size - 1 /* return type */) - - if (ErrorUtils.isError(classDescriptor)) { - return classDescriptor.defaultType - } - return KotlinTypeFactory.simpleNotNullType(annotations, classDescriptor, arguments) } fun getKPropertyType(annotations: Annotations, receiverType: KotlinType?, returnType: KotlinType, mutable: Boolean): KotlinType { - val classDescriptor = - when { - receiverType != null -> when { - mutable -> kMutableProperty1 - else -> kProperty1 - } - else -> when { - mutable -> kMutableProperty0 - else -> kProperty0 - } - } - - if (ErrorUtils.isError(classDescriptor)) { - return classDescriptor.defaultType + val classDescriptor = when { + receiverType != null -> when { + mutable -> kMutableProperty1 + else -> kProperty1 + } + else -> when { + mutable -> kMutableProperty0 + else -> kProperty0 + } } val arguments = ArrayList(2) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt index e6e396ed75c..56a5e1c96e2 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsReflectionAPICallChecker.kt @@ -25,12 +25,18 @@ import org.jetbrains.kotlin.diagnostics.Errors.UNSUPPORTED import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext +import org.jetbrains.kotlin.serialization.deserialization.NotFoundClasses import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.getValue private val ALLOWED_KCLASS_MEMBERS = setOf("simpleName", "isInstance") -class JsReflectionAPICallChecker(private val module: ModuleDescriptor, storageManager: StorageManager) : AbstractReflectionApiCallChecker(module, storageManager) { +class JsReflectionAPICallChecker( + module: ModuleDescriptor, + private val reflectionTypes: ReflectionTypes, + notFoundClasses: NotFoundClasses, + storageManager: StorageManager +) : AbstractReflectionApiCallChecker(module, notFoundClasses, storageManager) { override val isWholeReflectionApiAvailable: Boolean get() = false @@ -38,7 +44,7 @@ class JsReflectionAPICallChecker(private val module: ModuleDescriptor, storageMa context.trace.report(UNSUPPORTED.on(element, "This reflection API is not supported yet in JavaScript")) } - private val kClass by storageManager.createLazyValue { ReflectionTypes(module).kClass } + private val kClass by storageManager.createLazyValue { reflectionTypes.kClass } override fun isAllowedReflectionApi(descriptor: CallableDescriptor, containingClass: ClassDescriptor): Boolean = super.isAllowedReflectionApi(descriptor, containingClass) ||