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
This commit is contained in:
Alexander Udalov
2017-02-22 14:13:17 +03:00
parent 794cc1e3be
commit 12b48f86e7
9 changed files with 61 additions and 66 deletions
@@ -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
}
@@ -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)
}
@@ -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
)
@@ -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))
}
+1
View File
@@ -2,6 +2,7 @@ import kotlin.reflect.*
fun foo() {
String::class.primaryConstructor
Void::class
::foo.isExternal
listOf(42)
}
+5 -5
View File
@@ -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
@@ -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
)
@@ -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<TypeProjection>(2)
@@ -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) ||