[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 excludeDeclarationsFromCodegenPhase then
expectDeclarationsRemovingPhase then expectDeclarationsRemovingPhase then
jsInteropFunctionsLowering then
jsInteropFunctionCallsLowering then
// TODO: Need some helpers from stdlib // TODO: Need some helpers from stdlib
// arrayConstructorPhase then // arrayConstructorPhase then
wrapInlineDeclarationsWithReifiedTypeParametersPhase then wrapInlineDeclarationsWithReifiedTypeParametersPhase then
@@ -535,6 +532,9 @@ val wasmPhases = NamedCompilerPhase(
delegateToPrimaryConstructorLoweringPhase then delegateToPrimaryConstructorLoweringPhase then
// Common prefix ends // Common prefix ends
jsInteropFunctionsLowering then
jsInteropFunctionCallsLowering then
enumEntryInstancesLoweringPhase then enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase 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.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer 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.ir.createStaticFunctionWithReceivers
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder 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.isBuiltInWasmRefType
import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExported import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExported
import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExternalType 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.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.*
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.expressions.* 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.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -386,46 +383,73 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
name = Name.identifier("f") name = Name.identifier("f")
type = context.wasmSymbols.externalInterfaceType type = context.wasmSymbols.externalInterfaceType
} }
val builder = context.createIrBuilder(result.symbol)
val backendContext = context val closureClass = context.irFactory.buildClass {
result.body = builder.irBlockBody { name = Name.identifier("__JsClosureToKotlinClosure_${info.hashString}")
val lambda = context.irFactory.buildFun { }.apply {
name = Name.identifier("kotlinClosure_${info.hashString}") createImplicitParameterDeclarationWithWrappedDescriptor()
returnType = info.originalResultType 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 body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
val lambdaBuilder = backendContext.createIrBuilder(lambda.symbol) +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 -> info.originalParameterTypes.forEachIndexed { index, irType ->
lambda.addValueParameter { addValueParameter {
name = Name.identifier("p$index") name = Name.identifier("p$index")
type = irType type = irType
} }
} }
lambda.body = lambdaBuilder.irBlockBody { val lambdaBuilder = context.createIrBuilder(symbol)
body = lambdaBuilder.irBlockBody {
val jsClosureCallerCall = irCall(jsClosureCaller) 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()) { for ((adapterIndex, paramAdapter) in info.parametersAdapters.withIndex()) {
jsClosureCallerCall.putValueArgument( jsClosureCallerCall.putValueArgument(
adapterIndex + 1, adapterIndex + 1,
paramAdapter.adaptIfNeeded( paramAdapter.adaptIfNeeded(
irGet(lambda.valueParameters[adapterIndex]), irGet(valueParameters[adapterIndex]),
lambdaBuilder lambdaBuilder
) )
) )
} }
+irReturn(info.resultAdapter.adaptIfNeeded(jsClosureCallerCall, lambdaBuilder)) +irReturn(info.resultAdapter.adaptIfNeeded(jsClosureCallerCall, lambdaBuilder))
} }
+irReturn(
IrFunctionExpressionImpl( overriddenSymbols =
UNDEFINED_OFFSET, overriddenSymbols + functionType.classOrNull!!.functions.single { it.owner.name == Name.identifier("invoke") }
UNDEFINED_OFFSET,
functionType,
lambda,
origin = KOTLIN_WASM_CLOSURE_FOR_JS_CLOSURE,
)
)
} }
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 additionalDeclarations += result
return result return result
} }
+34 -17
View File
@@ -199,25 +199,42 @@ fun box(): String {
) )
if (externalWithLambdas2Count != 11) return "Fail externalWithLambdas2" if (externalWithLambdas2Count != 11) return "Fail externalWithLambdas2"
val externalWithLambdas2Ref = ::externalWithLambdas2
val jsLambda = createJsLambda() val externalWithLambdas2RefCount = externalWithLambdas2Ref.invoke(
{ true },
val jsLambdaCount = jsLambda( { 100.toByte() },
true, { 200.toShort() },
100.toByte(), { 'я' },
200.toShort(), { 300 },
'я', { 400L },
300, { 500.5f },
400L, { 600.5 },
500.5f, { "700" },
600.5, { create123Array() },
"700", { DC(800, 800) },
create123Array(),
DC(800, 800),
{ it.y } { it.y }
) )
if (jsLambdaCount != 11) if (externalWithLambdas2RefCount != 11) return "Fail externalWithLambdas2"
return "Fail 3"
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() complexHigherOrerTest()