[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
This commit is contained in:
Svyatoslav Kuzmich
2021-11-10 13:41:20 +03:00
parent 916379c0e7
commit c0761bf34a
3 changed files with 90 additions and 49 deletions
@@ -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
@@ -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
}
+34 -17
View File
@@ -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()