Use experimental javaType in full reflect where it's not supported yet

#KT-22936 Fixed
 #KT-34344 Fixed
This commit is contained in:
Alexander Udalov
2020-05-28 20:48:35 +02:00
parent 9e37b62f62
commit 117aae8a6b
14 changed files with 126 additions and 42 deletions
@@ -201,10 +201,7 @@ val KClass<*>.allSupertypes: Collection<KType>
supertypes.map { supertype ->
val substituted = substitutor.substitute((supertype as KTypeImpl).type, Variance.INVARIANT)
?: throw KotlinReflectionInternalError("Type substitution failed: $supertype ($current)")
KTypeImpl(substituted) {
// TODO
TODO("Java type for supertype")
}
KTypeImpl(substituted)
}
}
},
@@ -19,7 +19,10 @@ package kotlin.reflect.full
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.types.*
import kotlin.reflect.*
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.KVariance
import kotlin.reflect.jvm.internal.KClassifierImpl
import kotlin.reflect.jvm.internal.KTypeImpl
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
@@ -56,11 +59,7 @@ fun KClassifier.createType(
if (annotations.isEmpty()) Annotations.EMPTY
else Annotations.EMPTY // TODO: support type annotations
val kotlinType = createKotlinType(typeAnnotations, typeConstructor, arguments, nullable)
return KTypeImpl(kotlinType) {
TODO("Java type is not yet supported for types created with createType (classifier = $this)")
}
return KTypeImpl(createKotlinType(typeAnnotations, typeConstructor, arguments, nullable))
}
private fun createKotlinType(
@@ -17,10 +17,8 @@
@file:JvmName("KTypes")
package kotlin.reflect.full
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import kotlin.reflect.*
import kotlin.reflect.KType
import kotlin.reflect.jvm.internal.KTypeImpl
/**
@@ -28,15 +26,7 @@ import kotlin.reflect.jvm.internal.KTypeImpl
*/
@SinceKotlin("1.1")
fun KType.withNullability(nullable: Boolean): KType {
if (isMarkedNullable) {
return if (nullable) this else KTypeImpl(TypeUtils.makeNotNullable((this as KTypeImpl).type)) { javaType }
}
// If the type is not marked nullable, it's either a non-null type or a platform type.
val kotlinType = (this as KTypeImpl).type
if (kotlinType.isFlexible()) return KTypeImpl(TypeUtils.makeNullableAsSpecified(kotlinType, nullable)) { javaType }
return if (!nullable) this else KTypeImpl(TypeUtils.makeNullable(kotlinType)) { javaType }
return (this as KTypeImpl).makeNullableAsSpecified(nullable)
}
@@ -21,6 +21,7 @@ package kotlin.reflect.jvm
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import java.lang.reflect.*
import kotlin.reflect.*
import kotlin.reflect.javaType as stdlibJavaType
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.functions
import kotlin.reflect.full.memberProperties
@@ -76,7 +77,8 @@ val <T> KFunction<T>.javaConstructor: Constructor<T>?
* the JVM class [Unit] when it's the type of a parameter, or to `void` when it's the return type of a function.
*/
val KType.javaType: Type
get() = (this as KTypeImpl).javaType
@OptIn(ExperimentalStdlibApi::class)
get() = (this as KTypeImpl).javaType ?: stdlibJavaType
// Java reflection -> Kotlin reflection
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.runtime.structure.primitiveByWrapper
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.isFlexible
import java.lang.reflect.GenericArrayType
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
@@ -37,15 +38,19 @@ import kotlin.reflect.jvm.jvmErasure
internal class KTypeImpl(
val type: KotlinType,
computeJavaType: () -> Type
computeJavaType: (() -> Type)? = null
) : KTypeBase {
override val javaType: Type by ReflectProperties.lazySoft(computeJavaType)
@Suppress("UNCHECKED_CAST")
private val computeJavaType =
computeJavaType as? ReflectProperties.LazySoftVal<Type> ?: computeJavaType?.let(ReflectProperties::lazySoft)
override val javaType: Type?
get() = computeJavaType?.invoke()
override val classifier: KClassifier? by ReflectProperties.lazySoft { convert(type) }
private fun convert(type: KotlinType): KClassifier? {
val descriptor = type.constructor.declarationDescriptor
when (descriptor) {
when (val descriptor = type.constructor.declarationDescriptor) {
is ClassDescriptor -> {
val jClass = descriptor.toJavaClass() ?: return null
if (jClass.isArray) {
@@ -53,7 +58,7 @@ internal class KTypeImpl(
val argument = type.arguments.singleOrNull()?.type ?: return KClassImpl(jClass)
val elementClassifier =
convert(argument)
?: throw KotlinReflectionInternalError("Cannot determine classifier for array element type: $this")
?: throw KotlinReflectionInternalError("Cannot determine classifier for array element type: $this")
return KClassImpl(elementClassifier.jvmErasure.java.createArrayType())
}
@@ -73,15 +78,14 @@ internal class KTypeImpl(
val typeArguments = type.arguments
if (typeArguments.isEmpty()) return@arguments emptyList<KTypeProjection>()
val parameterizedTypeArguments by lazy(PUBLICATION) { javaType.parameterizedTypeArguments }
val parameterizedTypeArguments by lazy(PUBLICATION) { javaType!!.parameterizedTypeArguments }
typeArguments.mapIndexed { i, typeProjection ->
if (typeProjection.isStarProjection) {
KTypeProjection.STAR
} else {
val type = KTypeImpl(typeProjection.type) {
val javaType = javaType
when (javaType) {
val type = KTypeImpl(typeProjection.type, if (computeJavaType == null) null else fun(): Type {
return when (val javaType = javaType) {
is Class<*> -> {
// It's either an array or a raw type.
// TODO: return upper bound of the corresponding parameter for a raw type?
@@ -99,7 +103,7 @@ internal class KTypeImpl(
}
else -> throw KotlinReflectionInternalError("Non-generic type has been queried for arguments: $this")
}
}
})
when (typeProjection.projectionKind) {
Variance.INVARIANT -> KTypeProjection.invariant(type)
Variance.IN_VARIANCE -> KTypeProjection.contravariant(type)
@@ -115,6 +119,13 @@ internal class KTypeImpl(
override val annotations: List<Annotation>
get() = type.computeAnnotations()
internal fun makeNullableAsSpecified(nullable: Boolean): KTypeImpl {
// If the type is not marked nullable, it's either a non-null type or a platform type.
if (!type.isFlexible() && isMarkedNullable == nullable) return this
return KTypeImpl(TypeUtils.makeNullableAsSpecified(type, nullable), computeJavaType)
}
override fun equals(other: Any?) =
other is KTypeImpl && type == other.type
@@ -36,13 +36,7 @@ internal class KTypeParameterImpl(
override val name: String
get() = descriptor.name.asString()
override val upperBounds: List<KType> by ReflectProperties.lazySoft {
descriptor.upperBounds.map { kotlinType ->
KTypeImpl(kotlinType) {
TODO("Java type is not yet supported for type parameters: $descriptor")
}
}
}
override val upperBounds: List<KType> by ReflectProperties.lazySoft { descriptor.upperBounds.map(::KTypeImpl) }
override val variance: KVariance
get() = when (descriptor.variance) {
@@ -68,7 +68,7 @@ public class ReflectProperties {
// A delegate for a lazy property on a soft reference, whose initializer may be invoked multiple times
// including simultaneously from different threads
public static class LazySoftVal<T> extends Val<T> {
public static class LazySoftVal<T> extends Val<T> implements Function0<T> {
private final Function0<T> initializer;
private volatile SoftReference<Object> value = null;