[JS IR] Implement new function references scheme

This commit is contained in:
Roman Artemev
2020-02-21 17:05:16 +03:00
committed by romanart
parent 2f087fe7a2
commit 26237f8bd5
12 changed files with 1097 additions and 532 deletions
@@ -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?,
@@ -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")
}
}
@@ -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")
}
}
@@ -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())
@@ -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)
@@ -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 ->
@@ -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) {
@@ -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 {