From c0761bf34a8f06fd5ce4ee8cb0a297801fac809c Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Wed, 10 Nov 2021 13:41:20 +0300 Subject: [PATCH] [Wasm] Support callable references to external functions 1. Move jsInteropFunctionsLowering after callable reference lowering, so it can continue to deal with just functions and function calls. 2. Generate lowered closure class in JsInteropFunctionsLowering because --- .../kotlin/backend/wasm/WasmLoweringPhases.kt | 6 +- .../wasm/lower/JsInteropFunctionsLowering.kt | 82 ++++++++++++------- .../codegen/boxWasmJsInterop/functionTypes.kt | 51 ++++++++---- 3 files changed, 90 insertions(+), 49 deletions(-) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt index 6bac1868f58..321532cf7c7 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt @@ -504,9 +504,6 @@ val wasmPhases = NamedCompilerPhase( excludeDeclarationsFromCodegenPhase then expectDeclarationsRemovingPhase then - jsInteropFunctionsLowering then - jsInteropFunctionCallsLowering then - // TODO: Need some helpers from stdlib // arrayConstructorPhase then wrapInlineDeclarationsWithReifiedTypeParametersPhase then @@ -535,6 +532,9 @@ val wasmPhases = NamedCompilerPhase( delegateToPrimaryConstructorLoweringPhase then // Common prefix ends + jsInteropFunctionsLowering then + jsInteropFunctionCallsLowering then + enumEntryInstancesLoweringPhase then enumEntryInstancesBodyLoweringPhase then enumClassCreateInitializerLoweringPhase then diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt index 8b537ed9550..c0a81cd5ffe 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.wasm.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder @@ -14,17 +15,13 @@ import org.jetbrains.kotlin.backend.wasm.WasmBackendContext import org.jetbrains.kotlin.backend.wasm.ir2wasm.isBuiltInWasmRefType import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExported import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExternalType -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter -import org.jetbrains.kotlin.ir.builders.declarations.buildFun -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -386,46 +383,73 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT name = Name.identifier("f") type = context.wasmSymbols.externalInterfaceType } - val builder = context.createIrBuilder(result.symbol) - val backendContext = context - result.body = builder.irBlockBody { - val lambda = context.irFactory.buildFun { - name = Name.identifier("kotlinClosure_${info.hashString}") - returnType = info.originalResultType + + val closureClass = context.irFactory.buildClass { + name = Name.identifier("__JsClosureToKotlinClosure_${info.hashString}") + }.apply { + createImplicitParameterDeclarationWithWrappedDescriptor() + superTypes = listOf(functionType) + parent = currentParent + } + + val closureClassField = closureClass.addField { + name = Name.identifier("jsClosure") + type = context.wasmSymbols.externalInterfaceType + visibility = DescriptorVisibilities.PRIVATE + isFinal = true + } + + val closureClassConstructor = closureClass.addConstructor { + isPrimary = true + }.apply { + val parameter = addValueParameter { + name = closureClassField.name + type = closureClassField.type } - lambda.parent = result - val lambdaBuilder = backendContext.createIrBuilder(lambda.symbol) + body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + +irSetField(irGet(closureClass.thisReceiver!!), closureClassField, irGet(parameter)) + +IrInstanceInitializerCallImpl(startOffset, endOffset, closureClass.symbol, context.irBuiltIns.unitType) + } + } + + closureClass.addFunction { + name = Name.identifier("invoke") + returnType = info.originalResultType + }.apply { + addDispatchReceiver { type = closureClass.defaultType } info.originalParameterTypes.forEachIndexed { index, irType -> - lambda.addValueParameter { + addValueParameter { name = Name.identifier("p$index") type = irType } } - lambda.body = lambdaBuilder.irBlockBody { + val lambdaBuilder = context.createIrBuilder(symbol) + body = lambdaBuilder.irBlockBody { val jsClosureCallerCall = irCall(jsClosureCaller) - jsClosureCallerCall.putValueArgument(0, irGet(result.valueParameters[0])) + jsClosureCallerCall.putValueArgument(0, irGetField(irGet(dispatchReceiverParameter!!), closureClassField)) for ((adapterIndex, paramAdapter) in info.parametersAdapters.withIndex()) { jsClosureCallerCall.putValueArgument( adapterIndex + 1, paramAdapter.adaptIfNeeded( - irGet(lambda.valueParameters[adapterIndex]), + irGet(valueParameters[adapterIndex]), lambdaBuilder ) ) } +irReturn(info.resultAdapter.adaptIfNeeded(jsClosureCallerCall, lambdaBuilder)) } - +irReturn( - IrFunctionExpressionImpl( - UNDEFINED_OFFSET, - UNDEFINED_OFFSET, - functionType, - lambda, - origin = KOTLIN_WASM_CLOSURE_FOR_JS_CLOSURE, - ) - ) + + overriddenSymbols = + overriddenSymbols + functionType.classOrNull!!.functions.single { it.owner.name == Name.identifier("invoke") } } + val builder = context.createIrBuilder(result.symbol) + result.body = builder.irBlockBody { + +irReturn(irCall(closureClassConstructor).also { it.putValueArgument(0, irGet(result.valueParameters[0])) }) + } + + additionalDeclarations += closureClass additionalDeclarations += result return result } diff --git a/compiler/testData/codegen/boxWasmJsInterop/functionTypes.kt b/compiler/testData/codegen/boxWasmJsInterop/functionTypes.kt index 9a9e9874e98..318497460be 100644 --- a/compiler/testData/codegen/boxWasmJsInterop/functionTypes.kt +++ b/compiler/testData/codegen/boxWasmJsInterop/functionTypes.kt @@ -199,25 +199,42 @@ fun box(): String { ) if (externalWithLambdas2Count != 11) return "Fail externalWithLambdas2" - - val jsLambda = createJsLambda() - - val jsLambdaCount = jsLambda( - true, - 100.toByte(), - 200.toShort(), - 'я', - 300, - 400L, - 500.5f, - 600.5, - "700", - create123Array(), - DC(800, 800), + val externalWithLambdas2Ref = ::externalWithLambdas2 + val externalWithLambdas2RefCount = externalWithLambdas2Ref.invoke( + { true }, + { 100.toByte() }, + { 200.toShort() }, + { 'я' }, + { 300 }, + { 400L }, + { 500.5f }, + { 600.5 }, + { "700" }, + { create123Array() }, + { DC(800, 800) }, { it.y } ) - if (jsLambdaCount != 11) - return "Fail 3" + if (externalWithLambdas2RefCount != 11) return "Fail externalWithLambdas2" + + val createJsLambdaRef = ::createJsLambda + for (jsLambda in arrayOf(createJsLambda(), createJsLambdaRef.invoke())) { + val jsLambdaCount = jsLambda( + true, + 100.toByte(), + 200.toShort(), + 'я', + 300, + 400L, + 500.5f, + 600.5, + "700", + create123Array(), + DC(800, 800), + { it.y } + ) + if (jsLambdaCount != 11) + return "Fail 3" + } complexHigherOrerTest()