Extract JVM descriptors type mapping from backend to core

Also unbind it from ASM types
This commit is contained in:
Denis Zharkov
2016-04-20 15:40:52 +03:00
parent 12bbbb6ff1
commit 8cf29ea4f3
8 changed files with 338 additions and 223 deletions
@@ -0,0 +1,126 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
class TypeMappingMode private constructor(
val needPrimitiveBoxing: Boolean = true,
val isForAnnotationParameter: Boolean = false,
// Here DeclarationSiteWildcards means wildcard generated because of declaration-site variance
val skipDeclarationSiteWildcards: Boolean = false,
val skipDeclarationSiteWildcardsIfPossible: Boolean = false,
private val genericArgumentMode: TypeMappingMode? = null,
private val genericContravariantArgumentMode: TypeMappingMode? = genericArgumentMode,
private val genericInvariantArgumentMode: TypeMappingMode? = genericArgumentMode
) {
companion object {
/**
* kotlin.Int is mapped to Ljava/lang/Integer;
*/
@JvmField
val GENERIC_ARGUMENT = TypeMappingMode()
/**
* kotlin.Int is mapped to I
*/
@JvmField
val DEFAULT = TypeMappingMode(genericArgumentMode = GENERIC_ARGUMENT, needPrimitiveBoxing = false)
/**
* kotlin.Int is mapped to Ljava/lang/Integer;
* No projections allowed in immediate arguments
*/
@JvmField
val SUPER_TYPE = TypeMappingMode(skipDeclarationSiteWildcards = true, genericArgumentMode = GENERIC_ARGUMENT)
/**
* kotlin.reflect.KClass mapped to java.lang.Class
* Other types mapped as DEFAULT
*/
@JvmField
val VALUE_FOR_ANNOTATION = TypeMappingMode(
isForAnnotationParameter = true,
needPrimitiveBoxing = false,
genericArgumentMode = TypeMappingMode(isForAnnotationParameter = true, genericArgumentMode = GENERIC_ARGUMENT))
@JvmStatic
fun getModeForReturnTypeNoGeneric(
isAnnotationMethod: Boolean
) = if (isAnnotationMethod) VALUE_FOR_ANNOTATION else DEFAULT
@JvmStatic
fun getOptimalModeForValueParameter(
type: KotlinType
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = false, canBeUsedInSupertypePosition = true)
@JvmStatic
fun getOptimalModeForReturnType(
type: KotlinType,
isAnnotationMethod: Boolean
) = getOptimalModeForSignaturePart(type, isForAnnotationParameter = isAnnotationMethod, canBeUsedInSupertypePosition = false)
private fun getOptimalModeForSignaturePart(
type: KotlinType,
isForAnnotationParameter: Boolean,
canBeUsedInSupertypePosition: Boolean
): TypeMappingMode {
if (type.arguments.isEmpty()) return DEFAULT
val contravariantArgumentMode =
if (!canBeUsedInSupertypePosition)
TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = false,
skipDeclarationSiteWildcardsIfPossible = true)
else
null
val invariantArgumentMode =
if (canBeUsedInSupertypePosition)
getOptimalModeForSignaturePart(type, isForAnnotationParameter, canBeUsedInSupertypePosition = false)
else
null
return TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = !canBeUsedInSupertypePosition,
skipDeclarationSiteWildcardsIfPossible = true,
genericContravariantArgumentMode = contravariantArgumentMode,
genericInvariantArgumentMode = invariantArgumentMode)
}
@JvmStatic
fun createWithConstantDeclarationSiteWildcardsMode(
skipDeclarationSiteWildcards: Boolean,
isForAnnotationParameter: Boolean,
fallbackMode: TypeMappingMode? = null
) = TypeMappingMode(
isForAnnotationParameter = isForAnnotationParameter,
skipDeclarationSiteWildcards = skipDeclarationSiteWildcards,
genericArgumentMode = fallbackMode)
}
fun toGenericArgumentMode(effectiveVariance: Variance): TypeMappingMode =
when (effectiveVariance) {
Variance.IN_VARIANCE -> genericContravariantArgumentMode ?: this
Variance.INVARIANT -> genericInvariantArgumentMode ?: this
else -> genericArgumentMode ?: this
}
}
@@ -0,0 +1,234 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.kotlin.utils.DO_NOTHING_3
interface JvmTypeFactory<T : Any> {
fun boxType(possiblyPrimitiveType: T): T
fun createFromString(representation: String): T
fun createObjectType(internalName: String): T
fun toString(type: T): String
val javaLangClassType: T
}
private fun <T : Any> JvmTypeFactory<T>.boxTypeIfNeeded(possiblyPrimitiveType: T, needBoxedType: Boolean) =
if (needBoxedType) boxType(possiblyPrimitiveType) else possiblyPrimitiveType
interface TypeMappingConfiguration<out T : Any> {
fun commonSupertype(types: Collection<@JvmSuppressWildcards KotlinType>): KotlinType
fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): T?
fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor)
}
fun <T : Any> mapType(
kotlinType: KotlinType,
factory: JvmTypeFactory<T>,
mode: TypeMappingMode,
typeMappingConfiguration: TypeMappingConfiguration<T>,
descriptorTypeWriter: JvmDescriptorTypeWriter<T>?,
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3
): T {
mapBuiltInType(kotlinType, factory)?.let { builtInType ->
val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing)
writeGenericType(kotlinType, jvmType, mode)
return jvmType
}
val constructor = kotlinType.constructor
if (constructor is IntersectionTypeConstructor) {
val commonSupertype = typeMappingConfiguration.commonSupertype(constructor.supertypes)
// interface In<in E>
// open class A : In<A>
// open class B : In<B>
// commonSupertype(A, B) = In<A & B>
// So replace arguments with star-projections to prevent infinite recursive mapping
// It's not very important because such types anyway are prohibited in declarations
return mapType(
commonSupertype.replaceArgumentsWithStarProjections(),
factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType)
}
val descriptor =
constructor.declarationDescriptor
?: throw UnsupportedOperationException("no descriptor for type constructor of " + kotlinType)
when {
ErrorUtils.isError(descriptor) -> {
val jvmType = factory.createObjectType("error/NonExistentClass")
typeMappingConfiguration.processErrorType(kotlinType, descriptor as ClassDescriptor)
descriptorTypeWriter?.writeClass(jvmType)
return jvmType
}
descriptor is ClassDescriptor && KotlinBuiltIns.isArray(kotlinType) -> {
if (kotlinType.arguments.size != 1) {
throw UnsupportedOperationException("arrays must have one type argument")
}
val memberProjection = kotlinType.arguments[0]
val memberType = memberProjection.type
val arrayElementType: T
if (memberProjection.projectionKind === Variance.IN_VARIANCE) {
arrayElementType = factory.createObjectType("java/lang/Object")
descriptorTypeWriter?.apply {
writeArrayType()
writeClass(arrayElementType)
writeArrayEnd()
}
}
else {
descriptorTypeWriter?.writeArrayType()
arrayElementType =
mapType(
memberType, factory,
mode.toGenericArgumentMode(memberProjection.projectionKind),
typeMappingConfiguration, descriptorTypeWriter, writeGenericType)
descriptorTypeWriter?.writeArrayEnd()
}
return factory.createFromString("[" + factory.toString(arrayElementType))
}
descriptor is ClassDescriptor -> {
val jvmType =
if (mode.isForAnnotationParameter && KotlinBuiltIns.isKClass(descriptor))
factory.javaLangClassType
else
typeMappingConfiguration.getPredefinedTypeForClass(descriptor.original)
?: factory.createObjectType(computeInternalName(descriptor.original))
writeGenericType(kotlinType, jvmType, mode)
return jvmType
}
descriptor is TypeParameterDescriptor -> {
val type = mapType(getRepresentativeUpperBound(descriptor),
factory, mode, typeMappingConfiguration, writeGenericType = DO_NOTHING_3, descriptorTypeWriter = null)
descriptorTypeWriter?.writeTypeVariable(descriptor.getName(), type);
return type
}
else -> throw UnsupportedOperationException("Unknown type " + kotlinType)
}
}
private fun <T : Any> mapBuiltInType(
type: KotlinType,
typeFactory: JvmTypeFactory<T>
): T? {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val fqName = descriptor.fqNameUnsafe
val primitiveType = KotlinBuiltIns.getPrimitiveTypeByFqName(fqName)
if (primitiveType != null) {
val jvmType = typeFactory.createFromString(JvmPrimitiveType.get(primitiveType).desc)
val isNullableInJava = TypeUtils.isNullableType(type) || type.hasEnhancedNullability()
return typeFactory.boxTypeIfNeeded(jvmType, isNullableInJava)
}
val arrayElementType = KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(fqName)
if (arrayElementType != null) {
return typeFactory.createFromString("[" + JvmPrimitiveType.get(arrayElementType).desc)
}
val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName)
if (classId != null) {
return typeFactory.createObjectType(JvmClassName.byClassId(classId).internalName)
}
return null
}
private fun computeInternalName(klass: ClassDescriptor): String {
val container = klass.containingDeclaration
val name = SpecialNames.safeIdentifier(klass.name).identifier
if (container is PackageFragmentDescriptor) {
val fqName = container.fqName
return if (fqName.isRoot) name else fqName.asString().replace('.', '/') + '/' + name
}
assert(container is ClassDescriptor) { "Unexpected container: $container for $klass" }
val containerInternalName = computeInternalName(container as ClassDescriptor)
return if (klass.kind == ClassKind.ENUM_ENTRY) containerInternalName else containerInternalName + "$" + name
}
private fun getRepresentativeUpperBound(descriptor: TypeParameterDescriptor): KotlinType {
val upperBounds = descriptor.upperBounds
assert(!upperBounds.isEmpty()) { "Upper bounds should not be empty: " + descriptor }
return upperBounds.firstOrNull {
val classDescriptor = it.constructor.declarationDescriptor as? ClassDescriptor ?: return@firstOrNull false
classDescriptor.kind != ClassKind.INTERFACE && classDescriptor.kind != ClassKind.ANNOTATION_CLASS
} ?: upperBounds.first()
}
open class JvmDescriptorTypeWriter<T : Any>(private val jvmTypeFactory: JvmTypeFactory<T>) {
private var jvmCurrentTypeArrayLevel: Int = 0
protected var jvmCurrentType: T? = null
private set
protected fun clearCurrentType() {
jvmCurrentType = null
jvmCurrentTypeArrayLevel = 0
}
open fun writeArrayType() {
if (jvmCurrentType == null) {
++jvmCurrentTypeArrayLevel
}
}
open fun writeArrayEnd() {
}
open public fun writeClass(objectType: T) {
writeJvmTypeAsIs(objectType)
}
protected fun writeJvmTypeAsIs(type: T) {
if (jvmCurrentType == null) {
jvmCurrentType = jvmTypeFactory.createFromString("[".repeat(jvmCurrentTypeArrayLevel) + jvmTypeFactory.toString(type))
}
}
open fun writeTypeVariable(name: Name, type: T) {
writeJvmTypeAsIs(type)
}
}