[Wasm] Support exporting functions via @kotlin.js.JsExport

Previously we exported main function only.
Extend this feature to arbitrary number of annotated functions.
Type restrictions and conversions are similar to imported functions.
This commit is contained in:
Svyatoslav Kuzmich
2021-09-08 13:05:07 +03:00
parent de7fa8c778
commit 35a5c9ea71
2 changed files with 25 additions and 11 deletions
@@ -172,7 +172,21 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
} }
override fun visitGetValue(expression: IrGetValue) { override fun visitGetValue(expression: IrGetValue) {
body.buildGetLocal(context.referenceLocal(expression.symbol)) val valueSymbol = expression.symbol
body.buildGetLocal(context.referenceLocal(valueSymbol))
val valueDeclaration = valueSymbol.owner
if (valueSymbol.owner is IrValueParameter) {
val parent = valueDeclaration.parent
if (parent is IrFunction && parent.isExported(backendContext)) {
val type = context.transformType(valueDeclaration.type)
if (type is WasmRefNullType) {
// TODO: Add these casts as IR-2-IR lowering instead
generateTypeRTT(valueDeclaration.type)
body.buildRefCast()
}
}
}
} }
override fun visitSetValue(expression: IrSetValue) { override fun visitSetValue(expression: IrSetValue) {
@@ -296,7 +310,7 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
// Return types of imported functions cannot have concrete struct/array references. // Return types of imported functions cannot have concrete struct/array references.
// Non-primitive return types are represented as eqref which need to be casted back to expected type on call site. // Non-primitive return types are represented as eqref which need to be casted back to expected type on call site.
if (function.getWasmImportAnnotation() != null || function.getJsFunAnnotation() != null || function.isExternal) { if (function.getWasmImportAnnotation() != null || function.getJsFunAnnotation() != null || function.isExternal || function.isExported(backendContext)) {
val resT = context.transformResultType(function.returnType) val resT = context.transformResultType(function.returnType)
if (resT is WasmRefNullType) { if (resT is WasmRefNullType) {
generateTypeRTT(function.returnType) generateTypeRTT(function.returnType)
@@ -71,30 +71,30 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
val watName = declaration.fqNameWhenAvailable.toString() val watName = declaration.fqNameWhenAvailable.toString()
val irParameters = declaration.getEffectiveValueParameters() val irParameters = declaration.getEffectiveValueParameters()
// TODO: Exported types should be transformed in a separate lowering by creating shim functions for each export. // TODO: Exported types should be transformed in a separate lowering by creating shim functions for each export.
val isExported = declaration.isExported(context.backendContext)
val resultType = val resultType =
when { when {
declaration.isExported(context.backendContext) -> context.transformExportedResultType(declaration.returnType) isExported -> context.transformExportedResultType(declaration.returnType)
// Unit_getInstance returns true Unit reference instead of "void" // Unit_getInstance returns true Unit reference instead of "void"
declaration == unitGetInstanceFunction -> context.transformType(declaration.returnType) declaration == unitGetInstanceFunction -> context.transformType(declaration.returnType)
else -> context.transformResultType(declaration.returnType) else -> context.transformResultType(declaration.returnType)
} }
val isExportedOrImported = isExported || importedName != null
val wasmFunctionType = val wasmFunctionType =
WasmFunctionType( WasmFunctionType(
name = watName, name = watName,
parameterTypes = irParameters.map { parameterTypes = irParameters.map {
val t = context.transformValueParameterType(it) val t = context.transformValueParameterType(it)
if (importedName != null && t is WasmRefNullType) { if (isExportedOrImported && t is WasmRefNullType) {
WasmEqRef WasmRefNullType(WasmHeapType.Simple.Data)
} else { } else {
t t
} }
}, },
resultTypes = listOfNotNull( resultTypes = listOfNotNull(
run { resultType.let {
if (importedName != null && resultType is WasmRefNullType) if (isExportedOrImported && it is WasmRefNullType) WasmRefNullType(WasmHeapType.Simple.Data) else it
WasmEqRef
else
resultType
} }
) )
) )
@@ -377,4 +377,4 @@ fun IrFunction.getEffectiveValueParameters(): List<IrValueParameter> {
} }
fun IrFunction.isExported(context: WasmBackendContext): Boolean = fun IrFunction.isExported(context: WasmBackendContext): Boolean =
visibility == DescriptorVisibilities.PUBLIC && fqNameWhenAvailable in context.additionalExportedDeclarations fqNameWhenAvailable in context.additionalExportedDeclarations || isJsExport()