[JS IR] Implement new function references scheme
This commit is contained in:
+1
@@ -105,6 +105,7 @@ inline fun IrProperty.addGetter(builder: IrFunctionBuilder.() -> Unit = {}): IrS
|
|||||||
builder()
|
builder()
|
||||||
buildFun().also { getter ->
|
buildFun().also { getter ->
|
||||||
this@addGetter.getter = getter
|
this@addGetter.getter = getter
|
||||||
|
getter.correspondingPropertySymbol = this@addGetter.symbol
|
||||||
getter.parent = this@addGetter.parent
|
getter.parent = this@addGetter.parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,6 +283,12 @@ private val sharedVariablesLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Box captured mutable variables"
|
description = "Box captured mutable variables"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val callableReferenceLoweringX = makeBodyLoweringPhase(
|
||||||
|
::CallableReferenceLowering,
|
||||||
|
name = "CallableReferenceLoweringXX",
|
||||||
|
description = "Build a callable reference class and capture arguments"
|
||||||
|
)
|
||||||
|
|
||||||
private val returnableBlockLoweringPhase = makeBodyLoweringPhase(
|
private val returnableBlockLoweringPhase = makeBodyLoweringPhase(
|
||||||
::ReturnableBlockLowering,
|
::ReturnableBlockLowering,
|
||||||
name = "ReturnableBlockLowering",
|
name = "ReturnableBlockLowering",
|
||||||
@@ -372,15 +378,15 @@ private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Rewrite the private member usages"
|
description = "Rewrite the private member usages"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val callableReferenceLoweringPhase = makeBodyLoweringPhase(
|
private val interopCallableReferenceLoweringPhase = makeBodyLoweringPhase(
|
||||||
::CallableReferenceLowering,
|
::InteropCallableReferenceLowering,
|
||||||
name = "CallableReferenceLowering",
|
name = "CallableReferenceLowering",
|
||||||
description = "Handle callable references",
|
description = "Handle callable references",
|
||||||
prerequisite = setOf(
|
prerequisite = setOf(
|
||||||
suspendFunctionsLoweringPhase,
|
suspendFunctionsLoweringPhase,
|
||||||
localDeclarationsLoweringPhase,
|
localDeclarationsLoweringPhase,
|
||||||
localDelegatedPropertiesLoweringPhase,
|
localDelegatedPropertiesLoweringPhase,
|
||||||
privateMembersLoweringPhase
|
callableReferenceLoweringX
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -401,7 +407,7 @@ private val defaultParameterInjectorPhase = makeBodyLoweringPhase(
|
|||||||
{ context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) },
|
{ context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) },
|
||||||
name = "DefaultParameterInjector",
|
name = "DefaultParameterInjector",
|
||||||
description = "Replace callsite with default parameters with corresponding stub function",
|
description = "Replace callsite with default parameters with corresponding stub function",
|
||||||
prerequisite = setOf(callableReferenceLoweringPhase, innerClassesLoweringPhase)
|
prerequisite = setOf(interopCallableReferenceLoweringPhase, innerClassesLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase(
|
private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase(
|
||||||
@@ -420,7 +426,7 @@ private val varargLoweringPhase = makeBodyLoweringPhase(
|
|||||||
::VarargLowering,
|
::VarargLowering,
|
||||||
name = "VarargLowering",
|
name = "VarargLowering",
|
||||||
description = "Lower vararg arguments",
|
description = "Lower vararg arguments",
|
||||||
prerequisite = setOf(callableReferenceLoweringPhase)
|
prerequisite = setOf(interopCallableReferenceLoweringPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val propertiesLoweringPhase = makeDeclarationTransformerPhase(
|
private val propertiesLoweringPhase = makeDeclarationTransformerPhase(
|
||||||
@@ -596,7 +602,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
functionInliningPhase,
|
functionInliningPhase,
|
||||||
copyInlineFunctionBodyLoweringPhase,
|
copyInlineFunctionBodyLoweringPhase,
|
||||||
createScriptFunctionsPhase,
|
createScriptFunctionsPhase,
|
||||||
provisionalFunctionExpressionPhase,
|
callableReferenceLoweringX,
|
||||||
singleAbstractMethodPhase,
|
singleAbstractMethodPhase,
|
||||||
lateinitNullableFieldsPhase,
|
lateinitNullableFieldsPhase,
|
||||||
lateinitDeclarationLoweringPhase,
|
lateinitDeclarationLoweringPhase,
|
||||||
@@ -627,6 +633,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
enumEntryRemovalLoweringPhase,
|
enumEntryRemovalLoweringPhase,
|
||||||
suspendFunctionsLoweringPhase,
|
suspendFunctionsLoweringPhase,
|
||||||
suspendLambdasRemovalLoweringPhase,
|
suspendLambdasRemovalLoweringPhase,
|
||||||
|
interopCallableReferenceLoweringPhase,
|
||||||
returnableBlockLoweringPhase,
|
returnableBlockLoweringPhase,
|
||||||
forLoopsLoweringPhase,
|
forLoopsLoweringPhase,
|
||||||
primitiveCompanionLoweringPhase,
|
primitiveCompanionLoweringPhase,
|
||||||
@@ -634,7 +641,6 @@ val loweringList = listOf<Lowering>(
|
|||||||
foldConstantLoweringPhase,
|
foldConstantLoweringPhase,
|
||||||
privateMembersLoweringPhase,
|
privateMembersLoweringPhase,
|
||||||
privateMemberUsagesLoweringPhase,
|
privateMemberUsagesLoweringPhase,
|
||||||
callableReferenceLoweringPhase,
|
|
||||||
defaultArgumentStubGeneratorPhase,
|
defaultArgumentStubGeneratorPhase,
|
||||||
defaultArgumentPatchOverridesPhase,
|
defaultArgumentPatchOverridesPhase,
|
||||||
defaultParameterInjectorPhase,
|
defaultParameterInjectorPhase,
|
||||||
|
|||||||
@@ -191,6 +191,9 @@ object JsIrBuilder {
|
|||||||
fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol, reflectionTarget: IrFunctionSymbol? = symbol) =
|
fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol, reflectionTarget: IrFunctionSymbol? = symbol) =
|
||||||
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, 0, reflectionTarget, null)
|
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, 0, reflectionTarget, null)
|
||||||
|
|
||||||
|
fun buildFunctionExpression(type: IrType, function: IrSimpleFunction) =
|
||||||
|
IrFunctionExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, function, SYNTHESIZED_STATEMENT)
|
||||||
|
|
||||||
fun buildVar(
|
fun buildVar(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
parent: IrDeclarationParent?,
|
parent: IrDeclarationParent?,
|
||||||
|
|||||||
+304
-501
@@ -1,562 +1,365 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 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.
|
* 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.ir.backend.js.lower
|
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.moveBodyTo
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.asString
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedTypeParameterDescriptor
|
import org.jetbrains.kotlin.ir.descriptors.WrappedReceiverParameterDescriptor
|
||||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
|
||||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.file
|
|
||||||
import org.jetbrains.kotlin.ir.util.isInlined
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
|
||||||
data class CallableReferenceKey(
|
class CallableReferenceLowering(private val context: CommonBackendContext) : BodyLoweringPass {
|
||||||
val declaration: IrFunction,
|
|
||||||
val hasDispatchReceiver: Boolean,
|
|
||||||
val hasExtensionReceiver: Boolean,
|
|
||||||
val signature: String
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
|
|
||||||
class CallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
|
||||||
private val callableToFactoryFunction = context.callableReferencesCache
|
|
||||||
private val newDeclarations = mutableListOf<IrDeclaration>()
|
|
||||||
private lateinit var implicitDeclarationFile: IrFile //= context.implicitDeclarationFile
|
|
||||||
|
|
||||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
newDeclarations.clear()
|
val realContainer = container as? IrDeclarationParent ?: container.parent
|
||||||
implicitDeclarationFile = container.file // TODO
|
irBody.transformChildrenVoid(ReferenceTransformer(realContainer))
|
||||||
irBody.transformChildrenVoid(CallableReferenceLowerTransformer())
|
|
||||||
implicitDeclarationFile.declarations += newDeclarations
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
|
private val nothingType = context.irBuiltIns.nothingType
|
||||||
CallableReferenceKey(
|
private val stringType = context.irBuiltIns.stringType
|
||||||
declaration,
|
|
||||||
reference.dispatchReceiver != null,
|
private inner class ReferenceTransformer(private val container: IrDeclarationParent) : IrElementTransformerVoid() {
|
||||||
reference.extensionReceiver != null,
|
override fun visitFunctionExpression(expression: IrFunctionExpression): IrExpression {
|
||||||
reference.type.asString()
|
expression.transformChildrenVoid(this)
|
||||||
)
|
|
||||||
|
if (expression.function.isSuspend) {
|
||||||
|
val startOffset = expression.startOffset
|
||||||
|
val endOffset = expression.endOffset
|
||||||
|
val type = expression.type
|
||||||
|
val origin = expression.origin
|
||||||
|
val function = expression.function
|
||||||
|
|
||||||
|
return IrBlockImpl(
|
||||||
|
startOffset, endOffset, type, origin,
|
||||||
|
listOf(
|
||||||
|
function,
|
||||||
|
IrFunctionReferenceImpl(
|
||||||
|
startOffset, endOffset, type,
|
||||||
|
function.symbol,
|
||||||
|
typeArgumentsCount = 0,
|
||||||
|
reflectionTarget = null,
|
||||||
|
origin = origin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val function = expression.function
|
||||||
|
val (clazz, ctor) = buildLambdaReference(function, expression)
|
||||||
|
|
||||||
|
clazz.parent = container
|
||||||
|
|
||||||
|
return expression.run {
|
||||||
|
val vpCount = if (function.isSuspend) 1 else 0
|
||||||
|
val ctorCall =
|
||||||
|
IrConstructorCallImpl(startOffset, endOffset, type, ctor.symbol, 0 /*TODO: properly set type arguments*/, 0, vpCount, CALLABLE_REFERNCE_CREATE).apply {
|
||||||
|
if (function.isSuspend) {
|
||||||
|
putValueArgument(0, IrConstImpl.constNull(startOffset, endOffset, context.irBuiltIns.nothingNType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IrCompositeImpl(startOffset, endOffset, type, origin, listOf(clazz, ctorCall))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inner class CallableReferenceLowerTransformer : IrElementTransformerVoid() {
|
|
||||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
val declaration = expression.symbol.owner
|
|
||||||
if (declaration.origin == JsIrBackendContext.callableClosureOrigin) return expression
|
val (clazz, ctor) = buildFunctionReference(expression)
|
||||||
val key = makeCallableKey(declaration, expression)
|
|
||||||
val factory = callableToFactoryFunction.getOrPut(key) { lowerKFunctionReference(declaration, expression) }
|
clazz.parent = container
|
||||||
return redirectToFunction(expression, factory)
|
|
||||||
|
return expression.run {
|
||||||
|
val ctorCall =
|
||||||
|
IrConstructorCallImpl(startOffset, endOffset, type, ctor.symbol, 0 /*TODO: properly set type arguments*/, 0, 0, CALLABLE_REFERNCE_CREATE)
|
||||||
|
IrCompositeImpl(startOffset, endOffset, type, origin, listOf(clazz, ctorCall))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
private fun buildFunctionReference(expression: IrFunctionReference): Pair<IrClass, IrConstructor> {
|
||||||
expression.transformChildrenVoid(this)
|
return CallableReferenceBuilder(expression.symbol.owner, expression, false).build()
|
||||||
val declaration = expression.getter!!.owner
|
|
||||||
val key = makeCallableKey(declaration, expression)
|
|
||||||
val factory = callableToFactoryFunction.getOrPut(key) { lowerKPropertyReference(declaration, expression) }
|
|
||||||
return redirectToFunction(expression, factory)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
|
private fun buildLambdaReference(function: IrSimpleFunction, expression: IrFunctionExpression): Pair<IrClass, IrConstructor> {
|
||||||
expression.transformChildrenVoid(this)
|
return CallableReferenceBuilder(function, expression, true).build()
|
||||||
val key = makeCallableKey(expression.getter.owner, expression)
|
|
||||||
val factory = callableToFactoryFunction.getOrPut(key) { lowerLocalKPropertyReference(expression) }
|
|
||||||
return redirectToFunction(expression, factory)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun redirectToFunction(callable: IrCallableReference, newTarget: IrFunction) =
|
private inner class CallableReferenceBuilder(
|
||||||
IrCallImpl(
|
private val function: IrFunction,
|
||||||
callable.startOffset, callable.endOffset,
|
private val reference: IrExpression,
|
||||||
newTarget.symbol.owner.returnType,
|
private val isLambda: Boolean
|
||||||
newTarget.symbol,
|
) {
|
||||||
callable.origin
|
|
||||||
).apply {
|
private val isKReference: Boolean get() = superFunctionInterface.name.identifier[0] == 'K'
|
||||||
copyTypeArgumentsFrom(callable)
|
|
||||||
var index = 0
|
private val isSuspendLambda = isLambda && function.isSuspend
|
||||||
callable.dispatchReceiver?.let { putValueArgument(index++, it) }
|
private val superClass = if (isSuspendLambda) context.ir.symbols.coroutineImpl.owner.defaultType else context.irBuiltIns.anyType
|
||||||
callable.extensionReceiver?.let { putValueArgument(index++, it) }
|
|
||||||
for (i in 0 until callable.valueArgumentsCount) {
|
private var boundReceiverField: IrField? = null
|
||||||
val arg = callable.getValueArgument(i)
|
|
||||||
if (arg != null) {
|
private val superFunctionInterface = reference.type.classOrNull?.owner ?: error("Expected functional type")
|
||||||
putValueArgument(index++, arg)
|
|
||||||
|
private fun buildReferenceClass(): IrClass {
|
||||||
|
return buildClass {
|
||||||
|
setSourceRange(reference)
|
||||||
|
visibility = Visibilities.LOCAL
|
||||||
|
// A callable reference results in a synthetic class, while a lambda is not synthetic.
|
||||||
|
// We don't produce GENERATED_SAM_IMPLEMENTATION, which is always synthetic.
|
||||||
|
origin = if (isKReference) FUNCTION_REFERENCE_IMPL else LAMBDA_IMPL
|
||||||
|
name = SpecialNames.NO_NAME_PROVIDED
|
||||||
|
}.apply {
|
||||||
|
superTypes = listOf(superClass, reference.type)
|
||||||
|
// if (samSuperType == null)
|
||||||
|
// superTypes += functionSuperClass.typeWith(parameterTypes)
|
||||||
|
// if (irFunctionReference.isSuspend) superTypes += context.ir.symbols.suspendFunctionInterface.defaultType
|
||||||
|
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||||
|
createReceiverField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrClass.createReceiverField() {
|
||||||
|
if (isLambda) return
|
||||||
|
|
||||||
|
val funRef = reference as IrFunctionReference
|
||||||
|
val boundReceiver = funRef.run { dispatchReceiver ?: extensionReceiver }
|
||||||
|
|
||||||
|
if (boundReceiver != null) {
|
||||||
|
boundReceiverField = addField(BOUND_RECEIVER_NAME, boundReceiver.type).apply {
|
||||||
|
initializer = IrExpressionBodyImpl(boundReceiver.startOffset, boundReceiver.endOffset, boundReceiver)
|
||||||
|
parent = this@createReceiverField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFunctionFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KFunctionFactory")
|
private fun IrClass.createDispatchReceiver(): IrValueParameter {
|
||||||
private fun createPropertyFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KPropertyFactory")
|
val vpDescriptor = WrappedReceiverParameterDescriptor()
|
||||||
private fun createClosureInstanceName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KReferenceClosure")
|
val vpSymbol = IrValueParameterSymbolImpl(vpDescriptor)
|
||||||
|
val declaration = IrValueParameterImpl(startOffset, endOffset, origin, vpSymbol, THIS_NAME, -1, defaultType, null, false, false)
|
||||||
private fun createHelperFunctionName(declaration: IrDeclaration, suffix: String): String {
|
vpDescriptor.bind(declaration)
|
||||||
val nameBuilder = StringBuilder()
|
return declaration
|
||||||
if (declaration is IrConstructor) {
|
|
||||||
val klass = declaration.parent as IrClass
|
|
||||||
nameBuilder.append(klass.name.asString())
|
|
||||||
nameBuilder.append('_')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when (declaration) {
|
private fun createConstructor(clazz: IrClass): IrConstructor {
|
||||||
is IrFunction -> nameBuilder.append(declaration.name)
|
return clazz.addConstructor {
|
||||||
is IrProperty -> nameBuilder.append(declaration.name)
|
origin = GENERATED_MEMBER_IN_CALLABLE_REFERENCE
|
||||||
is IrVariable -> nameBuilder.append(declaration.name)
|
returnType = clazz.defaultType
|
||||||
else -> TODO("Unexpected declaration type")
|
isPrimary = true
|
||||||
|
}.apply {
|
||||||
|
|
||||||
|
val superConstructor = superClass.classOrNull!!.owner.declarations.single { it is IrConstructor && it.isPrimary } as IrConstructor
|
||||||
|
|
||||||
|
var continuation: IrValueParameter? = null
|
||||||
|
|
||||||
|
if (isSuspendLambda) {
|
||||||
|
val superContinuation = superConstructor.valueParameters.single()
|
||||||
|
continuation = addValueParameter {
|
||||||
|
name = superContinuation.name
|
||||||
|
type = superContinuation.type
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
|
||||||
|
+irDelegatingConstructorCall(superConstructor).apply {
|
||||||
|
continuation?.let {
|
||||||
|
putValueArgument(0, getValue(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+IrInstanceInitializerCallImpl(startOffset, endOffset, clazz.symbol, context.irBuiltIns.unitType)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nameBuilder.append('_')
|
private fun createInvokeMethod(clazz: IrClass): IrSimpleFunction {
|
||||||
nameBuilder.append(suffix)
|
val superMethods = superFunctionInterface.declarations.filterIsInstance<IrSimpleFunction>()
|
||||||
return nameBuilder.toString()
|
val superMethod = superMethods.single { it.name.asString() == "invoke" }
|
||||||
}
|
return clazz.addFunction {
|
||||||
|
setSourceRange(if (isLambda) function else reference)
|
||||||
|
name = superMethod.name
|
||||||
private fun getReferenceName(declaration: IrDeclaration) = when (declaration) {
|
returnType = function.returnType
|
||||||
is IrConstructor -> (declaration.parent as IrClass).name.identifier
|
isSuspend = superMethod.isSuspend
|
||||||
is IrProperty -> declaration.name.identifier
|
}.apply {
|
||||||
is IrSimpleFunction -> declaration.name.asString()
|
overriddenSymbols = listOf(superMethod.symbol)
|
||||||
is IrVariable -> declaration.name.asString().replace("\$delegate", "")
|
dispatchReceiverParameter = clazz.createDispatchReceiver().also { it.parent = this }
|
||||||
else -> TODO("Unexpected declaration type")
|
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod()
|
||||||
}
|
|
||||||
|
|
||||||
private fun lowerKFunctionReference(declaration: IrFunction, functionReference: IrFunctionReference): IrSimpleFunction {
|
|
||||||
// transform
|
|
||||||
// x = Foo::bar ->
|
|
||||||
// x = Foo_bar_KreferenceGet(c1: closure$C1, c2: closure$C2) : KFunctionN<Foo, T2, ..., TN, TReturn> {
|
|
||||||
// [ if ($cache$ == null) { ] // in case reference has no closure param cache it
|
|
||||||
// val x = fun Foo_bar_KreferenceClosure(p0: Foo, p1: T2, p2: T3): TReturn {
|
|
||||||
// return p0.bar(c1, c2, p1, p2)
|
|
||||||
// }
|
|
||||||
// x.callableName = "bar"
|
|
||||||
// [ $cache$ = x } ]
|
|
||||||
// return {$cache$|x}
|
|
||||||
// }
|
|
||||||
|
|
||||||
// KFunctionN<Foo, T2, ..., TN, TReturn>, arguments.size = N + 1
|
|
||||||
|
|
||||||
val factoryFunction = buildFactoryFunction(declaration, functionReference, createFunctionFactoryName(declaration))
|
|
||||||
val closureFunction = buildClosureFunction(declaration, factoryFunction, functionReference, functionReference.type.arity)
|
|
||||||
|
|
||||||
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
|
||||||
val irClosureReference = JsIrBuilder.buildFunctionReference(functionReference.type, closureFunction.symbol)
|
|
||||||
|
|
||||||
val irVar = JsIrBuilder.buildVar(irClosureReference.type, factoryFunction, initializer = irClosureReference)
|
|
||||||
|
|
||||||
// TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.)
|
|
||||||
val irSetName = setDynamicProperty(irVar.symbol, Namer.KCALLABLE_NAME) {
|
|
||||||
JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair(listOf(closureFunction, irVar, irSetName), irVar.symbol)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newDeclarations += additionalDeclarations + factoryFunction
|
private fun IrSimpleFunction.createLambdaInvokeMethod() {
|
||||||
|
annotations = function.annotations
|
||||||
|
val valueParameterMap = function.explicitParameters.withIndex().associate { (index, param) ->
|
||||||
|
param to param.copyTo(this, index = index)
|
||||||
|
}
|
||||||
|
valueParameters = valueParameterMap.values.toList()
|
||||||
|
body = function.moveBodyTo(this, valueParameterMap)
|
||||||
|
}
|
||||||
|
|
||||||
return factoryFunction
|
fun getValue(d: IrValueDeclaration): IrGetValue =
|
||||||
}
|
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, d.type, d.symbol, CALLABLE_REFERNCE_INVOKE)
|
||||||
|
|
||||||
private fun lowerKPropertyReference(getterDeclaration: IrSimpleFunction, propertyReference: IrPropertyReference): IrSimpleFunction {
|
|
||||||
// transform
|
|
||||||
// x = Foo::bar ->
|
|
||||||
// x = Foo_bar_KreferenceGet() : KPropertyN<Foo, PType> {
|
|
||||||
// if ($cache$ == null) { // very likely property reference is going to be cached
|
|
||||||
// val x = fun Foo_bar_KreferenceClosure_get(r: Foo): PType {
|
|
||||||
// return r.<get>()
|
|
||||||
// }
|
|
||||||
// x.get = x
|
|
||||||
// x.callableName = "bar"
|
|
||||||
// if (mutable) {
|
|
||||||
// x.set = fun Foo_bar_KreferenceClosure_set(r: Foo, v: PType>) {
|
|
||||||
// r.<set>(v)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// $cache$ = x
|
|
||||||
// }
|
|
||||||
// return $cache$
|
|
||||||
// }
|
|
||||||
|
|
||||||
val arity = propertyReference.type.arity
|
private fun IrSimpleFunction.buildInvoke(): IrFunctionAccessExpression {
|
||||||
val factoryName = createPropertyFactoryName(getterDeclaration.correspondingPropertySymbol!!.owner)
|
val callee = function
|
||||||
val factoryFunction = buildFactoryFunction(propertyReference.getter!!.owner, propertyReference, factoryName)
|
val irCall = reference.run {
|
||||||
|
if (callee is IrConstructor) {
|
||||||
val getterFunction = propertyReference.getter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity) }!!
|
IrConstructorCallImpl(startOffset, endOffset, callee.parentAsClass.defaultType, callee.symbol, callee.typeParameters.size, 0 /* TODO */, callee.valueParameters.size, CALLABLE_REFERNCE_INVOKE)
|
||||||
val setterFunction = propertyReference.setter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity + 1) }
|
} else {
|
||||||
|
IrCallImpl(startOffset, endOffset, callee.returnType, callee.symbol, callee.typeParameters.size, callee.valueParameters.size, CALLABLE_REFERNCE_INVOKE)
|
||||||
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
}
|
||||||
val statements = mutableListOf<IrStatement>(getterFunction)
|
|
||||||
|
|
||||||
val getterFunctionTypeSymbol = context.ir.symbols.functionN(getterFunction.valueParameters.size + 1)
|
|
||||||
|
|
||||||
val getterFunctionIrType = IrSimpleTypeImpl(getterFunctionTypeSymbol, false, emptyList(), emptyList())
|
|
||||||
val irGetReference = JsIrBuilder.buildFunctionReference(getterFunctionIrType, getterFunction.symbol)
|
|
||||||
val irVar = JsIrBuilder.buildVar(getterFunctionIrType, factoryFunction, initializer = irGetReference)
|
|
||||||
|
|
||||||
statements += irVar
|
|
||||||
|
|
||||||
statements += setDynamicProperty(irVar.symbol, Namer.KPROPERTY_GET) {
|
|
||||||
JsIrBuilder.buildGetValue(irVar.symbol)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setterFunction != null) {
|
val funRef = reference as IrFunctionReference
|
||||||
statements += setterFunction
|
|
||||||
val setterFunctionTypeSymbol = context.ir.symbols.functionN(setterFunction.valueParameters.size + 1)
|
val boundReceiver = funRef.run { dispatchReceiver ?: extensionReceiver } != null
|
||||||
val setterFunctionIrType = IrSimpleTypeImpl(setterFunctionTypeSymbol, false, emptyList(), emptyList())
|
val hasReceiver = callee.run { dispatchReceiverParameter ?: extensionReceiverParameter } != null
|
||||||
val irSetReference = JsIrBuilder.buildFunctionReference(setterFunctionIrType, setterFunction.symbol)
|
|
||||||
statements += setDynamicProperty(irVar.symbol, Namer.KPROPERTY_SET) { irSetReference }
|
irCall.dispatchReceiver = funRef.dispatchReceiver
|
||||||
|
irCall.extensionReceiver = funRef.extensionReceiver
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
val valueParameters = valueParameters
|
||||||
|
|
||||||
|
for (ti in 0 until funRef.typeArgumentsCount) {
|
||||||
|
irCall.putTypeArgument(ti, funRef.getTypeArgument(ti))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.)
|
if (hasReceiver) {
|
||||||
statements += setDynamicProperty(irVar.symbol, Namer.KCALLABLE_NAME) {
|
if (!boundReceiver) {
|
||||||
JsIrBuilder.buildString(
|
if (callee.dispatchReceiverParameter != null) irCall.dispatchReceiver = getValue(valueParameters[i++])
|
||||||
context.irBuiltIns.stringType,
|
if (callee.extensionReceiverParameter != null) irCall.extensionReceiver = getValue(valueParameters[i++])
|
||||||
getReferenceName(getterDeclaration.correspondingPropertySymbol!!.owner)
|
} else {
|
||||||
|
val boundReceiverField = boundReceiverField
|
||||||
|
if (boundReceiverField != null) {
|
||||||
|
val thisValue = getValue(dispatchReceiverParameter!!)
|
||||||
|
val value =
|
||||||
|
IrGetFieldImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
boundReceiverField.symbol,
|
||||||
|
boundReceiverField.type,
|
||||||
|
thisValue,
|
||||||
|
CALLABLE_REFERNCE_INVOKE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (funRef.dispatchReceiver != null) irCall.dispatchReceiver = value
|
||||||
|
if (funRef.extensionReceiver != null) irCall.extensionReceiver = value
|
||||||
|
}
|
||||||
|
if (callee.dispatchReceiverParameter != null && funRef.dispatchReceiver == null) {
|
||||||
|
irCall.dispatchReceiver = getValue(valueParameters[i++])
|
||||||
|
}
|
||||||
|
if (callee.extensionReceiverParameter != null && funRef.extensionReceiver == null) {
|
||||||
|
irCall.extensionReceiver = getValue(valueParameters[i++])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var j = 0
|
||||||
|
|
||||||
|
while (i < valueParameters.size) {
|
||||||
|
irCall.putValueArgument(j++, getValue(valueParameters[i++]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return irCall
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrSimpleFunction.createFunctionReferenceInvokeMethod() {
|
||||||
|
val parameterTypes = (reference.type as IrSimpleType).arguments.map { (it as IrTypeProjection).type }
|
||||||
|
val argumentTypes = parameterTypes.dropLast(1)
|
||||||
|
|
||||||
|
valueParameters = argumentTypes.mapIndexed { i, t ->
|
||||||
|
buildValueParameter {
|
||||||
|
name = Name.identifier("p$i")
|
||||||
|
type = t
|
||||||
|
index = i
|
||||||
|
}.also { it.parent = this }
|
||||||
|
}
|
||||||
|
|
||||||
|
body = IrBlockBodyImpl(reference.startOffset, reference.endOffset, listOf(reference.run {
|
||||||
|
IrReturnImpl(
|
||||||
|
startOffset,
|
||||||
|
endOffset, nothingType,
|
||||||
|
symbol,
|
||||||
|
buildInvoke()
|
||||||
)
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNameProperty(clazz: IrClass) {
|
||||||
|
if (!isKReference) return
|
||||||
|
|
||||||
|
// if (superFunctionInterface.name.identifier[0] != 'K') return
|
||||||
|
|
||||||
|
val superProperty = superFunctionInterface.declarations.filterIsInstance<IrProperty>().single()
|
||||||
|
val supperGetter = superProperty.getter ?: error("Expected getter for KFunction.name property")
|
||||||
|
|
||||||
|
val nameProperty = clazz.addProperty {
|
||||||
|
visibility = superProperty.visibility
|
||||||
|
name = superProperty.name
|
||||||
|
origin = GENERATED_MEMBER_IN_CALLABLE_REFERENCE
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair(statements, irVar.symbol)
|
val getter = nameProperty.addGetter() {
|
||||||
}
|
returnType = stringType
|
||||||
|
|
||||||
newDeclarations += additionalDeclarations + factoryFunction
|
|
||||||
|
|
||||||
return factoryFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun lowerLocalKPropertyReference(propertyReference: IrLocalDelegatedPropertyReference): IrSimpleFunction {
|
|
||||||
// transform
|
|
||||||
// ::bar ->
|
|
||||||
// Foo_bar_KreferenceGet() : KPropertyN<Foo, PType> {
|
|
||||||
// if ($cache$ == null) {
|
|
||||||
// val x = fun Foo_bar_KreferenceClosure_get(): PType {
|
|
||||||
// throw IllegalStateException()
|
|
||||||
// }
|
|
||||||
// x.get = x
|
|
||||||
// x.callableName = "bar"
|
|
||||||
// $cache$ = x
|
|
||||||
// }
|
|
||||||
// return $cache$
|
|
||||||
// }
|
|
||||||
|
|
||||||
val arity = propertyReference.type.arity
|
|
||||||
val declaration = propertyReference.delegate.owner
|
|
||||||
val factoryName = createPropertyFactoryName(declaration)
|
|
||||||
val factoryFunction = buildFactoryFunction(propertyReference.getter.owner, propertyReference, factoryName)
|
|
||||||
val closureFunction = buildClosureFunction(context.irBuiltIns.throwIseSymbol.owner, factoryFunction, propertyReference, arity)
|
|
||||||
|
|
||||||
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
|
||||||
val statements = mutableListOf<IrStatement>(closureFunction)
|
|
||||||
|
|
||||||
val getterFunctionTypeSymbol = context.ir.symbols.functionN(closureFunction.valueParameters.size + 1)
|
|
||||||
val getterFunctionIrType = IrSimpleTypeImpl(getterFunctionTypeSymbol, false, emptyList(), emptyList())
|
|
||||||
val irGetReference = JsIrBuilder.buildFunctionReference(getterFunctionIrType, closureFunction.symbol)
|
|
||||||
val irVar = JsIrBuilder.buildVar(getterFunctionIrType, factoryFunction, initializer = irGetReference)
|
|
||||||
val irVarSymbol = irVar.symbol
|
|
||||||
|
|
||||||
statements += irVar
|
|
||||||
|
|
||||||
statements += setDynamicProperty(irVarSymbol, Namer.KPROPERTY_GET) {
|
|
||||||
JsIrBuilder.buildGetValue(irVarSymbol)
|
|
||||||
}
|
}
|
||||||
|
getter.overriddenSymbols += supperGetter.symbol
|
||||||
|
getter.dispatchReceiverParameter = buildValueParameter {
|
||||||
|
name = THIS_NAME
|
||||||
|
type = clazz.defaultType
|
||||||
|
}.also { it.parent = getter }
|
||||||
|
|
||||||
statements += setDynamicProperty(irVarSymbol, Namer.KCALLABLE_NAME) {
|
getter.body = IrBlockBodyImpl(
|
||||||
JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration))
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
|
||||||
}
|
IrReturnImpl(
|
||||||
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingType, getter.symbol, IrConstImpl.string(
|
||||||
Pair(statements, irVarSymbol)
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, stringType, function.name.asString()
|
||||||
}
|
)
|
||||||
|
)
|
||||||
newDeclarations += additionalDeclarations + factoryFunction
|
)
|
||||||
|
|
||||||
return factoryFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateFactoryBodyWithGuard(
|
|
||||||
factoryFunction: IrSimpleFunction,
|
|
||||||
builder: () -> Pair<List<IrStatement>, IrValueSymbol>
|
|
||||||
): List<IrDeclaration> {
|
|
||||||
|
|
||||||
val (bodyStatements, varSymbol) = builder()
|
|
||||||
val statements = mutableListOf<IrStatement>()
|
|
||||||
val returnValue: IrExpression
|
|
||||||
val returnStatements: List<IrDeclaration>
|
|
||||||
if (factoryFunction.valueParameters.isEmpty()) {
|
|
||||||
// compose cache for 'direct' closure
|
|
||||||
// if ($cache$ === null) {
|
|
||||||
// $cache$ = <body>
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
val cacheName = "${factoryFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}"
|
|
||||||
val type = factoryFunction.returnType.makeNullable()
|
|
||||||
val irNull = { JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) }
|
|
||||||
val cacheVar = JsIrBuilder.buildVar(type, factoryFunction.parent, cacheName, true, initializer = irNull())
|
|
||||||
|
|
||||||
val irCacheValue = { JsIrBuilder.buildGetValue(cacheVar.symbol) }
|
|
||||||
val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply {
|
|
||||||
putValueArgument(0, irCacheValue())
|
|
||||||
putValueArgument(1, irNull())
|
|
||||||
}
|
|
||||||
val irSetCache =
|
|
||||||
JsIrBuilder.buildSetVariable(cacheVar.symbol, JsIrBuilder.buildGetValue(varSymbol), context.irBuiltIns.unitType)
|
|
||||||
val thenStatements = mutableListOf<IrStatement>().apply {
|
|
||||||
addAll(bodyStatements)
|
|
||||||
add(irSetCache)
|
|
||||||
}
|
|
||||||
val irThenBranch = JsIrBuilder.buildBlock(context.irBuiltIns.unitType, thenStatements)
|
|
||||||
val irIfNode = JsIrBuilder.buildIfElse(context.irBuiltIns.unitType, irIfCondition, irThenBranch)
|
|
||||||
statements += irIfNode
|
|
||||||
returnValue = irCacheValue()
|
|
||||||
returnStatements = listOf(cacheVar)
|
|
||||||
} else {
|
|
||||||
statements += bodyStatements
|
|
||||||
returnValue = JsIrBuilder.buildGetValue(varSymbol)
|
|
||||||
returnStatements = emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
statements += JsIrBuilder.buildReturn(factoryFunction.symbol, returnValue, context.irBuiltIns.nothingType)
|
|
||||||
|
|
||||||
factoryFunction.body = JsIrBuilder.buildBlockBody(statements)
|
|
||||||
return returnStatements
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun IrType.boxIfInlined() = if (isInlined()) {
|
|
||||||
context.irBuiltIns.anyNType
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateSignatureForClosure(
|
|
||||||
callable: IrFunction,
|
|
||||||
factory: IrFunction,
|
|
||||||
closure: IrSimpleFunction,
|
|
||||||
reference: IrCallableReference,
|
|
||||||
arity: Int
|
|
||||||
): List<IrValueParameter> {
|
|
||||||
val result = mutableListOf<IrValueParameter>()
|
|
||||||
|
|
||||||
/*
|
|
||||||
* class D {
|
|
||||||
* fun foo(a, b, c) {} <= callable signature
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* val d = D()
|
|
||||||
* val reference = d::foo <= binds dispatch receiver
|
|
||||||
*
|
|
||||||
* is translated into
|
|
||||||
*
|
|
||||||
* function foo_get(d) { <= factory signature(d)
|
|
||||||
* return function (a, b, c) { <= result signature (a, b, c)
|
|
||||||
* return d.foo(a, b, c)
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
var capturedParams = factory.valueParameters.size
|
|
||||||
val functionSignature = reference.type.arguments.dropLast(1).map { (it as IrTypeProjection).type }.toList()
|
|
||||||
|
|
||||||
callable.dispatchReceiverParameter?.let { dispatch ->
|
|
||||||
if (reference.dispatchReceiver == null) {
|
|
||||||
result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type.boxIfInlined()).also {
|
|
||||||
it.parent = closure
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// do not add dispatch receiver in result signature if it is bound
|
|
||||||
capturedParams--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callable.extensionReceiverParameter?.let { ext ->
|
|
||||||
if (reference.extensionReceiver == null) {
|
|
||||||
result.add(JsIrBuilder.buildValueParameter(ext.name, result.size, ext.type.boxIfInlined()).also { it.parent = closure })
|
|
||||||
} else {
|
|
||||||
// the same as for dispatch
|
|
||||||
capturedParams--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((index, param) in (result.size until arity).zip(callable.valueParameters.drop(capturedParams))) {
|
|
||||||
val type = if (index < functionSignature.size) functionSignature[index] else param.type
|
|
||||||
val paramName = param.name.run { if (!isSpecial) identifier else "p$index" }
|
|
||||||
result += JsIrBuilder.buildValueParameter(paramName, result.size, type.boxIfInlined()).also { it.parent = closure }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (result.size < arity) {
|
|
||||||
// That means there are still implicit vararg arguments
|
|
||||||
val lastParam = result.last()
|
|
||||||
for (index in result.size until arity) {
|
|
||||||
val paramName = "${lastParam.name}_$index"
|
|
||||||
result += JsIrBuilder.buildValueParameter(paramName, result.size, lastParam.type).also { it.parent = closure }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildFactoryFunction(declaration: IrFunction, reference: IrCallableReference, getterName: String): IrSimpleFunction {
|
|
||||||
// The `getter` function takes only closure parameters
|
|
||||||
val receivers = mutableListOf<IrValueParameter>()
|
|
||||||
|
|
||||||
reference.dispatchReceiver?.let {
|
|
||||||
// in case outer `this` for inner constructors
|
|
||||||
receivers += declaration.dispatchReceiverParameter ?: declaration.valueParameters[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
reference.extensionReceiver?.let {
|
|
||||||
receivers += declaration.extensionReceiverParameter!!
|
|
||||||
}
|
|
||||||
|
|
||||||
val boundValueParameters = receivers + declaration.valueParameters.filter { it.origin == BOUND_VALUE_PARAMETER }
|
|
||||||
|
|
||||||
val factoryDeclaration = JsIrBuilder.buildFunction(getterName, reference.type, implicitDeclarationFile, Visibilities.INTERNAL)
|
|
||||||
|
|
||||||
for ((i, p) in boundValueParameters.withIndex()) {
|
|
||||||
val descriptor = WrappedValueParameterDescriptor()
|
|
||||||
factoryDeclaration.valueParameters += IrValueParameterImpl(
|
|
||||||
p.startOffset,
|
|
||||||
p.endOffset,
|
|
||||||
p.origin,
|
|
||||||
IrValueParameterSymbolImpl(descriptor),
|
|
||||||
p.name,
|
|
||||||
i,
|
|
||||||
p.type,
|
|
||||||
p.varargElementType,
|
|
||||||
p.isCrossinline,
|
|
||||||
p.isNoinline
|
|
||||||
).also {
|
|
||||||
descriptor.bind(it)
|
|
||||||
it.parent = factoryDeclaration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val typeParameters =
|
|
||||||
if (declaration is IrConstructor) (declaration.parent as IrClass).typeParameters else declaration.typeParameters
|
|
||||||
|
|
||||||
for (t in typeParameters) {
|
|
||||||
val descriptor = WrappedTypeParameterDescriptor()
|
|
||||||
factoryDeclaration.typeParameters += IrTypeParameterImpl(
|
|
||||||
t.startOffset,
|
|
||||||
t.endOffset,
|
|
||||||
t.origin,
|
|
||||||
IrTypeParameterSymbolImpl(descriptor),
|
|
||||||
t.name,
|
|
||||||
t.index,
|
|
||||||
t.isReified,
|
|
||||||
t.variance
|
|
||||||
).also {
|
|
||||||
descriptor.bind(it)
|
|
||||||
it.parent = factoryDeclaration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return factoryDeclaration
|
|
||||||
}
|
|
||||||
|
|
||||||
private val IrType.arguments get() = (this as? IrSimpleType)?.arguments ?: emptyList()
|
|
||||||
private val IrType.arity get() = arguments.size - 1
|
|
||||||
|
|
||||||
private fun buildClosureFunction(
|
|
||||||
declaration: IrFunction,
|
|
||||||
factoryFunction: IrSimpleFunction,
|
|
||||||
reference: IrCallableReference,
|
|
||||||
arity: Int
|
|
||||||
): IrFunction {
|
|
||||||
val closureName = createClosureInstanceName(declaration)
|
|
||||||
val returnType = declaration.returnType.boxIfInlined()
|
|
||||||
val closureFunction =
|
|
||||||
JsIrBuilder.buildFunction(
|
|
||||||
closureName,
|
|
||||||
returnType,
|
|
||||||
factoryFunction,
|
|
||||||
Visibilities.LOCAL,
|
|
||||||
origin = JsIrBackendContext.callableClosureOrigin
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// the params which are passed to closure
|
|
||||||
val boundParamSymbols = factoryFunction.valueParameters.map { it.symbol }
|
|
||||||
val unboundParamDeclarations = generateSignatureForClosure(declaration, factoryFunction, closureFunction, reference, arity)
|
|
||||||
val unboundParamSymbols = unboundParamDeclarations.map { it.symbol }
|
|
||||||
|
|
||||||
closureFunction.valueParameters += unboundParamDeclarations
|
|
||||||
|
|
||||||
val callTarget = declaration
|
|
||||||
|
|
||||||
val target = callTarget.symbol
|
|
||||||
|
|
||||||
val irCall =
|
|
||||||
if (target is IrConstructorSymbol) IrConstructorCallImpl.fromSymbolOwner(returnType, target) else JsIrBuilder.buildCall(
|
|
||||||
callTarget.symbol,
|
|
||||||
type = returnType
|
|
||||||
)
|
|
||||||
|
|
||||||
var cp = 0
|
|
||||||
var gp = 0
|
|
||||||
|
|
||||||
if (callTarget.dispatchReceiverParameter != null) {
|
|
||||||
val dispatchReceiverDeclaration =
|
|
||||||
if (reference.dispatchReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
|
|
||||||
irCall.dispatchReceiver = JsIrBuilder.buildGetValue(dispatchReceiverDeclaration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callTarget.extensionReceiverParameter != null) {
|
fun build(): Pair<IrClass, IrConstructor> {
|
||||||
val extensionReceiverDeclaration =
|
val clazz = buildReferenceClass()
|
||||||
if (reference.extensionReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
|
val ctor = createConstructor(clazz)
|
||||||
irCall.extensionReceiver = JsIrBuilder.buildGetValue(extensionReceiverDeclaration)
|
val invoke = createInvokeMethod(clazz)
|
||||||
}
|
createNameProperty(clazz)
|
||||||
|
// TODO: create name property for KFunction*
|
||||||
|
|
||||||
var j = 0
|
return Pair(clazz, ctor)
|
||||||
|
|
||||||
for (i in gp until boundParamSymbols.size) {
|
|
||||||
irCall.putValueArgument(j++, JsIrBuilder.buildGetValue(boundParamSymbols[i]))
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i in cp until unboundParamSymbols.size) {
|
|
||||||
val closureParam = unboundParamSymbols[i].owner
|
|
||||||
val value = JsIrBuilder.buildGetValue(unboundParamSymbols[i])
|
|
||||||
val parameter = callTarget.valueParameters[j]
|
|
||||||
val argument =
|
|
||||||
if (parameter.varargElementType == closureParam.type) {
|
|
||||||
// fun foo(x: X, vararg y: Y): Z
|
|
||||||
// val r: (X, Y) -> Z = ::foo
|
|
||||||
val tailValues = unboundParamSymbols.drop(i)
|
|
||||||
IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter.type, parameter.varargElementType!!, tailValues.map {
|
|
||||||
JsIrBuilder.buildGetValue(it)
|
|
||||||
})
|
|
||||||
} else value
|
|
||||||
irCall.putValueArgument(j++, argument)
|
|
||||||
if (j == callTarget.valueParameters.size) break
|
|
||||||
}
|
|
||||||
|
|
||||||
val irClosureReturn = JsIrBuilder.buildReturn(closureFunction.symbol, irCall, context.irBuiltIns.nothingType)
|
|
||||||
|
|
||||||
closureFunction.body = JsIrBuilder.buildBlockBody(listOf(irClosureReturn))
|
|
||||||
|
|
||||||
return closureFunction
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun setDynamicProperty(r: IrValueSymbol, property: String, value: () -> IrExpression): IrStatement {
|
|
||||||
return IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, IrDynamicOperator.EQ).apply {
|
|
||||||
receiver = IrDynamicMemberExpressionImpl(startOffset, endOffset, context.dynamicType, property, JsIrBuilder.buildGetValue(r))
|
|
||||||
arguments += value()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
companion object {
|
||||||
|
object LAMBDA_IMPL : IrDeclarationOriginImpl("LAMBDA_IMPL")
|
||||||
|
object FUNCTION_REFERENCE_IMPL : IrDeclarationOriginImpl("FUNCTION_REFERENCE_IMPL")
|
||||||
|
object GENERATED_MEMBER_IN_CALLABLE_REFERENCE : IrDeclarationOriginImpl("GENERATED_MEMBER_IN_CALLABLE_REFERENCE")
|
||||||
|
|
||||||
|
object CALLABLE_REFERNCE_CREATE : IrStatementOriginImpl("CALLABLE_REFERENCE_CREATE")
|
||||||
|
object CALLABLE_REFERNCE_INVOKE : IrStatementOriginImpl("CALLABLE_REFERENCE_INVOKE")
|
||||||
|
|
||||||
|
val THIS_NAME = Name.special("<this>")
|
||||||
|
val BOUND_RECEIVER_NAME = Name.identifier("\$boundThis")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+744
@@ -0,0 +1,744 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 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.ir.backend.js.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.copyToWithoutSuperTypes
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.asString
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.WrappedTypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||||
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||||
|
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||||
|
import org.jetbrains.kotlin.ir.util.file
|
||||||
|
import org.jetbrains.kotlin.ir.util.isInlined
|
||||||
|
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
data class CallableReferenceKey(
|
||||||
|
val declaration: IrFunction,
|
||||||
|
val hasDispatchReceiver: Boolean,
|
||||||
|
val hasExtensionReceiver: Boolean,
|
||||||
|
val signature: String
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
|
||||||
|
class InteropCallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||||
|
private val callableToFactoryFunction = context.callableReferencesCache
|
||||||
|
private val newDeclarations = mutableListOf<IrDeclaration>()
|
||||||
|
private lateinit var implicitDeclarationFile: IrFile //= context.implicitDeclarationFile
|
||||||
|
|
||||||
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
|
newDeclarations.clear()
|
||||||
|
implicitDeclarationFile = container.file // TODO
|
||||||
|
irBody.transformChildrenVoid(CallableReferenceLowerTransformer())
|
||||||
|
implicitDeclarationFile.declarations += newDeclarations
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
|
||||||
|
CallableReferenceKey(
|
||||||
|
declaration,
|
||||||
|
reference.dispatchReceiver != null,
|
||||||
|
reference.extensionReceiver != null,
|
||||||
|
reference.type.asString()
|
||||||
|
)
|
||||||
|
|
||||||
|
inner class CallableReferenceLowerTransformer : IrElementTransformerVoid() {
|
||||||
|
// override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||||
|
// expression.transformChildrenVoid(this)
|
||||||
|
// val declaration = expression.symbol.owner
|
||||||
|
// if (declaration.origin == JsIrBackendContext.callableClosureOrigin) return expression
|
||||||
|
// val key = makeCallableKey(declaration, expression)
|
||||||
|
// val factory = callableToFactoryFunction.getOrPut(key) { lowerKFunctionReference(declaration, expression) }
|
||||||
|
// return redirectToFunction(expression, factory)
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
val declaration = expression.getter!!.owner
|
||||||
|
val key = makeCallableKey(declaration, expression)
|
||||||
|
val factory = callableToFactoryFunction.getOrPut(key) { lowerKPropertyReference(declaration, expression) }
|
||||||
|
return redirectToFunction(expression, factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
val key = makeCallableKey(expression.getter.owner, expression)
|
||||||
|
val factory = callableToFactoryFunction.getOrPut(key) { lowerLocalKPropertyReference(expression) }
|
||||||
|
return redirectToFunction(expression, factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
if (expression.origin === CallableReferenceLowering.Companion.CALLABLE_REFERNCE_CREATE) {
|
||||||
|
return transformToJavaScriptFunction(expression)
|
||||||
|
}
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformToJavaScriptFunction(expression: IrConstructorCall): IrExpression {
|
||||||
|
// TODO: perform inlining
|
||||||
|
val factory = buildFactoryFunction2(expression)
|
||||||
|
newDeclarations += factory
|
||||||
|
val newCall = expression.run {
|
||||||
|
IrCallImpl(startOffset, endOffset, type, factory.symbol, typeArgumentsCount, valueArgumentsCount, origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCall.dispatchReceiver = expression.dispatchReceiver
|
||||||
|
newCall.extensionReceiver = expression.extensionReceiver
|
||||||
|
|
||||||
|
for (i in 0 until expression.typeArgumentsCount) {
|
||||||
|
newCall.putTypeArgument(i, expression.getTypeArgument(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until expression.valueArgumentsCount) {
|
||||||
|
newCall.putValueArgument(i, expression.getValueArgument(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCall
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildLambdaBody(instance: IrVariable, lambdaDeclaration: IrSimpleFunction, invokeFun: IrSimpleFunction): IrBlockBody {
|
||||||
|
val invokeExpression = IrCallImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
invokeFun.returnType,
|
||||||
|
invokeFun.symbol,
|
||||||
|
0,
|
||||||
|
invokeFun.valueParameters.size,
|
||||||
|
EXPLICIT_INVOKE,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getValue(d: IrValueDeclaration): IrExpression = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, d.symbol)
|
||||||
|
|
||||||
|
invokeExpression.dispatchReceiver = getValue(instance)
|
||||||
|
for ((i, vp) in lambdaDeclaration.valueParameters.withIndex()) {
|
||||||
|
invokeExpression.putValueArgument(i, getValue(vp))
|
||||||
|
}
|
||||||
|
|
||||||
|
return IrBlockBodyImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
listOf(
|
||||||
|
IrReturnImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
context.irBuiltIns.nothingType,
|
||||||
|
lambdaDeclaration.symbol,
|
||||||
|
invokeExpression
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFactoryBody(factoryFunction: IrSimpleFunction, expression: IrConstructorCall): IrBlockBody {
|
||||||
|
val constructor = expression.symbol.owner
|
||||||
|
val lambdaClass = constructor.parentAsClass
|
||||||
|
val invokeFun = lambdaClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "invoke" }
|
||||||
|
val lambdaName = Name.identifier("${lambdaClass.name.asString()}\$lambda")
|
||||||
|
|
||||||
|
val lambdaDeclaration = buildFun {
|
||||||
|
startOffset = invokeFun.startOffset
|
||||||
|
endOffset = invokeFun.endOffset
|
||||||
|
returnType = invokeFun.returnType
|
||||||
|
name = lambdaName
|
||||||
|
isSuspend = invokeFun.isSuspend
|
||||||
|
}
|
||||||
|
|
||||||
|
lambdaDeclaration.parent = factoryFunction
|
||||||
|
|
||||||
|
lambdaDeclaration.valueParameters = invokeFun.valueParameters.map { it.copyTo(lambdaDeclaration) }
|
||||||
|
|
||||||
|
val statements = ArrayList<IrStatement>(4)
|
||||||
|
|
||||||
|
val instanceVal = JsIrBuilder.buildVar(expression.type, factoryFunction, "i").apply {
|
||||||
|
initializer = expression.run {
|
||||||
|
val newCtorCall = IrConstructorCallImpl(startOffset, endOffset, type, symbol, typeArgumentsCount, constructorTypeArgumentsCount, valueArgumentsCount, origin)
|
||||||
|
// TODO: forward type arguments
|
||||||
|
require(expression.dispatchReceiver == null)
|
||||||
|
require(expression.extensionReceiver == null)
|
||||||
|
|
||||||
|
for ((i, vp) in factoryFunction.valueParameters.withIndex()) {
|
||||||
|
newCtorCall.putValueArgument(i, IrGetValueImpl(startOffset, endOffset, vp.type, vp.symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
newCtorCall
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.add(instanceVal)
|
||||||
|
|
||||||
|
lambdaDeclaration.body = buildLambdaBody(instanceVal, lambdaDeclaration, invokeFun)
|
||||||
|
|
||||||
|
val functionExpression =
|
||||||
|
expression.run { IrFunctionExpressionImpl(startOffset, endOffset, type, lambdaDeclaration, expression.origin!!) }
|
||||||
|
|
||||||
|
val nameGetter = lambdaClass.declarations.filterIsInstance<IrSimpleFunction>().singleOrNull { it.correspondingPropertySymbol != null }
|
||||||
|
|
||||||
|
if (nameGetter != null || lambdaDeclaration.isSuspend) {
|
||||||
|
val tmpVar = JsIrBuilder.buildVar(functionExpression.type, factoryFunction, "l", initializer = functionExpression)
|
||||||
|
statements.add(tmpVar)
|
||||||
|
|
||||||
|
if (nameGetter != null) {
|
||||||
|
statements.add(setDynamicProperty(tmpVar.symbol, Namer.KCALLABLE_NAME) {
|
||||||
|
IrCallImpl(
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
nameGetter.returnType,
|
||||||
|
nameGetter.symbol,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CallableReferenceLowering.Companion.CALLABLE_REFERNCE_INVOKE
|
||||||
|
).apply {
|
||||||
|
dispatchReceiver = JsIrBuilder.buildGetValue(instanceVal.symbol)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lambdaDeclaration.isSuspend) {
|
||||||
|
statements.add(setDynamicProperty(tmpVar.symbol, Namer.KCALLABLE_ARITY) {
|
||||||
|
IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, lambdaDeclaration.valueParameters.size)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, JsIrBuilder.buildGetValue(tmpVar.symbol), context.irBuiltIns.nothingType))
|
||||||
|
} else {
|
||||||
|
statements.add(JsIrBuilder.buildReturn(factoryFunction.symbol, functionExpression, context.irBuiltIns.nothingType))
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression.run {
|
||||||
|
IrBlockBodyImpl(startOffset, endOffset, statements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFactoryFunction2(expression: IrConstructorCall): IrSimpleFunction {
|
||||||
|
|
||||||
|
val constructor = expression.symbol.owner
|
||||||
|
val lambdaClass = constructor.parentAsClass
|
||||||
|
|
||||||
|
val factoryName = Name.identifier("${lambdaClass.name.asString()}\$factory")
|
||||||
|
|
||||||
|
val factoryDeclaration = buildFun {
|
||||||
|
startOffset = expression.startOffset
|
||||||
|
endOffset = expression.endOffset
|
||||||
|
returnType = expression.type
|
||||||
|
name = factoryName
|
||||||
|
}
|
||||||
|
|
||||||
|
factoryDeclaration.parent = implicitDeclarationFile
|
||||||
|
|
||||||
|
factoryDeclaration.valueParameters = constructor.valueParameters.map { it.copyTo(factoryDeclaration) }
|
||||||
|
factoryDeclaration.typeParameters = constructor.typeParameters.map {
|
||||||
|
it.copyToWithoutSuperTypes(factoryDeclaration).also { tp ->
|
||||||
|
// TODO: make sure it is done well
|
||||||
|
tp.superTypes += it.superTypes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factoryDeclaration.body = buildFactoryBody(factoryDeclaration, expression)
|
||||||
|
|
||||||
|
return factoryDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun redirectToFunction(callable: IrCallableReference, newTarget: IrFunction) =
|
||||||
|
IrCallImpl(
|
||||||
|
callable.startOffset, callable.endOffset,
|
||||||
|
newTarget.symbol.owner.returnType,
|
||||||
|
newTarget.symbol,
|
||||||
|
callable.origin
|
||||||
|
).apply {
|
||||||
|
copyTypeArgumentsFrom(callable)
|
||||||
|
var index = 0
|
||||||
|
callable.dispatchReceiver?.let { putValueArgument(index++, it) }
|
||||||
|
callable.extensionReceiver?.let { putValueArgument(index++, it) }
|
||||||
|
for (i in 0 until callable.valueArgumentsCount) {
|
||||||
|
val arg = callable.getValueArgument(i)
|
||||||
|
if (arg != null) {
|
||||||
|
putValueArgument(index++, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFunctionFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KFunctionFactory")
|
||||||
|
private fun createPropertyFactoryName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KPropertyFactory")
|
||||||
|
private fun createClosureInstanceName(declaration: IrDeclaration) = createHelperFunctionName(declaration, "KReferenceClosure")
|
||||||
|
|
||||||
|
private fun createHelperFunctionName(declaration: IrDeclaration, suffix: String): String {
|
||||||
|
val nameBuilder = StringBuilder()
|
||||||
|
if (declaration is IrConstructor) {
|
||||||
|
val klass = declaration.parent as IrClass
|
||||||
|
nameBuilder.append(klass.name.asString())
|
||||||
|
nameBuilder.append('_')
|
||||||
|
}
|
||||||
|
|
||||||
|
when (declaration) {
|
||||||
|
is IrFunction -> nameBuilder.append(declaration.name)
|
||||||
|
is IrProperty -> nameBuilder.append(declaration.name)
|
||||||
|
is IrVariable -> nameBuilder.append(declaration.name)
|
||||||
|
else -> TODO("Unexpected declaration type")
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBuilder.append('_')
|
||||||
|
nameBuilder.append(suffix)
|
||||||
|
return nameBuilder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getReferenceName(declaration: IrDeclaration) = when (declaration) {
|
||||||
|
is IrConstructor -> (declaration.parent as IrClass).name.identifier
|
||||||
|
is IrProperty -> declaration.name.identifier
|
||||||
|
is IrSimpleFunction -> declaration.name.asString()
|
||||||
|
is IrVariable -> declaration.name.asString().replace("\$delegate", "")
|
||||||
|
else -> TODO("Unexpected declaration type")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lowerKFunctionReference(declaration: IrFunction, functionReference: IrFunctionReference): IrSimpleFunction {
|
||||||
|
// transform
|
||||||
|
// x = Foo::bar ->
|
||||||
|
// x = Foo_bar_KreferenceGet(c1: closure$C1, c2: closure$C2) : KFunctionN<Foo, T2, ..., TN, TReturn> {
|
||||||
|
// [ if ($cache$ == null) { ] // in case reference has no closure param cache it
|
||||||
|
// val x = fun Foo_bar_KreferenceClosure(p0: Foo, p1: T2, p2: T3): TReturn {
|
||||||
|
// return p0.bar(c1, c2, p1, p2)
|
||||||
|
// }
|
||||||
|
// x.callableName = "bar"
|
||||||
|
// [ $cache$ = x } ]
|
||||||
|
// return {$cache$|x}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// KFunctionN<Foo, T2, ..., TN, TReturn>, arguments.size = N + 1
|
||||||
|
|
||||||
|
val factoryFunction = buildFactoryFunction(declaration, functionReference, createFunctionFactoryName(declaration))
|
||||||
|
val closureFunction = buildClosureFunction(declaration, factoryFunction, functionReference, functionReference.type.arity)
|
||||||
|
|
||||||
|
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
||||||
|
val irClosureReference = JsIrBuilder.buildFunctionReference(functionReference.type, closureFunction.symbol)
|
||||||
|
|
||||||
|
val irVar = JsIrBuilder.buildVar(irClosureReference.type, factoryFunction, initializer = irClosureReference)
|
||||||
|
|
||||||
|
// TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.)
|
||||||
|
val irSetName = setDynamicProperty(irVar.symbol, Namer.KCALLABLE_NAME) {
|
||||||
|
JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration))
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(listOf(closureFunction, irVar, irSetName), irVar.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDeclarations += additionalDeclarations + factoryFunction
|
||||||
|
|
||||||
|
return factoryFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lowerKPropertyReference(getterDeclaration: IrSimpleFunction, propertyReference: IrPropertyReference): IrSimpleFunction {
|
||||||
|
// transform
|
||||||
|
// x = Foo::bar ->
|
||||||
|
// x = Foo_bar_KreferenceGet() : KPropertyN<Foo, PType> {
|
||||||
|
// if ($cache$ == null) { // very likely property reference is going to be cached
|
||||||
|
// val x = fun Foo_bar_KreferenceClosure_get(r: Foo): PType {
|
||||||
|
// return r.<get>()
|
||||||
|
// }
|
||||||
|
// x.get = x
|
||||||
|
// x.callableName = "bar"
|
||||||
|
// if (mutable) {
|
||||||
|
// x.set = fun Foo_bar_KreferenceClosure_set(r: Foo, v: PType>) {
|
||||||
|
// r.<set>(v)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// $cache$ = x
|
||||||
|
// }
|
||||||
|
// return $cache$
|
||||||
|
// }
|
||||||
|
|
||||||
|
val arity = propertyReference.type.arity
|
||||||
|
val factoryName = createPropertyFactoryName(getterDeclaration.correspondingPropertySymbol!!.owner)
|
||||||
|
val factoryFunction = buildFactoryFunction(propertyReference.getter!!.owner, propertyReference, factoryName)
|
||||||
|
|
||||||
|
val getterFunction = propertyReference.getter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity) }!!
|
||||||
|
val setterFunction = propertyReference.setter?.let { buildClosureFunction(it.owner, factoryFunction, propertyReference, arity + 1) }
|
||||||
|
|
||||||
|
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
||||||
|
val statements = mutableListOf<IrStatement>(getterFunction)
|
||||||
|
|
||||||
|
val getterFunctionTypeSymbol = context.ir.symbols.functionN(getterFunction.valueParameters.size + 1)
|
||||||
|
|
||||||
|
val getterFunctionIrType = IrSimpleTypeImpl(getterFunctionTypeSymbol, false, emptyList(), emptyList())
|
||||||
|
val irGetReference = JsIrBuilder.buildFunctionReference(getterFunctionIrType, getterFunction.symbol)
|
||||||
|
val irVar = JsIrBuilder.buildVar(getterFunctionIrType, factoryFunction, initializer = irGetReference)
|
||||||
|
|
||||||
|
statements += irVar
|
||||||
|
|
||||||
|
statements += setDynamicProperty(irVar.symbol, Namer.KPROPERTY_GET) {
|
||||||
|
JsIrBuilder.buildGetValue(irVar.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setterFunction != null) {
|
||||||
|
statements += setterFunction
|
||||||
|
val setterFunctionTypeSymbol = context.ir.symbols.functionN(setterFunction.valueParameters.size + 1)
|
||||||
|
val setterFunctionIrType = IrSimpleTypeImpl(setterFunctionTypeSymbol, false, emptyList(), emptyList())
|
||||||
|
val irSetReference = JsIrBuilder.buildFunctionReference(setterFunctionIrType, setterFunction.symbol)
|
||||||
|
statements += setDynamicProperty(irVar.symbol, Namer.KPROPERTY_SET) { irSetReference }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fill other fields of callable reference (returnType, parameters, isFinal, etc.)
|
||||||
|
statements += setDynamicProperty(irVar.symbol, Namer.KCALLABLE_NAME) {
|
||||||
|
JsIrBuilder.buildString(
|
||||||
|
context.irBuiltIns.stringType,
|
||||||
|
getReferenceName(getterDeclaration.correspondingPropertySymbol!!.owner)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(statements, irVar.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDeclarations += additionalDeclarations + factoryFunction
|
||||||
|
|
||||||
|
return factoryFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lowerLocalKPropertyReference(propertyReference: IrLocalDelegatedPropertyReference): IrSimpleFunction {
|
||||||
|
// transform
|
||||||
|
// ::bar ->
|
||||||
|
// Foo_bar_KreferenceGet() : KPropertyN<Foo, PType> {
|
||||||
|
// if ($cache$ == null) {
|
||||||
|
// val x = fun Foo_bar_KreferenceClosure_get(): PType {
|
||||||
|
// throw IllegalStateException()
|
||||||
|
// }
|
||||||
|
// x.get = x
|
||||||
|
// x.callableName = "bar"
|
||||||
|
// $cache$ = x
|
||||||
|
// }
|
||||||
|
// return $cache$
|
||||||
|
// }
|
||||||
|
|
||||||
|
val arity = propertyReference.type.arity
|
||||||
|
val declaration = propertyReference.delegate.owner
|
||||||
|
val factoryName = createPropertyFactoryName(declaration)
|
||||||
|
val factoryFunction = buildFactoryFunction(propertyReference.getter.owner, propertyReference, factoryName)
|
||||||
|
val closureFunction = buildClosureFunction(context.irBuiltIns.throwIseSymbol.owner, factoryFunction, propertyReference, arity)
|
||||||
|
|
||||||
|
val additionalDeclarations = generateFactoryBodyWithGuard(factoryFunction) {
|
||||||
|
val statements = mutableListOf<IrStatement>(closureFunction)
|
||||||
|
|
||||||
|
val getterFunctionTypeSymbol = context.ir.symbols.functionN(closureFunction.valueParameters.size + 1)
|
||||||
|
val getterFunctionIrType = IrSimpleTypeImpl(getterFunctionTypeSymbol, false, emptyList(), emptyList())
|
||||||
|
val irGetReference = JsIrBuilder.buildFunctionReference(getterFunctionIrType, closureFunction.symbol)
|
||||||
|
val irVar = JsIrBuilder.buildVar(getterFunctionIrType, factoryFunction, initializer = irGetReference)
|
||||||
|
val irVarSymbol = irVar.symbol
|
||||||
|
|
||||||
|
statements += irVar
|
||||||
|
|
||||||
|
statements += setDynamicProperty(irVarSymbol, Namer.KPROPERTY_GET) {
|
||||||
|
JsIrBuilder.buildGetValue(irVarSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
statements += setDynamicProperty(irVarSymbol, Namer.KCALLABLE_NAME) {
|
||||||
|
JsIrBuilder.buildString(context.irBuiltIns.stringType, getReferenceName(declaration))
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(statements, irVarSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDeclarations += additionalDeclarations + factoryFunction
|
||||||
|
|
||||||
|
return factoryFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateFactoryBodyWithGuard(
|
||||||
|
factoryFunction: IrSimpleFunction,
|
||||||
|
builder: () -> Pair<List<IrStatement>, IrValueSymbol>
|
||||||
|
): List<IrDeclaration> {
|
||||||
|
|
||||||
|
val (bodyStatements, varSymbol) = builder()
|
||||||
|
val statements = mutableListOf<IrStatement>()
|
||||||
|
val returnValue: IrExpression
|
||||||
|
val returnStatements: List<IrDeclaration>
|
||||||
|
if (factoryFunction.valueParameters.isEmpty()) {
|
||||||
|
// compose cache for 'direct' closure
|
||||||
|
// if ($cache$ === null) {
|
||||||
|
// $cache$ = <body>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
val cacheName = "${factoryFunction.name}_${Namer.KCALLABLE_CACHE_SUFFIX}"
|
||||||
|
val type = factoryFunction.returnType.makeNullable()
|
||||||
|
val irNull = { JsIrBuilder.buildNull(context.irBuiltIns.nothingNType) }
|
||||||
|
val cacheVar = JsIrBuilder.buildVar(type, factoryFunction.parent, cacheName, true, initializer = irNull())
|
||||||
|
|
||||||
|
val irCacheValue = { JsIrBuilder.buildGetValue(cacheVar.symbol) }
|
||||||
|
val irIfCondition = JsIrBuilder.buildCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||||
|
putValueArgument(0, irCacheValue())
|
||||||
|
putValueArgument(1, irNull())
|
||||||
|
}
|
||||||
|
val irSetCache =
|
||||||
|
JsIrBuilder.buildSetVariable(cacheVar.symbol, JsIrBuilder.buildGetValue(varSymbol), context.irBuiltIns.unitType)
|
||||||
|
val thenStatements = mutableListOf<IrStatement>().apply {
|
||||||
|
addAll(bodyStatements)
|
||||||
|
add(irSetCache)
|
||||||
|
}
|
||||||
|
val irThenBranch = JsIrBuilder.buildBlock(context.irBuiltIns.unitType, thenStatements)
|
||||||
|
val irIfNode = JsIrBuilder.buildIfElse(context.irBuiltIns.unitType, irIfCondition, irThenBranch)
|
||||||
|
statements += irIfNode
|
||||||
|
returnValue = irCacheValue()
|
||||||
|
returnStatements = listOf(cacheVar)
|
||||||
|
} else {
|
||||||
|
statements += bodyStatements
|
||||||
|
returnValue = JsIrBuilder.buildGetValue(varSymbol)
|
||||||
|
returnStatements = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
statements += JsIrBuilder.buildReturn(factoryFunction.symbol, returnValue, context.irBuiltIns.nothingType)
|
||||||
|
|
||||||
|
factoryFunction.body = JsIrBuilder.buildBlockBody(statements)
|
||||||
|
return returnStatements
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrType.boxIfInlined() = if (isInlined()) {
|
||||||
|
context.irBuiltIns.anyNType
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateSignatureForClosure(
|
||||||
|
callable: IrFunction,
|
||||||
|
factory: IrFunction,
|
||||||
|
closure: IrSimpleFunction,
|
||||||
|
reference: IrCallableReference,
|
||||||
|
arity: Int
|
||||||
|
): List<IrValueParameter> {
|
||||||
|
val result = mutableListOf<IrValueParameter>()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* class D {
|
||||||
|
* fun foo(a, b, c) {} <= callable signature
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* val d = D()
|
||||||
|
* val reference = d::foo <= binds dispatch receiver
|
||||||
|
*
|
||||||
|
* is translated into
|
||||||
|
*
|
||||||
|
* function foo_get(d) { <= factory signature(d)
|
||||||
|
* return function (a, b, c) { <= result signature (a, b, c)
|
||||||
|
* return d.foo(a, b, c)
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
var capturedParams = factory.valueParameters.size
|
||||||
|
val functionSignature = reference.type.arguments.dropLast(1).map { (it as IrTypeProjection).type }.toList()
|
||||||
|
|
||||||
|
callable.dispatchReceiverParameter?.let { dispatch ->
|
||||||
|
if (reference.dispatchReceiver == null) {
|
||||||
|
result.add(JsIrBuilder.buildValueParameter(dispatch.name, result.size, dispatch.type.boxIfInlined()).also {
|
||||||
|
it.parent = closure
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// do not add dispatch receiver in result signature if it is bound
|
||||||
|
capturedParams--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callable.extensionReceiverParameter?.let { ext ->
|
||||||
|
if (reference.extensionReceiver == null) {
|
||||||
|
result.add(JsIrBuilder.buildValueParameter(ext.name, result.size, ext.type.boxIfInlined()).also { it.parent = closure })
|
||||||
|
} else {
|
||||||
|
// the same as for dispatch
|
||||||
|
capturedParams--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((index, param) in (result.size until arity).zip(callable.valueParameters.drop(capturedParams))) {
|
||||||
|
val type = if (index < functionSignature.size) functionSignature[index] else param.type
|
||||||
|
val paramName = param.name.run { if (!isSpecial) identifier else "p$index" }
|
||||||
|
result += JsIrBuilder.buildValueParameter(paramName, result.size, type.boxIfInlined()).also { it.parent = closure }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (result.size < arity) {
|
||||||
|
// That means there are still implicit vararg arguments
|
||||||
|
val lastParam = result.last()
|
||||||
|
for (index in result.size until arity) {
|
||||||
|
val paramName = "${lastParam.name}_$index"
|
||||||
|
result += JsIrBuilder.buildValueParameter(paramName, result.size, lastParam.type).also { it.parent = closure }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFactoryFunction(declaration: IrFunction, reference: IrCallableReference, getterName: String): IrSimpleFunction {
|
||||||
|
// The `getter` function takes only closure parameters
|
||||||
|
val receivers = mutableListOf<IrValueParameter>()
|
||||||
|
|
||||||
|
reference.dispatchReceiver?.let {
|
||||||
|
// in case outer `this` for inner constructors
|
||||||
|
receivers += declaration.dispatchReceiverParameter ?: declaration.valueParameters[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.extensionReceiver?.let {
|
||||||
|
receivers += declaration.extensionReceiverParameter!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val boundValueParameters = receivers + declaration.valueParameters.filter { it.origin == BOUND_VALUE_PARAMETER }
|
||||||
|
|
||||||
|
val factoryDeclaration = JsIrBuilder.buildFunction(getterName, reference.type, implicitDeclarationFile, Visibilities.INTERNAL)
|
||||||
|
|
||||||
|
for ((i, p) in boundValueParameters.withIndex()) {
|
||||||
|
val descriptor = WrappedValueParameterDescriptor()
|
||||||
|
factoryDeclaration.valueParameters += IrValueParameterImpl(
|
||||||
|
p.startOffset,
|
||||||
|
p.endOffset,
|
||||||
|
p.origin,
|
||||||
|
IrValueParameterSymbolImpl(descriptor),
|
||||||
|
p.name,
|
||||||
|
i,
|
||||||
|
p.type,
|
||||||
|
p.varargElementType,
|
||||||
|
p.isCrossinline,
|
||||||
|
p.isNoinline
|
||||||
|
).also {
|
||||||
|
descriptor.bind(it)
|
||||||
|
it.parent = factoryDeclaration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val typeParameters =
|
||||||
|
if (declaration is IrConstructor) (declaration.parent as IrClass).typeParameters else declaration.typeParameters
|
||||||
|
|
||||||
|
for (t in typeParameters) {
|
||||||
|
val descriptor = WrappedTypeParameterDescriptor()
|
||||||
|
factoryDeclaration.typeParameters += IrTypeParameterImpl(
|
||||||
|
t.startOffset,
|
||||||
|
t.endOffset,
|
||||||
|
t.origin,
|
||||||
|
IrTypeParameterSymbolImpl(descriptor),
|
||||||
|
t.name,
|
||||||
|
t.index,
|
||||||
|
t.isReified,
|
||||||
|
t.variance
|
||||||
|
).also {
|
||||||
|
descriptor.bind(it)
|
||||||
|
it.parent = factoryDeclaration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return factoryDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IrType.arguments get() = (this as? IrSimpleType)?.arguments ?: emptyList()
|
||||||
|
private val IrType.arity get() = arguments.size - 1
|
||||||
|
|
||||||
|
private fun buildClosureFunction(
|
||||||
|
declaration: IrFunction,
|
||||||
|
factoryFunction: IrSimpleFunction,
|
||||||
|
reference: IrCallableReference,
|
||||||
|
arity: Int
|
||||||
|
): IrFunction {
|
||||||
|
val closureName = createClosureInstanceName(declaration)
|
||||||
|
val returnType = declaration.returnType.boxIfInlined()
|
||||||
|
val closureFunction =
|
||||||
|
JsIrBuilder.buildFunction(
|
||||||
|
closureName,
|
||||||
|
returnType,
|
||||||
|
factoryFunction,
|
||||||
|
Visibilities.LOCAL,
|
||||||
|
origin = JsIrBackendContext.callableClosureOrigin
|
||||||
|
)
|
||||||
|
|
||||||
|
// the params which are passed to closure
|
||||||
|
val boundParamSymbols = factoryFunction.valueParameters.map { it.symbol }
|
||||||
|
val unboundParamDeclarations = generateSignatureForClosure(declaration, factoryFunction, closureFunction, reference, arity)
|
||||||
|
val unboundParamSymbols = unboundParamDeclarations.map { it.symbol }
|
||||||
|
|
||||||
|
closureFunction.valueParameters += unboundParamDeclarations
|
||||||
|
|
||||||
|
val callTarget = declaration
|
||||||
|
|
||||||
|
val target = callTarget.symbol
|
||||||
|
|
||||||
|
val irCall =
|
||||||
|
if (target is IrConstructorSymbol) IrConstructorCallImpl.fromSymbolOwner(returnType, target) else JsIrBuilder.buildCall(
|
||||||
|
callTarget.symbol,
|
||||||
|
type = returnType
|
||||||
|
)
|
||||||
|
|
||||||
|
var cp = 0
|
||||||
|
var gp = 0
|
||||||
|
|
||||||
|
if (callTarget.dispatchReceiverParameter != null) {
|
||||||
|
val dispatchReceiverDeclaration =
|
||||||
|
if (reference.dispatchReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
|
||||||
|
irCall.dispatchReceiver = JsIrBuilder.buildGetValue(dispatchReceiverDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callTarget.extensionReceiverParameter != null) {
|
||||||
|
val extensionReceiverDeclaration =
|
||||||
|
if (reference.extensionReceiver != null) boundParamSymbols[gp++] else unboundParamSymbols[cp++]
|
||||||
|
irCall.extensionReceiver = JsIrBuilder.buildGetValue(extensionReceiverDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
var j = 0
|
||||||
|
|
||||||
|
for (i in gp until boundParamSymbols.size) {
|
||||||
|
irCall.putValueArgument(j++, JsIrBuilder.buildGetValue(boundParamSymbols[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in cp until unboundParamSymbols.size) {
|
||||||
|
val closureParam = unboundParamSymbols[i].owner
|
||||||
|
val value = JsIrBuilder.buildGetValue(unboundParamSymbols[i])
|
||||||
|
val parameter = callTarget.valueParameters[j]
|
||||||
|
val argument =
|
||||||
|
if (parameter.varargElementType == closureParam.type) {
|
||||||
|
// fun foo(x: X, vararg y: Y): Z
|
||||||
|
// val r: (X, Y) -> Z = ::foo
|
||||||
|
val tailValues = unboundParamSymbols.drop(i)
|
||||||
|
IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter.type, parameter.varargElementType!!, tailValues.map {
|
||||||
|
JsIrBuilder.buildGetValue(it)
|
||||||
|
})
|
||||||
|
} else value
|
||||||
|
irCall.putValueArgument(j++, argument)
|
||||||
|
if (j == callTarget.valueParameters.size) break
|
||||||
|
}
|
||||||
|
|
||||||
|
val irClosureReturn = JsIrBuilder.buildReturn(closureFunction.symbol, irCall, context.irBuiltIns.nothingType)
|
||||||
|
|
||||||
|
closureFunction.body = JsIrBuilder.buildBlockBody(listOf(irClosureReturn))
|
||||||
|
|
||||||
|
return closureFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun setDynamicProperty(r: IrValueSymbol, property: String, value: () -> IrExpression): IrStatement {
|
||||||
|
return IrDynamicOperatorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.unitType, IrDynamicOperator.EQ).apply {
|
||||||
|
receiver = IrDynamicMemberExpressionImpl(startOffset, endOffset, context.dynamicType, property, JsIrBuilder.buildGetValue(r))
|
||||||
|
arguments += value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
object EXPLICIT_INVOKE : IrStatementOriginImpl("EXPLICIT_INVOKE")
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-19
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
@@ -35,7 +34,7 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
irFile.declarations.forEach {
|
irFile.declarations.forEach {
|
||||||
if (it is IrClass) {
|
if (it is IrClass) {
|
||||||
generateTestCalls(it) { suiteForPackage(irFile.fqName).body }
|
generateTestCalls(it) { suiteForPackage(irFile.fqName).function }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO top-level functions
|
// TODO top-level functions
|
||||||
@@ -45,42 +44,37 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
private val packageSuites = mutableMapOf<FqName, FunctionWithBody>()
|
private val packageSuites = mutableMapOf<FqName, FunctionWithBody>()
|
||||||
|
|
||||||
private fun suiteForPackage(fqName: FqName) = packageSuites.getOrPut(fqName) {
|
private fun suiteForPackage(fqName: FqName) = packageSuites.getOrPut(fqName) {
|
||||||
context.suiteFun!!.createInvocation(fqName.asString(), context.testContainer.body as IrBlockBody)
|
context.suiteFun!!.createInvocation(fqName.asString(), context.testContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class FunctionWithBody(val function: IrSimpleFunction, val body: IrBlockBody)
|
private data class FunctionWithBody(val function: IrSimpleFunction, val body: IrBlockBody)
|
||||||
|
|
||||||
private fun IrSimpleFunctionSymbol.createInvocation(
|
private fun IrSimpleFunctionSymbol.createInvocation(
|
||||||
name: String,
|
name: String,
|
||||||
parentBody: IrBlockBody,
|
parentFunction: IrSimpleFunction,
|
||||||
ignored: Boolean = false
|
ignored: Boolean = false
|
||||||
): FunctionWithBody {
|
): FunctionWithBody {
|
||||||
val body = JsIrBuilder.buildBlockBody(emptyList())
|
val body = JsIrBuilder.buildBlockBody(emptyList())
|
||||||
|
|
||||||
val function = JsIrBuilder.buildFunction(
|
val function = JsIrBuilder.buildFunction("$name test fun", context.irBuiltIns.unitType, parentFunction)
|
||||||
"$name test fun",
|
function.body = body
|
||||||
context.irBuiltIns.unitType,
|
|
||||||
context.implicitDeclarationFile
|
|
||||||
).also {
|
|
||||||
it.body = body
|
|
||||||
context.implicitDeclarationFile.declarations += it
|
|
||||||
}
|
|
||||||
|
|
||||||
|
val parentBody = parentFunction.body as IrBlockBody
|
||||||
parentBody.statements += JsIrBuilder.buildCall(this).apply {
|
parentBody.statements += JsIrBuilder.buildCall(this).apply {
|
||||||
putValueArgument(0, JsIrBuilder.buildString(context.irBuiltIns.stringType, name))
|
putValueArgument(0, JsIrBuilder.buildString(context.irBuiltIns.stringType, name))
|
||||||
putValueArgument(1, JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, ignored))
|
putValueArgument(1, JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, ignored))
|
||||||
|
|
||||||
val refType = IrSimpleTypeImpl(context.ir.symbols.functionN(0), false, emptyList(), emptyList())
|
val refType = IrSimpleTypeImpl(context.ir.symbols.functionN(0), false, emptyList(), emptyList())
|
||||||
putValueArgument(2, JsIrBuilder.buildFunctionReference(refType, function.symbol))
|
putValueArgument(2, JsIrBuilder.buildFunctionExpression(refType, function))
|
||||||
}
|
}
|
||||||
|
|
||||||
return FunctionWithBody(function, body)
|
return FunctionWithBody(function, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateTestCalls(irClass: IrClass, parentBody: () -> IrBlockBody) {
|
private fun generateTestCalls(irClass: IrClass, parentFunction: () -> IrSimpleFunction) {
|
||||||
if (irClass.modality == Modality.ABSTRACT || irClass.isEffectivelyExternal() || irClass.isExpect) return
|
if (irClass.modality == Modality.ABSTRACT || irClass.isEffectivelyExternal() || irClass.isExpect) return
|
||||||
|
|
||||||
val suiteFunBody by lazy { context.suiteFun!!.createInvocation(irClass.name.asString(), parentBody(), irClass.isIgnored) }
|
val suiteFunBody by lazy { context.suiteFun!!.createInvocation(irClass.name.asString(), parentFunction(), irClass.isIgnored) }
|
||||||
|
|
||||||
val beforeFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isBefore }
|
val beforeFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isBefore }
|
||||||
val afterFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isAfter }
|
val afterFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isAfter }
|
||||||
@@ -88,10 +82,10 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
irClass.declarations.forEach {
|
irClass.declarations.forEach {
|
||||||
when {
|
when {
|
||||||
it is IrClass ->
|
it is IrClass ->
|
||||||
generateTestCalls(it) { suiteFunBody.body }
|
generateTestCalls(it) { suiteFunBody.function }
|
||||||
|
|
||||||
it is IrSimpleFunction && it.isTest ->
|
it is IrSimpleFunction && it.isTest ->
|
||||||
generateCodeForTestMethod(it, beforeFunctions, afterFunctions, irClass, suiteFunBody.body)
|
generateCodeForTestMethod(it, beforeFunctions, afterFunctions, irClass, suiteFunBody.function)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,9 +95,9 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
|||||||
beforeFuns: List<IrSimpleFunction>,
|
beforeFuns: List<IrSimpleFunction>,
|
||||||
afterFuns: List<IrSimpleFunction>,
|
afterFuns: List<IrSimpleFunction>,
|
||||||
irClass: IrClass,
|
irClass: IrClass,
|
||||||
parentBody: IrBlockBody
|
parentFunction: IrSimpleFunction
|
||||||
) {
|
) {
|
||||||
val (fn, body) = context.testFun!!.createInvocation(testFun.name.asString(), parentBody, testFun.isIgnored)
|
val (fn, body) = context.testFun!!.createInvocation(testFun.name.asString(), parentFunction, testFun.isIgnored)
|
||||||
|
|
||||||
val classVal = JsIrBuilder.buildVar(irClass.defaultType, fn, initializer = irClass.instance())
|
val classVal = JsIrBuilder.buildVar(irClass.defaultType, fn, initializer = irClass.instance())
|
||||||
|
|
||||||
|
|||||||
+1
@@ -229,6 +229,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
|||||||
private fun generateTypeCheckNonNull(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
|
private fun generateTypeCheckNonNull(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
|
||||||
assert(!toType.isMarkedNullable())
|
assert(!toType.isMarkedNullable())
|
||||||
return when {
|
return when {
|
||||||
|
toType is IrDynamicType -> argument
|
||||||
toType.isAny() -> generateIsObjectCheck(argument)
|
toType.isAny() -> generateIsObjectCheck(argument)
|
||||||
toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse))
|
toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse))
|
||||||
toType.isSuspendFunctionTypeOrSubtype() -> generateSuspendFunctionCheck(argument, toType)
|
toType.isSuspendFunctionTypeOrSubtype() -> generateSuspendFunctionCheck(argument, toType)
|
||||||
|
|||||||
+2
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl
|
||||||
@@ -34,6 +35,7 @@ class ReflectionCallsTransformer(private val context: JsIrBackendContext) : Call
|
|||||||
addWithPredicate(
|
addWithPredicate(
|
||||||
Name.special(Namer.KCALLABLE_GET_NAME),
|
Name.special(Namer.KCALLABLE_GET_NAME),
|
||||||
{ call ->
|
{ call ->
|
||||||
|
call.origin != CallableReferenceLowering.Companion.CALLABLE_REFERNCE_INVOKE &&
|
||||||
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false
|
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false
|
||||||
},
|
},
|
||||||
{ call ->
|
{ call ->
|
||||||
|
|||||||
+5
@@ -31,6 +31,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
return irFunction.accept(IrFunctionToJsTransformer(), context).apply { name = null }
|
return irFunction.accept(IrFunctionToJsTransformer(), context).apply { name = null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionExpression(expression: IrFunctionExpression, context: JsGenerationContext): JsExpression {
|
||||||
|
val irFunction = expression.function
|
||||||
|
return irFunction.accept(IrFunctionToJsTransformer(), context).apply { name = null }
|
||||||
|
}
|
||||||
|
|
||||||
override fun <T> visitConst(expression: IrConst<T>, context: JsGenerationContext): JsExpression {
|
override fun <T> visitConst(expression: IrConst<T>, context: JsGenerationContext): JsExpression {
|
||||||
val kind = expression.kind
|
val kind = expression.kind
|
||||||
return when (kind) {
|
return when (kind) {
|
||||||
|
|||||||
+6
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
|
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.InteropCallableReferenceLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
@@ -67,6 +68,8 @@ private fun isNativeInvoke(call: IrCall): Boolean {
|
|||||||
|
|
||||||
if (simpleFunction.isSuspend) return false
|
if (simpleFunction.isSuspend) return false
|
||||||
|
|
||||||
|
if (call.origin === InteropCallableReferenceLowering.Companion.EXPLICIT_INVOKE) return false
|
||||||
|
|
||||||
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
|
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +209,9 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
|
|||||||
val argument = expression.getValueArgument(index)
|
val argument = expression.getValueArgument(index)
|
||||||
val result = argument?.accept(transformer, context)
|
val result = argument?.accept(transformer, context)
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
assert(expression is IrFunctionAccessExpression && expression.symbol.owner.isExternalOrInheritedFromExternal())
|
assert(expression is IrFunctionAccessExpression && expression.symbol.owner.isExternalOrInheritedFromExternal()) {
|
||||||
|
1
|
||||||
|
}
|
||||||
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
|
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
|
||||||
} else
|
} else
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ object Namer {
|
|||||||
val KPROPERTY_GET = "get"
|
val KPROPERTY_GET = "get"
|
||||||
val KPROPERTY_SET = "set"
|
val KPROPERTY_SET = "set"
|
||||||
val KCALLABLE_CACHE_SUFFIX = "\$cache"
|
val KCALLABLE_CACHE_SUFFIX = "\$cache"
|
||||||
|
const val KCALLABLE_ARITY = "\$arity"
|
||||||
}
|
}
|
||||||
@@ -75,11 +75,11 @@ public fun isInterface(ctor: dynamic, IType: dynamic): Boolean {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean {
|
internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean {
|
||||||
val ctor = obj.constructor?.unsafeCast<Ctor>() ?: return false
|
if (jsTypeOf(obj) == "function") {
|
||||||
|
return obj.`$arity`.unsafeCast<Int>() === arity
|
||||||
|
}
|
||||||
|
|
||||||
val metadata = ctor.`$metadata$`?.unsafeCast<Metadata>() ?: return false
|
return false
|
||||||
|
|
||||||
return metadata.suspendArity === arity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isObject(obj: dynamic): Boolean {
|
fun isObject(obj: dynamic): Boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user