[Wasm] Use static import for wasm imports
^KT-65777 fixed
This commit is contained in:
committed by
Space Team
parent
68e5fc7344
commit
abb5f55087
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.wasm.ir2wasm.JsModuleAndQualifierReference
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.toJsStringLiteral
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.JsInteropFunctionsLowering
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.markExportedDeclarations
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.SourceMapGenerator
|
||||
import org.jetbrains.kotlin.backend.wasm.export.ExportModelGenerator
|
||||
@@ -34,6 +35,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToBinary
|
||||
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToText
|
||||
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
|
||||
@@ -176,7 +178,10 @@ fun compileWasm(
|
||||
"./$baseFileName.wasm",
|
||||
backendContext.jsModuleAndQualifierReferences
|
||||
)
|
||||
jsWrapper = generateEsmExportsWrapper("./$baseFileName.uninstantiated.mjs")
|
||||
jsWrapper = compiledWasmModule.generateEsmExportsWrapper(
|
||||
"./$baseFileName.uninstantiated.mjs",
|
||||
backendContext.jsModuleAndQualifierReferences
|
||||
)
|
||||
} else {
|
||||
jsUninstantiatedWrapper = null
|
||||
jsWrapper = compiledWasmModule.generateAsyncWasiWrapper("./$baseFileName.wasm")
|
||||
@@ -215,7 +220,8 @@ const wasmInstance = new WebAssembly.Instance(wasmModule, wasi.getImportObject()
|
||||
|
||||
wasi.initialize(wasmInstance);
|
||||
|
||||
export default wasmInstance.exports;
|
||||
const exports = wasmInstance.exports
|
||||
${generateExports()}
|
||||
"""
|
||||
|
||||
fun WasmCompiledModuleFragment.generateAsyncJsWrapper(
|
||||
@@ -234,7 +240,7 @@ fun WasmCompiledModuleFragment.generateAsyncJsWrapper(
|
||||
.sorted()
|
||||
.joinToString("") {
|
||||
val moduleSpecifier = it.toJsStringLiteral()
|
||||
" $moduleSpecifier: imports[$moduleSpecifier] ?? await import($moduleSpecifier),\n"
|
||||
" $moduleSpecifier: imports[$moduleSpecifier],\n"
|
||||
}
|
||||
|
||||
val referencesToQualifiedAndImportedDeclarations = jsModuleAndQualifierReferences
|
||||
@@ -246,7 +252,7 @@ fun WasmCompiledModuleFragment.generateAsyncJsWrapper(
|
||||
append(it.jsVariableName)
|
||||
append(" = ")
|
||||
if (module != null) {
|
||||
append("(imports[${module.toJsStringLiteral()}] ?? await import(${module.toJsStringLiteral()}))")
|
||||
append("imports[${module.toJsStringLiteral()}]")
|
||||
if (qualifier != null)
|
||||
append(".")
|
||||
}
|
||||
@@ -352,10 +358,59 @@ For more information, see https://kotl.in/wasm-help
|
||||
"""
|
||||
}
|
||||
|
||||
fun generateEsmExportsWrapper(asyncWrapperFileName: String): String = /*language=js */ """
|
||||
fun WasmCompiledModuleFragment.generateEsmExportsWrapper(
|
||||
asyncWrapperFileName: String,
|
||||
jsModuleAndQualifierReferences: MutableSet<JsModuleAndQualifierReference>
|
||||
): String {
|
||||
val importedModules = jsModuleImports
|
||||
.map {
|
||||
val moduleSpecifier = it.toJsStringLiteral().toString()
|
||||
val importVariableString = JsModuleAndQualifierReference.encode(it)
|
||||
moduleSpecifier to importVariableString
|
||||
}
|
||||
|
||||
val referencesToImportedDeclarations = jsModuleAndQualifierReferences
|
||||
.filter { it.module != null }
|
||||
.map {
|
||||
val module = it.module!!
|
||||
val stringLiteral = module.toJsStringLiteral().toString()
|
||||
stringLiteral to if (it.qualifier != null) {
|
||||
it.importVariableName
|
||||
} else {
|
||||
it.jsVariableName
|
||||
}
|
||||
}
|
||||
|
||||
val allModules = (importedModules + referencesToImportedDeclarations)
|
||||
.distinctBy {
|
||||
it.first
|
||||
}.sortedBy { it.first }
|
||||
|
||||
val importsImportedSection = allModules.joinToString("\n") {
|
||||
buildString {
|
||||
append("import * as ")
|
||||
append(it.second)
|
||||
append(" from ")
|
||||
append(it.first)
|
||||
append(";")
|
||||
}
|
||||
}
|
||||
|
||||
val imports = allModules.joinToString(",\n") {
|
||||
" ${it.first}: ${it.second}"
|
||||
}
|
||||
|
||||
/*language=js */
|
||||
return """
|
||||
$importsImportedSection
|
||||
import { instantiate } from ${asyncWrapperFileName.toJsStringLiteral()};
|
||||
export default (await instantiate()).exports;
|
||||
|
||||
const exports = (await instantiate({
|
||||
$imports
|
||||
})).exports;
|
||||
${generateExports()}
|
||||
"""
|
||||
}
|
||||
|
||||
fun writeCompilationResult(
|
||||
result: WasmCompilerResult,
|
||||
@@ -384,3 +439,37 @@ fun writeCompilationResult(
|
||||
File(dir, "$fileNameBase.d.ts").writeText(result.dts)
|
||||
}
|
||||
}
|
||||
|
||||
fun WasmCompiledModuleFragment.generateExports(): String {
|
||||
// TODO: necessary to move export check onto common place
|
||||
val exportNames = exports
|
||||
.filterNot { it.name.startsWith(JsInteropFunctionsLowering.CALL_FUNCTION) }
|
||||
.ifNotEmpty {
|
||||
joinToString(",\n") {
|
||||
" ${it.name}"
|
||||
}
|
||||
}?.let {
|
||||
"""
|
||||
|const {
|
||||
|$it
|
||||
|}
|
||||
""".trimMargin()
|
||||
}
|
||||
|
||||
/*language=js */
|
||||
return """
|
||||
export default new Proxy(exports, {
|
||||
_shownError: false,
|
||||
get(target, prop) {
|
||||
if (!this._shownError) {
|
||||
this._shownError = true;
|
||||
if (typeof console !== "undefined") {
|
||||
console.error("Do not use default import. Use corresponding named import instead.")
|
||||
}
|
||||
}
|
||||
return target[prop];
|
||||
}
|
||||
});
|
||||
${exportNames?.let { "export $it = exports;" }}
|
||||
"""
|
||||
}
|
||||
|
||||
+14
-5
@@ -15,11 +15,20 @@ data class JsModuleAndQualifierReference(
|
||||
val module: String?,
|
||||
val qualifier: String?,
|
||||
) {
|
||||
val jsVariableName = run {
|
||||
private val moduleBase64 = module?.let { encode(it) }.orEmpty()
|
||||
|
||||
private val qualifierBase64 = qualifier?.let { encode(it) }.orEmpty()
|
||||
|
||||
val jsVariableName = "_ref_${moduleBase64}_$qualifierBase64"
|
||||
|
||||
val importVariableName = "_import_${moduleBase64}_$qualifierBase64"
|
||||
|
||||
companion object {
|
||||
// Encode variable name as base64 to have a valid unique JS identifier
|
||||
val encoder = Base64.getEncoder().withoutPadding()
|
||||
val moduleBase64 = module?.let { encoder.encodeToString(module.encodeToByteArray()) }.orEmpty()
|
||||
val qualifierBase64 = qualifier?.let { encoder.encodeToString(qualifier.encodeToByteArray()) }.orEmpty()
|
||||
"_ref_${moduleBase64}_$qualifierBase64"
|
||||
private val encoder = Base64.getEncoder().withoutPadding()
|
||||
|
||||
fun encode(value: String): String {
|
||||
return encoder.encodeToString(value.encodeToByteArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -466,7 +466,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
|
||||
private fun createKotlinClosureCaller(info: FunctionTypeInfo): IrSimpleFunction {
|
||||
val result = context.irFactory.buildFun {
|
||||
name = Name.identifier("__callFunction_${info.signatureString}")
|
||||
name = Name.identifier("$CALL_FUNCTION${info.signatureString}")
|
||||
returnType = info.adaptedResultType
|
||||
origin = KOTLIN_TO_JS_CLOSURE_ORIGIN
|
||||
}
|
||||
@@ -522,7 +522,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
append("(f) => (")
|
||||
appendParameterList(arity)
|
||||
append(") => wasmExports[")
|
||||
append("__callFunction_${info.signatureString}".toJsStringLiteral())
|
||||
append("$CALL_FUNCTION${info.signatureString}".toJsStringLiteral())
|
||||
append("](f, ")
|
||||
appendParameterList(arity)
|
||||
append(")")
|
||||
@@ -911,6 +911,10 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CALL_FUNCTION = "__callFunction_"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun StringBuilder.appendParameterList(size: Int, name: String = "p", isEnd: Boolean = true) =
|
||||
|
||||
Reference in New Issue
Block a user