JVM_IR: mark inlined callable references with a different origin
Makes them easier to find.
This commit is contained in:
+3
-3
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
@@ -41,8 +41,8 @@ private class BytecodeInliningPreparationLowering(val context: JvmBackendContext
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference, data: String) {
|
||||
// Remove inline lambdas from their declaration parents. They should not appear in the output
|
||||
// bytecode in non-inlined form.
|
||||
if (expression.origin.isInlineIrExpression()) {
|
||||
// bytecode in non-inlined form. TODO: this does not remove unused lambdas; need to mark function origin?
|
||||
if (expression.origin == JvmLoweredStatementOrigin.INLINE_LAMBDA) {
|
||||
loweredLambdasToDelete.add(expression.symbol.owner)
|
||||
}
|
||||
super.visitFunctionReference(expression, data)
|
||||
|
||||
+16
-30
@@ -37,44 +37,30 @@ internal class FakeInliningLocalVariablesLowering(val context: JvmBackendContext
|
||||
override fun visitFunction(declaration: IrFunction, data: IrDeclaration?) {
|
||||
super.visitFunction(declaration, data)
|
||||
if (declaration.isInline && !declaration.origin.isSynthetic && declaration.body != null && !declaration.isInlineOnly()) {
|
||||
declaration.addFakeInliningLocalVariables()
|
||||
val currentFunctionName = context.methodSignatureMapper.mapFunctionName(declaration)
|
||||
val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION}$currentFunctionName"
|
||||
declaration.addFakeLocalVariable(localName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) {
|
||||
val lambda = argument.symbol.owner
|
||||
if (lambda.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) {
|
||||
val argumentToFunctionName = context.methodSignatureMapper.mapFunctionName(callee)
|
||||
val lambdaReferenceName = context.getLocalClassType(argument)!!.internalName.substringAfterLast("/")
|
||||
val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT}-$argumentToFunctionName-$lambdaReferenceName"
|
||||
lambda.addFakeLocalVariable(localName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrFunction.addFakeInliningLocalVariables() {
|
||||
val currentFunctionName = context.methodSignatureMapper.mapFunctionName(this)
|
||||
val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION}$currentFunctionName"
|
||||
addFakeLocalVariable(localName)
|
||||
val argumentToFunctionName = context.methodSignatureMapper.mapFunctionName(callee)
|
||||
val lambdaReferenceName = context.getLocalClassType(argument)!!.internalName.substringAfterLast("/")
|
||||
val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT}-$argumentToFunctionName-$lambdaReferenceName"
|
||||
lambda.addFakeLocalVariable(localName)
|
||||
}
|
||||
|
||||
private fun IrFunction.addFakeLocalVariable(name: String) {
|
||||
val oldBody = body
|
||||
context.createIrBuilder(symbol).run {
|
||||
body = irBlockBody {
|
||||
// Create temporary variable, but make sure it's origin is `DEFINED` so that
|
||||
// it will materialize in the code.
|
||||
// Also, do not forget to remove $$forInline suffix, otherwise, IDE will not be able to navigate to inline function.
|
||||
createTmpVariable(irInt(0), name.removeSuffix(FOR_INLINE_SUFFIX), origin = IrDeclarationOrigin.DEFINED)
|
||||
when (oldBody) {
|
||||
is IrExpressionBody -> {
|
||||
+irReturn(oldBody.expression)
|
||||
}
|
||||
is IrBlockBody ->
|
||||
oldBody.statements.forEach { +it }
|
||||
else -> {
|
||||
throw AssertionError("Unexpected body:\n${this@addFakeLocalVariable.dump()}")
|
||||
}
|
||||
}
|
||||
body = context.createIrBuilder(symbol).irBlockBody {
|
||||
// Create temporary variable, but make sure it's origin is `DEFINED` so that
|
||||
// it will materialize in the code.
|
||||
// Also, do not forget to remove $$forInline suffix, otherwise, IDE will not be able to navigate to inline function.
|
||||
createTmpVariable(irInt(0), name.removeSuffix(FOR_INLINE_SUFFIX), origin = IrDeclarationOrigin.DEFINED)
|
||||
when (val oldBody = body) {
|
||||
is IrExpressionBody -> +irReturn(oldBody.expression)
|
||||
is IrBlockBody -> oldBody.statements.forEach { +it }
|
||||
else -> throw AssertionError("Unexpected body:\n${this@addFakeLocalVariable.dump()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-25
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.lower.parents
|
||||
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.JvmLoweredStatementOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArguments
|
||||
@@ -45,42 +46,26 @@ internal val functionReferencePhase = makeIrFilePhase(
|
||||
)
|
||||
|
||||
internal class FunctionReferenceLowering(private val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoidWithContext() {
|
||||
// This pass ignores function references used as inline arguments. `InlineCallableReferenceToLambdaPhase`
|
||||
// converts them into lambdas instead, so that after inlining there is only a direct call left, with no
|
||||
// function reference classes needed.
|
||||
private val ignoredFunctionReferences = mutableSetOf<IrCallableReference<*>>()
|
||||
|
||||
private val crossinlineLambdas = HashSet<IrSimpleFunction>()
|
||||
|
||||
private val IrFunctionReference.isIgnored: Boolean
|
||||
get() = (!type.isFunctionOrKFunction() && !isSuspendFunctionReference()) || ignoredFunctionReferences.contains(this)
|
||||
get() = (!type.isFunctionOrKFunction() && !isSuspendFunctionReference()) || origin == JvmLoweredStatementOrigin.INLINE_LAMBDA
|
||||
|
||||
// `suspend` function references are the same as non-`suspend` ones, just with a `suspend` invoke;
|
||||
// however, suspending lambdas require different generation implemented in AddContinuationLowering
|
||||
// `suspend` function references are the same as non-`suspend` ones, just with an extra continuation parameter;
|
||||
// however, suspending lambdas require different generation implemented in SuspendLambdaLowering
|
||||
// because they are also their own continuation classes.
|
||||
// TODO: Currently, origin of callable references explicitly written in source code is null. Do we need to create one?
|
||||
private fun IrFunctionReference.isSuspendFunctionReference(): Boolean = isSuspend &&
|
||||
(origin == null || origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || origin == IrStatementOrigin.SUSPEND_CONVERSION)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.accept(
|
||||
object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineLambda(
|
||||
argument: IrFunctionReference,
|
||||
callee: IrFunction,
|
||||
parameter: IrValueParameter,
|
||||
scope: IrDeclaration
|
||||
) {
|
||||
ignoredFunctionReferences.add(argument)
|
||||
val argumentFun = argument.symbol.owner
|
||||
if (parameter.isCrossinline && argumentFun is IrSimpleFunction) {
|
||||
crossinlineLambdas.add(argumentFun)
|
||||
}
|
||||
}
|
||||
},
|
||||
null
|
||||
)
|
||||
irFile.findInlineLambdas(context) { argument, _, parameter, _ ->
|
||||
if (parameter.isCrossinline) {
|
||||
crossinlineLambdas.add(argument.symbol.owner as IrSimpleFunction)
|
||||
}
|
||||
}
|
||||
irFile.transformChildrenVoid(this)
|
||||
crossinlineLambdas.clear()
|
||||
}
|
||||
|
||||
private val shouldGenerateIndySamConversions =
|
||||
|
||||
+108
-140
@@ -6,15 +6,16 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.irArray
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addExtensionReceiver
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
@@ -25,13 +26,13 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal val inlineCallableReferenceToLambdaPhase = makeIrFilePhase(
|
||||
::InlineCallableReferenceToLambdaPhase,
|
||||
name = "InlineCallableReferenceToLambdaPhase",
|
||||
description = "Transform callable reference to inline lambda"
|
||||
description = "Transform callable reference to inline lambdas, mark inline lambdas for later passes"
|
||||
)
|
||||
|
||||
// This lowering transforms CR passed to inline function to lambda which would be inlined
|
||||
@@ -43,158 +44,125 @@ internal val inlineCallableReferenceToLambdaPhase = makeIrFilePhase(
|
||||
// foo(::smth) -> foo { a -> smth(a) }
|
||||
//
|
||||
internal class InlineCallableReferenceToLambdaPhase(val context: JvmBackendContext) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
val inlinableReferences = mutableSetOf<IrCallableReference<*>>()
|
||||
irFile.accept(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineReference(argument: IrCallableReference<*>) {
|
||||
inlinableReferences.add(argument)
|
||||
}
|
||||
|
||||
override fun visitInlineLambda(
|
||||
argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration
|
||||
) {
|
||||
// Obviously needs no extra wrapping.
|
||||
}
|
||||
}, null)
|
||||
irFile.transformChildrenVoid(InlineCallableReferenceToLambdaTransformer(context, inlinableReferences))
|
||||
}
|
||||
override fun lower(irFile: IrFile) =
|
||||
irFile.accept(InlineCallableReferenceToLambdaVisitor(context), null)
|
||||
}
|
||||
|
||||
const val STUB_FOR_INLINING = "stub_for_inlining"
|
||||
|
||||
private class InlineCallableReferenceToLambdaTransformer(
|
||||
val context: JvmBackendContext,
|
||||
val inlinableReferences: Set<IrCallableReference<*>>
|
||||
) : IrElementTransformerVoidWithContext() {
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
if (expression !in inlinableReferences) return expression
|
||||
return expandInlineFunctionReferenceToLambda(expression, expression.symbol.owner)
|
||||
}
|
||||
private class InlineCallableReferenceToLambdaVisitor(val context: JvmBackendContext) : IrElementVisitor<Unit, IrDeclaration?> {
|
||||
override fun visitElement(element: IrElement, data: IrDeclaration?) =
|
||||
element.acceptChildren(this, element as? IrDeclaration ?: data)
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
if (expression !in inlinableReferences) return expression
|
||||
|
||||
return if (expression.field?.owner == null) {
|
||||
// Use getter if field is absent ...
|
||||
expandInlineFunctionReferenceToLambda(expression, expression.getter!!.owner)
|
||||
} else {
|
||||
// ... else use field itself
|
||||
expandInlineFieldReferenceToLambda(expression, expression.field!!.owner)
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: IrDeclaration?) {
|
||||
expression.acceptChildren(this, data)
|
||||
val function = expression.symbol.owner
|
||||
if (function.isInlineFunctionCall(context)) {
|
||||
for (parameter in function.valueParameters) {
|
||||
if (parameter.isInlineParameter()) {
|
||||
expression.putValueArgument(parameter.index, expression.getValueArgument(parameter.index)?.transform(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandInlineFieldReferenceToLambda(expression: IrPropertyReference, field: IrField): IrExpression {
|
||||
val irBuilder = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
return irBuilder.irBlock(expression, IrStatementOrigin.LAMBDA) {
|
||||
val boundReceiver = expression.dispatchReceiver ?: expression.extensionReceiver
|
||||
val function = context.irFactory.buildFun {
|
||||
setSourceRange(expression)
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
name = Name.identifier("stub_for_inline")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
returnType = field.type
|
||||
isSuspend = false
|
||||
}.apply {
|
||||
parent = currentDeclarationParent ?: error("No current declaration parent at ${expression.dump()}")
|
||||
val receiver = when {
|
||||
private fun IrExpression.transform(scope: IrDeclaration?) = when {
|
||||
this is IrBlock && origin.isInlinable -> apply {
|
||||
// Already a lambda or similar, just mark it with an origin.
|
||||
statements[statements.size - 1] =
|
||||
(statements[statements.size - 1] as IrFunctionReference).replaceOrigin(JvmLoweredStatementOrigin.INLINE_LAMBDA)
|
||||
}
|
||||
|
||||
this is IrFunctionReference -> // ::function -> { args... -> function(args...) }
|
||||
wrapFunction(symbol.owner).toLambda(this, scope!!)
|
||||
|
||||
this is IrPropertyReference -> // ::property -> { receiver -> receiver.property }; prefer direct field access if allowed.
|
||||
(if (field != null) wrapField(field!!.owner) else wrapFunction(getter!!.owner)).toLambda(this, scope!!)
|
||||
|
||||
else -> this // not an inline argument
|
||||
}
|
||||
|
||||
private fun IrPropertyReference.wrapField(field: IrField): IrSimpleFunction =
|
||||
context.irFactory.buildFun {
|
||||
setSourceRange(this@wrapField)
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
name = Name.identifier(STUB_FOR_INLINING)
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
returnType = field.type
|
||||
}.apply {
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
val boundReceiver = dispatchReceiver ?: extensionReceiver
|
||||
val fieldReceiver = when {
|
||||
field.isStatic -> null
|
||||
boundReceiver != null -> irGet(addExtensionReceiver(boundReceiver.type))
|
||||
else -> irGet(addValueParameter("receiver", field.parentAsClass.defaultType))
|
||||
}
|
||||
body = this@InlineCallableReferenceToLambdaTransformer.context.createIrBuilder(symbol).run {
|
||||
irExprBody(irGetField(receiver, field))
|
||||
}
|
||||
}
|
||||
|
||||
+function
|
||||
+IrFunctionReferenceImpl.fromSymbolOwner(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
function.symbol,
|
||||
typeArgumentsCount = 0,
|
||||
reflectionTarget = null,
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
).apply {
|
||||
copyAttributes(expression)
|
||||
extensionReceiver = boundReceiver
|
||||
irExprBody(irGetField(fieldReceiver, field))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandInlineFunctionReferenceToLambda(expression: IrCallableReference<*>, referencedFunction: IrFunction): IrExpression {
|
||||
val irBuilder = context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
|
||||
return irBuilder.irBlock(expression, IrStatementOrigin.LAMBDA) {
|
||||
val (receiverParameter, receiverValue) = expression.getArgumentsWithIr().singleOrNull() ?: (null to null)
|
||||
val kFunctionArguments = (expression.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||
val argumentTypes = kFunctionArguments.dropLast(1)
|
||||
val resultType = kFunctionArguments.last()
|
||||
|
||||
val function = context.irFactory.buildFun {
|
||||
setSourceRange(expression)
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
name = Name.identifier(STUB_FOR_INLINING)
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
returnType = resultType
|
||||
isSuspend = referencedFunction.isSuspend
|
||||
}.apply {
|
||||
parent = currentDeclarationParent!!
|
||||
if (receiverValue != null) {
|
||||
addExtensionReceiver(receiverValue.type)
|
||||
private fun IrCallableReference<*>.wrapFunction(referencedFunction: IrFunction): IrSimpleFunction =
|
||||
context.irFactory.buildFun {
|
||||
setSourceRange(this@wrapFunction)
|
||||
origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
name = Name.identifier(STUB_FOR_INLINING)
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
returnType = ((type as IrSimpleType).arguments.last() as IrTypeProjection).type
|
||||
isSuspend = referencedFunction.isSuspend
|
||||
}.apply {
|
||||
body = context.createJvmIrBuilder(symbol, startOffset, endOffset).run {
|
||||
// TODO: could there be a star projection here?
|
||||
val argumentTypes = (type as IrSimpleType).arguments.dropLast(1).map { (it as IrTypeProjection).type }
|
||||
val boundReceiver = dispatchReceiver ?: extensionReceiver
|
||||
val boundReceiverParameter = when {
|
||||
dispatchReceiver != null -> referencedFunction.dispatchReceiverParameter
|
||||
extensionReceiver != null -> referencedFunction.extensionReceiverParameter
|
||||
else -> null
|
||||
}
|
||||
for ((index, argumentType) in argumentTypes.withIndex()) {
|
||||
addValueParameter {
|
||||
name = Name.identifier("p$index")
|
||||
type = argumentType
|
||||
}
|
||||
}
|
||||
|
||||
body = this@InlineCallableReferenceToLambdaTransformer.context.createJvmIrBuilder(
|
||||
symbol,
|
||||
expression.startOffset,
|
||||
expression.endOffset
|
||||
).run {
|
||||
irExprBody(irCall(referencedFunction.symbol, resultType).apply {
|
||||
symbol.owner.allTypeParameters.forEach {
|
||||
putTypeArgument(it.index, expression.getTypeArgument(it.index))
|
||||
}
|
||||
|
||||
var unboundIndex = 0
|
||||
for (parameter in referencedFunction.explicitParameters) {
|
||||
when {
|
||||
receiverParameter == parameter ->
|
||||
irGet(extensionReceiverParameter!!)
|
||||
parameter.isVararg && unboundIndex < argumentTypes.size && parameter.type == valueParameters[unboundIndex].type ->
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
parameter.isVararg && (unboundIndex < argumentTypes.size || !parameter.hasDefaultValue()) ->
|
||||
irArray(parameter.type) {
|
||||
(unboundIndex until argumentTypes.size).forEach { +irGet(valueParameters[unboundIndex++]) }
|
||||
irExprBody(irCall(referencedFunction.symbol, returnType).apply {
|
||||
copyTypeArgumentsFrom(this@wrapFunction)
|
||||
for (parameter in referencedFunction.explicitParameters) {
|
||||
val next = valueParameters.size
|
||||
when {
|
||||
boundReceiverParameter == parameter ->
|
||||
irGet(addExtensionReceiver(boundReceiver!!.type))
|
||||
parameter.isVararg && next < argumentTypes.size && parameter.type == argumentTypes[next] ->
|
||||
irGet(addValueParameter("p$next", argumentTypes[next]))
|
||||
parameter.isVararg && (next < argumentTypes.size || !parameter.hasDefaultValue()) ->
|
||||
irArray(parameter.type) {
|
||||
for (i in next until argumentTypes.size) {
|
||||
+irGet(addValueParameter("p$i", argumentTypes[i]))
|
||||
}
|
||||
unboundIndex >= argumentTypes.size ->
|
||||
null
|
||||
else ->
|
||||
irGet(valueParameters[unboundIndex++])
|
||||
}?.let { putArgument(referencedFunction, parameter, it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+function
|
||||
+IrFunctionReferenceImpl.fromSymbolOwner(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
function.symbol,
|
||||
typeArgumentsCount = function.typeParameters.size,
|
||||
reflectionTarget = null,
|
||||
origin = IrStatementOrigin.LAMBDA
|
||||
).apply {
|
||||
copyAttributes(expression)
|
||||
extensionReceiver = receiverValue
|
||||
}
|
||||
next >= argumentTypes.size ->
|
||||
null
|
||||
else ->
|
||||
irGet(addValueParameter("p$next", argumentTypes[next]))
|
||||
}?.let { putArgument(referencedFunction, parameter, it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.toLambda(original: IrCallableReference<*>, scope: IrDeclaration) =
|
||||
context.createIrBuilder(scope.symbol).irBlock(startOffset, endOffset, IrStatementOrigin.LAMBDA) {
|
||||
this@toLambda.parent = parent
|
||||
+this@toLambda
|
||||
+IrFunctionReferenceImpl.fromSymbolOwner(
|
||||
startOffset, endOffset, original.type, symbol, typeArgumentsCount = 0, reflectionTarget = null,
|
||||
origin = JvmLoweredStatementOrigin.INLINE_LAMBDA
|
||||
).apply {
|
||||
copyAttributes(original)
|
||||
extensionReceiver = original.dispatchReceiver ?: original.extensionReceiver
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IrStatementOrigin?.isInlinable: Boolean
|
||||
get() = isLambda || this == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || this == IrStatementOrigin.SUSPEND_CONVERSION
|
||||
|
||||
private fun IrFunctionReference.replaceOrigin(origin: IrStatementOrigin): IrFunctionReference =
|
||||
IrFunctionReferenceImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, valueArgumentsCount, reflectionTarget, origin).also {
|
||||
it.copyAttributes(this)
|
||||
it.copyTypeAndValueArgumentsFrom(this)
|
||||
}
|
||||
|
||||
+4
-10
@@ -8,23 +8,17 @@ package org.jetbrains.kotlin.backend.jvm.lower
|
||||
import org.jetbrains.kotlin.backend.common.ScopeWithIr
|
||||
import org.jetbrains.kotlin.backend.common.lower.LocalClassPopupLowering
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.findInlineLambdas
|
||||
import org.jetbrains.kotlin.backend.jvm.isGeneratedLambdaClass
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
|
||||
class JvmLocalClassPopupLowering(context: JvmBackendContext) : LocalClassPopupLowering(context) {
|
||||
private val inlineLambdaToScope = mutableMapOf<IrFunction, IrDeclaration>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.accept(object : IrInlineReferenceLocator(context as JvmBackendContext) {
|
||||
override fun visitInlineLambda(
|
||||
argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration
|
||||
) {
|
||||
inlineLambdaToScope[argument.symbol.owner] = scope
|
||||
}
|
||||
}, null)
|
||||
irFile.findInlineLambdas(context as JvmBackendContext) { argument, _, _, scope ->
|
||||
inlineLambdaToScope[argument.symbol.owner] = scope
|
||||
}
|
||||
super.lower(irFile)
|
||||
inlineLambdaToScope.clear()
|
||||
}
|
||||
|
||||
+1
-3
@@ -15,7 +15,6 @@ 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.isReadOfCrossinline
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.hasChild
|
||||
import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_LABEL_FIELD_NAME
|
||||
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
||||
@@ -99,11 +98,10 @@ internal abstract class SuspendLoweringUtils(protected val context: JvmBackendCo
|
||||
|
||||
private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLoweringUtils(context), FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
val inlineReferences = IrInlineReferenceLocator.scan(context, irFile)
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
|
||||
override fun visitBlock(expression: IrBlock): IrExpression {
|
||||
val reference = expression.statements.lastOrNull() as? IrFunctionReference ?: return super.visitBlock(expression)
|
||||
if (reference.isSuspend && reference.origin.isLambda && reference !in inlineReferences) {
|
||||
if (reference.isSuspend && reference.origin.isLambda) {
|
||||
assert(expression.statements.size == 2 && expression.statements[0] is IrFunction)
|
||||
expression.transformChildrenVoid(this)
|
||||
val parent = currentDeclarationParent ?: error("No current declaration parent at ${reference.dump()}")
|
||||
|
||||
+3
@@ -81,6 +81,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
||||
}, null)
|
||||
|
||||
irFile.transformChildrenVoid(this)
|
||||
inlineLambdaToCallSite.clear()
|
||||
inlineFunctionToCallSites.clear()
|
||||
|
||||
for (accessor in pendingAccessorsToAdd) {
|
||||
assert(accessor.fileOrNull == irFile) {
|
||||
@@ -90,6 +92,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
||||
}
|
||||
(accessor.parent as IrDeclarationContainer).declarations.add(accessor)
|
||||
}
|
||||
pendingAccessorsToAdd.clear()
|
||||
}
|
||||
|
||||
private data class FieldKey(val fieldSymbol: IrFieldSymbol, val parent: IrDeclarationParent, val superQualifierSymbol: IrClassSymbol?)
|
||||
|
||||
@@ -10,4 +10,5 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl
|
||||
interface JvmLoweredStatementOrigin {
|
||||
object DEFAULT_STUB_CALL_TO_IMPLEMENTATION : IrStatementOriginImpl("DEFAULT_STUB_CALL_TO_IMPLEMENTATION")
|
||||
object DO_WHILE_COUNTER_LOOP: IrStatementOriginImpl("DO_WHILE_COUNTER_LOOP")
|
||||
object INLINE_LAMBDA : IrStatementOriginImpl("INLINE_LAMBDA")
|
||||
}
|
||||
|
||||
+11
-17
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredStatementOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
||||
@@ -13,6 +14,7 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.ValueKind
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
@@ -85,10 +87,9 @@ class IrInlineCodegen(
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
val isInlineParameter = irValueParameter.isInlineParameter()
|
||||
if (isInlineParameter && argumentExpression.isInlineIrExpression()) {
|
||||
val irReference = (argumentExpression as IrBlock).statements.last() as IrFunctionReference
|
||||
val lambdaInfo = IrExpressionLambdaImpl(codegen, irReference)
|
||||
val inlineLambda = argumentExpression.unwrapInlineLambda()
|
||||
if (inlineLambda != null) {
|
||||
val lambdaInfo = IrExpressionLambdaImpl(codegen, inlineLambda)
|
||||
rememberClosure(parameterType, irValueParameter.index, lambdaInfo)
|
||||
lambdaInfo.generateLambdaBody(sourceCompiler)
|
||||
lambdaInfo.reference.getArgumentsWithIr().forEachIndexed { index, (_, ir) ->
|
||||
@@ -97,6 +98,7 @@ class IrInlineCodegen(
|
||||
putCapturedToLocalVal(onStack, param, ir.type.toIrBasedKotlinType())
|
||||
}
|
||||
} else {
|
||||
val isInlineParameter = irValueParameter.isInlineParameter()
|
||||
val kind = when {
|
||||
irValueParameter.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION ->
|
||||
ValueKind.DEFAULT_MASK
|
||||
@@ -210,19 +212,11 @@ class IrExpressionLambdaImpl(
|
||||
}
|
||||
}
|
||||
|
||||
fun IrExpression.isInlineIrExpression() =
|
||||
when (this) {
|
||||
is IrBlock -> origin.isInlineIrExpression()
|
||||
is IrCallableReference<*> -> true.also {
|
||||
assert((0 until valueArgumentsCount).none { getValueArgument(it) != null }) {
|
||||
"Expecting 0 value arguments for bound callable reference: ${dump()}"
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun IrStatementOrigin?.isInlineIrExpression() =
|
||||
isLambda || this == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE || this == IrStatementOrigin.SUSPEND_CONVERSION
|
||||
fun IrStatement.unwrapInlineLambda(): IrFunctionReference? = when (this) {
|
||||
is IrBlock -> statements.lastOrNull()?.unwrapInlineLambda()
|
||||
is IrFunctionReference -> takeIf { it.origin == JvmLoweredStatementOrigin.INLINE_LAMBDA }
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun IrFunction.isInlineFunctionCall(context: JvmBackendContext) =
|
||||
(!context.state.isInlineDisabled || typeParameters.any { it.isReified }) && (isInline || isInlineArrayConstructor(context))
|
||||
|
||||
+14
-39
@@ -7,59 +7,34 @@ package org.jetbrains.kotlin.backend.jvm.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.unwrapInlineLambda
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlock
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
open class IrInlineReferenceLocator(private val context: JvmBackendContext) : IrElementVisitor<Unit, IrDeclaration?> {
|
||||
override fun visitElement(element: IrElement, data: IrDeclaration?) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrDeclaration?) {
|
||||
val scope = if (declaration is IrVariable) data else declaration
|
||||
declaration.acceptChildren(this, scope)
|
||||
}
|
||||
abstract class IrInlineReferenceLocator(private val context: JvmBackendContext) : IrElementVisitor<Unit, IrDeclaration?> {
|
||||
override fun visitElement(element: IrElement, data: IrDeclaration?) =
|
||||
element.acceptChildren(this, if (element is IrDeclaration && element !is IrVariable) element else data)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: IrDeclaration?) {
|
||||
val function = expression.symbol.owner
|
||||
if (function.isInlineFunctionCall(context)) {
|
||||
for (parameter in function.valueParameters) {
|
||||
if (!parameter.isInlineParameter())
|
||||
continue
|
||||
|
||||
val valueArgument = expression.getValueArgument(parameter.index) ?: continue
|
||||
if (!valueArgument.isInlineIrExpression())
|
||||
continue
|
||||
|
||||
if (valueArgument is IrBlock) {
|
||||
visitInlineLambda(valueArgument.statements.last() as IrFunctionReference, function, parameter, data!!)
|
||||
} else if (valueArgument is IrCallableReference<*>) {
|
||||
visitInlineReference(valueArgument)
|
||||
}
|
||||
val lambda = expression.getValueArgument(parameter.index)?.unwrapInlineLambda() ?: continue
|
||||
visitInlineLambda(lambda, function, parameter, data!!)
|
||||
}
|
||||
}
|
||||
return super.visitFunctionAccess(expression, data)
|
||||
}
|
||||
|
||||
open fun visitInlineReference(argument: IrCallableReference<*>) {}
|
||||
|
||||
open fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) =
|
||||
visitInlineReference(argument)
|
||||
|
||||
companion object {
|
||||
fun scan(context: JvmBackendContext, element: IrElement): Set<IrCallableReference<*>> =
|
||||
mutableSetOf<IrCallableReference<*>>().apply {
|
||||
element.accept(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineReference(argument: IrCallableReference<*>) {
|
||||
add(argument)
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
}
|
||||
abstract fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration)
|
||||
}
|
||||
|
||||
inline fun IrFile.findInlineLambdas(
|
||||
context: JvmBackendContext, crossinline onLambda: (IrFunctionReference, IrFunction, IrValueParameter, IrDeclaration) -> Unit
|
||||
) = accept(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) =
|
||||
onLambda(argument, callee, parameter, scope)
|
||||
}, null)
|
||||
|
||||
Reference in New Issue
Block a user