JVM_IR: implement -Xpolymorphic-signature
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -231,6 +231,7 @@ private val jvmFilePhases =
|
||||
inventNamesForLocalClassesPhase then
|
||||
kCallableNamePropertyPhase then
|
||||
annotationPhase then
|
||||
polymorphicSignaturePhase then
|
||||
varargPhase then
|
||||
arrayConstructorPhase then
|
||||
checkNotNullPhase then
|
||||
|
||||
+90
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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() {
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// !LANGUAGE: +PolymorphicSignature
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// FULL_JDK
|
||||
// SKIP_JDK6
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// !LANGUAGE: +PolymorphicSignature
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// FULL_JDK
|
||||
// SKIP_JDK6
|
||||
// WITH_RUNTIME
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
-1
@@ -1,7 +1,6 @@
|
||||
// !LANGUAGE: +PolymorphicSignature
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// FULL_JDK
|
||||
// SKIP_JDK6
|
||||
// WITH_REFLECT
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// !LANGUAGE: +PolymorphicSignature
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// FULL_JDK
|
||||
// SKIP_JDK6
|
||||
// WITH_RUNTIME
|
||||
|
||||
Reference in New Issue
Block a user