JVM_IR indy-SAM conversions: inline classes
KT-44278 KT-26060 KT-42621
This commit is contained in:
@@ -454,7 +454,7 @@ class JvmSymbols(
|
||||
}
|
||||
|
||||
val receiverFieldName = Name.identifier("receiver")
|
||||
klass.addProperty() {
|
||||
klass.addProperty {
|
||||
name = receiverFieldName
|
||||
}.apply {
|
||||
backingField = irFactory.buildField {
|
||||
@@ -658,7 +658,7 @@ class JvmSymbols(
|
||||
collectionToArrayClass.functions.single { it.owner.name.asString() == "toArray" && it.owner.valueParameters.size == 2 }
|
||||
|
||||
val kClassJava: IrPropertySymbol =
|
||||
irFactory.buildProperty() {
|
||||
irFactory.buildProperty {
|
||||
name = Name.identifier("java")
|
||||
}.apply {
|
||||
parent = kotlinJvmPackage
|
||||
|
||||
+30
-24
@@ -15,9 +15,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.overrides.buildFakeOverrideMember
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Handle
|
||||
@@ -136,38 +134,46 @@ object JvmInvokeDynamic : IntrinsicMethod() {
|
||||
?: fail("Argument in ${irCall.symbol.owner.name} call is expected to be a raw function reference")
|
||||
val irOriginalFun = irRawFunRef.symbol.owner as? IrSimpleFunction
|
||||
?: fail("IrSimpleFunction expected: ${irRawFunRef.symbol.owner.render()}")
|
||||
val substitutedType = irCall.getTypeArgument(0) as? IrSimpleType
|
||||
val superType = irCall.getTypeArgument(0) as? IrSimpleType
|
||||
?: fail("Type argument expected")
|
||||
|
||||
// Force boxing on primitive types, otherwise D8 fails to accept resulting MethodType in presence of primitive types in arguments
|
||||
// (LambdaMetafactory is ok with such types, though).
|
||||
val substitutedTypeWithNullableArgs =
|
||||
substitutedType.classifier.typeWithArguments(
|
||||
substitutedType.arguments.map {
|
||||
when (it) {
|
||||
is IrStarProjection -> it
|
||||
is IrTypeProjection -> {
|
||||
val type = it.type
|
||||
if (type !is IrSimpleType || type.hasQuestionMark)
|
||||
it
|
||||
else
|
||||
makeTypeProjection(type.withHasQuestionMark(true), it.variance)
|
||||
}
|
||||
else ->
|
||||
fail("Unexpected type argument '${it}' :: ${it::class.simpleName}")
|
||||
}
|
||||
}
|
||||
)
|
||||
val patchedSuperType = replaceTypeArgumentsWithNullable(superType)
|
||||
|
||||
val fakeClass = codegen.context.irFactory.buildClass { name = Name.special("<fake>") }
|
||||
fakeClass.parent = codegen.context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage
|
||||
val irFakeOverride = buildFakeOverrideMember(substitutedTypeWithNullableArgs, irOriginalFun, fakeClass) as IrSimpleFunction
|
||||
val irFakeOverride = buildFakeOverrideMember(patchedSuperType, irOriginalFun, fakeClass) as IrSimpleFunction
|
||||
irFakeOverride.overriddenSymbols = listOf(irOriginalFun.symbol)
|
||||
|
||||
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFakeOverride)
|
||||
return Type.getMethodType(asmMethod.descriptor)
|
||||
}
|
||||
|
||||
// Given the following functional interface
|
||||
// fun interface IFoo<T> {
|
||||
// fun foo(x: T): T
|
||||
// }
|
||||
// To comply with java.lang.invoke.LambdaMetafactory requirements, we need an instance method that accepts references
|
||||
// (not primitives, and not unboxed inline classes).
|
||||
// In order to do so, we replace type arguments with nullable types.
|
||||
private fun replaceTypeArgumentsWithNullable(substitutedType: IrSimpleType) =
|
||||
substitutedType.classifier.typeWithArguments(
|
||||
substitutedType.arguments.map { typeArgument ->
|
||||
when (typeArgument) {
|
||||
is IrStarProjection -> typeArgument
|
||||
is IrTypeProjection -> {
|
||||
val type = typeArgument.type
|
||||
if (type !is IrSimpleType || type.hasQuestionMark)
|
||||
typeArgument
|
||||
else {
|
||||
makeTypeProjection(type.withHasQuestionMark(true), typeArgument.variance)
|
||||
}
|
||||
}
|
||||
else ->
|
||||
throw AssertionError("Unexpected type argument '$typeArgument' :: ${typeArgument::class.simpleName}")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun IrExpression.getIntConst() =
|
||||
if (this is IrConst<*> && kind == IrConstKind.Int)
|
||||
this.value as Int
|
||||
|
||||
+81
-3
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.lower.SamEqualsHashCodeMethodsGenerat
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.representativeUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.config.JvmSamConversions
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
internal val functionReferencePhase = makeIrFilePhase(
|
||||
::FunctionReferenceLowering,
|
||||
@@ -115,21 +117,54 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
// TODO special mode that would generate indy everywhere?
|
||||
if (reference.origin != IrStatementOrigin.LAMBDA)
|
||||
return false
|
||||
|
||||
// TODO wrap intrinsic function in lambda?
|
||||
if (context.irIntrinsics.getIntrinsic(reference.symbol) != null)
|
||||
return false
|
||||
|
||||
// Can't use JDK LambdaMetafactory if lambda signature contains an inline class mapped to a non-null reference type.
|
||||
val target = reference.symbol.owner
|
||||
if (target.extensionReceiverParameter?.run { type.isProhibitedTypeForIndySamConversion() } == true ||
|
||||
target.valueParameters.any { it.type.isProhibitedTypeForIndySamConversion() } ||
|
||||
target.returnType.isProhibitedTypeForIndySamConversion()
|
||||
)
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun IrType.isProhibitedTypeForIndySamConversion(): Boolean {
|
||||
if (this !is IrSimpleType) return false
|
||||
|
||||
val erasedType = when (val classifier = classifier.owner) {
|
||||
is IrTypeParameter ->
|
||||
classifier.representativeUpperBound.withHasQuestionMark(hasQuestionMark)
|
||||
else ->
|
||||
this
|
||||
}
|
||||
val erasedClass = erasedType.getClass() ?: return false
|
||||
|
||||
if (!erasedType.isInlined()) return false
|
||||
|
||||
val underlyingType = getInlineClassUnderlyingType(erasedClass) as? IrSimpleType
|
||||
?: throw AssertionError("Underlying type for inline class should be a simple type: ${erasedClass.render()}")
|
||||
return !underlyingType.hasQuestionMark && !underlyingType.isJvmPrimitiveType()
|
||||
}
|
||||
|
||||
private fun IrType.isJvmPrimitiveType() =
|
||||
isBoolean() || isChar() || isByte() || isShort() || isInt() || isLong() || isFloat() || isDouble()
|
||||
|
||||
private fun wrapSamConversionArgumentWithIndySamConversion(expression: IrTypeOperatorCall): IrExpression {
|
||||
val samType = expression.typeOperand
|
||||
return when (val argument = expression.argument) {
|
||||
is IrFunctionReference ->
|
||||
wrapWithIndySamConversion(expression.typeOperand, argument)
|
||||
is IrFunctionReference -> {
|
||||
wrapWithIndySamConversion(samType, argument)
|
||||
}
|
||||
is IrBlock -> {
|
||||
val last = argument.statements.last()
|
||||
val functionReference = last as? IrFunctionReference
|
||||
?: throw AssertionError("Function reference expected: ${last.render()}")
|
||||
argument.statements[argument.statements.size - 1] = wrapWithIndySamConversion(expression.typeOperand, functionReference)
|
||||
argument.statements[argument.statements.size - 1] = wrapWithIndySamConversion(samType, functionReference)
|
||||
return argument
|
||||
}
|
||||
else -> throw AssertionError("Block or function reference expected: ${expression.render()}")
|
||||
@@ -145,6 +180,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
)
|
||||
|
||||
private fun wrapWithIndySamConversion(samType: IrType, irFunRef: IrFunctionReference): IrCall {
|
||||
patchSignatureForIndySamConversion(irFunRef.symbol.owner, samType)
|
||||
val notNullSamType = samType.makeNotNull()
|
||||
.removeAnnotations { it.type.classFqName in specialNullabilityAnnotationsFqNames }
|
||||
return context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
|
||||
@@ -161,6 +197,48 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchSignatureForIndySamConversion(irLambda: IrFunction, samType: IrType) {
|
||||
if (irLambda.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA)
|
||||
throw AssertionError("Can't patch a signature of a non-lambda: ${irLambda.render()}")
|
||||
|
||||
val samClass = samType.classOrNull?.owner
|
||||
?: throw AssertionError("SAM type should be a class type: '${samType.render()}'")
|
||||
val samMethod = samClass.functions.singleOrNull { it.modality == Modality.ABSTRACT }
|
||||
?: throw AssertionError("SAM method not found:\n${samClass.dump()}")
|
||||
|
||||
val samMethodParameters = collectValueParameters(samMethod)
|
||||
val irLambdaParameters = collectValueParameters(irLambda)
|
||||
if (samMethodParameters.size != irLambdaParameters.size) {
|
||||
throw AssertionError(
|
||||
"SAM method and implementing lambda have mismatching value parameters " +
|
||||
"(${samMethodParameters.size} != ${irLambdaParameters.size}:\n" +
|
||||
"samMethod: ${samMethod.render()}\n" +
|
||||
"lambda: ${irLambda.render()}"
|
||||
)
|
||||
}
|
||||
|
||||
for ((irLambdaParameter, samMethodParameter) in irLambdaParameters.zip(samMethodParameters)) {
|
||||
irLambdaParameter.type = patchTypeForIndySamConversion(irLambdaParameter.type, samMethodParameter.type)
|
||||
}
|
||||
|
||||
irLambda.returnType = patchTypeForIndySamConversion(irLambda.returnType, samMethod.returnType)
|
||||
}
|
||||
|
||||
private fun collectValueParameters(irFunction: IrFunction): List<IrValueParameter> =
|
||||
ArrayList<IrValueParameter>().apply {
|
||||
addIfNotNull(irFunction.extensionReceiverParameter)
|
||||
addAll(irFunction.valueParameters)
|
||||
}
|
||||
|
||||
private fun patchTypeForIndySamConversion(originalType: IrType, targetType: IrType): IrType {
|
||||
if (originalType.isUnboxedInlineClassType() && !targetType.isUnboxedInlineClassType())
|
||||
return targetType
|
||||
return originalType
|
||||
}
|
||||
|
||||
private fun IrType.isUnboxedInlineClassType() =
|
||||
this is IrSimpleType && isInlined() && !hasQuestionMark
|
||||
|
||||
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) {
|
||||
private val isLambda = irFunctionReference.origin.isLambda
|
||||
|
||||
|
||||
Reference in New Issue
Block a user