[WASM] Support for mjs universal launcher

This commit is contained in:
Igor Yakovlev
2022-06-03 18:45:40 +02:00
committed by teamcity
parent c8806763fa
commit f996278171
20 changed files with 103 additions and 171 deletions
@@ -249,13 +249,6 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-Xwasm-debug-info", description = "Add debug info to WebAssembly compiled module")
var wasmDebug: Boolean by FreezableVar(true)
@Argument(
value = "-Xwasm-launcher",
valueDescription = "esm|nodejs|d8",
description = "Picks flavor for the wasm launcher. Default is ESM."
)
var wasmLauncher: String? by NullableStringFreezableVar("esm")
@Argument(value = "-Xwasm-kclass-fqn", description = "Enable support for FQ names in KClass")
var wasmKClassFqn: Boolean by FreezableVar(false)
@@ -11,7 +11,6 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.backend.common.CompilationException
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.wasm.WasmLoaderKind
import org.jetbrains.kotlin.backend.wasm.compileWasm
import org.jetbrains.kotlin.backend.wasm.compileToLoweredIr
import org.jetbrains.kotlin.backend.wasm.wasmPhases
@@ -362,18 +361,9 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
generateWat = true,
)
val launcherKind = when (arguments.wasmLauncher) {
"esm" -> WasmLoaderKind.BROWSER
"nodejs" -> WasmLoaderKind.NODE
"d8" -> WasmLoaderKind.D8
"d8NodeCompatible" -> WasmLoaderKind.D8NodeCompatible
else -> throw IllegalArgumentException("Unrecognized flavor for the wasm launcher")
}
writeCompilationResult(
result = res,
dir = outputFile.parentFile,
loaderKind = launcherKind,
fileNameBase = outputFile.nameWithoutExtension
)
@@ -120,66 +120,49 @@ fun WasmCompiledModuleFragment.generateJs(): String {
return runtime + jsCode
}
enum class WasmLoaderKind {
D8,
D8NodeCompatible,
NODE,
BROWSER,
}
fun generateJsWasmLoader(kind: WasmLoaderKind, wasmFilePath: String, externalJs: String): String {
val nodeExitOnD8 =
if (kind == WasmLoaderKind.D8NodeCompatible) "if ((typeof process !== 'undefined') && (typeof process.versions.node !== 'undefined')) process.exit(0)\n"
else ""
val instantiation = when (kind) {
WasmLoaderKind.D8, WasmLoaderKind.D8NodeCompatible ->
"""
const wasmModule = new WebAssembly.Module(read('$wasmFilePath', 'binary'));
const wasmInstance = new WebAssembly.Instance(wasmModule, { js_code });
""".trimIndent()
WasmLoaderKind.NODE ->
"""
const fs = require('fs');
var path = require('path');
const wasmBuffer = fs.readFileSync(path.resolve(__dirname, './$wasmFilePath'));
const wasmModule = new WebAssembly.Module(wasmBuffer);
const wasmInstance = new WebAssembly.Instance(wasmModule, { js_code });
""".trimIndent()
WasmLoaderKind.BROWSER ->
"""
const { instance: wasmInstance } = await WebAssembly.instantiateStreaming(fetch("$wasmFilePath"), { js_code });
""".trimIndent()
fun generateJsWasmLoader(wasmFilePath: String, externalJs: String): String =
externalJs + """
const isNodeJs = (typeof process !== 'undefined') && (process.release.name === 'node');
const isD8 = !isNodeJs && (typeof d8 !== 'undefined');
const isBrowser = !isNodeJs && !isD8 && (typeof window !== 'undefined');
if (!isNodeJs && !isD8 && !isBrowser) {
throw "Supported JS engine not detected";
}
val init =
"""
const wasmExports = wasmInstance.exports;
wasmExports.__init();
wasmExports.startUnitTests?.();
""".trimIndent()
val export = when (kind) {
WasmLoaderKind.D8, WasmLoaderKind.BROWSER ->
"export default wasmExports;\n"
WasmLoaderKind.NODE ->
"module.exports = wasmExports;\n"
WasmLoaderKind.D8NodeCompatible -> ""
let wasmInstance;
let require; // Placed here to give access to it from externals (js_code)
if (isNodeJs) {
const module = await import('node:module');
require = module.createRequire(import.meta.url);
const fs = require('fs');
const path = require('path');
const url = require('url');
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const wasmBuffer = fs.readFileSync(path.resolve(__dirname, './$wasmFilePath'));
const wasmModule = new WebAssembly.Module(wasmBuffer);
wasmInstance = new WebAssembly.Instance(wasmModule, { js_code });
}
return nodeExitOnD8 + externalJs + instantiation + init + export
}
if (isD8) {
const wasmBuffer = read('$wasmFilePath', 'binary');
const wasmModule = new WebAssembly.Module(wasmBuffer);
wasmInstance = new WebAssembly.Instance(wasmModule, { js_code });
}
if (isBrowser) {
wasmInstance = (await WebAssembly.instantiateStreaming(fetch('$wasmFilePath'), { js_code })).instance;
}
const wasmExports = wasmInstance.exports;
wasmExports.__init();
export default wasmExports;
""".trimIndent()
fun writeCompilationResult(
result: WasmCompilerResult,
dir: File,
loaderKind: WasmLoaderKind,
fileNameBase: String = "index",
) {
dir.mkdirs()
@@ -187,6 +170,7 @@ fun writeCompilationResult(
File(dir, "$fileNameBase.wat").writeText(result.wat)
}
File(dir, "$fileNameBase.wasm").writeBytes(result.wasm)
val jsWithLoader = generateJsWasmLoader(loaderKind, "./$fileNameBase.wasm", result.js)
File(dir, "$fileNameBase.js").writeText(jsWithLoader)
val jsWithLoader = generateJsWasmLoader("./$fileNameBase.wasm", result.js)
File(dir, "$fileNameBase.mjs").writeText(jsWithLoader)
}
-1
View File
@@ -45,7 +45,6 @@ where advanced options include:
Turn on range checks for the array access functions
-Xwasm-enable-asserts Turn on asserts
-Xwasm-kclass-fqn Enable support for FQ names in KClass
-Xwasm-launcher=esm|nodejs|d8 Picks flavor for the wasm launcher. Default is ESM.
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
-Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type
-Xbuiltins-from-sources Compile builtIns from sources