diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt index f292acf624d..bb7f0ddd618 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/builders/declarations/declarationBuilders.kt @@ -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 } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 15ca5974840..3a5cd3bd105 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -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( functionInliningPhase, copyInlineFunctionBodyLoweringPhase, createScriptFunctionsPhase, - provisionalFunctionExpressionPhase, + callableReferenceLoweringX, singleAbstractMethodPhase, lateinitNullableFieldsPhase, lateinitDeclarationLoweringPhase, @@ -627,6 +633,7 @@ val loweringList = listOf( enumEntryRemovalLoweringPhase, suspendFunctionsLoweringPhase, suspendLambdasRemovalLoweringPhase, + interopCallableReferenceLoweringPhase, returnableBlockLoweringPhase, forLoopsLoweringPhase, primitiveCompanionLoweringPhase, @@ -634,7 +641,6 @@ val loweringList = listOf( foldConstantLoweringPhase, privateMembersLoweringPhase, privateMemberUsagesLoweringPhase, - callableReferenceLoweringPhase, defaultArgumentStubGeneratorPhase, defaultArgumentPatchOverridesPhase, defaultParameterInjectorPhase, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index 99ac8ba1ec7..1091ada1068 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -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?, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt index ce7af07a176..66e22632f3c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt @@ -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() - 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 { + 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 { + 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 { - // [ 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, 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() + 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 { - // if ($cache$ == null) { // very likely property reference is going to be cached - // val x = fun Foo_bar_KreferenceClosure_get(r: Foo): PType { - // return r.() - // } - // x.get = x - // x.callableName = "bar" - // if (mutable) { - // x.set = fun Foo_bar_KreferenceClosure_set(r: Foo, v: PType>) { - // r.(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(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().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 { - // 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(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, IrValueSymbol> - ): List { - - val (bodyStatements, varSymbol) = builder() - val statements = mutableListOf() - val returnValue: IrExpression - val returnStatements: List - if (factoryFunction.valueParameters.isEmpty()) { - // compose cache for 'direct' closure - // if ($cache$ === null) { - // $cache$ = - // } - // - 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().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 { - val result = mutableListOf() - - /* - * 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() - - 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 { + 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("") + val BOUND_RECEIVER_NAME = Name.identifier("\$boundThis") + } + +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt new file mode 100644 index 00000000000..f79cb257c61 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/InteropCallableReferenceLowering.kt @@ -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() + 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().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(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().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 { + // [ 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, 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 { + // if ($cache$ == null) { // very likely property reference is going to be cached + // val x = fun Foo_bar_KreferenceClosure_get(r: Foo): PType { + // return r.() + // } + // x.get = x + // x.callableName = "bar" + // if (mutable) { + // x.set = fun Foo_bar_KreferenceClosure_set(r: Foo, v: PType>) { + // r.(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(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 { + // 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(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, IrValueSymbol> + ): List { + + val (bodyStatements, varSymbol) = builder() + val statements = mutableListOf() + val returnValue: IrExpression + val returnStatements: List + if (factoryFunction.valueParameters.isEmpty()) { + // compose cache for 'direct' closure + // if ($cache$ === null) { + // $cache$ = + // } + // + 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().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 { + val result = mutableListOf() + + /* + * 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() + + 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") + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt index 42834abd5b5..9e48282d670 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt @@ -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() 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().filter { it.isBefore } val afterFunctions = irClass.declarations.filterIsInstance().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, afterFuns: List, 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()) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index 3ea29aa4fec..8df332dbc8a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt index e85bf485a6c..3e5ede24c6b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt @@ -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 -> diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 9b40afdb95a..f2dea5e3afa 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -31,6 +31,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer visitConst(expression: IrConst, context: JsGenerationContext): JsExpression { val kind = expression.kind return when (kind) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index 26e9a06cd59..39c66b7937b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index 9b48338276a..bf714b731f3 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -42,4 +42,5 @@ object Namer { val KPROPERTY_GET = "get" val KPROPERTY_SET = "set" val KCALLABLE_CACHE_SUFFIX = "\$cache" + const val KCALLABLE_ARITY = "\$arity" } \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index 43e41341237..6067afa6187 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -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() ?: return false + if (jsTypeOf(obj) == "function") { + return obj.`$arity`.unsafeCast() === arity + } - val metadata = ctor.`$metadata$`?.unsafeCast() ?: return false - - return metadata.suspendArity === arity + return false } fun isObject(obj: dynamic): Boolean {