diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index c1ffa73e0d7..7036e63be7f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -48,4 +48,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW") object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true) object INTERFACE_COMPANION_PRIVATE_INSTANCE : IrDeclarationOriginImpl("INTERFACE_COMPANION_PRIVATE_INSTANCE", isSynthetic = true) + object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 6ea1574fc00..5effaf81677 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -231,6 +231,7 @@ private val jvmFilePhases = inventNamesForLocalClassesPhase then kCallableNamePropertyPhase then annotationPhase then + polymorphicSignaturePhase then varargPhase then arrayConstructorPhase then checkNotNullPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PolymorphicSignatureLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PolymorphicSignatureLowering.kt new file mode 100644 index 00000000000..85de4dae993 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PolymorphicSignatureLowering.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2019 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.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom +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.config.LanguageFeature +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isNullableAny +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.name.FqName + +internal val polymorphicSignaturePhase = makeIrFilePhase( + ::PolymorphicSignatureLowering, + name = "PolymorphicSignature", + description = "Replace polymorphic methods with fake ones according to types at the call site" +) + +private val polymorphicSignatureAnnotation = FqName("java.lang.invoke.MethodHandle.PolymorphicSignature") + +class PolymorphicSignatureLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass { + override fun lower(irFile: IrFile) { + if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature)) + irFile.transformChildrenVoid() + } + + private fun IrTypeOperatorCall.isCast(): Boolean = + operator != IrTypeOperator.INSTANCEOF && operator != IrTypeOperator.NOT_INSTANCEOF + + // If the return type is Any?, then it is also polymorphic (e.g. MethodHandle.invokeExact + // has polymorphic return type, while VarHandle.compareAndSet does not). + override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression = + (expression.argument as? IrCall)?.takeIf { expression.isCast() && it.type.isNullableAny() }?.transform(expression.typeOperand) + ?: super.visitTypeOperator(expression) + + override fun visitCall(expression: IrCall): IrExpression = + expression.transform(null) ?: super.visitCall(expression) + + private fun IrCall.transform(castReturnType: IrType?): IrCall? { + val function = symbol.owner as? IrSimpleFunction ?: return null + if (!function.hasAnnotation(polymorphicSignatureAnnotation)) + return null + assert(function.valueParameters.singleOrNull()?.varargElementType != null) { + "@PolymorphicSignature methods should only have a single vararg argument: ${dump()}" + } + + val values = (getValueArgument(0) as IrVararg?)?.elements?.map { + when (it) { + is IrExpression -> it + is IrSpreadElement -> it.expression // `*xs` acts as `xs` (for compatibility?) + else -> throw AssertionError("unknown IrVarargElement: $it") + } + } ?: listOf() + val fakeFunction = buildFun { + updateFrom(function) + name = function.name + origin = JvmLoweredDeclarationOrigin.POLYMORPHIC_SIGNATURE_INSTANTIATION + returnType = castReturnType ?: function.returnType + }.apply { + parent = function.parent + copyTypeParametersFrom(function) + dispatchReceiverParameter = function.dispatchReceiverParameter + extensionReceiverParameter = function.extensionReceiverParameter + for ((i, value) in values.withIndex()) { + addValueParameter("\$$i", value.type, JvmLoweredDeclarationOrigin.POLYMORPHIC_SIGNATURE_INSTANTIATION) + } + } + return IrCallImpl(startOffset, endOffset, fakeFunction.returnType, fakeFunction.symbol, origin, superQualifierSymbol).apply { + copyTypeArgumentsFrom(this@transform) + dispatchReceiver = this@transform.dispatchReceiver + extensionReceiver = this@transform.extensionReceiver + values.forEachIndexed(::putValueArgument) + transformChildrenVoid() + } + } +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt index fb846deed18..c3592a3aaa3 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/VarargLowering.kt @@ -29,7 +29,8 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly val varargPhase = makeIrFilePhase( ::VarargLowering, name = "VarargLowering", - description = "Replace varargs with array arguments and lower arrayOf calls" + description = "Replace varargs with array arguments and lower arrayOf calls", + prerequisite = setOf(polymorphicSignaturePhase) ) private class VarargLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt index 31d5a8ef9ff..d59bdc02045 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/MemoizedInlineClassReplacements.kt @@ -50,7 +50,8 @@ class MemoizedInlineClassReplacements { !it.hasMangledParameters || it.isSyntheticInlineClassMember || it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || - it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR -> null + it.origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR || + it.origin == JvmLoweredDeclarationOrigin.POLYMORPHIC_SIGNATURE_INSTANTIATION -> null it.hasMethodReplacement -> createMethodReplacement(it) it.hasStaticReplacement -> createStaticReplacement(it) else -> null diff --git a/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt b/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt index 27ebba3df9c..2ab5d53fed3 100644 --- a/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt +++ b/compiler/testData/codegen/box/polymorphicSignature/anonymousSubclass.kt @@ -1,7 +1,6 @@ // !LANGUAGE: +PolymorphicSignature // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // SKIP_JDK6 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/polymorphicSignature/invoke.kt b/compiler/testData/codegen/box/polymorphicSignature/invoke.kt index 93b4eafbcce..3dac5cd020c 100644 --- a/compiler/testData/codegen/box/polymorphicSignature/invoke.kt +++ b/compiler/testData/codegen/box/polymorphicSignature/invoke.kt @@ -1,7 +1,6 @@ // !LANGUAGE: +PolymorphicSignature // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // SKIP_JDK6 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt b/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt index d011a79e092..d5bad50b81f 100644 --- a/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt +++ b/compiler/testData/codegen/box/polymorphicSignature/invokeExact.kt @@ -1,7 +1,6 @@ // !LANGUAGE: +PolymorphicSignature // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // SKIP_JDK6 // WITH_RUNTIME @@ -11,6 +10,11 @@ import java.lang.invoke.MethodType class C { fun foo(s: String, d: Double, x: Int): String = "$s$d$x" + + companion object { + @JvmStatic + fun bar(): Any = "OK" + } } fun box(): String { @@ -19,5 +23,9 @@ fun box(): String { MethodType.methodType(String::class.java, String::class.java, Double::class.java, Int::class.java) ) val result: String = mh.invokeExact(C(), "Hello", 0.01, 42) as String - return if (result == "Hello0.0142") "OK" else "Fail: $result" + if (result != "Hello0.0142") return "Fail 1: $result" + + val mh2 = MethodHandles.lookup().findStatic(C::class.java, "bar", MethodType.methodType(Object::class.java)) + val result2 = mh2.invokeExact() is String + return if (result2) "OK" else "Fail 2" } diff --git a/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt b/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt index fd85125c572..60f528a629b 100644 --- a/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt +++ b/compiler/testData/codegen/box/polymorphicSignature/invokeExactWithInlineClass.kt @@ -1,7 +1,6 @@ // !LANGUAGE: +PolymorphicSignature // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // SKIP_JDK6 // WITH_REFLECT diff --git a/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt index 0f48fa1801f..785d1333a3c 100644 --- a/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt +++ b/compiler/testData/codegen/box/polymorphicSignature/varargOfObjects_after.kt @@ -1,7 +1,6 @@ // !LANGUAGE: +PolymorphicSignature // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // SKIP_JDK6 // WITH_RUNTIME