JS IR: move inliner to common part; inline suspendCoroutineUninterceptedOrReturnJS
This commit is contained in:
@@ -179,6 +179,14 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, private val
|
||||
|
||||
abstract val getContinuation: IrSimpleFunctionSymbol
|
||||
|
||||
abstract val coroutineContextGetter: IrSimpleFunctionSymbol
|
||||
|
||||
abstract val suspendCoroutineUninterceptedOrReturn: IrSimpleFunctionSymbol
|
||||
|
||||
abstract val coroutineGetContext: IrSimpleFunctionSymbol
|
||||
|
||||
abstract val returnIfSuspended: IrSimpleFunctionSymbol
|
||||
|
||||
private val binaryOperatorCache = mutableMapOf<Triple<Name, KotlinType, KotlinType>, IrSimpleFunctionSymbol>()
|
||||
|
||||
fun getBinaryOperator(name: Name, lhsType: KotlinType, rhsType: KotlinType): IrSimpleFunctionSymbol {
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.common.lower.inline
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
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.name.Name
|
||||
|
||||
internal class DeepCopyIrTreeWithSymbolsForInliner(
|
||||
val context: CommonBackendContext,
|
||||
val typeArguments: Map<IrTypeParameterSymbol, IrType?>?,
|
||||
val parent: IrDeclarationParent?
|
||||
) {
|
||||
|
||||
fun copy(irElement: IrElement): IrElement {
|
||||
// Create new symbols.
|
||||
irElement.acceptVoid(symbolRemapper)
|
||||
|
||||
// Make symbol remapper aware of the callsite's type arguments.
|
||||
symbolRemapper.typeArguments = typeArguments
|
||||
|
||||
// Copy IR.
|
||||
val result = irElement.transform(copier, data = null)
|
||||
|
||||
// Bind newly created IR with wrapped descriptors.
|
||||
result.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
(declaration.descriptor as WrappedClassDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor) {
|
||||
(declaration.descriptor as WrappedClassConstructorDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitEnumEntry(declaration: IrEnumEntry) {
|
||||
(declaration.descriptor as WrappedClassDescriptor).bind(
|
||||
declaration.correspondingClass ?: declaration.parentAsClass
|
||||
)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
(declaration.descriptor as WrappedFieldDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
(declaration.descriptor as WrappedPropertyDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
(declaration.descriptor as WrappedSimpleFunctionDescriptor).bind(declaration as IrSimpleFunction)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitValueParameter(declaration: IrValueParameter) {
|
||||
(declaration.descriptor as? WrappedValueParameterDescriptor)?.bind(declaration)
|
||||
(declaration.descriptor as? WrappedReceiverParameterDescriptor)?.bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitTypeParameter(declaration: IrTypeParameter) {
|
||||
(declaration.descriptor as WrappedTypeParameterDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable) {
|
||||
(declaration.descriptor as WrappedVariableDescriptor).bind(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
})
|
||||
|
||||
result.patchDeclarationParents(parent)
|
||||
return result
|
||||
}
|
||||
|
||||
private var nameIndex = 0
|
||||
|
||||
private fun generateCopyName(name: Name) = Name.identifier(name.toString() + "_" + (nameIndex++).toString())
|
||||
|
||||
private inner class InlinerSymbolRenamer : SymbolRenamer {
|
||||
private val map = mutableMapOf<IrSymbol, Name>()
|
||||
|
||||
override fun getClassName(symbol: IrClassSymbol) = map.getOrPut(symbol) { generateCopyName(symbol.owner.name) }
|
||||
override fun getFunctionName(symbol: IrSimpleFunctionSymbol) = map.getOrPut(symbol) { generateCopyName(symbol.owner.name) }
|
||||
override fun getFieldName(symbol: IrFieldSymbol) = symbol.owner.name
|
||||
override fun getFileName(symbol: IrFileSymbol) = symbol.owner.fqName
|
||||
override fun getExternalPackageFragmentName(symbol: IrExternalPackageFragmentSymbol) = symbol.owner.fqName
|
||||
override fun getEnumEntryName(symbol: IrEnumEntrySymbol) = symbol.owner.name
|
||||
override fun getVariableName(symbol: IrVariableSymbol) = map.getOrPut(symbol) { generateCopyName(symbol.owner.name) }
|
||||
override fun getTypeParameterName(symbol: IrTypeParameterSymbol) = symbol.owner.name
|
||||
override fun getValueParameterName(symbol: IrValueParameterSymbol) = symbol.owner.name
|
||||
}
|
||||
|
||||
private inner class DescriptorsToIrRemapper : DescriptorsRemapper {
|
||||
override fun remapDeclaredClass(descriptor: ClassDescriptor) =
|
||||
WrappedClassDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredConstructor(descriptor: ClassConstructorDescriptor) =
|
||||
WrappedClassConstructorDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredEnumEntry(descriptor: ClassDescriptor) =
|
||||
WrappedClassDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredField(descriptor: PropertyDescriptor) =
|
||||
WrappedFieldDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredProperty(descriptor: PropertyDescriptor) =
|
||||
WrappedPropertyDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredSimpleFunction(descriptor: FunctionDescriptor) =
|
||||
WrappedSimpleFunctionDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredTypeParameter(descriptor: TypeParameterDescriptor) =
|
||||
WrappedTypeParameterDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredVariable(descriptor: VariableDescriptor) =
|
||||
WrappedVariableDescriptor(descriptor.annotations, descriptor.source)
|
||||
|
||||
override fun remapDeclaredValueParameter(descriptor: ParameterDescriptor): ParameterDescriptor =
|
||||
if (descriptor is ReceiverParameterDescriptor)
|
||||
WrappedReceiverParameterDescriptor(descriptor.annotations, descriptor.source)
|
||||
else
|
||||
WrappedValueParameterDescriptor(descriptor.annotations, descriptor.source)
|
||||
}
|
||||
|
||||
private inner class InlinerTypeRemapper(
|
||||
val symbolRemapper: SymbolRemapper,
|
||||
val typeArguments: Map<IrTypeParameterSymbol, IrType?>?
|
||||
) : TypeRemapper {
|
||||
|
||||
override fun enterScope(irTypeParametersContainer: IrTypeParametersContainer) {}
|
||||
|
||||
override fun leaveScope() {}
|
||||
|
||||
private fun remapTypeArguments(arguments: List<IrTypeArgument>) =
|
||||
arguments.map { argument ->
|
||||
(argument as? IrTypeProjection)?.let { makeTypeProjection(remapType(it.type), it.variance) }
|
||||
?: argument
|
||||
}
|
||||
|
||||
override fun remapType(type: IrType): IrType {
|
||||
if (type !is IrSimpleType) return type
|
||||
|
||||
val substitutedType = typeArguments?.get(type.classifier)
|
||||
|
||||
if (substitutedType is IrDynamicType) return substitutedType
|
||||
|
||||
if (substitutedType is IrSimpleType) {
|
||||
return substitutedType.buildSimpleType {
|
||||
kotlinType = null
|
||||
hasQuestionMark = type.hasQuestionMark or substitutedType.isMarkedNullable()
|
||||
}
|
||||
}
|
||||
|
||||
return type.buildSimpleType {
|
||||
kotlinType = null
|
||||
classifier = symbolRemapper.getReferencedClassifier(type.classifier)
|
||||
arguments = remapTypeArguments(type.arguments)
|
||||
annotations = type.annotations.map { it.transform(copier, null) as IrConstructorCall }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolRemapperImpl(descriptorsRemapper: DescriptorsRemapper) : DeepCopySymbolRemapper(descriptorsRemapper) {
|
||||
|
||||
var typeArguments: Map<IrTypeParameterSymbol, IrType?>? = null
|
||||
set(value) {
|
||||
if (field != null) return
|
||||
field = value?.asSequence()?.associate {
|
||||
(getReferencedClassifier(it.key) as IrTypeParameterSymbol) to it.value
|
||||
}
|
||||
}
|
||||
|
||||
override fun getReferencedClassifier(symbol: IrClassifierSymbol): IrClassifierSymbol {
|
||||
val result = super.getReferencedClassifier(symbol)
|
||||
if (result !is IrTypeParameterSymbol)
|
||||
return result
|
||||
return typeArguments?.get(result)?.classifierOrNull ?: result
|
||||
}
|
||||
}
|
||||
|
||||
private val symbolRemapper = SymbolRemapperImpl(DescriptorsToIrRemapper())
|
||||
private val copier =
|
||||
DeepCopyIrTreeWithSymbols(symbolRemapper, InlinerTypeRemapper(symbolRemapper, typeArguments), InlinerSymbolRenamer())
|
||||
}
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 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.common.lower.inline
|
||||
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.common.ir.createTemporaryVariableWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.lower.CoroutineIntrinsicLambdaOrigin
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
class FunctionInlining(val context: CommonBackendContext) : IrElementTransformerVoidWithContext() {
|
||||
|
||||
fun inline(irModule: IrModuleFragment) = irModule.accept(this, data = null)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
val callee = when (expression) {
|
||||
is IrCall -> expression.symbol.owner
|
||||
is IrConstructorCall -> expression.symbol.owner
|
||||
else -> return expression
|
||||
}
|
||||
if (!callee.needsInlining)
|
||||
return expression
|
||||
if (Symbols.isLateinitIsInitializedPropertyGetter(callee.symbol))
|
||||
return expression
|
||||
|
||||
val actualCallee = getFunctionDeclaration(callee.symbol)
|
||||
|
||||
actualCallee.transformChildrenVoid(this) // Process recursive inline.
|
||||
|
||||
val parent = allScopes.map { it.irElement }.filterIsInstance<IrDeclarationParent>().lastOrNull()
|
||||
val inliner = Inliner(expression, actualCallee, currentScope!!, parent, context)
|
||||
return inliner.inline()
|
||||
}
|
||||
|
||||
private fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction {
|
||||
val descriptor = symbol.descriptor.original
|
||||
val languageVersionSettings = context.configuration.languageVersionSettings
|
||||
// TODO: Remove these hacks when coroutine intrinsics are fixed.
|
||||
return when {
|
||||
descriptor.isBuiltInIntercepted(languageVersionSettings) ->
|
||||
error("Continuation.intercepted is not available with release coroutines")
|
||||
|
||||
descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
|
||||
context.ir.symbols.suspendCoroutineUninterceptedOrReturn.owner
|
||||
|
||||
symbol == context.ir.symbols.coroutineContextGetter ->
|
||||
context.ir.symbols.coroutineGetContext.owner
|
||||
|
||||
else -> (symbol.owner as? IrSimpleFunction)?.resolveFakeOverride() ?: symbol.owner
|
||||
}
|
||||
}
|
||||
|
||||
private val IrFunction.needsInlining get() = (this.isInline && !this.isExternal)
|
||||
|
||||
private inner class Inliner(val callSite: IrFunctionAccessExpression,
|
||||
val callee: IrFunction,
|
||||
val currentScope: ScopeWithIr,
|
||||
val parent: IrDeclarationParent?,
|
||||
val context: CommonBackendContext) {
|
||||
|
||||
val copyIrElement = run {
|
||||
val typeParameters =
|
||||
if (callee is IrConstructor)
|
||||
callee.parentAsClass.typeParameters
|
||||
else callee.typeParameters
|
||||
val typeArguments =
|
||||
(0 until callSite.typeArgumentsCount).map {
|
||||
typeParameters[it].symbol to callSite.getTypeArgument(it)
|
||||
}.associate { it }
|
||||
DeepCopyIrTreeWithSymbolsForInliner(context, typeArguments, parent)
|
||||
}
|
||||
|
||||
val substituteMap = mutableMapOf<IrValueParameter, IrExpression>()
|
||||
|
||||
fun inline() = inlineFunction(callSite, callee)
|
||||
|
||||
/**
|
||||
* TODO: JVM inliner crashed on attempt inline this function from transform.kt with:
|
||||
* j.l.IllegalStateException: Couldn't obtain compiled function body for
|
||||
* public inline fun <reified T : org.jetbrains.kotlin.ir.IrElement> kotlin.collections.MutableList<T>.transform...
|
||||
*/
|
||||
private inline fun <reified T : IrElement> MutableList<T>.transform(transformation: (T) -> IrElement) {
|
||||
forEachIndexed { i, item ->
|
||||
set(i, transformation(item) as T)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inlineFunction(callSite: IrFunctionAccessExpression, callee: IrFunction): IrReturnableBlock {
|
||||
val copiedCallee = copyIrElement.copy(callee) as IrFunction
|
||||
|
||||
val evaluationStatements = evaluateArguments(callSite, copiedCallee)
|
||||
val statements = (copiedCallee.body as IrBlockBody).statements
|
||||
|
||||
val irReturnableBlockSymbol = IrReturnableBlockSymbolImpl(copiedCallee.descriptor.original)
|
||||
val startOffset = callee.startOffset
|
||||
val endOffset = callee.endOffset
|
||||
val irBuilder = context.createIrBuilder(irReturnableBlockSymbol, startOffset, endOffset)
|
||||
|
||||
val transformer = ParameterSubstitutor()
|
||||
statements.transform { it.transform(transformer, data = null) }
|
||||
statements.addAll(0, evaluationStatements)
|
||||
|
||||
val isCoroutineIntrinsicCall = callSite.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(
|
||||
context.configuration.languageVersionSettings)
|
||||
|
||||
return IrReturnableBlockImpl(
|
||||
startOffset = startOffset,
|
||||
endOffset = endOffset,
|
||||
type = callSite.type,
|
||||
symbol = irReturnableBlockSymbol,
|
||||
origin = if (isCoroutineIntrinsicCall) CoroutineIntrinsicLambdaOrigin else null,
|
||||
statements = statements,
|
||||
inlineFunctionSymbol = callee.symbol
|
||||
).apply {
|
||||
transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
if (expression.returnTargetSymbol == copiedCallee.symbol)
|
||||
return irBuilder.irReturn(expression.value)
|
||||
return expression
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------//
|
||||
|
||||
private inner class ParameterSubstitutor : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
val newExpression = super.visitGetValue(expression) as IrGetValue
|
||||
val argument = substituteMap[newExpression.symbol.owner] ?: return newExpression
|
||||
|
||||
argument.transformChildrenVoid(this) // Default argument can contain subjects for substitution.
|
||||
return copyIrElement.copy(argument) as IrExpression
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------//
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
if (!isLambdaCall(expression))
|
||||
return super.visitCall(expression)
|
||||
|
||||
val dispatchReceiver = expression.dispatchReceiver as IrGetValue
|
||||
val functionArgument = substituteMap[dispatchReceiver.symbol.owner] ?: return super.visitCall(expression)
|
||||
if ((dispatchReceiver.symbol.owner as? IrValueParameter)?.isNoinline == true)
|
||||
return super.visitCall(expression)
|
||||
|
||||
if (functionArgument is IrFunctionReference) {
|
||||
val function = functionArgument.symbol.owner
|
||||
val functionParameters = function.explicitParameters
|
||||
val boundFunctionParameters = functionArgument.getArgumentsWithIr()
|
||||
val unboundFunctionParameters = functionParameters - boundFunctionParameters.map { it.first }
|
||||
val boundFunctionParametersMap = boundFunctionParameters.associate { it.first to it.second }
|
||||
|
||||
var unboundIndex = 0
|
||||
val unboundArgsSet = unboundFunctionParameters.toSet()
|
||||
val valueParameters = expression.getArgumentsWithIr().drop(1) // Skip dispatch receiver.
|
||||
|
||||
val immediateCall = with(expression) {
|
||||
if (function is IrConstructor) {
|
||||
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, function.symbol)
|
||||
} else {
|
||||
IrCallImpl(startOffset, endOffset, type, functionArgument.symbol)
|
||||
}
|
||||
}.apply {
|
||||
functionParameters.forEach {
|
||||
val argument =
|
||||
if (it !in unboundArgsSet)
|
||||
boundFunctionParametersMap[it]!!
|
||||
else
|
||||
valueParameters.getOrNull(unboundIndex++)?.second
|
||||
when (it) {
|
||||
function.dispatchReceiverParameter -> this.dispatchReceiver = argument
|
||||
function.extensionReceiverParameter -> this.extensionReceiver = argument
|
||||
else -> putValueArgument(it.index, argument)
|
||||
}
|
||||
}
|
||||
assert(unboundIndex >= valueParameters.size) { "Not all arguments of <invoke> are used" }
|
||||
for (index in 0 until functionArgument.typeArgumentsCount)
|
||||
putTypeArgument(index, functionArgument.getTypeArgument(index))
|
||||
}
|
||||
|
||||
return this@FunctionInlining.visitExpression(super.visitExpression(immediateCall))
|
||||
}
|
||||
if (functionArgument !is IrBlock)
|
||||
return super.visitCall(expression)
|
||||
|
||||
val functionDeclaration = functionArgument.statements[0] as IrFunction
|
||||
val newExpression = inlineFunction(expression, functionDeclaration) // Inline the lambda. Lambda parameters will be substituted with lambda arguments.
|
||||
return newExpression.transform(this, null) // Substitute lambda arguments with target function arguments.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------//
|
||||
|
||||
override fun visitElement(element: IrElement) = element.accept(this, null)
|
||||
}
|
||||
|
||||
private fun isLambdaCall(irCall: IrFunctionAccessExpression): Boolean {
|
||||
val callee = irCall.symbol.owner
|
||||
val dispatchReceiver = callee.dispatchReceiverParameter ?: return false
|
||||
assert(!dispatchReceiver.type.isKFunction())
|
||||
|
||||
return (dispatchReceiver.type.isFunction() || dispatchReceiver.type.isSuspendFunction())
|
||||
&& callee.name == OperatorNameConventions.INVOKE
|
||||
&& irCall.dispatchReceiver is IrGetValue
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun IrValueParameter.isInlineParameter() =
|
||||
!isNoinline && !type.isNullable() && type.isFunctionOrKFunction()
|
||||
|
||||
private inner class ParameterToArgument(val parameter: IrValueParameter,
|
||||
val argumentExpression: IrExpression) {
|
||||
|
||||
val isInlinableLambdaArgument: Boolean
|
||||
get() {
|
||||
if (!parameter.isInlineParameter()) return false
|
||||
if (argumentExpression is IrFunctionReference) return true
|
||||
|
||||
// Do pattern-matching on IR.
|
||||
if (argumentExpression !is IrBlock) return false
|
||||
if (argumentExpression.origin != IrStatementOrigin.LAMBDA &&
|
||||
argumentExpression.origin != IrStatementOrigin.ANONYMOUS_FUNCTION) return false
|
||||
val statements = argumentExpression.statements
|
||||
val irFunction = statements[0]
|
||||
val irCallableReference = statements[1]
|
||||
if (irFunction !is IrFunction) return false
|
||||
if (irCallableReference !is IrCallableReference) return false
|
||||
return true
|
||||
}
|
||||
|
||||
val isImmutableVariableLoad: Boolean
|
||||
get() = argumentExpression.let {
|
||||
it is IrGetValue && !it.symbol.owner.let { it is IrVariable && it.isVar }
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun buildParameterToArgument(callSite: IrFunctionAccessExpression, callee: IrFunction): List<ParameterToArgument> {
|
||||
|
||||
val parameterToArgument = mutableListOf<ParameterToArgument>()
|
||||
|
||||
if (callSite.dispatchReceiver != null && // Only if there are non null dispatch receivers both
|
||||
callee.dispatchReceiverParameter != null) // on call site and in function declaration.
|
||||
parameterToArgument += ParameterToArgument(
|
||||
parameter = callee.dispatchReceiverParameter!!,
|
||||
argumentExpression = callSite.dispatchReceiver!!
|
||||
)
|
||||
|
||||
val valueArguments =
|
||||
callSite.symbol.owner.valueParameters.map { callSite.getValueArgument(it.index) }.toMutableList()
|
||||
|
||||
if (callee.extensionReceiverParameter != null) {
|
||||
parameterToArgument += ParameterToArgument(
|
||||
parameter = callee.extensionReceiverParameter!!,
|
||||
argumentExpression = if (callSite.extensionReceiver != null) {
|
||||
callSite.extensionReceiver!!
|
||||
} else {
|
||||
// Special case: lambda with receiver is called as usual lambda:
|
||||
valueArguments.removeAt(0)!!
|
||||
}
|
||||
)
|
||||
} else if (callSite.extensionReceiver != null) {
|
||||
// Special case: usual lambda is called as lambda with receiver:
|
||||
valueArguments.add(0, callSite.extensionReceiver!!)
|
||||
}
|
||||
|
||||
val parametersWithDefaultToArgument = mutableListOf<ParameterToArgument>()
|
||||
for (parameter in callee.valueParameters) {
|
||||
val argument = valueArguments[parameter.index]
|
||||
when {
|
||||
argument != null -> {
|
||||
parameterToArgument += ParameterToArgument(
|
||||
parameter = parameter,
|
||||
argumentExpression = argument
|
||||
)
|
||||
}
|
||||
|
||||
// After ExpectDeclarationsRemoving pass default values from expect declarations
|
||||
// are represented correctly in IR.
|
||||
parameter.defaultValue != null -> { // There is no argument - try default value.
|
||||
parametersWithDefaultToArgument += ParameterToArgument(
|
||||
parameter = parameter,
|
||||
argumentExpression = parameter.defaultValue!!.expression
|
||||
)
|
||||
}
|
||||
|
||||
parameter.varargElementType != null -> {
|
||||
val emptyArray = IrVarargImpl(
|
||||
startOffset = callSite.startOffset,
|
||||
endOffset = callSite.endOffset,
|
||||
type = parameter.type,
|
||||
varargElementType = parameter.varargElementType!!
|
||||
)
|
||||
parameterToArgument += ParameterToArgument(
|
||||
parameter = parameter,
|
||||
argumentExpression = emptyArray
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val message = "Incomplete expression: call to ${callee.descriptor} " +
|
||||
"has no argument at index ${parameter.index}"
|
||||
throw Error(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameterToArgument + parametersWithDefaultToArgument // All arguments except default are evaluated at callsite,
|
||||
// but default arguments are evaluated inside callee.
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateArguments(callSite: IrFunctionAccessExpression, callee: IrFunction): List<IrStatement> {
|
||||
|
||||
val parameterToArgumentOld = buildParameterToArgument(callSite, callee)
|
||||
val evaluationStatements = mutableListOf<IrStatement>()
|
||||
val substitutor = ParameterSubstitutor()
|
||||
parameterToArgumentOld.forEach {
|
||||
/*
|
||||
* We need to create temporary variable for each argument except inlinable lambda arguments.
|
||||
* For simplicity and to produce simpler IR we don't create temporaries for every immutable variable,
|
||||
* not only for those referring to inlinable lambdas.
|
||||
*/
|
||||
if (it.isInlinableLambdaArgument) {
|
||||
substituteMap[it.parameter] = it.argumentExpression
|
||||
return@forEach
|
||||
}
|
||||
|
||||
if (it.isImmutableVariableLoad) {
|
||||
substituteMap[it.parameter] = it.argumentExpression.transform(substitutor, data = null) // Arguments may reference the previous ones - substitute them.
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val newVariable = currentScope.scope.createTemporaryVariableWithWrappedDescriptor( // Create new variable and init it with the parameter expression.
|
||||
irExpression = it.argumentExpression.transform(substitutor, data = null), // Arguments may reference the previous ones - substitute them.
|
||||
nameHint = callee.symbol.owner.name.toString(),
|
||||
isMutable = false)
|
||||
|
||||
evaluationStatements.add(newVariable)
|
||||
val getVal = IrGetValueImpl(
|
||||
startOffset = currentScope.irElement.startOffset,
|
||||
endOffset = currentScope.irElement.endOffset,
|
||||
type = newVariable.type,
|
||||
symbol = newVariable.symbol
|
||||
)
|
||||
substituteMap[it.parameter] = getVal
|
||||
}
|
||||
return evaluationStatements
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user