From 199fe7df08cfbc80d0cb13dfba76903770055877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 13 May 2024 17:57:56 +0200 Subject: [PATCH] Export top-level defs as `function` functions, not arrow functions. This is necessary for exported Scala classes to be instantiable with `new ExportedClass()`. --- .../linker/backend/wasmemitter/ClassEmitter.scala | 13 +++++-------- .../linker/backend/wasmemitter/CoreWasmLib.scala | 4 +++- .../linker/backend/wasmemitter/Emitter.scala | 9 +++++---- .../linker/backend/wasmemitter/LoaderContent.scala | 5 ++++- .../scalajs/linker/backend/wasmemitter/VarGen.scala | 4 +++- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala index dd280440..91cb62d9 100644 --- a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala +++ b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/ClassEmitter.scala @@ -1109,14 +1109,11 @@ class ClassEmitter(coreSpec: CoreSpec) { resultType = AnyType ) - if (method.restParam.isEmpty) { - ctx.addExport(wamod.Export.Function(exportedName, functionName)) - } else { - /* We cannot directly export the function. We will create a closure - * wrapper in the start function and export that instead. - */ - genDelayedTopLevelExport(exportedName) - } + /* We cannot directly export the function because it would not be considered + * a `function`. Instead, we will explicitly create a closure wrapper in the + * start function and export that instead. + */ + genDelayedTopLevelExport(exportedName) } private def transformTopLevelFieldExportDef( diff --git a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala index 9331683e..5fc33dd2 100644 --- a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala +++ b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/CoreWasmLib.scala @@ -377,8 +377,10 @@ object CoreWasmLib { List(RefType.func, anyref, Int32), List(RefType.any) ) + + addHelperImport(genFunctionName.makeExportedDef, List(RefType.func), List(RefType.any)) addHelperImport( - genFunctionName.closureRestNoData, + genFunctionName.makeExportedDefRest, List(RefType.func, Int32), List(RefType.any) ) diff --git a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala index 72add60a..4305f254 100644 --- a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala +++ b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/Emitter.scala @@ -190,13 +190,14 @@ final class Emitter(config: Emitter.Config) { instrs += wa.CALL(genFunctionName.loadModule(tle.owningClass)) instrs += wa.GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName)) case TopLevelMethodExportDef(_, methodDef) => - // We only need initialization if there is a restParam + instrs += ctx.refFuncWithDeclaration(genFunctionName.forExport(tle.exportName)) if (methodDef.restParam.isDefined) { - instrs += ctx.refFuncWithDeclaration(genFunctionName.forExport(tle.exportName)) instrs += wa.I32_CONST(methodDef.args.size) - instrs += wa.CALL(genFunctionName.closureRestNoData) - instrs += wa.GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName)) + instrs += wa.CALL(genFunctionName.makeExportedDefRest) + } else { + instrs += wa.CALL(genFunctionName.makeExportedDef) } + instrs += wa.GLOBAL_SET(genGlobalName.forTopLevelExport(tle.exportName)) case TopLevelFieldExportDef(_, _, _) => // Nothing to do () diff --git a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala index 2b0c80b5..86960338 100644 --- a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala +++ b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/LoaderContent.scala @@ -119,7 +119,10 @@ const scalaJSHelpers = { closureThis: (f, data) => function(...args) { return f(data, this, ...args); }, closureRest: (f, data, n) => ((...args) => f(data, ...args.slice(0, n), args.slice(n))), closureThisRest: (f, data, n) => function(...args) { return f(data, this, ...args.slice(0, n), args.slice(n)); }, - closureRestNoData: (f, n) => ((...args) => f(...args.slice(0, n), args.slice(n))), + + // Top-level exported defs -- they must be `function`s but have no actual `this` nor `data` + makeExportedDef: (f) => function(...args) { return f(...args); }, + makeExportedDefRest: (f, n) => function(...args) { return f(...args.slice(0, n), args.slice(n)); }, // Strings emptyString: "", diff --git a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala index 422c7355..33d1f789 100644 --- a/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala +++ b/wasm/src/main/scala/org/scalajs/linker/backend/wasmemitter/VarGen.scala @@ -153,7 +153,9 @@ object VarGen { val closureThis = make("closureThis") val closureRest = make("closureRest") val closureThisRest = make("closureThisRest") - val closureRestNoData = make("closureRestNoData") + + val makeExportedDef = make("makeExportedDef") + val makeExportedDefRest = make("makeExportedDefRest") val stringLength = make("stringLength") val stringCharAt = make("stringCharAt")