Introduce AbstractTypeMapper based on type markers

This commit is contained in:
Dmitriy Novozhilov
2020-09-22 18:20:57 +03:00
parent 7380abac13
commit 86d4d320c4
5 changed files with 208 additions and 51 deletions
@@ -26,11 +26,11 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.computeExpandedTypeForInlineClass
import org.jetbrains.kotlin.load.kotlin.mapBuiltInType
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.computeExpandedTypeForInlineClass
import org.jetbrains.org.objectweb.asm.Type
class IrTypeMapper(private val context: JvmBackendContext) : KotlinTypeMapperBase() {
@@ -0,0 +1,126 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.types
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.signature.AsmTypeFactory
import org.jetbrains.kotlin.load.kotlin.JvmDescriptorTypeWriter
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.mapBuiltInType
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.org.objectweb.asm.Type
interface TypeMappingContext {
val typeContext: TypeSystemCommonBackendContextForTypeMapping
fun getClassInternalName(typeConstructor: TypeConstructorMarker): String
fun JvmDescriptorTypeWriter<Type>.writeGenericType(type: SimpleTypeMarker, asmType: Type, mode: TypeMappingMode)
}
object AbstractTypeMapper {
fun mapClass(context: TypeMappingContext, typeConstructor: TypeConstructorMarker): Type {
return with(context.typeContext) {
when {
typeConstructor.isClassTypeConstructor() -> {
mapType(context, typeConstructor.defaultType(), TypeMappingMode.CLASS_DECLARATION)
}
typeConstructor.isTypeParameter() -> {
mapType(context, typeConstructor.defaultType())
}
else -> error("Unknown type constructor: $typeConstructor")
}
}
}
@OptIn(ExperimentalStdlibApi::class)
fun TypeSystemCommonBackendContextForTypeMapping.mapType(
context: TypeMappingContext,
type: SimpleTypeMarker,
mode: TypeMappingMode = TypeMappingMode.DEFAULT,
sw: JvmDescriptorTypeWriter<Type>? = null
): Type {
if (type.isSuspendFunction()) {
val argumentsCount = type.argumentsCount()
val argumentsList = type.asArgumentList()
val arguments = buildList {
for (i in 0 until (argumentsCount - 1)) {
this += argumentsList[i].adjustedType()
}
this += continuationTypeConstructor().typeWithArguments(argumentsList[argumentsCount - 1].adjustedType())
this += nullableAnyType()
}
val runtimeFunctionType = functionNTypeConstructor(arguments.size - 1).typeWithArguments(arguments)
return mapType(context, runtimeFunctionType, mode, sw)
}
mapBuiltInType(type, AsmTypeFactory, mode)?.let { builtInType ->
return boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing).also { asmType ->
with(context) { sw?.writeGenericType(type, asmType, mode) }
}
}
val typeConstructor = type.typeConstructor()
when {
type.isArrayOrNullableArray() -> {
val typeArgument = type.asArgumentList()[0]
val (variance, memberType) = when {
typeArgument.isStarProjection() -> Variance.OUT_VARIANCE to nullableAnyType()
else -> typeArgument.getVariance().toVariance() to typeArgument.getType()
}
require(memberType is SimpleTypeMarker)
val arrayElementType: Type
sw?.writeArrayType()
if (variance == Variance.IN_VARIANCE) {
arrayElementType = AsmTypes.OBJECT_TYPE
sw?.writeClass(arrayElementType)
} else {
arrayElementType = mapType(context, memberType, mode.toGenericArgumentMode(variance, ofArray = true), sw)
}
sw?.writeArrayEnd()
return AsmUtil.getArrayType(arrayElementType)
}
typeConstructor.isClassTypeConstructor() -> {
if (typeConstructor.isInlineClass() && !mode.needInlineClassWrapping) {
val expandedType = computeExpandedTypeForInlineClass(type)
require(expandedType is SimpleTypeMarker?)
if (expandedType != null) {
return mapType(context, expandedType, mode.wrapInlineClassesMode(), sw)
}
}
val asmType = if (mode.isForAnnotationParameter && type.isKClass())
AsmTypes.JAVA_CLASS_TYPE
else
Type.getObjectType(context.getClassInternalName(typeConstructor))
with(context) { sw?.writeGenericType(type, asmType, mode) }
return asmType
}
typeConstructor.isTypeParameter() -> {
val typeParameter = typeConstructor as TypeParameterMarker
return mapType(context, typeParameter.representativeUpperBound(), mode, null).also { asmType ->
sw?.writeTypeVariable(typeParameter.getName(), asmType)
}
}
else -> throw UnsupportedOperationException("Unknown type $type")
}
}
private fun boxTypeIfNeeded(possiblyPrimitiveType: Type, needBoxedType: Boolean): Type =
if (needBoxedType) AsmUtil.boxType(possiblyPrimitiveType) else possiblyPrimitiveType
private fun TypeVariance.toVariance(): Variance = when (this) {
TypeVariance.IN -> Variance.IN_VARIANCE
TypeVariance.OUT -> Variance.OUT_VARIANCE
TypeVariance.INV -> Variance.INVARIANT
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.types
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
fun TypeSystemCommonBackendContext.computeExpandedTypeForInlineClass(inlineClassType: KotlinTypeMarker): KotlinTypeMarker? =
computeExpandedTypeInner(inlineClassType, hashSetOf())
private fun TypeSystemCommonBackendContext.computeExpandedTypeInner(
kotlinType: KotlinTypeMarker, visitedClassifiers: HashSet<TypeConstructorMarker>
): KotlinTypeMarker? {
val classifier = kotlinType.typeConstructor()
if (!visitedClassifiers.add(classifier)) return null
val typeParameter = classifier.getTypeParameterClassifier()
return when {
typeParameter != null ->
computeExpandedTypeInner(typeParameter.getRepresentativeUpperBound(), visitedClassifiers)
?.let { expandedUpperBound ->
if (expandedUpperBound.isNullableType() || !kotlinType.isMarkedNullable())
expandedUpperBound
else
expandedUpperBound.makeNullable()
}
classifier.isInlineClass() -> {
// kotlinType is the boxed inline class type
val underlyingType = kotlinType.getSubstitutedUnderlyingType() ?: return null
val expandedUnderlyingType = computeExpandedTypeInner(underlyingType, visitedClassifiers) ?: return null
when {
!kotlinType.isNullableType() -> expandedUnderlyingType
// Here inline class type is nullable. Apply nullability to the expandedUnderlyingType.
// Nullable types become inline class boxes
expandedUnderlyingType.isNullableType() -> kotlinType
// Primitives become inline class boxes
expandedUnderlyingType is SimpleTypeMarker && expandedUnderlyingType.isPrimitiveType() -> kotlinType
// Non-null reference types become nullable reference types
else -> expandedUnderlyingType.makeNullable()
}
}
else -> kotlinType
}
}
@@ -50,3 +50,28 @@ interface TypeSystemCommonBackendContext : TypeSystemContext {
fun KotlinTypeMarker.isInterfaceOrAnnotationClass(): Boolean
}
interface TypeSystemCommonBackendContextForTypeMapping : TypeSystemCommonBackendContext {
fun TypeConstructorMarker.isTypeParameter(): Boolean
fun TypeConstructorMarker.defaultType(): SimpleTypeMarker
fun SimpleTypeMarker.isSuspendFunction(): Boolean
fun SimpleTypeMarker.isKClass(): Boolean
fun KotlinTypeMarker.isRawType(): Boolean
fun TypeConstructorMarker.typeWithArguments(arguments: List<KotlinTypeMarker>): SimpleTypeMarker
fun TypeConstructorMarker.typeWithArguments(vararg arguments: KotlinTypeMarker): SimpleTypeMarker {
return typeWithArguments(arguments.toList())
}
fun TypeArgumentMarker.adjustedType(): KotlinTypeMarker {
if (this.isStarProjection()) return nullableAnyType()
return getType()
}
fun TypeParameterMarker.representativeUpperBound(): SimpleTypeMarker
fun continuationTypeConstructor(): TypeConstructorMarker
fun functionNTypeConstructor(n: Int): TypeConstructorMarker
}
@@ -10,11 +10,7 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.substitutedUnderlyingType
import org.jetbrains.kotlin.resolve.unsubstitutedUnderlyingType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
internal fun computeUnderlyingType(inlineClassType: KotlinType): KotlinType? {
@@ -27,52 +23,6 @@ internal fun computeUnderlyingType(inlineClassType: KotlinType): KotlinType? {
inlineClassType.substitutedUnderlyingType()
}
fun TypeSystemCommonBackendContext.computeExpandedTypeForInlineClass(inlineClassType: KotlinTypeMarker): KotlinTypeMarker? =
computeExpandedTypeInner(inlineClassType, hashSetOf())
private fun TypeSystemCommonBackendContext.computeExpandedTypeInner(
kotlinType: KotlinTypeMarker, visitedClassifiers: HashSet<TypeConstructorMarker>
): KotlinTypeMarker? {
val classifier = kotlinType.typeConstructor()
if (!visitedClassifiers.add(classifier)) return null
val typeParameter = classifier.getTypeParameterClassifier()
return when {
typeParameter != null ->
computeExpandedTypeInner(typeParameter.getRepresentativeUpperBound(), visitedClassifiers)
?.let { expandedUpperBound ->
if (expandedUpperBound.isNullableType() || !kotlinType.isMarkedNullable())
expandedUpperBound
else
expandedUpperBound.makeNullable()
}
classifier.isInlineClass() -> {
// kotlinType is the boxed inline class type
val underlyingType = kotlinType.getSubstitutedUnderlyingType() ?: return null
val expandedUnderlyingType = computeExpandedTypeInner(underlyingType, visitedClassifiers) ?: return null
when {
!kotlinType.isNullableType() -> expandedUnderlyingType
// Here inline class type is nullable. Apply nullability to the expandedUnderlyingType.
// Nullable types become inline class boxes
expandedUnderlyingType.isNullableType() -> kotlinType
// Primitives become inline class boxes
expandedUnderlyingType is SimpleTypeMarker && expandedUnderlyingType.isPrimitiveType() -> kotlinType
// Non-null reference types become nullable reference types
else -> expandedUnderlyingType.makeNullable()
}
}
else -> kotlinType
}
}
internal fun shouldUseUnderlyingType(inlineClassType: KotlinType): Boolean {
val underlyingType = inlineClassType.unsubstitutedUnderlyingType() ?: return false