JVM_IR indy-SAM conversions, 1st passing tests
KT-44278 KT-26060 KT-42621
This commit is contained in:
+20
-13
@@ -65,7 +65,11 @@ fun IrClass.addField(fieldName: Name, fieldType: IrType, fieldVisibility: Descri
|
||||
visibility = fieldVisibility
|
||||
}
|
||||
|
||||
fun IrClass.addField(fieldName: String, fieldType: IrType, fieldVisibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE): IrField =
|
||||
fun IrClass.addField(
|
||||
fieldName: String,
|
||||
fieldType: IrType,
|
||||
fieldVisibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
|
||||
): IrField =
|
||||
addField(Name.identifier(fieldName), fieldType, fieldVisibility)
|
||||
|
||||
@PublishedApi
|
||||
@@ -141,16 +145,16 @@ inline fun IrClass.addFunction(builder: IrFunctionBuilder.() -> Unit): IrSimpleF
|
||||
factory.addFunction(this, builder)
|
||||
|
||||
fun IrClass.addFunction(
|
||||
name: String,
|
||||
returnType: IrType,
|
||||
modality: Modality = Modality.FINAL,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
|
||||
isStatic: Boolean = false,
|
||||
isSuspend: Boolean = false,
|
||||
isFakeOverride: Boolean = false,
|
||||
origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
name: String,
|
||||
returnType: IrType,
|
||||
modality: Modality = Modality.FINAL,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
|
||||
isStatic: Boolean = false,
|
||||
isSuspend: Boolean = false,
|
||||
isFakeOverride: Boolean = false,
|
||||
origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
): IrSimpleFunction =
|
||||
addFunction {
|
||||
this.startOffset = startOffset
|
||||
@@ -216,7 +220,7 @@ internal fun IrFactory.buildValueParameter(builder: IrValueParameterBuilder, par
|
||||
|
||||
|
||||
inline fun <D> buildValueParameter(declaration: D, builder: IrValueParameterBuilder.() -> Unit): IrValueParameter
|
||||
where D : IrDeclaration, D : IrDeclarationParent =
|
||||
where D : IrDeclaration, D : IrDeclarationParent =
|
||||
IrValueParameterBuilder().run {
|
||||
builder()
|
||||
declaration.factory.buildValueParameter(this, declaration)
|
||||
@@ -234,8 +238,11 @@ inline fun IrFunction.addValueParameter(builder: IrValueParameterBuilder.() -> U
|
||||
}
|
||||
|
||||
fun IrFunction.addValueParameter(name: String, type: IrType, origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED): IrValueParameter =
|
||||
addValueParameter(Name.identifier(name), type, origin)
|
||||
|
||||
fun IrFunction.addValueParameter(name: Name, type: IrType, origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED): IrValueParameter =
|
||||
addValueParameter {
|
||||
this.name = Name.identifier(name)
|
||||
this.name = name
|
||||
this.type = type
|
||||
this.origin = origin
|
||||
}
|
||||
|
||||
+1
@@ -46,4 +46,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_PROPERTY_BACKING_FIELD")
|
||||
object FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE")
|
||||
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
|
||||
object INVOVEDYNAMIC_CALL_TARGET : IrDeclarationOriginImpl("INVOVEDYNAMIC_CALL_TARGET")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.ir.addChild
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
@@ -51,6 +50,17 @@ class JvmSymbols(
|
||||
private val kotlinReflectPackage: IrPackageFragment = createPackage(FqName("kotlin.reflect"))
|
||||
private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang"))
|
||||
|
||||
// Special package for functions representing dynamic symbols referenced by 'INVOKEDYNAMIC' instruction - e.g.,
|
||||
// 'get(Ljava/lang/String;)Ljava/util/function/Supplier;'
|
||||
// in
|
||||
// INVOKEDYNAMIC get(Ljava/lang/String;)Ljava/util/function/Supplier; [
|
||||
// H_INVOKESTATIC java/lang/invoke/LambdaMetafactory.metafactory(...)Ljava/lang/invoke/CallSite;
|
||||
// ...
|
||||
// ]
|
||||
// Such functions don't exist as methods in the actual bytecode
|
||||
// (they are expected to be provided at run-time by the corresponding bootstrap method).
|
||||
val kotlinJvmInternalInvokeDynamicPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.internal.invokeDynamic"))
|
||||
|
||||
private val generateOptimizedCallableReferenceSuperClasses = context.state.generateOptimizedCallableReferenceSuperClasses
|
||||
|
||||
private fun createPackage(fqName: FqName): IrPackageFragment =
|
||||
@@ -535,6 +545,82 @@ class JvmSymbols(
|
||||
returnType = dst.defaultType
|
||||
}.symbol
|
||||
|
||||
val indySamConversionIntrinsic: IrSimpleFunctionSymbol =
|
||||
irFactory.buildFun {
|
||||
name = Name.special("<jvm-indy-sam-conversion>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
val samType = addTypeParameter("SAM_TYPE", irBuiltIns.anyType)
|
||||
addValueParameter("method", irBuiltIns.anyNType)
|
||||
returnType = samType.defaultType
|
||||
}.symbol
|
||||
|
||||
val arrayOfAnyType = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyType)
|
||||
|
||||
// Intrinsic to represent INVOKEDYNAMIC calls in IR.
|
||||
// fun <T> `<jvm-indy>`(
|
||||
// dynamicCall: T,
|
||||
// bootstrapMethodTag: Int,
|
||||
// bootstrapMethodOwner: String,
|
||||
// bootstrapMethodName: String,
|
||||
// bootstrapMethodDesc: String,
|
||||
// vararg bootstrapMethodArgs: Any
|
||||
// ): T
|
||||
// Bootstrap method handle is encoded as a bunch of constants.
|
||||
// For example,
|
||||
// REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(
|
||||
// Ljava/lang/invoke/MethodHandles$Lookup;
|
||||
// Ljava/lang/String;
|
||||
// Ljava/lang/invoke/MethodType;
|
||||
// Ljava/lang/invoke/MethodType;
|
||||
// Ljava/lang/invoke/MethodHandle;
|
||||
// Ljava/lang/invoke/MethodType;
|
||||
// )Ljava/lang/invoke/CallSite;
|
||||
// is represented as
|
||||
// bootstrapMethodTag: IrConst<Int>(value = H_INVOKESTATIC)
|
||||
// bootstrapMethodOwner: IrConst<String>(value = "java/lang/invoke/LambdaMetafactory")
|
||||
// bootstrapMethodName: IrConst<String>(value = "metafactory")
|
||||
// bootstrapMethodDesc: IrConst<String>(value = "(...)Ljava/lang/invoke/CallSite;")
|
||||
// Bootstrap method owner is assumed to be a class (which is true for both LambdaMetafactory and StringConcatFactory).
|
||||
val jvmIndyIntrinsic: IrSimpleFunctionSymbol =
|
||||
irFactory.buildFun {
|
||||
name = Name.special("<jvm-indy>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
val t = addTypeParameter("T", irBuiltIns.anyNType)
|
||||
addValueParameter("dynamicCall", t.defaultType)
|
||||
addValueParameter("bootstrapMethodTag", irBuiltIns.intType)
|
||||
addValueParameter("bootstrapMethodOwner", irBuiltIns.stringType)
|
||||
addValueParameter("bootstrapMethodName", irBuiltIns.stringType)
|
||||
addValueParameter("bootstrapMethodDesc", irBuiltIns.stringType)
|
||||
addValueParameter {
|
||||
name = Name.identifier("bootstrapMethodArguments")
|
||||
type = arrayOfAnyType
|
||||
varargElementType = irBuiltIns.anyType
|
||||
}
|
||||
returnType = t.defaultType
|
||||
}.symbol
|
||||
|
||||
// Intrinsic used to represent MethodType objects in bootstrap method arguments (see jvmInvokeDynamicIntrinsic above).
|
||||
// Type argument is a possibly substituted method owner type (e.g., 'java.lang.function.Supplier<String>').
|
||||
// Value argument is a raw function reference to a corresponding method (e.g., 'java.lang.function.Supplier#get').
|
||||
val jvmMethodTypeIntrinsic: IrSimpleFunctionSymbol =
|
||||
irFactory.buildFun {
|
||||
name = Name.special("<jvm-method-type>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
addTypeParameter("OwnerT", irBuiltIns.anyType)
|
||||
addValueParameter("method", irBuiltIns.anyType)
|
||||
returnType = irBuiltIns.anyType
|
||||
}.symbol
|
||||
|
||||
val flexibleNullabilityAnnotationFqName = JvmGeneratorExtensions.FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME
|
||||
val enhancedNullabilityAnnotationFqName = JvmGeneratorExtensions.ENHANCED_NULLABILITY_ANNOTATION_FQ_NAME
|
||||
val rawTypeAnnotationFQN = JvmGeneratorExtensions.RAW_TYPE_ANNOTATION_FQ_NAME
|
||||
|
||||
private val collectionToArrayClass: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.CollectionToArray")) { klass ->
|
||||
klass.origin = JvmLoweredDeclarationOrigin.TO_ARRAY
|
||||
|
||||
|
||||
+1
-1
@@ -418,7 +418,7 @@ class ExpressionCodegen(
|
||||
|
||||
val callee = expression.symbol.owner
|
||||
require(callee.parent is IrClass) { "Unhandled intrinsic in ExpressionCodegen: ${callee.render()}" }
|
||||
val callable = methodSignatureMapper.mapToCallableMethod(irFunction, expression)
|
||||
val callable = methodSignatureMapper.mapToCallableMethod(expression, irFunction)
|
||||
val callGenerator = getOrCreateCallGenerator(expression, data, callable.signature)
|
||||
val isSuspensionPoint = expression.isSuspensionPoint()
|
||||
|
||||
|
||||
+12
-7
@@ -366,7 +366,8 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
if (valueArgumentsCount > 0) (getValueArgument(0) as? IrConst<*>)?.value as? Boolean ?: true else null
|
||||
}
|
||||
|
||||
fun mapToCallableMethod(caller: IrFunction, expression: IrCall): IrCallableMethod {
|
||||
// TODO get rid of 'caller' argument
|
||||
internal fun mapToCallableMethod(expression: IrCall, caller: IrFunction?): IrCallableMethod {
|
||||
val callee = expression.symbol.owner
|
||||
val calleeParent = expression.superQualifierSymbol?.owner
|
||||
?: expression.dispatchReceiver?.type?.classOrNull?.owner
|
||||
@@ -385,17 +386,21 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
}
|
||||
|
||||
val declaration = findSuperDeclaration(callee, isSuperCall)
|
||||
val signature = mapOverriddenSpecialBuiltinIfNeeded(caller, declaration, isSuperCall)
|
||||
?: mapSignatureSkipGeneric(declaration)
|
||||
val signature =
|
||||
if (caller != null && caller.isBridge()) {
|
||||
// Do not remap special builtin methods when called from a bridge. The bridges are there to provide the
|
||||
// remapped name or signature and forward to the actually declared method.
|
||||
mapSignatureSkipGeneric(declaration)
|
||||
} else {
|
||||
mapOverriddenSpecialBuiltinIfNeeded(declaration, isSuperCall)
|
||||
?: mapSignatureSkipGeneric(declaration)
|
||||
}
|
||||
|
||||
return IrCallableMethod(owner, invokeOpcode, signature, isInterface)
|
||||
}
|
||||
|
||||
// TODO: get rid of this (probably via some special lowering)
|
||||
private fun mapOverriddenSpecialBuiltinIfNeeded(caller: IrFunction, callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
|
||||
// Do not remap special builtin methods when called from a bridge. The bridges are there to provide the
|
||||
// remapped name or signature and forward to the actually declared method.
|
||||
if (caller.isBridge()) return null
|
||||
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
|
||||
// Do not remap calls to static replacements of inline class methods, since they have completely different signatures.
|
||||
if (callee.isStaticInlineClassReplacement) return null
|
||||
val overriddenSpecialBuiltinFunction =
|
||||
|
||||
+1
@@ -120,6 +120,7 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
|
||||
symbols.throwTypeCastException.toKey()!! to ThrowException(Type.getObjectType("kotlin/TypeCastException")),
|
||||
symbols.throwUnsupportedOperationException.toKey()!! to ThrowException(Type.getObjectType("java/lang/UnsupportedOperationException")),
|
||||
symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException,
|
||||
symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic
|
||||
) +
|
||||
numberConversionMethods() +
|
||||
unaryFunForPrimitives("plus", UnaryPlus) +
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.v
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
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.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Handle
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object JvmInvokeDynamic : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
fun fail(message: String): Nothing =
|
||||
throw AssertionError("$message; expression:\n${expression.dump()}")
|
||||
|
||||
val dynamicCall = expression.getValueArgument(0) as? IrCall
|
||||
?: fail("'dynamicCall' is expected to be a call")
|
||||
val dynamicCallee = dynamicCall.symbol.owner
|
||||
if (dynamicCallee.parent != codegen.context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage ||
|
||||
dynamicCallee.origin != JvmLoweredDeclarationOrigin.INVOVEDYNAMIC_CALL_TARGET
|
||||
)
|
||||
fail("Unexpected dynamicCallee: '${dynamicCallee.render()}'")
|
||||
|
||||
val bootstrapMethodTag = expression.getValueArgument(1)?.getIntConst()
|
||||
?: fail("'bootstrapMethodTag' is expected to be an int const")
|
||||
val bootstrapMethodOwner = expression.getValueArgument(2)?.getStringConst()
|
||||
?: fail("'bootstrapMethodOwner' is expected to be a string const")
|
||||
val bootstrapMethodName = expression.getValueArgument(3)?.getStringConst()
|
||||
?: fail("'bootstrapMethodName' is expected to be a string const")
|
||||
val bootstrapMethodDesc = expression.getValueArgument(4)?.getStringConst()
|
||||
?: fail("'bootstrapMethodDesc' is expected to be a string const")
|
||||
val bootstrapMethodArgs = (expression.getValueArgument(5)?.safeAs<IrVararg>()
|
||||
?: fail("'bootstrapMethodArgs' is expected to be a vararg"))
|
||||
|
||||
val asmBootstrapMethodArgs = bootstrapMethodArgs.elements
|
||||
.map { generateBootstrapMethodArg(it, codegen) }
|
||||
.toTypedArray()
|
||||
|
||||
val dynamicCalleeMethod = codegen.methodSignatureMapper.mapAsmMethod(dynamicCallee)
|
||||
val bootstrapMethodHandle = Handle(bootstrapMethodTag, bootstrapMethodOwner, bootstrapMethodName, bootstrapMethodDesc, false)
|
||||
|
||||
val dynamicCallGenerator = IrCallGenerator.DefaultCallGenerator
|
||||
val dynamicCalleeArgumentTypes = dynamicCalleeMethod.argumentTypes
|
||||
for (i in dynamicCallee.valueParameters.indices) {
|
||||
val dynamicCalleeParameter = dynamicCallee.valueParameters[i]
|
||||
val dynamicCalleeArgument = dynamicCall.getValueArgument(i)
|
||||
?: fail("No argument #$i in 'dynamicCall'")
|
||||
val dynamicCalleeArgumentType = dynamicCalleeArgumentTypes.getOrElse(i) {
|
||||
fail("No argument type #$i in dynamic callee: $dynamicCalleeMethod")
|
||||
}
|
||||
dynamicCallGenerator.genValueAndPut(dynamicCalleeParameter, dynamicCalleeArgument, dynamicCalleeArgumentType, codegen, data)
|
||||
}
|
||||
|
||||
codegen.v.invokedynamic(dynamicCalleeMethod.name, dynamicCalleeMethod.descriptor, bootstrapMethodHandle, asmBootstrapMethodArgs)
|
||||
|
||||
return MaterialValue(codegen, dynamicCalleeMethod.returnType, expression.type)
|
||||
}
|
||||
|
||||
private fun generateBootstrapMethodArg(element: IrVarargElement, codegen: ExpressionCodegen): Any =
|
||||
when (element) {
|
||||
is IrRawFunctionReference ->
|
||||
generateMethodHandle(element, codegen)
|
||||
is IrCall ->
|
||||
when (element.symbol) {
|
||||
codegen.context.ir.symbols.jvmMethodTypeIntrinsic ->
|
||||
generateMethodType(element, codegen)
|
||||
else ->
|
||||
throw AssertionError("Unexpected callee in bootstrap method argument:\n${element.dump()}")
|
||||
}
|
||||
is IrConst<*> ->
|
||||
when (element.kind) {
|
||||
IrConstKind.Byte -> (element.value as Byte).toInt()
|
||||
IrConstKind.Short -> (element.value as Short).toInt()
|
||||
IrConstKind.Int -> element.value as Int
|
||||
IrConstKind.Long -> element.value as Long
|
||||
IrConstKind.Float -> element.value as Float
|
||||
IrConstKind.Double -> element.value as Double
|
||||
IrConstKind.String -> element.value as String
|
||||
else ->
|
||||
throw AssertionError("Unexpected constant expression in bootstrap method argument:\n${element.dump()}")
|
||||
}
|
||||
else ->
|
||||
throw AssertionError("Unexpected bootstrap method argument:\n${element.dump()}")
|
||||
}
|
||||
|
||||
private fun generateMethodHandle(irRawFunctionReference: IrRawFunctionReference, codegen: ExpressionCodegen): Any {
|
||||
val irFun = irRawFunctionReference.symbol.owner
|
||||
val irParentClass = irFun.parentAsClass
|
||||
val owner = codegen.typeMapper.mapOwner(irParentClass)
|
||||
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun)
|
||||
val handleTag = when {
|
||||
irFun.dispatchReceiverParameter == null ->
|
||||
Opcodes.H_INVOKESTATIC
|
||||
else ->
|
||||
Opcodes.H_INVOKEVIRTUAL
|
||||
}
|
||||
return Handle(handleTag, owner.internalName, asmMethod.name, asmMethod.descriptor, irParentClass.isJvmInterface)
|
||||
}
|
||||
|
||||
private fun generateMethodType(jvmMethodTypeCall: IrCall, codegen: ExpressionCodegen): Any {
|
||||
val irRawFunctionReference = jvmMethodTypeCall.getValueArgument(0) as? IrRawFunctionReference
|
||||
?: throw AssertionError(
|
||||
"Argument in ${jvmMethodTypeCall.symbol.owner.name} call is expected to be a raw function reference:\n" +
|
||||
jvmMethodTypeCall.dump()
|
||||
)
|
||||
val irFun = irRawFunctionReference.symbol.owner
|
||||
// TODO substitute signature
|
||||
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun)
|
||||
return Type.getMethodType(asmMethod.descriptor)
|
||||
}
|
||||
|
||||
private fun IrExpression.getIntConst() =
|
||||
if (this is IrConst<*> && kind == IrConstKind.Int)
|
||||
this.value as Int
|
||||
else
|
||||
null
|
||||
|
||||
private fun IrExpression.getStringConst() =
|
||||
if (this is IrConst<*> && kind == IrConstKind.String)
|
||||
this.value as String
|
||||
else
|
||||
null
|
||||
|
||||
}
|
||||
+64
-17
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.config.JvmSamConversions
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
@@ -21,10 +22,7 @@ import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -80,6 +78,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
FunctionReferenceBuilder(expression).build()
|
||||
}
|
||||
|
||||
private val shouldUseIndySamConversions =
|
||||
context.state.samConversionsScheme == JvmSamConversions.INDY
|
||||
|
||||
// Handle SAM conversions which wrap a function reference:
|
||||
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
|
||||
//
|
||||
@@ -87,20 +88,66 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
|
||||
// This is actually very common, as `Interface { something }` is a local function + a SAM-conversion
|
||||
// of a reference to it into an implementation.
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
|
||||
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
|
||||
val invokable = expression.argument
|
||||
val reference = if (invokable is IrFunctionReference) {
|
||||
invokable
|
||||
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
|
||||
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
|
||||
invokable.statements.last() as IrFunctionReference
|
||||
} else {
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
reference.transformChildrenVoid()
|
||||
return FunctionReferenceBuilder(reference, expression.typeOperand).build()
|
||||
if (expression.operator != IrTypeOperator.SAM_CONVERSION) {
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
val invokable = expression.argument
|
||||
val reference = if (invokable is IrFunctionReference) {
|
||||
invokable
|
||||
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
|
||||
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
|
||||
invokable.statements.last() as IrFunctionReference
|
||||
} else {
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
reference.transformChildrenVoid()
|
||||
|
||||
return if (shouldUseIndySamConversions) {
|
||||
wrapSamConversionArgumentWithIndySamConversion(expression)
|
||||
} else {
|
||||
FunctionReferenceBuilder(reference, expression.typeOperand).build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapSamConversionArgumentWithIndySamConversion(expression: IrTypeOperatorCall): IrExpression {
|
||||
return when (val argument = expression.argument) {
|
||||
is IrFunctionReference ->
|
||||
wrapWithIndySamConversion(expression.typeOperand, 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)
|
||||
return argument
|
||||
}
|
||||
else -> throw AssertionError("Block or function reference expected: ${expression.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
private val jvmIndySamConversionIntrinsic = context.ir.symbols.indySamConversionIntrinsic
|
||||
|
||||
private val specialNullabilityAnnotationsFqNames =
|
||||
setOf(
|
||||
context.ir.symbols.flexibleNullabilityAnnotationFqName,
|
||||
context.ir.symbols.enhancedNullabilityAnnotationFqName
|
||||
)
|
||||
|
||||
private fun wrapWithIndySamConversion(samType: IrType, irFunRef: IrFunctionReference): IrCall {
|
||||
val notNullSamType = samType.makeNotNull()
|
||||
.removeAnnotations { it.type.classFqName in specialNullabilityAnnotationsFqNames }
|
||||
return context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
|
||||
// We should produce the following expression:
|
||||
// `<jvm-indy-sam-conversion>`<samType>(method)
|
||||
// where:
|
||||
// - 'samType' is a substituted SAM type;
|
||||
// - 'method' is a function reference to the actual method we are going to call
|
||||
// (note that we need an IrFunctionReference here, so that further transformations would extract closure properly).
|
||||
irCall(jvmIndySamConversionIntrinsic, notNullSamType).apply {
|
||||
putTypeArgument(0, notNullSamType)
|
||||
putValueArgument(0, irFunRef)
|
||||
}
|
||||
}
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) {
|
||||
|
||||
-2
@@ -15,8 +15,6 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.util.isLambda
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
|
||||
internal val removeDeclarationsThatWouldBeInlined = makeIrModulePhase(
|
||||
|
||||
+180
-4
@@ -11,26 +11,33 @@ import org.jetbrains.kotlin.backend.common.lower.at
|
||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||
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.fileParent
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Handle
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
// After this pass runs there are only four kinds of IrTypeOperatorCalls left:
|
||||
//
|
||||
@@ -95,6 +102,175 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil
|
||||
builder.irAs(argument, type)
|
||||
}
|
||||
|
||||
private val indySamConversionIntrinsic = context.ir.symbols.indySamConversionIntrinsic
|
||||
|
||||
private val indyIntrinsic = context.ir.symbols.jvmIndyIntrinsic
|
||||
|
||||
private fun IrBuilderWithScope.jvmInvokeDynamic(
|
||||
dynamicCall: IrCall,
|
||||
bootstrapMethod: Handle,
|
||||
bootstrapMethodArguments: List<IrExpression>
|
||||
) =
|
||||
irCall(indyIntrinsic, dynamicCall.type).apply {
|
||||
putTypeArgument(0, dynamicCall.type)
|
||||
putValueArgument(0, dynamicCall)
|
||||
putValueArgument(1, irInt(bootstrapMethod.tag))
|
||||
putValueArgument(2, irString(bootstrapMethod.owner))
|
||||
putValueArgument(3, irString(bootstrapMethod.name))
|
||||
putValueArgument(4, irString(bootstrapMethod.desc))
|
||||
putValueArgument(5, irVararg(context.irBuiltIns.anyType, bootstrapMethodArguments))
|
||||
}
|
||||
|
||||
private val methodTypeIntrinsic = context.ir.symbols.jvmMethodTypeIntrinsic
|
||||
|
||||
private fun IrBuilderWithScope.jvmMethodType(ownerType: IrType, methodSymbol: IrFunctionSymbol) =
|
||||
irCall(methodTypeIntrinsic, context.irBuiltIns.anyType).apply {
|
||||
putTypeArgument(0, ownerType)
|
||||
putValueArgument(0, irRawFunctionReferefence(context.irBuiltIns.anyType, methodSymbol))
|
||||
}
|
||||
|
||||
private val lambdaMetafactoryHandle =
|
||||
Handle(
|
||||
Opcodes.H_INVOKESTATIC,
|
||||
"java/lang/invoke/LambdaMetafactory",
|
||||
"metafactory",
|
||||
"(" +
|
||||
"Ljava/lang/invoke/MethodHandles\$Lookup;" +
|
||||
"Ljava/lang/String;" +
|
||||
"Ljava/lang/invoke/MethodType;" +
|
||||
"Ljava/lang/invoke/MethodType;" +
|
||||
"Ljava/lang/invoke/MethodHandle;" +
|
||||
"Ljava/lang/invoke/MethodType;" +
|
||||
")Ljava/lang/invoke/CallSite;",
|
||||
false
|
||||
)
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
return when (expression.symbol) {
|
||||
indySamConversionIntrinsic -> updateIndySamConversionIntrinsicCall(expression)
|
||||
else -> super.visitCall(expression)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FunctionReferenceLowering.wrapWithIndySamConversion
|
||||
*/
|
||||
private fun updateIndySamConversionIntrinsicCall(call: IrCall): IrCall {
|
||||
fun fail(message: String): Nothing =
|
||||
throw AssertionError("$message, call:\n${call.dump()}")
|
||||
|
||||
// We expect:
|
||||
// `<jvm-indy-sam-conversion>`<samType>(method)
|
||||
// where
|
||||
// - 'samType' is a substituted SAM type;
|
||||
// - 'method' is an IrFunctionReference to an actual method that should be called,
|
||||
// with arguments captured by closure stored as function reference arguments.
|
||||
// We replace it with JVM INVOKEDYNAMIC intrinsic.
|
||||
|
||||
val startOffset = call.startOffset
|
||||
val endOffset = call.endOffset
|
||||
|
||||
val samType = call.getTypeArgument(0) as? IrSimpleType
|
||||
?: fail("'samType' is expected to be a simple type")
|
||||
val samClassSymbol = samType.classOrNull
|
||||
?: fail("'samType' is expected to be a class type: '${samType.render()}'")
|
||||
val samMethod = samClassSymbol.owner.functions.singleOrNull { it.modality == Modality.ABSTRACT }
|
||||
?: fail("'${samType.render()}' is not a SAM-type")
|
||||
|
||||
val irFunRef = call.getValueArgument(0) as? IrFunctionReference
|
||||
?: fail("'method' is expected to be 'IrFunctionReference'")
|
||||
val funSymbol = irFunRef.symbol
|
||||
|
||||
val erasedSamType = samClassSymbol.defaultType as IrSimpleType
|
||||
val dynamicCall = wrapClosureInDynamicCall(erasedSamType, samMethod, irFunRef)
|
||||
|
||||
return context.createJvmIrBuilder(
|
||||
funSymbol, // TODO actual symbol for outer scope
|
||||
startOffset, endOffset
|
||||
).run {
|
||||
val samMethodType = jvmMethodType(erasedSamType, samMethod.symbol)
|
||||
val irRawFunRef = irRawFunctionReferefence(irFunRef.type, funSymbol)
|
||||
val instanceMethodType = jvmMethodType(samType, samMethod.symbol)
|
||||
|
||||
jvmInvokeDynamic(
|
||||
dynamicCall,
|
||||
lambdaMetafactoryHandle,
|
||||
listOf(samMethodType, irRawFunRef, instanceMethodType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapClosureInDynamicCall(
|
||||
erasedSamType: IrSimpleType,
|
||||
samMethod: IrSimpleFunction,
|
||||
irFunRef: IrFunctionReference
|
||||
): IrCall {
|
||||
fun fail(message: String): Nothing =
|
||||
throw AssertionError("$message, irFunRef:\n${irFunRef.dump()}")
|
||||
|
||||
val dynamicCallArguments = ArrayList<IrExpression>()
|
||||
|
||||
val irDynamicCallTarget = context.irFactory.buildFun {
|
||||
origin = JvmLoweredDeclarationOrigin.INVOVEDYNAMIC_CALL_TARGET
|
||||
name = samMethod.name
|
||||
returnType = erasedSamType
|
||||
}.apply {
|
||||
parent = context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage
|
||||
|
||||
var syntheticParameterIndex = 0
|
||||
val targetFun = irFunRef.symbol.owner
|
||||
|
||||
val targetDispatchReceiverParameter = targetFun.dispatchReceiverParameter
|
||||
if (targetDispatchReceiverParameter != null) {
|
||||
addValueParameter("p${syntheticParameterIndex++}", targetDispatchReceiverParameter.type)
|
||||
val dispatchReceiver = irFunRef.dispatchReceiver
|
||||
?: fail("Captured dispatch receiver is not provided")
|
||||
dynamicCallArguments.add(dispatchReceiver)
|
||||
}
|
||||
|
||||
val targetExtensionReceiverParameter = targetFun.extensionReceiverParameter
|
||||
if (targetExtensionReceiverParameter != null) {
|
||||
addValueParameter("p${syntheticParameterIndex++}", targetExtensionReceiverParameter.type)
|
||||
val extensionReceiver = irFunRef.extensionReceiver
|
||||
?: fail("Captured extension receiver is not provided")
|
||||
dynamicCallArguments.add(extensionReceiver)
|
||||
}
|
||||
|
||||
val samMethodValueParametersCount = samMethod.valueParameters.size
|
||||
val targetFunValueParametersCount = targetFun.valueParameters.size
|
||||
for (i in 0 until targetFunValueParametersCount - samMethodValueParametersCount) {
|
||||
val targetFunValueParameter = targetFun.valueParameters[i]
|
||||
addValueParameter("p${syntheticParameterIndex++}", targetFunValueParameter.type)
|
||||
val capturedValueArgument = irFunRef.getValueArgument(i)
|
||||
?: fail("Captured value argument #$i (${targetFunValueParameter.name} not provided")
|
||||
dynamicCallArguments.add(capturedValueArgument)
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamicCallArguments.size != irDynamicCallTarget.valueParameters.size) {
|
||||
throw AssertionError(
|
||||
"Dynamic call target value parameters (${irDynamicCallTarget.valueParameters.size}) " +
|
||||
"don't match dynamic call arguments (${dynamicCallArguments.size}):\n" +
|
||||
"irDynamicCallTarget:\n" +
|
||||
irDynamicCallTarget.dump() +
|
||||
"dynamicCallArguments:\n" +
|
||||
dynamicCallArguments
|
||||
.withIndex()
|
||||
.joinToString(separator = "\n ", prefix = "[\n ", postfix = "\n]") { (index, irArg) ->
|
||||
"#$index: ${irArg.dump()}"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return context.createJvmIrBuilder(irDynamicCallTarget.symbol)
|
||||
.irCall(irDynamicCallTarget.symbol)
|
||||
.apply {
|
||||
for (i in dynamicCallArguments.indices) {
|
||||
putValueArgument(i, dynamicCallArguments[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression = with(builder) {
|
||||
at(expression)
|
||||
return when (expression.operator) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.ir.util.isImmutable
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
@@ -326,6 +327,11 @@ fun IrBuilderWithScope.irString(value: String) =
|
||||
fun IrBuilderWithScope.irConcat() =
|
||||
IrStringConcatenationImpl(startOffset, endOffset, context.irBuiltIns.stringType)
|
||||
|
||||
fun IrBuilderWithScope.irVararg(elementType: IrType, values: List<IrExpression>) =
|
||||
IrVarargImpl(startOffset, endOffset, context.irBuiltIns.arrayClass.typeWith(elementType), elementType, values)
|
||||
|
||||
fun IrBuilderWithScope.irRawFunctionReferefence(type: IrType, symbol: IrFunctionSymbol) =
|
||||
IrRawFunctionReferenceImpl(startOffset, endOffset, type, symbol)
|
||||
|
||||
inline fun IrBuilderWithScope.irBlock(
|
||||
startOffset: Int = this.startOffset,
|
||||
|
||||
@@ -645,6 +645,9 @@ class RenderIrElementVisitor(private val normalizeNames: Boolean = false) : IrEl
|
||||
"type=${expression.type.render()} origin=${expression.origin} " +
|
||||
"reflectionTarget=${renderReflectionTarget(expression)}"
|
||||
|
||||
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: Nothing?): String =
|
||||
"RAW_FUNCTION_REFERENCE '${expression.symbol.renderReference()}' type=${expression.type.render()}"
|
||||
|
||||
private fun renderReflectionTarget(expression: IrFunctionReference) =
|
||||
if (expression.symbol == expression.reflectionTarget)
|
||||
"<same>"
|
||||
|
||||
Reference in New Issue
Block a user