JVM IR: reuse JVM code for reified type parameter mappings instead of copy-paste

This commit is contained in:
Alexander Udalov
2019-09-04 16:19:14 +02:00
parent d1df453edc
commit d16bdded7f
10 changed files with 83 additions and 168 deletions
@@ -17,8 +17,13 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.inline.NameGenerator
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putReifiedOperationMarker
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
interface BaseExpressionCodegen {
@@ -29,6 +34,8 @@ interface BaseExpressionCodegen {
val inlineNameGenerator: NameGenerator
val typeSystem: TypeSystemCommonBackendContext
val lastLineNumber: Int
fun propagateChildReifiedTypeParametersUsages(reifiedTypeParametersUsages: ReifiedTypeParametersUsages)
@@ -41,4 +48,17 @@ interface BaseExpressionCodegen {
)
fun markLineNumberAfterInlineIfNeeded()
fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker)
@JvmDefault
fun putReifiedOperationMarkerIfTypeIsReifiedParameter(type: KotlinTypeMarker, operationKind: OperationKind) {
with(typeSystem) {
val (typeParameter, second) = extractReificationArgument(type) ?: return
if (typeParameter.isReified()) {
consumeReifiedOperationMarker(typeParameter)
putReifiedOperationMarker(operationKind, second, visitor)
}
}
}
}
@@ -80,6 +80,7 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContextImpl;
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
import org.jetbrains.kotlin.types.model.TypeParameterMarker;
import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
import org.jetbrains.kotlin.util.OperatorNameConventions;
import org.jetbrains.org.objectweb.asm.Label;
@@ -123,7 +124,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private final TailRecursionCodegen tailRecursionCodegen;
public final CallGenerator defaultCallGenerator = new CallGenerator.DefaultCallGenerator(this);
private final SwitchCodegenProvider switchCodegenProvider;
public final TypeSystemCommonBackendContext typeSystem;
private final TypeSystemCommonBackendContext typeSystem;
private final Stack<BlockStackElement> blockStackElements = new Stack<>();
@@ -2679,7 +2680,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
boolean isReified = key.isReified() || InlineUtil.isArrayConstructorWithLambda(resolvedCall.getResultingDescriptor());
Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
Pair<TypeParameterMarker, ReificationArgument> typeParameterAndReificationArgument =
extractReificationArgument(typeSystem, type);
if (typeParameterAndReificationArgument == null) {
KotlinType approximatedType = CapturedTypeApproximationKt.approximateCapturedTypes(entry.getValue()).getUpper();
// type is not generic
@@ -4841,19 +4843,6 @@ The "returned" value of try expression with no finally is either the last expres
});
}
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
) {
Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
if (typeParameterAndReificationArgument == null) return;
TypeParameterDescriptor typeParameter = typeParameterAndReificationArgument.getFirst();
if (typeParameter.isReified()) {
consumeReifiedOperationMarker(typeParameter);
ReifiedTypeInliner.putReifiedOperationMarker(operationKind, typeParameterAndReificationArgument.getSecond(), v);
}
}
@Override
public void propagateChildReifiedTypeParametersUsages(@NotNull ReifiedTypeParametersUsages usages) {
parentCodegen.getReifiedTypeParametersUsages().propagateChildUsagesWithinContext(
@@ -5077,10 +5066,18 @@ The "returned" value of try expression with no finally is either the last expres
return v;
}
private void consumeReifiedOperationMarker(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
@NotNull
@Override
public TypeSystemCommonBackendContext getTypeSystem() {
return typeSystem;
}
@Override
public void consumeReifiedOperationMarker(@NotNull TypeParameterMarker typeParameter) {
assert typeParameter instanceof TypeParameterDescriptor : "Type parameter should be a descriptor: " + typeParameter;
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) typeParameter;
if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) {
parentCodegen.getReifiedTypeParametersUsages().
addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
parentCodegen.getReifiedTypeParametersUsages().addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
}
}
}
@@ -51,8 +51,11 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes.*
@@ -381,18 +384,21 @@ fun InstructionAdapter.generateNewInstanceDupAndPlaceBeforeStackTop(
}
}
fun extractReificationArgument(initialType: KotlinType): Pair<TypeParameterDescriptor, ReificationArgument>? {
fun TypeSystemCommonBackendContext.extractReificationArgument(initialType: KotlinTypeMarker): Pair<TypeParameterMarker, ReificationArgument>? {
var type = initialType
var arrayDepth = 0
val isNullable = type.isMarkedNullable
while (KotlinBuiltIns.isArray(type)) {
val isNullable = type.isMarkedNullable()
while (type.isArrayOrNullableArray()) {
arrayDepth++
type = type.arguments[0].type
val argument = type.getArgument(0)
type =
if (argument.isStarProjection()) nullableAnyType()
else argument.getType()
}
val parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type) ?: return null
val typeParameter = type.typeConstructor().getTypeParameterClassifier() ?: return null
return Pair(parameterDescriptor, ReificationArgument(parameterDescriptor.name.asString(), isNullable, arrayDepth))
return Pair(typeParameter, ReificationArgument(typeParameter.getName().asString(), isNullable, arrayDepth))
}
fun unwrapInitialSignatureDescriptor(function: FunctionDescriptor): FunctionDescriptor =
@@ -26,9 +26,7 @@ import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
@@ -435,6 +433,8 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
override fun arrayType(componentType: KotlinTypeMarker): SimpleTypeMarker = TODO("not implemented")
override fun KotlinTypeMarker.isArrayOrNullableArray(): Boolean = TODO("not implemented")
override fun TypeConstructorMarker.isFinalClassOrEnumEntryOrAnnotationClassConstructor(): Boolean {
val firRegularClass = toFirRegularClass() ?: return false
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.codegen.CallGenerator
import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
import org.jetbrains.kotlin.codegen.extractReificationArgument
import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
@@ -45,6 +46,8 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.keysToMap
@@ -116,6 +119,9 @@ class ExpressionCodegen(
override val inlineNameGenerator: NameGenerator = NameGenerator("${classCodegen.type.internalName}\$todo") // TODO
override val typeSystem: TypeSystemCommonBackendContext
get() = typeMapper.typeSystem
override var lastLineNumber: Int = -1
private val closureReifiedMarkers = hashMapOf<IrClass, ReifiedTypeParametersUsages>()
@@ -319,8 +325,9 @@ class ExpressionCodegen(
mv.load(0, OBJECT_TYPE)
for (argumentIndex in 0 until expression.typeArgumentsCount) {
expression.getTypeArgument(argumentIndex)?.getTypeParameterOrNull()?.takeIf { it.isReified }?.let {
consumeReifiedOperationMarker(it)
val classifier = expression.getTypeArgument(argumentIndex)?.classifierOrNull
if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) {
consumeReifiedOperationMarker(classifier)
}
}
}
@@ -605,7 +612,7 @@ class ExpressionCodegen(
if (typeOperand.isReifiedTypeParameter) {
val operationKind = if (expression.operator == IrTypeOperator.CAST) AS else SAFE_AS
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, operationKind, mv, this)
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, operationKind)
v.checkcast(boxedRightType)
} else {
assert(expression.operator == IrTypeOperator.CAST) { "IrTypeOperator.SAFE_CAST should have been lowered." }
@@ -618,7 +625,7 @@ class ExpressionCodegen(
expression.argument.accept(this, data).coerce(context.irBuiltIns.anyNType).materialize()
val type = typeMapper.boxType(typeOperand)
if (typeOperand.isReifiedTypeParameter) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS, mv, this)
putReifiedOperationMarkerIfTypeIsReifiedParameter(typeOperand, ReifiedTypeInliner.OperationKind.IS)
v.instanceOf(type)
} else {
TypeIntrinsics.instanceOf(mv, kotlinType, type, state.languageVersionSettings.isReleaseCoroutines())
@@ -906,7 +913,7 @@ class ExpressionCodegen(
assert(classifier.owner.isReified) {
"Non-reified type parameter under ::class should be rejected by type checker: ${classifier.owner.dump()}"
}
putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS, mv, this)
putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS)
}
generateClassInstance(mv, classType)
@@ -948,9 +955,9 @@ class ExpressionCodegen(
element.getTypeArgumentOrDefault(it)
}
val mappings = IrTypeParameterMappings()
val mappings = TypeParameterMappings<IrType>()
for ((key, type) in typeArguments.entries) {
val reificationArgument = extractReificationArgument(type)
val reificationArgument = typeMapper.typeSystem.extractReificationArgument(type)
if (reificationArgument == null) {
// type is not generic
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.TYPE)
@@ -961,7 +968,7 @@ class ExpressionCodegen(
)
} else {
mappings.addParameterMappingForFurtherReification(
key.name.identifier, type, reificationArgument, key.isReified
key.name.identifier, type, reificationArgument.second, key.isReified
)
}
}
@@ -969,9 +976,8 @@ class ExpressionCodegen(
val original = (callee as? IrSimpleFunction)?.resolveFakeOverride() ?: irFunction
val methodOwner = callee.parent.safeAs<IrClass>()?.let(typeMapper::mapClass) ?: MethodSignatureMapper.FAKE_OWNER_TYPE
val sourceCompiler = IrSourceCompilerForInline(state, element, this, data)
val typeParameterMappings = mappings.toTypeParameterMappings()
val reifiedTypeInliner = ReifiedTypeInliner(typeParameterMappings, object : ReifiedTypeInliner.IntrinsicsSupport<IrType> {
val reifiedTypeInliner = ReifiedTypeInliner(mappings, object : ReifiedTypeInliner.IntrinsicsSupport<IrType> {
override fun putClassInstance(v: InstructionAdapter, type: IrType) {
generateClassInstance(v, type)
}
@@ -980,13 +986,14 @@ class ExpressionCodegen(
}, IrTypeCheckerContext(context.irBuiltIns), state.languageVersionSettings)
return IrInlineCodegen(
this, state, original.descriptor, methodOwner, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner
this, state, original.descriptor, methodOwner, signature, mappings, sourceCompiler, reifiedTypeInliner
)
}
private fun consumeReifiedOperationMarker(typeParameter: IrTypeParameter) {
if (typeParameter.parent != irFunction) {
classCodegen.reifiedTypeParametersUsages.addUsedReifiedParameter(typeParameter.name.asString())
override fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker) {
require(typeParameter is IrTypeParameterSymbol)
if (typeParameter.owner.parent != irFunction) {
classCodegen.reifiedTypeParametersUsages.addUsedReifiedParameter(typeParameter.owner.name.asString())
}
}
@@ -1015,128 +1022,6 @@ class ExpressionCodegen(
return irFunction.isInline || isInlineLambda
}
/* Borrowed and modified from compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt */
private fun extractReificationArgumentWithParameter(initialType: IrType): Pair<IrTypeParameter, IrReificationArgument>? {
var type = initialType
var arrayDepth = 0
val isNullable = type.isMarkedNullable()
while (type.isArray() || type.isNullableArray()) {
arrayDepth++
type = (type as IrSimpleType).arguments[0].safeAs<IrTypeProjection>()?.type ?: classCodegen.context.irBuiltIns.anyNType
}
val parameter = type.getTypeParameterOrNull() ?: return null
return Pair(parameter, IrReificationArgument(parameter.name.asString(), isNullable, arrayDepth))
}
private fun extractReificationArgument(initialType: IrType): IrReificationArgument? = extractReificationArgumentWithParameter(initialType)?.second
/* From ReifiedTypeInliner.kt */
inner class IrReificationArgument(
val parameterName: String, val nullable: Boolean, private val arrayDepth: Int
) {
fun asString() = "[".repeat(arrayDepth) + parameterName + (if (nullable) "?" else "")
fun combine(replacement: IrReificationArgument) =
IrReificationArgument(
replacement.parameterName,
this.nullable || (replacement.nullable && this.arrayDepth == 0),
this.arrayDepth + replacement.arrayDepth
)
fun reify(replacementAsmType: Type, irType: IrType) =
Pair(
Type.getType("[".repeat(arrayDepth) + replacementAsmType),
irType.arrayOf(arrayDepth).withHasQuestionMark(nullable)
)
private fun IrType.arrayOf(arrayDepth: Int): IrType {
val builtins = classCodegen.context.irBuiltIns
var currentType = this
repeat(arrayDepth) {
currentType = builtins.arrayClass.typeWith(currentType)
}
return currentType
}
fun toReificationArgument() = ReificationArgument(parameterName, nullable, arrayDepth)
}
/* From ExpressionCodegen.java */
fun putReifiedOperationMarkerIfTypeIsReifiedParameter(
type: IrType, operationKind: ReifiedTypeInliner.OperationKind, v: InstructionAdapter,
codegen: ExpressionCodegen?
) {
val typeParameterAndReificationArgument = extractReificationArgumentWithParameter(type)
if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.first.isReified) {
val irTypeParameter = typeParameterAndReificationArgument.first
codegen?.consumeReifiedOperationMarker(irTypeParameter)
ReifiedTypeInliner.putReifiedOperationMarker(
operationKind, typeParameterAndReificationArgument.second.toReificationArgument(), v
)
}
}
private val IrType.isReifiedTypeParameter: Boolean
get() = this.classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
}
val IrType.isReifiedTypeParameter: Boolean
get() = this.classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
/* From typeUtil.java */
fun IrType.getTypeParameterOrNull() = classifierOrNull?.owner?.safeAs<IrTypeParameter>()
/* From ReifiedTypeInliner.kt */
class IrTypeParameterMappings {
private val mappingsByName = hashMapOf<String, IrTypeParameterMapping>()
fun addParameterMappingToType(name: String, type: IrType, asmType: Type, signature: String, isReified: Boolean) {
mappingsByName[name] = IrTypeParameterMapping(
name, type, asmType, reificationArgument = null, signature = signature, isReified = isReified
)
}
fun addParameterMappingForFurtherReification(
name: String,
type: IrType,
reificationArgument: ExpressionCodegen.IrReificationArgument,
isReified: Boolean
) {
mappingsByName[name] = IrTypeParameterMapping(
name, type, asmType = null, reificationArgument = reificationArgument, signature = null, isReified = isReified
)
}
operator fun get(name: String): IrTypeParameterMapping? = mappingsByName[name]
fun hasReifiedParameters() = mappingsByName.values.any { it.isReified }
internal inline fun forEach(l: (IrTypeParameterMapping) -> Unit) {
mappingsByName.values.forEach(l)
}
fun toTypeParameterMappings() = TypeParameterMappings<IrType>().also { result ->
mappingsByName.forEach { (_, value) ->
if (value.asmType == null) {
result.addParameterMappingForFurtherReification(
value.name,
value.type,
value.reificationArgument!!.toReificationArgument(),
value.isReified
)
} else {
result.addParameterMappingToType(value.name, value.type, value.asmType, value.signature!!, value.isReified)
}
}
}
}
class IrTypeParameterMapping(
val name: String,
val type: IrType,
val asmType: Type?,
val reificationArgument: ExpressionCodegen.IrReificationArgument?,
val signature: String?,
val isReified: Boolean
)
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.types.Variance
import org.jetbrains.org.objectweb.asm.Type
class IrTypeMapper(private val context: JvmBackendContext) {
private val typeSystem = IrTypeCheckerContext(context.irBuiltIns)
internal val typeSystem = IrTypeCheckerContext(context.irBuiltIns)
fun classInternalName(irClass: IrClass): String =
context.getLocalClassInfo(irClass)?.internalName
@@ -20,9 +20,7 @@ object NewArray : IntrinsicMethod() {
return with(codegen) {
val elementIrType = expression.type.getArrayElementType(context.irBuiltIns)
if (expression.type.isArray()) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(
elementIrType, ReifiedTypeInliner.OperationKind.NEW_ARRAY, mv, this
)
putReifiedOperationMarkerIfTypeIsReifiedParameter(elementIrType, ReifiedTypeInliner.OperationKind.NEW_ARRAY)
mv.newarray(typeMapper.boxType(elementIrType))
} else {
mv.newarray(typeMapper.mapType(elementIrType))
@@ -273,6 +273,9 @@ interface IrTypeSystemContext : TypeSystemContext, TypeSystemCommonSuperTypesCon
override fun arrayType(componentType: KotlinTypeMarker): SimpleTypeMarker =
irBuiltIns.arrayClass.typeWith(componentType as IrType)
override fun KotlinTypeMarker.isArrayOrNullableArray(): Boolean =
(this as IrType).isArray() || isNullableArray()
override fun TypeConstructorMarker.isFinalClassOrEnumEntryOrAnnotationClassConstructor(): Boolean {
val symbol = this as IrClassifierSymbol
return symbol is IrClassSymbol && symbol.owner.let {
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.types.model.*
interface TypeSystemCommonBackendContext : TypeSystemContext {
fun nullableAnyType(): SimpleTypeMarker
fun arrayType(componentType: KotlinTypeMarker): SimpleTypeMarker
fun KotlinTypeMarker.isArrayOrNullableArray(): Boolean
fun TypeConstructorMarker.isFinalClassOrEnumEntryOrAnnotationClassConstructor(): Boolean
@@ -508,6 +508,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return builtIns.getArrayType(Variance.INVARIANT, componentType)
}
override fun KotlinTypeMarker.isArrayOrNullableArray(): Boolean {
require(this is KotlinType, this::errorMessage)
return KotlinBuiltIns.isArray(this)
}
override fun KotlinTypeMarker.hasAnnotation(fqName: FqName): Boolean {
require(this is KotlinType, this::errorMessage)
return annotations.hasAnnotation(fqName)