Remove laziness from LazyJavaTypeResolver.

This laziness was removed because main client of JavaTypeResolver is LazyJavaMemberScope. And this scope run enhance signatures which run resolve for this types.
This commit is contained in:
Stanislav Erokhin
2016-06-03 21:40:03 +03:00
parent e5aa8ebe7c
commit c30754ae3c
4 changed files with 196 additions and 198 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ class LazyJavaTypeResolver(
is JavaPrimitiveType -> { is JavaPrimitiveType -> {
val primitiveType = javaType.type val primitiveType = javaType.type
if (primitiveType != null) c.module.builtIns.getPrimitiveKotlinType(primitiveType) if (primitiveType != null) c.module.builtIns.getPrimitiveKotlinType(primitiveType)
else c.module.builtIns.getUnitType() else c.module.builtIns.unitType
} }
is JavaClassifierType -> transformJavaClassifierType(javaType, attr) is JavaClassifierType -> transformJavaClassifierType(javaType, attr)
is JavaArrayType -> transformArrayType(javaType, attr) is JavaArrayType -> transformArrayType(javaType, attr)
@@ -61,27 +61,6 @@ class LazyJavaTypeResolver(
} }
} }
private fun transformJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): KotlinType {
val allowFlexible = attr.allowFlexible && attr.howThisTypeIsUsed != SUPERTYPE
val lowerAttr = if (allowFlexible) attr.toFlexible(FLEXIBLE_LOWER_BOUND) else attr
val upperAttr = if (allowFlexible) attr.toFlexible(FLEXIBLE_UPPER_BOUND) else attr
return if (javaType.isRaw) {
RawTypeImpl(LazyJavaClassifierType(javaType, lowerAttr.toRawBound(RawBound.LOWER)),
LazyJavaClassifierType(javaType, upperAttr.toRawBound(RawBound.UPPER)))
}
else if (allowFlexible) {
KotlinTypeFactory.flexibleType(
LazyJavaClassifierType(javaType, lowerAttr),
LazyJavaClassifierType(javaType, upperAttr)
)
}
else {
LazyJavaClassifierType(javaType, attr)
}
}
fun transformArrayType(arrayType: JavaArrayType, attr: JavaTypeAttributes, isVararg: Boolean = false): KotlinType { fun transformArrayType(arrayType: JavaArrayType, attr: JavaTypeAttributes, isVararg: Boolean = false): KotlinType {
return run { return run {
val javaComponentType = arrayType.componentType val javaComponentType = arrayType.componentType
@@ -108,189 +87,208 @@ class LazyJavaTypeResolver(
}.replaceAnnotations(attr.typeAnnotations) }.replaceAnnotations(attr.typeAnnotations)
} }
private inner class LazyJavaClassifierType( private fun transformJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): KotlinType {
private val javaType: JavaClassifierType, fun errorType() = ErrorUtils.createErrorType("Unresolved java class ${javaType.presentableText}")
private val attr: JavaTypeAttributes
) : AbstractLazyType(c.storageManager) {
override val annotations = CompositeAnnotations(listOf(LazyJavaAnnotations(c, javaType), attr.typeAnnotations))
private val classifier: JavaClassifier? get() = javaType.classifier val allowFlexible = attr.allowFlexible && attr.howThisTypeIsUsed != SUPERTYPE
val isRaw = javaType.isRaw
if (!javaType.isRaw && !allowFlexible) {
return computeSimpleJavaClassifierType(javaType, attr) ?: errorType()
}
override fun computeTypeConstructor(): TypeConstructor { fun computeBound(lower: Boolean) = computeSimpleJavaClassifierType(javaType, attr.computeAttributes(allowFlexible, isRaw, forLower = lower))
val classifier = classifier ?: return createNotFoundClass()
return when (classifier) {
is JavaClass -> {
val fqName = classifier.fqName.sure { "Class type should have a FQ name: $classifier" }
val classData = mapKotlinClass(fqName) ?: c.components.moduleClassResolver.resolveClass(classifier) val lower = computeBound(lower = true) ?: return errorType()
classData?.typeConstructor ?: createNotFoundClass() val upper = computeBound(lower = false) ?: return errorType()
}
is JavaTypeParameter -> { return if (javaType.isRaw) {
typeParameterResolver.resolveTypeParameter(classifier)?.typeConstructor RawTypeImpl(lower, upper)
?: ErrorUtils.createErrorTypeConstructor("Unresolved Java type parameter: " + javaType.presentableText) }
} else {
else -> throw IllegalStateException("Unknown classifier kind: $classifier") KotlinTypeFactory.flexibleType(lower, upper)
}
}
private fun computeSimpleJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): SimpleType? {
val annotations = CompositeAnnotations(listOf(LazyJavaAnnotations(c, javaType), attr.typeAnnotations))
val constructor = computeTypeConstructor(javaType, attr) ?: return null
val arguments = computeArguments(javaType, attr, constructor)
val isNullable = isNullable(javaType, attr)
val memberScope = AbstractLazyType.computeMemberScope(constructor, arguments)
return KotlinTypeFactory.simpleType(annotations, constructor, arguments, isNullable, memberScope)
}
private fun computeTypeConstructor(javaType: JavaClassifierType, attr: JavaTypeAttributes): TypeConstructor? {
val classifier = javaType.classifier ?: return createNotFoundClass(javaType)
return when (classifier) {
is JavaClass -> {
val fqName = classifier.fqName.sure { "Class type should have a FQ name: $classifier" }
val classData = mapKotlinClass(javaType, attr, fqName) ?: c.components.moduleClassResolver.resolveClass(classifier)
classData?.typeConstructor ?: createNotFoundClass(javaType)
}
is JavaTypeParameter -> {
typeParameterResolver.resolveTypeParameter(classifier)?.typeConstructor
}
else -> throw IllegalStateException("Unknown classifier kind: $classifier")
}
}
// There's no way to extract precise type information in PSI when the type's classifier cannot be resolved.
// So we just take the canonical text of the type (which seems to be the only option at the moment), erase all type arguments
// and treat the resulting qualified name as if it references a simple top-level class.
// Note that this makes MISSING_DEPENDENCY_CLASS diagnostic messages not as precise as they could be in some corner cases.
private fun createNotFoundClass(javaType: JavaClassifierType): TypeConstructor {
val classId = parseCanonicalFqNameIgnoringTypeArguments(javaType.canonicalText)
return c.components.deserializedDescriptorResolver.components.notFoundClasses.get(classId, listOf(0))
}
private fun mapKotlinClass(javaType: JavaClassifierType, attr: JavaTypeAttributes, fqName: FqName): ClassDescriptor? {
if (attr.isForAnnotationParameter && fqName == JAVA_LANG_CLASS_FQ_NAME) {
return c.components.reflectionTypes.kClass
}
val javaToKotlin = JavaToKotlinClassMap.INSTANCE
val howThisTypeIsUsedEffectively = when {
attr.flexibility == FLEXIBLE_LOWER_BOUND -> MEMBER_SIGNATURE_COVARIANT
attr.flexibility == FLEXIBLE_UPPER_BOUND -> MEMBER_SIGNATURE_CONTRAVARIANT
// This case has to be checked before isMarkedReadOnly/isMarkedMutable, because those two are slow
// not mapped, we don't care about being marked mutable/read-only
javaToKotlin.mapPlatformClass(fqName, c.module.builtIns).isEmpty() -> attr.howThisTypeIsUsed
// Read (possibly external) annotations
else -> attr.howThisTypeIsUsedAccordingToAnnotations
}
val kotlinDescriptor = javaToKotlin.mapJavaToKotlin(fqName, c.module.builtIns) ?: return null
if (javaToKotlin.isReadOnly(kotlinDescriptor)) {
if (howThisTypeIsUsedEffectively == MEMBER_SIGNATURE_COVARIANT
|| howThisTypeIsUsedEffectively == SUPERTYPE
|| javaType.argumentsMakeSenseOnlyForMutableContainer(readOnlyContainer = kotlinDescriptor)) {
return javaToKotlin.convertReadOnlyToMutable(kotlinDescriptor)
} }
} }
// There's no way to extract precise type information in PSI when the type's classifier cannot be resolved. return kotlinDescriptor
// So we just take the canonical text of the type (which seems to be the only option at the moment), erase all type arguments }
// and treat the resulting qualified name as if it references a simple top-level class.
// Note that this makes MISSING_DEPENDENCY_CLASS diagnostic messages not as precise as they could be in some corner cases.
private fun createNotFoundClass(): TypeConstructor {
val classId = parseCanonicalFqNameIgnoringTypeArguments(javaType.canonicalText)
return c.components.deserializedDescriptorResolver.components.notFoundClasses.get(classId, listOf(0))
}
private fun mapKotlinClass(fqName: FqName): ClassDescriptor? { // Returns true for covariant read-only container that has mutable pair with invariant parameter
if (attr.isForAnnotationParameter && fqName == JAVA_LANG_CLASS_FQ_NAME) { // List<in A> does not make sense, but MutableList<in A> does
return c.components.reflectionTypes.kClass // Same for Map<K, in V>
} // But both Iterable<in A>, MutableIterable<in A> don't make sense as they are covariant, so return false
private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
readOnlyContainer: ClassDescriptor
): Boolean {
fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false
val javaToKotlin = JavaToKotlinClassMap.INSTANCE if (!typeArguments.lastOrNull().isSuperWildcard()) return false
val mutableLastParameterVariance = JavaToKotlinClassMap.INSTANCE.convertReadOnlyToMutable(readOnlyContainer)
.typeConstructor.parameters.lastOrNull()?.variance ?: return false
val howThisTypeIsUsedEffectively = when { return mutableLastParameterVariance != OUT_VARIANCE
attr.flexibility == FLEXIBLE_LOWER_BOUND -> MEMBER_SIGNATURE_COVARIANT }
attr.flexibility == FLEXIBLE_UPPER_BOUND -> MEMBER_SIGNATURE_CONTRAVARIANT
// This case has to be checked before isMarkedReadOnly/isMarkedMutable, because those two are slow fun computeArguments(javaType: JavaClassifierType, attr: JavaTypeAttributes, constructor: TypeConstructor): List<TypeProjection> {
// not mapped, we don't care about being marked mutable/read-only val eraseTypeParameters = run {
javaToKotlin.mapPlatformClass(fqName, c.module.builtIns).isEmpty() -> attr.howThisTypeIsUsed if (attr.rawBound != RawBound.NOT_RAW) return@run true
// Read (possibly external) annotations
else -> attr.howThisTypeIsUsedAccordingToAnnotations
}
val kotlinDescriptor = javaToKotlin.mapJavaToKotlin(fqName, c.module.builtIns) ?: return null
if (javaToKotlin.isReadOnly(kotlinDescriptor)) {
if (howThisTypeIsUsedEffectively == MEMBER_SIGNATURE_COVARIANT
|| howThisTypeIsUsedEffectively == SUPERTYPE
|| javaType.argumentsMakeSenseOnlyForMutableContainer(readOnlyContainer = kotlinDescriptor)) {
return javaToKotlin.convertReadOnlyToMutable(kotlinDescriptor)
}
}
return kotlinDescriptor
}
// Returns true for covariant read-only container that has mutable pair with invariant parameter
// List<in A> does not make sense, but MutableList<in A> does
// Same for Map<K, in V>
// But both Iterable<in A>, MutableIterable<in A> don't make sense as they are covariant, so return false
private fun JavaClassifierType.argumentsMakeSenseOnlyForMutableContainer(
readOnlyContainer: ClassDescriptor
): Boolean {
if (!typeArguments.lastOrNull().isSuperWildcard()) return false
val mutableLastParameterVariance = JavaToKotlinClassMap.INSTANCE.convertReadOnlyToMutable(readOnlyContainer)
.typeConstructor.parameters.lastOrNull()?.variance ?: return false
return mutableLastParameterVariance != OUT_VARIANCE
}
private fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false
private fun eraseTypeParameters(): Boolean {
if (attr.rawBound != RawBound.NOT_RAW) return true
// This option is needed because sometimes we get weird versions of JDK classes in the class path, // This option is needed because sometimes we get weird versions of JDK classes in the class path,
// such as collections with no generics, so the Java types are not raw, formally, but they don't match with // such as collections with no generics, so the Java types are not raw, formally, but they don't match with
// their Kotlin analogs, so we treat them as raw to avoid exceptions // their Kotlin analogs, so we treat them as raw to avoid exceptions
return javaType.typeArguments.isEmpty() && !constructor.parameters.isEmpty() javaType.typeArguments.isEmpty() && !constructor.parameters.isEmpty()
} }
override fun computeArguments(): List<TypeProjection> { val typeParameters = constructor.parameters
val typeParameters = constructor.parameters if (eraseTypeParameters) {
if (eraseTypeParameters()) { return typeParameters.map {
return typeParameters.map { parameter ->
parameter -> // Some activity for preventing recursion in cases like `class A<T extends A, F extends T>`
// Some activity for preventing recursion in cases like `class A<T extends A, F extends T>` //
// // When calculating upper bound of some parameter (attr.upperBoundOfTypeParameter),
// When calculating upper bound of some parameter (attr.upperBoundOfTypeParameter), // do not try to start upper bound calculation of it again.
// do not try to start upper bound calculation of it again. // If we met such recursive dependency it means that upper bound of `attr.upperBoundOfTypeParameter` based effectively
// If we met such recursive dependency it means that upper bound of `attr.upperBoundOfTypeParameter` based effectively // on the current class, so we can manually erase default type of current constructor.
// on the current class, so we can manually erase default type of current constructor. //
// // In example above corner cases are:
// In example above corner cases are: // - Calculating first argument for raw upper bound of T. It depends on T, so we just get A<*, *>
// - Calculating first argument for raw upper bound of T. It depends on T, so we just get A<*, *> // - Calculating second argument for raw upper bound of T. It depends on F, that again depends on upper bound of T,
// - Calculating second argument for raw upper bound of T. It depends on F, that again depends on upper bound of T, // so we get A<*, *>.
// so we get A<*, *>. // Summary result for upper bound of T is `A<A<*, *>, A<*, *>>..A<out A<*, *>, out A<*, *>>`
// Summary result for upper bound of T is `A<A<*, *>, A<*, *>>..A<out A<*, *>, out A<*, *>>` val erasedUpperBound =
val erasedUpperBound =
parameter.getErasedUpperBound(attr.upperBoundOfTypeParameter) { parameter.getErasedUpperBound(attr.upperBoundOfTypeParameter) {
constructor.declarationDescriptor!!.defaultType.replaceArgumentsWithStarProjections() constructor.declarationDescriptor!!.defaultType.replaceArgumentsWithStarProjections()
} }
RawSubstitution.computeProjection(parameter, attr, erasedUpperBound) RawSubstitution.computeProjection(parameter, attr, erasedUpperBound)
}.toReadOnlyList()
}
if (typeParameters.size != javaType.typeArguments.size) {
// Most of the time this means there is an error in the Java code
return typeParameters.map { p -> TypeProjectionImpl(ErrorUtils.createErrorType(p.name.asString())) }.toReadOnlyList()
}
val howTheProjectionIsUsed = if (attr.howThisTypeIsUsed == SUPERTYPE) SUPERTYPE_ARGUMENT else TYPE_ARGUMENT
return javaType.typeArguments.withIndex().map {
indexedArgument ->
val (i, javaTypeArgument) = indexedArgument
assert(i < typeParameters.size) {
"Argument index should be less then type parameters count, but $i > ${typeParameters.size}"
}
val parameter = typeParameters[i]
transformToTypeProjection(javaTypeArgument, howTheProjectionIsUsed.toAttributes(), parameter)
}.toReadOnlyList() }.toReadOnlyList()
} }
private fun transformToTypeProjection( if (typeParameters.size != javaType.typeArguments.size) {
javaType: JavaType, // Most of the time this means there is an error in the Java code
attr: JavaTypeAttributes, return typeParameters.map { p -> TypeProjectionImpl(ErrorUtils.createErrorType(p.name.asString())) }.toReadOnlyList()
typeParameter: TypeParameterDescriptor }
): TypeProjection { val howTheProjectionIsUsed = if (attr.howThisTypeIsUsed == SUPERTYPE) SUPERTYPE_ARGUMENT else TYPE_ARGUMENT
return when (javaType) { return javaType.typeArguments.withIndex().map {
is JavaWildcardType -> { indexedArgument ->
val bound = javaType.bound val (i, javaTypeArgument) = indexedArgument
val projectionKind = if (javaType.isExtends) OUT_VARIANCE else IN_VARIANCE
if (bound == null || projectionKind.isConflictingArgumentFor(typeParameter)) assert(i < typeParameters.size) {
makeStarProjection(typeParameter, attr) "Argument index should be less then type parameters count, but $i > ${typeParameters.size}"
else {
createProjection(
type = transformJavaType(bound, UPPER_BOUND.toAttributes()),
projectionKind = projectionKind,
typeParameterDescriptor = typeParameter
)
}
}
else -> TypeProjectionImpl(INVARIANT, transformJavaType(javaType, attr))
} }
}
private fun Variance.isConflictingArgumentFor(typeParameter: TypeParameterDescriptor): Boolean { val parameter = typeParameters[i]
if (typeParameter.variance == INVARIANT) return false transformToTypeProjection(javaTypeArgument, howTheProjectionIsUsed.toAttributes(), parameter)
return this != typeParameter.variance }.toReadOnlyList()
}
private val nullable = c.storageManager.createLazyValue l@ {
if (attr.flexibility == FLEXIBLE_LOWER_BOUND) return@l false
if (attr.flexibility == FLEXIBLE_UPPER_BOUND) return@l true
!attr.isMarkedNotNull &&
// 'L extends List<T>' in Java is a List<T> in Kotlin, not a List<T?>
// nullability will be taken care of in individual member signatures
when (classifier) {
is JavaTypeParameter -> {
attr.howThisTypeIsUsed !in setOf(TYPE_ARGUMENT, UPPER_BOUND, SUPERTYPE_ARGUMENT, SUPERTYPE)
}
is JavaClass,
null -> attr.howThisTypeIsUsed !in setOf(TYPE_ARGUMENT, SUPERTYPE_ARGUMENT, SUPERTYPE)
else -> error("Unknown classifier: ${classifier}")
}
}
override val isMarkedNullable: Boolean get() = nullable()
} }
private fun transformToTypeProjection(
javaType: JavaType,
attr: JavaTypeAttributes,
typeParameter: TypeParameterDescriptor
): TypeProjection {
return when (javaType) {
is JavaWildcardType -> {
val bound = javaType.bound
val projectionKind = if (javaType.isExtends) OUT_VARIANCE else IN_VARIANCE
if (bound == null || projectionKind.isConflictingArgumentFor(typeParameter))
makeStarProjection(typeParameter, attr)
else {
createProjection(
type = transformJavaType(bound, UPPER_BOUND.toAttributes()),
projectionKind = projectionKind,
typeParameterDescriptor = typeParameter
)
}
}
else -> TypeProjectionImpl(INVARIANT, transformJavaType(javaType, attr))
}
}
private fun Variance.isConflictingArgumentFor(typeParameter: TypeParameterDescriptor): Boolean {
if (typeParameter.variance == INVARIANT) return false
return this != typeParameter.variance
}
private fun isNullable(javaType: JavaClassifierType, attr: JavaTypeAttributes): Boolean {
if (attr.flexibility == FLEXIBLE_LOWER_BOUND) return false
if (attr.flexibility == FLEXIBLE_UPPER_BOUND) return true
return !attr.isMarkedNotNull &&
// 'L extends List<T>' in Java is a List<T> in Kotlin, not a List<T?>
// nullability will be taken care of in individual member signatures
when (javaType.classifier) {
is JavaTypeParameter -> {
attr.howThisTypeIsUsed !in setOf(TYPE_ARGUMENT, UPPER_BOUND, SUPERTYPE_ARGUMENT, SUPERTYPE)
}
is JavaClass,
null -> attr.howThisTypeIsUsed !in setOf(TYPE_ARGUMENT, SUPERTYPE_ARGUMENT, SUPERTYPE)
else -> error("Unknown classifier: ${javaType.classifier}")
}
}
} }
internal fun makeStarProjection( internal fun makeStarProjection(
@@ -372,14 +370,10 @@ fun TypeUsage.toAttributes(
override val upperBoundOfTypeParameter: TypeParameterDescriptor? = upperBoundForTypeParameter override val upperBoundOfTypeParameter: TypeParameterDescriptor? = upperBoundForTypeParameter
} }
fun JavaTypeAttributes.toFlexible(flexibility: JavaTypeFlexibility) = fun JavaTypeAttributes.computeAttributes(allowFlexible: Boolean, isRaw: Boolean, forLower: Boolean) =
object : JavaTypeAttributes by this { object : JavaTypeAttributes by this {
override val flexibility = flexibility override val flexibility = if (!allowFlexible) INFLEXIBLE else if(forLower) FLEXIBLE_LOWER_BOUND else FLEXIBLE_UPPER_BOUND
} override val rawBound = if (!isRaw) RawBound.NOT_RAW else if(forLower) RawBound.LOWER else RawBound.UPPER
fun JavaTypeAttributes.toRawBound(rawBound: RawBound) =
object : JavaTypeAttributes by this {
override val rawBound = rawBound
} }
@@ -85,8 +85,10 @@ class RawTypeImpl(lowerBound: SimpleType, upperBound: SimpleType) : FlexibleType
internal object RawSubstitution : TypeSubstitution() { internal object RawSubstitution : TypeSubstitution() {
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key)) override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
private val lowerTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toRawBound(RawBound.LOWER) private val lowerTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes()
private val upperTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toRawBound(RawBound.UPPER) .computeAttributes(allowFlexible = false, isRaw = true, forLower = true)
private val upperTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes()
.computeAttributes(allowFlexible = false, isRaw = true, forLower = false)
fun eraseType(type: KotlinType): KotlinType { fun eraseType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor val declaration = type.constructor.declarationDescriptor
@@ -35,16 +35,7 @@ abstract class AbstractLazyType(storageManager: StorageManager) : SimpleType(),
protected abstract fun computeArguments(): List<TypeProjection> protected abstract fun computeArguments(): List<TypeProjection>
override val memberScope by storageManager.createLazyValue { computeMemberScope() } override val memberScope by storageManager.createLazyValue { computeMemberScope(constructor, arguments) }
fun computeMemberScope(): MemberScope {
val descriptor = constructor.declarationDescriptor
return when (descriptor) {
is TypeParameterDescriptor -> descriptor.getDefaultType().memberScope
is ClassDescriptor -> descriptor.getMemberScope(TypeConstructorSubstitution.create(constructor, arguments))
else -> throw IllegalStateException("Unsupported classifier: $descriptor")
}
}
override val isMarkedNullable: Boolean get() = false override val isMarkedNullable: Boolean get() = false
@@ -63,4 +54,15 @@ abstract class AbstractLazyType(storageManager: StorageManager) : SimpleType(),
else "$constructor<not-computed>" else "$constructor<not-computed>"
else -> super.toString() else -> super.toString()
} }
companion object {
fun computeMemberScope(constructor: TypeConstructor, arguments: List<TypeProjection>): MemberScope {
val descriptor = constructor.declarationDescriptor
return when (descriptor) {
is TypeParameterDescriptor -> descriptor.getDefaultType().memberScope
is ClassDescriptor -> descriptor.getMemberScope(TypeConstructorSubstitution.create(constructor, arguments))
else -> throw IllegalStateException("Unsupported classifier: $descriptor")
}
}
}
} }