JVM_IR: implement -Xpolymorphic-signature

This commit is contained in:
pyos
2019-11-29 14:13:24 +01:00
committed by Alexander Udalov
parent fe6be0f436
commit ac7d9fa90d
10 changed files with 106 additions and 8 deletions
@@ -48,4 +48,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW") object SUSPEND_FUNCTION_VIEW : IrDeclarationOriginImpl("SUSPEND_FUNCTION_VIEW")
object SUSPEND_IMPL_STATIC_FUNCTION : IrDeclarationOriginImpl("SUSPEND_IMPL_STATIC_FUNCTION", isSynthetic = true) 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 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 inventNamesForLocalClassesPhase then
kCallableNamePropertyPhase then kCallableNamePropertyPhase then
annotationPhase then annotationPhase then
polymorphicSignaturePhase then
varargPhase then varargPhase then
arrayConstructorPhase then arrayConstructorPhase then
checkNotNullPhase then checkNotNullPhase then
@@ -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()
}
}
}
@@ -29,7 +29,8 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
val varargPhase = makeIrFilePhase( val varargPhase = makeIrFilePhase(
::VarargLowering, ::VarargLowering,
name = "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() { private class VarargLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
@@ -50,7 +50,8 @@ class MemoizedInlineClassReplacements {
!it.hasMangledParameters || !it.hasMangledParameters ||
it.isSyntheticInlineClassMember || it.isSyntheticInlineClassMember ||
it.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || 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.hasMethodReplacement -> createMethodReplacement(it)
it.hasStaticReplacement -> createStaticReplacement(it) it.hasStaticReplacement -> createStaticReplacement(it)
else -> null else -> null
@@ -1,7 +1,6 @@
// !LANGUAGE: +PolymorphicSignature // !LANGUAGE: +PolymorphicSignature
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// SKIP_JDK6 // SKIP_JDK6
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,7 +1,6 @@
// !LANGUAGE: +PolymorphicSignature // !LANGUAGE: +PolymorphicSignature
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// SKIP_JDK6 // SKIP_JDK6
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,7 +1,6 @@
// !LANGUAGE: +PolymorphicSignature // !LANGUAGE: +PolymorphicSignature
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// SKIP_JDK6 // SKIP_JDK6
// WITH_RUNTIME // WITH_RUNTIME
@@ -11,6 +10,11 @@ import java.lang.invoke.MethodType
class C { class C {
fun foo(s: String, d: Double, x: Int): String = "$s$d$x" fun foo(s: String, d: Double, x: Int): String = "$s$d$x"
companion object {
@JvmStatic
fun bar(): Any = "OK"
}
} }
fun box(): String { fun box(): String {
@@ -19,5 +23,9 @@ fun box(): String {
MethodType.methodType(String::class.java, String::class.java, Double::class.java, Int::class.java) 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 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,7 +1,6 @@
// !LANGUAGE: +PolymorphicSignature // !LANGUAGE: +PolymorphicSignature
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// SKIP_JDK6 // SKIP_JDK6
// WITH_REFLECT // WITH_REFLECT
@@ -1,7 +1,6 @@
// !LANGUAGE: +PolymorphicSignature // !LANGUAGE: +PolymorphicSignature
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// FULL_JDK // FULL_JDK
// SKIP_JDK6 // SKIP_JDK6
// WITH_RUNTIME // WITH_RUNTIME