[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()
|
||||
buildFun().also { getter ->
|
||||
this@addGetter.getter = getter
|
||||
getter.correspondingPropertySymbol = this@addGetter.symbol
|
||||
getter.parent = this@addGetter.parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +283,12 @@ private val sharedVariablesLoweringPhase = makeBodyLoweringPhase(
|
||||
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(
|
||||
::ReturnableBlockLowering,
|
||||
name = "ReturnableBlockLowering",
|
||||
@@ -372,15 +378,15 @@ private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase(
|
||||
description = "Rewrite the private member usages"
|
||||
)
|
||||
|
||||
private val callableReferenceLoweringPhase = makeBodyLoweringPhase(
|
||||
::CallableReferenceLowering,
|
||||
private val interopCallableReferenceLoweringPhase = makeBodyLoweringPhase(
|
||||
::InteropCallableReferenceLowering,
|
||||
name = "CallableReferenceLowering",
|
||||
description = "Handle callable references",
|
||||
prerequisite = setOf(
|
||||
suspendFunctionsLoweringPhase,
|
||||
localDeclarationsLoweringPhase,
|
||||
localDelegatedPropertiesLoweringPhase,
|
||||
privateMembersLoweringPhase
|
||||
callableReferenceLoweringX
|
||||
)
|
||||
)
|
||||
|
||||
@@ -401,7 +407,7 @@ private val defaultParameterInjectorPhase = makeBodyLoweringPhase(
|
||||
{ context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) },
|
||||
name = "DefaultParameterInjector",
|
||||
description = "Replace callsite with default parameters with corresponding stub function",
|
||||
prerequisite = setOf(callableReferenceLoweringPhase, innerClassesLoweringPhase)
|
||||
prerequisite = setOf(interopCallableReferenceLoweringPhase, innerClassesLoweringPhase)
|
||||
)
|
||||
|
||||
private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase(
|
||||
@@ -420,7 +426,7 @@ private val varargLoweringPhase = makeBodyLoweringPhase(
|
||||
::VarargLowering,
|
||||
name = "VarargLowering",
|
||||
description = "Lower vararg arguments",
|
||||
prerequisite = setOf(callableReferenceLoweringPhase)
|
||||
prerequisite = setOf(interopCallableReferenceLoweringPhase)
|
||||
)
|
||||
|
||||
private val propertiesLoweringPhase = makeDeclarationTransformerPhase(
|
||||
@@ -596,7 +602,7 @@ val loweringList = listOf<Lowering>(
|
||||
functionInliningPhase,
|
||||
copyInlineFunctionBodyLoweringPhase,
|
||||
createScriptFunctionsPhase,
|
||||
provisionalFunctionExpressionPhase,
|
||||
callableReferenceLoweringX,
|
||||
singleAbstractMethodPhase,
|
||||
lateinitNullableFieldsPhase,
|
||||
lateinitDeclarationLoweringPhase,
|
||||
@@ -627,6 +633,7 @@ val loweringList = listOf<Lowering>(
|
||||
enumEntryRemovalLoweringPhase,
|
||||
suspendFunctionsLoweringPhase,
|
||||
suspendLambdasRemovalLoweringPhase,
|
||||
interopCallableReferenceLoweringPhase,
|
||||
returnableBlockLoweringPhase,
|
||||
forLoopsLoweringPhase,
|
||||
primitiveCompanionLoweringPhase,
|
||||
@@ -634,7 +641,6 @@ val loweringList = listOf<Lowering>(
|
||||
foldConstantLoweringPhase,
|
||||
privateMembersLoweringPhase,
|
||||
privateMemberUsagesLoweringPhase,
|
||||
callableReferenceLoweringPhase,
|
||||
defaultArgumentStubGeneratorPhase,
|
||||
defaultArgumentPatchOverridesPhase,
|
||||
defaultParameterInjectorPhase,
|
||||
|
||||
@@ -191,6 +191,9 @@ object JsIrBuilder {
|
||||
fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol, reflectionTarget: IrFunctionSymbol? = symbol) =
|
||||
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(
|
||||
type: IrType,
|
||||
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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
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.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.*
|
||||
import org.jetbrains.kotlin.ir.builders.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.descriptors.WrappedTypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.WrappedReceiverParameterDescriptor
|
||||
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.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
|
||||
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 CallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
private val callableToFactoryFunction = context.callableReferencesCache
|
||||
private val newDeclarations = mutableListOf<IrDeclaration>()
|
||||
private lateinit var implicitDeclarationFile: IrFile //= context.implicitDeclarationFile
|
||||
|
||||
class CallableReferenceLowering(private val context: CommonBackendContext) : BodyLoweringPass {
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
newDeclarations.clear()
|
||||
implicitDeclarationFile = container.file // TODO
|
||||
irBody.transformChildrenVoid(CallableReferenceLowerTransformer())
|
||||
implicitDeclarationFile.declarations += newDeclarations
|
||||
val realContainer = container as? IrDeclarationParent ?: container.parent
|
||||
irBody.transformChildrenVoid(ReferenceTransformer(realContainer))
|
||||
}
|
||||
|
||||
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
|
||||
CallableReferenceKey(
|
||||
declaration,
|
||||
reference.dispatchReceiver != null,
|
||||
reference.extensionReceiver != null,
|
||||
reference.type.asString()
|
||||
)
|
||||
private val nothingType = context.irBuiltIns.nothingType
|
||||
private val stringType = context.irBuiltIns.stringType
|
||||
|
||||
private inner class ReferenceTransformer(private val container: IrDeclarationParent) : IrElementTransformerVoid() {
|
||||
override fun visitFunctionExpression(expression: IrFunctionExpression): IrExpression {
|
||||
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 {
|
||||
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)
|
||||
|
||||
val (clazz, ctor) = buildFunctionReference(expression)
|
||||
|
||||
clazz.parent = container
|
||||
|
||||
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 {
|
||||
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)
|
||||
private fun buildFunctionReference(expression: IrFunctionReference): Pair<IrClass, IrConstructor> {
|
||||
return CallableReferenceBuilder(expression.symbol.owner, expression, false).build()
|
||||
}
|
||||
|
||||
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)
|
||||
private fun buildLambdaReference(function: IrSimpleFunction, expression: IrFunctionExpression): Pair<IrClass, IrConstructor> {
|
||||
return CallableReferenceBuilder(function, expression, true).build()
|
||||
}
|
||||
}
|
||||
|
||||
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 inner class CallableReferenceBuilder(
|
||||
private val function: IrFunction,
|
||||
private val reference: IrExpression,
|
||||
private val isLambda: Boolean
|
||||
) {
|
||||
|
||||
private val isKReference: Boolean get() = superFunctionInterface.name.identifier[0] == 'K'
|
||||
|
||||
private val isSuspendLambda = isLambda && function.isSuspend
|
||||
private val superClass = if (isSuspendLambda) context.ir.symbols.coroutineImpl.owner.defaultType else context.irBuiltIns.anyType
|
||||
|
||||
private var boundReceiverField: IrField? = null
|
||||
|
||||
private val superFunctionInterface = reference.type.classOrNull?.owner ?: error("Expected functional type")
|
||||
|
||||
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 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('_')
|
||||
private fun IrClass.createDispatchReceiver(): IrValueParameter {
|
||||
val vpDescriptor = WrappedReceiverParameterDescriptor()
|
||||
val vpSymbol = IrValueParameterSymbolImpl(vpDescriptor)
|
||||
val declaration = IrValueParameterImpl(startOffset, endOffset, origin, vpSymbol, THIS_NAME, -1, defaultType, null, false, false)
|
||||
vpDescriptor.bind(declaration)
|
||||
return declaration
|
||||
}
|
||||
|
||||
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")
|
||||
private fun createConstructor(clazz: IrClass): IrConstructor {
|
||||
return clazz.addConstructor {
|
||||
origin = GENERATED_MEMBER_IN_CALLABLE_REFERENCE
|
||||
returnType = clazz.defaultType
|
||||
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('_')
|
||||
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))
|
||||
private fun createInvokeMethod(clazz: IrClass): IrSimpleFunction {
|
||||
val superMethods = superFunctionInterface.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
val superMethod = superMethods.single { it.name.asString() == "invoke" }
|
||||
return clazz.addFunction {
|
||||
setSourceRange(if (isLambda) function else reference)
|
||||
name = superMethod.name
|
||||
returnType = function.returnType
|
||||
isSuspend = superMethod.isSuspend
|
||||
}.apply {
|
||||
overriddenSymbols = listOf(superMethod.symbol)
|
||||
dispatchReceiverParameter = clazz.createDispatchReceiver().also { it.parent = this }
|
||||
if (isLambda) createLambdaInvokeMethod() else createFunctionReferenceInvokeMethod()
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
private fun IrSimpleFunction.buildInvoke(): IrFunctionAccessExpression {
|
||||
val callee = function
|
||||
val irCall = reference.run {
|
||||
if (callee is IrConstructor) {
|
||||
IrConstructorCallImpl(startOffset, endOffset, callee.parentAsClass.defaultType, callee.symbol, callee.typeParameters.size, 0 /* TODO */, callee.valueParameters.size, CALLABLE_REFERNCE_INVOKE)
|
||||
} else {
|
||||
IrCallImpl(startOffset, endOffset, callee.returnType, callee.symbol, callee.typeParameters.size, callee.valueParameters.size, CALLABLE_REFERNCE_INVOKE)
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
val funRef = reference as IrFunctionReference
|
||||
|
||||
val boundReceiver = funRef.run { dispatchReceiver ?: extensionReceiver } != null
|
||||
val hasReceiver = callee.run { dispatchReceiverParameter ?: extensionReceiverParameter } != null
|
||||
|
||||
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.)
|
||||
statements += setDynamicProperty(irVar.symbol, Namer.KCALLABLE_NAME) {
|
||||
JsIrBuilder.buildString(
|
||||
context.irBuiltIns.stringType,
|
||||
getReferenceName(getterDeclaration.correspondingPropertySymbol!!.owner)
|
||||
if (hasReceiver) {
|
||||
if (!boundReceiver) {
|
||||
if (callee.dispatchReceiverParameter != null) irCall.dispatchReceiver = getValue(valueParameters[i++])
|
||||
if (callee.extensionReceiverParameter != null) irCall.extensionReceiver = getValue(valueParameters[i++])
|
||||
} 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)
|
||||
}
|
||||
|
||||
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)
|
||||
val getter = nameProperty.addGetter() {
|
||||
returnType = stringType
|
||||
}
|
||||
getter.overriddenSymbols += supperGetter.symbol
|
||||
getter.dispatchReceiverParameter = buildValueParameter {
|
||||
name = THIS_NAME
|
||||
type = clazz.defaultType
|
||||
}.also { it.parent = getter }
|
||||
|
||||
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
|
||||
getter.body = IrBlockBodyImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
|
||||
IrReturnImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingType, getter.symbol, IrConstImpl.string(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, stringType, function.name.asString()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
fun build(): Pair<IrClass, IrConstructor> {
|
||||
val clazz = buildReferenceClass()
|
||||
val ctor = createConstructor(clazz)
|
||||
val invoke = createInvokeMethod(clazz)
|
||||
createNameProperty(clazz)
|
||||
// TODO: create name property for KFunction*
|
||||
|
||||
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()
|
||||
return Pair(clazz, ctor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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.Modality
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
@@ -35,7 +34,7 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.declarations.forEach {
|
||||
if (it is IrClass) {
|
||||
generateTestCalls(it) { suiteForPackage(irFile.fqName).body }
|
||||
generateTestCalls(it) { suiteForPackage(irFile.fqName).function }
|
||||
}
|
||||
|
||||
// TODO top-level functions
|
||||
@@ -45,42 +44,37 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
private val packageSuites = mutableMapOf<FqName, FunctionWithBody>()
|
||||
|
||||
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 fun IrSimpleFunctionSymbol.createInvocation(
|
||||
name: String,
|
||||
parentBody: IrBlockBody,
|
||||
parentFunction: IrSimpleFunction,
|
||||
ignored: Boolean = false
|
||||
): FunctionWithBody {
|
||||
val body = JsIrBuilder.buildBlockBody(emptyList())
|
||||
|
||||
val function = JsIrBuilder.buildFunction(
|
||||
"$name test fun",
|
||||
context.irBuiltIns.unitType,
|
||||
context.implicitDeclarationFile
|
||||
).also {
|
||||
it.body = body
|
||||
context.implicitDeclarationFile.declarations += it
|
||||
}
|
||||
val function = JsIrBuilder.buildFunction("$name test fun", context.irBuiltIns.unitType, parentFunction)
|
||||
function.body = body
|
||||
|
||||
val parentBody = parentFunction.body as IrBlockBody
|
||||
parentBody.statements += JsIrBuilder.buildCall(this).apply {
|
||||
putValueArgument(0, JsIrBuilder.buildString(context.irBuiltIns.stringType, name))
|
||||
putValueArgument(1, JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, ignored))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
private fun generateTestCalls(irClass: IrClass, parentBody: () -> IrBlockBody) {
|
||||
private fun generateTestCalls(irClass: IrClass, parentFunction: () -> IrSimpleFunction) {
|
||||
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 afterFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isAfter }
|
||||
@@ -88,10 +82,10 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
irClass.declarations.forEach {
|
||||
when {
|
||||
it is IrClass ->
|
||||
generateTestCalls(it) { suiteFunBody.body }
|
||||
generateTestCalls(it) { suiteFunBody.function }
|
||||
|
||||
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>,
|
||||
afterFuns: List<IrSimpleFunction>,
|
||||
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())
|
||||
|
||||
|
||||
+1
@@ -229,6 +229,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
private fun generateTypeCheckNonNull(argument: IrExpressionWithCopy, toType: IrType): IrExpression {
|
||||
assert(!toType.isMarkedNullable())
|
||||
return when {
|
||||
toType is IrDynamicType -> argument
|
||||
toType.isAny() -> generateIsObjectCheck(argument)
|
||||
toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse))
|
||||
toType.isSuspendFunctionTypeOrSubtype() -> generateSuspendFunctionCheck(argument, toType)
|
||||
|
||||
+2
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
||||
|
||||
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.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl
|
||||
@@ -34,6 +35,7 @@ class ReflectionCallsTransformer(private val context: JsIrBackendContext) : Call
|
||||
addWithPredicate(
|
||||
Name.special(Namer.KCALLABLE_GET_NAME),
|
||||
{ call ->
|
||||
call.origin != CallableReferenceLowering.Companion.CALLABLE_REFERNCE_INVOKE &&
|
||||
call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false
|
||||
},
|
||||
{ call ->
|
||||
|
||||
+5
@@ -31,6 +31,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
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 {
|
||||
val kind = expression.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.isSuspend
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.InteropCallableReferenceLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
@@ -67,6 +68,8 @@ private fun isNativeInvoke(call: IrCall): Boolean {
|
||||
|
||||
if (simpleFunction.isSuspend) return false
|
||||
|
||||
if (call.origin === InteropCallableReferenceLowering.Companion.EXPLICIT_INVOKE) return false
|
||||
|
||||
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
|
||||
}
|
||||
|
||||
@@ -206,7 +209,9 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
|
||||
val argument = expression.getValueArgument(index)
|
||||
val result = argument?.accept(transformer, context)
|
||||
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))
|
||||
} else
|
||||
result
|
||||
|
||||
@@ -42,4 +42,5 @@ object Namer {
|
||||
val KPROPERTY_GET = "get"
|
||||
val KPROPERTY_SET = "set"
|
||||
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 {
|
||||
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 metadata.suspendArity === arity
|
||||
return false
|
||||
}
|
||||
|
||||
fun isObject(obj: dynamic): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user