[Wasm] Basic CLI
-Xwasm option that will produce wasm instead of JS when used with -Xir-produce-js Does not affect klib production
This commit is contained in:
+4
-1
@@ -178,6 +178,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xerror-tolerance-policy", description = "Set up error tolerance policy (NONE, SEMANTIC, SYNTAX, ALL)")
|
||||
var errorTolerancePolicy: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(value = "-Xwasm", description = "Use experimental WebAssembly compiler backend")
|
||||
var wasm: Boolean by FreezableVar(false)
|
||||
|
||||
override fun checkIrSupport(languageVersionSettings: LanguageVersionSettings, collector: MessageCollector) {
|
||||
if (!isIrBackendEnabled()) return
|
||||
|
||||
@@ -196,4 +199,4 @@ fun K2JSCompilerArguments.isPreIrBackendDisabled(): Boolean =
|
||||
irOnly || irProduceJs || irProduceKlibFile
|
||||
|
||||
fun K2JSCompilerArguments.isIrBackendEnabled(): Boolean =
|
||||
irProduceKlibDir || irProduceJs || irProduceKlibFile
|
||||
irProduceKlibDir || irProduceJs || irProduceKlibFile || wasm
|
||||
@@ -12,6 +12,7 @@ dependencies {
|
||||
compile(project(":compiler:ir.backend.common"))
|
||||
compile(project(":compiler:ir.serialization.js"))
|
||||
compile(project(":compiler:backend.js"))
|
||||
compile(project(":compiler:backend.wasm"))
|
||||
compile(project(":js:js.translator"))
|
||||
compile(project(":js:js.serializer"))
|
||||
compile(project(":js:js.dce"))
|
||||
|
||||
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.cli.js
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||
import org.jetbrains.kotlin.backend.wasm.compileWasm
|
||||
import org.jetbrains.kotlin.backend.wasm.wasmPhases
|
||||
import org.jetbrains.kotlin.cli.common.*
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode.OK
|
||||
@@ -34,6 +37,7 @@ import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||
import org.jetbrains.kotlin.js.config.*
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.util.Logger
|
||||
@@ -216,6 +220,33 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
MainModule.SourceFiles(sourcesFiles)
|
||||
}
|
||||
|
||||
if (arguments.wasm) {
|
||||
val res = compileWasm(
|
||||
projectJs,
|
||||
mainModule,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
config.configuration,
|
||||
PhaseConfig(wasmPhases),
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = friendDependencies,
|
||||
exportedDeclarations = setOf(FqName("main"))
|
||||
)
|
||||
val outputWasmFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wasm")!!
|
||||
outputWasmFile.writeBytes(res.wasm)
|
||||
val outputWatFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wat")!!
|
||||
outputWatFile.writeText(res.wat)
|
||||
|
||||
val runner = """
|
||||
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||
wasmInstance.exports.main();
|
||||
""".trimIndent()
|
||||
|
||||
outputFile.writeText(res.js + "\n" + runner)
|
||||
return OK
|
||||
}
|
||||
|
||||
val compiledModule = try {
|
||||
compile(
|
||||
projectJs,
|
||||
|
||||
@@ -32,7 +32,7 @@ class WasmCompilerResult(val wat: String, val js: String, val wasm: ByteArray)
|
||||
|
||||
fun compileWasm(
|
||||
project: Project,
|
||||
files: List<KtFile>,
|
||||
mainModule: MainModule,
|
||||
analyzer: AbstractAnalyzerWithCompilerReport,
|
||||
configuration: CompilerConfiguration,
|
||||
phaseConfig: PhaseConfig,
|
||||
@@ -42,21 +42,26 @@ fun compileWasm(
|
||||
): WasmCompilerResult {
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||
loadIr(
|
||||
project, MainModule.SourceFiles(files), analyzer, configuration, allDependencies, friendDependencies,
|
||||
project, mainModule, analyzer, configuration, allDependencies, friendDependencies,
|
||||
PersistentIrFactory
|
||||
)
|
||||
|
||||
val allModules = when (mainModule) {
|
||||
is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment)
|
||||
is MainModule.Klib -> dependencyModules
|
||||
}
|
||||
|
||||
val moduleDescriptor = moduleFragment.descriptor
|
||||
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
|
||||
|
||||
// Load declarations referenced during `context` initialization
|
||||
dependencyModules.forEach {
|
||||
allModules.forEach {
|
||||
val irProviders = generateTypicalIrProviderList(it.descriptor, irBuiltIns, symbolTable, deserializer)
|
||||
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings)
|
||||
.generateUnboundSymbolsAsDependencies()
|
||||
}
|
||||
|
||||
val irFiles = dependencyModules.flatMap { it.files } + moduleFragment.files
|
||||
val irFiles = allModules.flatMap { it.files }
|
||||
|
||||
moduleFragment.files.clear()
|
||||
moduleFragment.files += irFiles
|
||||
@@ -77,13 +82,86 @@ fun compileWasm(
|
||||
watGenerator.appendWasmModule(linkedModule)
|
||||
val wat = watGenerator.toString()
|
||||
|
||||
val js = compiledWasmModule.generateJs()
|
||||
|
||||
val os = ByteArrayOutputStream()
|
||||
WasmIrToBinary(os, linkedModule).appendWasmModule()
|
||||
val byteArray = os.toByteArray()
|
||||
|
||||
return WasmCompilerResult(
|
||||
wat,
|
||||
generateStringLiteralsSupport(compiledWasmModule.stringLiterals),
|
||||
wat = wat,
|
||||
js = js,
|
||||
wasm = byteArray
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun WasmCompiledModuleFragment.generateJs(): String {
|
||||
val runtime = """
|
||||
const runtime = {
|
||||
String_plus(str1, str2) {
|
||||
return str1 + String(str2);
|
||||
},
|
||||
|
||||
String_getLength(str) {
|
||||
return str.length;
|
||||
},
|
||||
|
||||
String_getChar(str, index) {
|
||||
return str.charCodeAt(index);
|
||||
},
|
||||
|
||||
String_compareTo(str1, str2) {
|
||||
if (str1 > str2) return 1;
|
||||
if (str1 < str2) return -1;
|
||||
return 0;
|
||||
},
|
||||
|
||||
String_equals(str, other) {
|
||||
return str === other;
|
||||
},
|
||||
|
||||
String_subsequence(str, startIndex, endIndex) {
|
||||
return str.substring(startIndex, endIndex);
|
||||
},
|
||||
|
||||
String_getLiteral(index) {
|
||||
return runtime.stringLiterals[index];
|
||||
},
|
||||
|
||||
coerceToString(value) {
|
||||
return String(value);
|
||||
},
|
||||
|
||||
Char_toString(char) {
|
||||
return String.fromCharCode(char)
|
||||
},
|
||||
|
||||
JsArray_new(size) {
|
||||
return new Array(size);
|
||||
},
|
||||
|
||||
JsArray_get(array, index) {
|
||||
return array[index];
|
||||
},
|
||||
|
||||
JsArray_set(array, index, value) {
|
||||
array[index] = value;
|
||||
},
|
||||
|
||||
JsArray_getSize(array) {
|
||||
return array.length;
|
||||
},
|
||||
|
||||
identity(x) {
|
||||
return x;
|
||||
},
|
||||
|
||||
println(value) {
|
||||
console.log(">>> " + value)
|
||||
}
|
||||
};
|
||||
""".trimIndent()
|
||||
|
||||
return runtime + generateStringLiteralsSupport(stringLiterals)
|
||||
}
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ where advanced options include:
|
||||
-Xir-produce-klib-file Generate packed klib into file specified by -output. Disables pre-IR backend
|
||||
-Xmetadata-only Generate *.meta.js and *.kjsm files only
|
||||
-Xtyped-arrays Translate primitive arrays to JS typed arrays
|
||||
-Xwasm Use experimental WebAssembly compiler backend
|
||||
-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
|
||||
-Xcheck-phase-conditions Check pre- and postconditions on phases
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.MainModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.loadKlib
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||
@@ -114,7 +115,7 @@ abstract class BasicWasmBoxTest(
|
||||
testFunction: String
|
||||
) {
|
||||
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
|
||||
val debugMode =getBoolean("kotlin.js.debugMode")
|
||||
val debugMode = getBoolean("kotlin.js.debugMode")
|
||||
|
||||
val phaseConfig = if (debugMode) {
|
||||
val allPhasesSet = wasmPhases.toPhaseMap().values.toSet()
|
||||
@@ -137,7 +138,7 @@ abstract class BasicWasmBoxTest(
|
||||
|
||||
val compilerResult = compileWasm(
|
||||
project = config.project,
|
||||
files = filesToCompile,
|
||||
mainModule = MainModule.SourceFiles(filesToCompile),
|
||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||
configuration = config.configuration,
|
||||
phaseConfig = phaseConfig,
|
||||
@@ -150,8 +151,6 @@ abstract class BasicWasmBoxTest(
|
||||
outputWatFile.write(compilerResult.wat)
|
||||
outputWasmFile.writeBytes(compilerResult.wasm)
|
||||
|
||||
val runtime = File("libraries/stdlib/wasm/runtime/runtime.js").readText()
|
||||
|
||||
val testRunner = """
|
||||
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||
@@ -162,7 +161,7 @@ abstract class BasicWasmBoxTest(
|
||||
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
|
||||
""".trimIndent()
|
||||
|
||||
outputJsFile.write(runtime + "\n" + compilerResult.js + "\n" + testRunner)
|
||||
outputJsFile.write(compilerResult.js + "\n" + testRunner)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
const runtime = {
|
||||
/**
|
||||
* kotlin.String#plus(other: Any?): String
|
||||
* @return {string}
|
||||
*/
|
||||
String_plus(str1, str2) {
|
||||
if (typeof str1 != "string") throw `Illegal argument str1: ${str1}`;
|
||||
return str1 + String(str2);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
String_getLength(str) {
|
||||
if (typeof str != "string") throw `Illegal argument x: ${str}`;
|
||||
return str.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
String_getChar(str, index) {
|
||||
if (typeof str != "string") throw `Illegal argument str: ${str}`;
|
||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
||||
return str.charCodeAt(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
String_compareTo(str1, str2) {
|
||||
if (str1 > str2) return 1;
|
||||
if (str1 < str2) return -1;
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
String_equals(str, other) {
|
||||
// if (typeof str != "string") throw `Illegal argument str: ${str}`;
|
||||
return str === other;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
String_subsequence(str, startIndex, endIndex) {
|
||||
return str.substring(startIndex, endIndex);
|
||||
},
|
||||
|
||||
String_getLiteral(index) {
|
||||
return runtime.stringLiterals[index];
|
||||
},
|
||||
|
||||
coerceToString(value) {
|
||||
return String(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
Char_toString(char) {
|
||||
return String.fromCharCode(char)
|
||||
},
|
||||
|
||||
JsArray_new(size) {
|
||||
if (typeof size != "number") throw `Illegal argument size: ${size}`;
|
||||
return new Array(size);
|
||||
},
|
||||
|
||||
JsArray_get(array, index) {
|
||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
||||
if (array.length <= index) throw `Index out of bounds: index=${index} length=${array.length}`;
|
||||
return array[index];
|
||||
},
|
||||
|
||||
JsArray_set(array, index, value) {
|
||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
||||
if (array.length <= index) throw `Index out of bounds: index=${index} length=${array.length}`;
|
||||
array[index] = value;
|
||||
},
|
||||
|
||||
JsArray_getSize(array) {
|
||||
return array.length;
|
||||
},
|
||||
|
||||
identity(x) {
|
||||
return x;
|
||||
},
|
||||
|
||||
println(value) {
|
||||
console.log(">>> " + value)
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user