Introduced support for javascript Math and Kotlin Double to WASM32.
Implemented jsinterop tool for interaction with js math.
This commit is contained in:
committed by
alexander-gorshenev
parent
1399ac9777
commit
f703877160
@@ -0,0 +1,39 @@
|
||||
import org.jetbrains.kotlin.KlibInstall
|
||||
|
||||
apply plugin: 'konan'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url kotlinCompilerRepo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin"
|
||||
}
|
||||
|
||||
ext.konanHome = distDir.absolutePath
|
||||
ext.jvmArgs = project.findProperty("platformLibsJvmArgs") ?: "-Xmx3G"
|
||||
ext.setProperty("konan.home", konanHome)
|
||||
ext.setProperty("konan.jvmArgs", jvmArgs)
|
||||
}
|
||||
|
||||
konan.targets = [ 'wasm32' ]
|
||||
|
||||
konanArtifacts {
|
||||
library("jsinterop") {
|
||||
dependsOn ':wasm32CrossDist' // our plugin runs konanc, so make sure it is already there.
|
||||
extraOpts '-includeBinary', project.file('src/main/js/jsinterop.js')
|
||||
}
|
||||
}
|
||||
|
||||
task installJsRuntime(type: KlibInstall) {
|
||||
dependsOn konanArtifacts.jsinterop.wasm32
|
||||
klib = konanArtifacts.jsinterop.wasm32.artifact
|
||||
repo = file("$konanHome/klib/platform/wasm32")
|
||||
it.target = 'wasm32'
|
||||
}
|
||||
|
||||
+11
@@ -16,6 +16,17 @@ external public fun freeArena(arena: Arena)
|
||||
@SymbolName("Konan_js_pushIntToArena")
|
||||
external public fun pushIntToArena(arena: Arena, value: Int)
|
||||
|
||||
const val upperWord = 0xffffffff.toLong() shl 32
|
||||
|
||||
fun doubleUpper(value: Double): Int =
|
||||
((value.toBits() and upperWord) ushr 32) .toInt()
|
||||
|
||||
fun doubleLower(value: Double): Int =
|
||||
(value.toBits() and 0x00000000ffffffff) .toInt()
|
||||
|
||||
@SymbolName("ReturnSlot_getDouble")
|
||||
external public fun ReturnSlot_getDouble(): Double
|
||||
|
||||
@SymbolName("Kotlin_String_utf16pointer")
|
||||
external public fun stringPointer(message: String): Pointer
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
|
||||
internal fun resolveLibraries(staticLibraries: List<String>, libraryPaths: List<String>): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
staticLibraries.forEach { library ->
|
||||
|
||||
val resolution = libraryPaths.map { "$it/$library" }
|
||||
.find { File(it).exists }
|
||||
|
||||
if (resolution != null) {
|
||||
result.add(resolution)
|
||||
} else {
|
||||
error("Could not find '$library' binary in neither of $libraryPaths")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun argsToCompiler(staticLibraries: Array<String>, libraryPaths: Array<String>) = argsToCompiler(staticLibraries.toList(), libraryPaths.toList())
|
||||
|
||||
internal fun argsToCompiler(staticLibraries: List<String>, libraryPaths: List<String>) =
|
||||
resolveLibraries(staticLibraries, libraryPaths)
|
||||
.map { it -> listOf("-includeBinary", it) }
|
||||
.flatten()
|
||||
.toTypedArray()
|
||||
|
||||
+14
-14
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.native.interop.tool
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
|
||||
open class CommonInteropArguments : CommonToolArguments() {
|
||||
@Argument(value = "-flavor", valueDescription = "<flavor>", description = "One of: jvm, native")
|
||||
@Argument(value = "-flavor", valueDescription = "<flavor>", description = "One of: jvm, native or wasm")
|
||||
var flavor: String? = null
|
||||
|
||||
@Argument(value = "-pkg", valueDescription = "<fully qualified name>", description = "place generated bindings to the package")
|
||||
@@ -27,6 +27,18 @@ open class CommonInteropArguments : CommonToolArguments() {
|
||||
|
||||
@Argument(value = "-generated", valueDescription = "<dir>", description = "place generated bindings to the directory")
|
||||
var generated: String? = null
|
||||
|
||||
@Argument(value = "-natives", valueDescription = "<directory>", description = "where to put the built native files")
|
||||
var natives: String? = null
|
||||
|
||||
@Argument(value = "-manifest", valueDescription = "<file>", description = "library manifest addend")
|
||||
var manifest: String? = null
|
||||
|
||||
@Argument(value = "-staticLibrary", valueDescription = "<file>", description = "embed static library to the result")
|
||||
var staticLibrary: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-libraryPath", valueDescription = "<dir>", description = "add a library search path")
|
||||
var libraryPath: Array<String> = arrayOf()
|
||||
}
|
||||
|
||||
class CInteropArguments : CommonInteropArguments() {
|
||||
@@ -36,15 +48,9 @@ class CInteropArguments : CommonInteropArguments() {
|
||||
@Argument(value = "-target", valueDescription = "<target>", description = "native target to compile to")
|
||||
var target: String? = null
|
||||
|
||||
@Argument(value = "-natives", valueDescription = "<directory>", description = "where to put the built native files")
|
||||
var natives: String? = null
|
||||
|
||||
@Argument(value = "-def", valueDescription = "<file>", description = "the library definition file")
|
||||
var def: String? = null
|
||||
|
||||
@Argument(value = "-manifest", valueDescription = "<file>", description = "library manifest addend")
|
||||
var manifest: String? = null
|
||||
|
||||
@Argument(value = "-properties", valueDescription = "<file>", description = "an alternative location of konan.properties file")
|
||||
var properties: String? = null
|
||||
|
||||
@@ -66,14 +72,8 @@ class CInteropArguments : CommonInteropArguments() {
|
||||
var shims: Boolean = false
|
||||
|
||||
@Argument(value = "-linker", valueDescription = "<file>", description = "use specified linker")
|
||||
|
||||
var linker: String? = null
|
||||
|
||||
@Argument(value = "-staticLibrary", valueDescription = "<file>", description = "embed static library to the result")
|
||||
var staticLibrary: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-libraryPath", valueDescription = "<dir>", description = "add a library search path")
|
||||
var libraryPath: Array<String> = arrayOf()
|
||||
|
||||
@Argument(value = "-cstubsname", valueDescription = "<name>", description = "provide a name for the generated c stubs file")
|
||||
var cstubsname: String? = null
|
||||
|
||||
|
||||
-1
@@ -1001,5 +1001,4 @@ class StubGenerator(
|
||||
|
||||
val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
|
||||
}
|
||||
|
||||
+16
-32
@@ -16,23 +16,28 @@
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.tool.*
|
||||
import org.jetbrains.kotlin.konan.util.DefFile
|
||||
import org.jetbrains.kotlin.native.interop.gen.HeadersInclusionPolicyImpl
|
||||
import org.jetbrains.kotlin.native.interop.gen.ImportsImpl
|
||||
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
|
||||
import org.jetbrains.kotlin.native.interop.gen.wasm.processIdlLib
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import org.jetbrains.kotlin.native.interop.tool.*
|
||||
import java.io.File
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.nio.file.*
|
||||
import java.util.*
|
||||
|
||||
fun main(args: Array<String>) = interop(args, null)
|
||||
|
||||
fun interop(args: Array<String>, argsToCompiler: MutableList<String>? = null) {
|
||||
val arguments = parseCommandLine(args, CInteropArguments())
|
||||
processLib(arguments, argsToCompiler)
|
||||
fun main(args: Array<String>) {
|
||||
processCLib(args)
|
||||
}
|
||||
|
||||
fun interop(flavor: String, args: Array<String>) = when(flavor) {
|
||||
"jvm", "native" -> processCLib(args)
|
||||
"wasm" -> processIdlLib(args)
|
||||
else -> error("Unexpected flavor")
|
||||
}
|
||||
|
||||
// Options, whose values are space-separated and can be escaped.
|
||||
val escapedOptions = setOf("-compilerOpts", "-linkerOpts")
|
||||
|
||||
@@ -162,27 +167,6 @@ private fun selectNativeLanguage(config: DefFile.DefFileConfig): Language {
|
||||
error("Unexpected language '$language'. Possible values are: ${languages.keys.joinToString { "'$it'" }}")
|
||||
}
|
||||
|
||||
private fun resolveLibraries(staticLibraries: List<String>, libraryPaths: List<String>): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
staticLibraries.forEach { library ->
|
||||
|
||||
val resolution = libraryPaths.map { "$it/$library" }
|
||||
.find { File(it).exists() }
|
||||
|
||||
if (resolution != null) {
|
||||
result.add(resolution)
|
||||
} else {
|
||||
error("Could not find '$library' binary in neither of $libraryPaths")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun argsToCompiler(staticLibraries: List<String>, libraryPaths: List<String>): List<String> {
|
||||
return resolveLibraries(staticLibraries, libraryPaths)
|
||||
.map { it -> listOf("-includeBinary", it) } .flatten()
|
||||
}
|
||||
|
||||
private fun parseImports(imports: Array<String>): ImportsImpl {
|
||||
val headerIdToPackage = imports.map { arg ->
|
||||
val (pkg, joinedIds) = arg.split(':')
|
||||
@@ -247,9 +231,9 @@ private fun findFilesByGlobs(roots: List<Path>, globs: List<String>): Map<Path,
|
||||
}
|
||||
|
||||
|
||||
private fun processLib(arguments: CInteropArguments,
|
||||
argsToCompiler: MutableList<String>?) {
|
||||
private fun processCLib(args: Array<String>): Array<String>? {
|
||||
|
||||
val arguments = parseCommandLine(args, CInteropArguments())
|
||||
val userDir = System.getProperty("user.dir")
|
||||
val ktGenRoot = arguments.generated ?: userDir
|
||||
val nativeLibsDir = arguments.natives ?: userDir
|
||||
@@ -260,7 +244,7 @@ private fun processLib(arguments: CInteropArguments,
|
||||
|
||||
if (defFile == null && arguments.pkg == null) {
|
||||
usage()
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
val tool = ToolConfig(
|
||||
@@ -314,8 +298,6 @@ private fun processLib(arguments: CInteropArguments,
|
||||
val excludedFunctions = def.config.excludedFunctions.toSet()
|
||||
val staticLibraries = def.config.staticLibraries + arguments.staticLibrary
|
||||
val libraryPaths = def.config.libraryPaths + arguments.libraryPath
|
||||
argsToCompiler ?. let { it.addAll(argsToCompiler(staticLibraries, libraryPaths)) }
|
||||
|
||||
val fqParts = (arguments.pkg ?: def.config.packageName)?.let {
|
||||
it.split('.')
|
||||
} ?: defFile!!.name.split('.').reversed().drop(1)
|
||||
@@ -414,4 +396,6 @@ private fun processLib(arguments: CInteropArguments,
|
||||
if (!arguments.keepcstubs) {
|
||||
outCFile.delete()
|
||||
}
|
||||
|
||||
return argsToCompiler(staticLibraries, libraryPaths)
|
||||
}
|
||||
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.wasm
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.native.interop.gen.argsToCompiler
|
||||
import org.jetbrains.kotlin.native.interop.gen.wasm.idl.*
|
||||
import org.jetbrains.kotlin.native.interop.tool.CommonInteropArguments
|
||||
import org.jetbrains.kotlin.native.interop.tool.parseCommandLine
|
||||
|
||||
fun kotlinHeader(packageName: String): String {
|
||||
return "package $packageName\n" +
|
||||
"import kotlinx.wasm.jsinterop.*\n"
|
||||
}
|
||||
|
||||
fun Type.toKotlinType(argName: String? = null): String = when (this) {
|
||||
is idlVoid -> "Unit"
|
||||
is idlInt -> "Int"
|
||||
is idlFloat -> "Float"
|
||||
is idlDouble -> "Double"
|
||||
is idlString -> "String"
|
||||
is idlObject -> "JsValue"
|
||||
is idlFunction -> "KtFunction<R${argName!!}>"
|
||||
is idlInterfaceRef -> name
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
|
||||
fun Arg.wasmMapping(): String = when (type) {
|
||||
is idlVoid -> error("An arg can not be idlVoid")
|
||||
is idlInt -> name
|
||||
is idlFloat -> name
|
||||
is idlDouble -> "doubleUpper($name), doubleLower($name)"
|
||||
is idlString -> "stringPointer($name), stringLengthBytes($name)"
|
||||
is idlObject -> TODO("implement me")
|
||||
is idlFunction -> "wrapFunction<R$name>($name), ArenaManager.currentArena"
|
||||
is idlInterfaceRef -> TODO("Implement me")
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
|
||||
fun Type.wasmReturnArg(): String =
|
||||
when (this) {
|
||||
is idlVoid -> "ArenaManager.currentArena" // TODO: optimize.
|
||||
is idlInt -> "ArenaManager.currentArena"
|
||||
is idlFloat -> "ArenaManager.currentArena"
|
||||
is idlDouble -> "ArenaManager.currentArena"
|
||||
is idlString -> "ArenaManager.currentArena"
|
||||
is idlObject -> "ArenaManager.currentArena"
|
||||
is idlFunction -> "ArenaManager.currentArena"
|
||||
is idlInterfaceRef -> "ArenaManager.currentArena"
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
val Operation.wasmReturnArg: String get() = returnType.wasmReturnArg()
|
||||
val Attribute.wasmReturnArg: String get() = type.wasmReturnArg()
|
||||
|
||||
fun Arg.wasmArgNames(): List<String> = when (type) {
|
||||
is idlVoid -> error("An arg can not be idlVoid")
|
||||
is idlInt -> listOf(name)
|
||||
is idlFloat -> listOf(name)
|
||||
is idlDouble -> listOf("${name}Upper", "${name}Lower")
|
||||
is idlString -> listOf("${name}Ptr", "${name}Len")
|
||||
is idlObject -> TODO("implement me (idlObject)")
|
||||
is idlFunction -> listOf("${name}Index", "${name}ResultArena")
|
||||
is idlInterfaceRef -> TODO("Implement me (idlInterfaceRef)")
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
|
||||
fun Type.wasmReturnMapping(value: String): String = when (this) {
|
||||
is idlVoid -> ""
|
||||
is idlInt -> value
|
||||
is idlFloat -> value
|
||||
is idlDouble -> value
|
||||
is idlString -> "TODO(\"Implement me\")"
|
||||
is idlObject -> "JsValue(ArenaManager.currentArena, $value)"
|
||||
is idlFunction -> "TODO(\"Implement me\")"
|
||||
is idlInterfaceRef -> "$name(ArenaManager.currentArena, $value)"
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
|
||||
fun wasmFunctionName(functionName: String, interfaceName: String)
|
||||
= "knjs__${interfaceName}_$functionName"
|
||||
|
||||
fun wasmSetterName(propertyName: String, interfaceName: String)
|
||||
= "knjs_set__${interfaceName}_$propertyName"
|
||||
|
||||
fun wasmGetterName(propertyName: String, interfaceName: String)
|
||||
= "knjs_get__${interfaceName}_$propertyName"
|
||||
|
||||
val Operation.kotlinTypeParameters: String get() {
|
||||
val lambdaRetTypes = args.filter { it.type is idlFunction }
|
||||
.map { "R${it.name}" }. joinToString(", ")
|
||||
return if (lambdaRetTypes == "") "" else "<$lambdaRetTypes>"
|
||||
}
|
||||
|
||||
val Interface.wasmReceiverArgs get() =
|
||||
if (isGlobal) emptyList()
|
||||
else listOf("this.arena", "this.index")
|
||||
|
||||
fun Member.wasmReceiverArgs(parent: Interface) =
|
||||
if (isStatic) emptyList()
|
||||
else parent.wasmReceiverArgs
|
||||
|
||||
fun Type.generateKotlinCall(name: String, wasmArgList: String) =
|
||||
"$name($wasmArgList)"
|
||||
|
||||
fun Type.generateKotlinCallWithReturn(name: String, wasmArgList: String) =
|
||||
when(this) {
|
||||
is idlVoid -> " ${generateKotlinCall(name, wasmArgList)}\n"
|
||||
is idlDouble -> " ${generateKotlinCall(name, wasmArgList)}\n" +
|
||||
" val wasmRetVal = ReturnSlot_getDouble()\n"
|
||||
|
||||
else -> " val wasmRetVal = ${generateKotlinCall(name, wasmArgList)}\n"
|
||||
}
|
||||
|
||||
fun Operation.generateKotlinCallWithReturn(parent_name: String, wasmArgList: String) =
|
||||
returnType.generateKotlinCallWithReturn(
|
||||
wasmFunctionName(name, parent_name),
|
||||
wasmArgList)
|
||||
|
||||
fun Attribute.generateKotlinGetterCallWithReturn(parent_name: String, wasmArgList: String) =
|
||||
type.generateKotlinCallWithReturn(
|
||||
wasmGetterName(name, parent_name),
|
||||
wasmArgList)
|
||||
|
||||
fun Operation.generateKotlin(parent: Interface): String {
|
||||
val argList = args.map {
|
||||
"${it.name}: ${it.type.toKotlinType(it.name)}"
|
||||
}.joinToString(", ")
|
||||
|
||||
val wasmArgList = (wasmReceiverArgs(parent) + args.map(Arg::wasmMapping) + wasmReturnArg).joinToString(", ")
|
||||
|
||||
// TODO: there can be multiple Rs.
|
||||
return " fun $kotlinTypeParameters $name(" +
|
||||
argList +
|
||||
"): ${returnType.toKotlinType()} {\n" +
|
||||
generateKotlinCallWithReturn(parent.name, wasmArgList) +
|
||||
" return ${returnType.wasmReturnMapping("wasmRetVal")}\n"+
|
||||
" }\n\n"
|
||||
}
|
||||
|
||||
fun Attribute.generateKotlinSetter(parent: Interface): String {
|
||||
val kotlinType = type.toKotlinType(name)
|
||||
return " set(value: $kotlinType) {\n" +
|
||||
" ${wasmSetterName(name, parent.name)}(" +
|
||||
(wasmReceiverArgs(parent) + Arg("value", type).wasmMapping()).joinToString(", ") +
|
||||
")\n" +
|
||||
" }\n\n"
|
||||
}
|
||||
|
||||
fun Attribute.generateKotlinGetter(parent: Interface): String {
|
||||
val wasmArgList = (wasmReceiverArgs(parent) + wasmReturnArg).joinToString(", ")
|
||||
return " get() {\n" +
|
||||
generateKotlinGetterCallWithReturn(parent.name, wasmArgList) +
|
||||
" return ${type.wasmReturnMapping("wasmRetVal")}\n"+
|
||||
" }\n\n"
|
||||
}
|
||||
|
||||
fun Attribute.generateKotlin(parent: Interface): String {
|
||||
val kotlinType = type.toKotlinType(name)
|
||||
val varOrVal = if (readOnly) "val" else "var"
|
||||
return " $varOrVal $name: $kotlinType\n" +
|
||||
generateKotlinGetter(parent) +
|
||||
if (!readOnly) generateKotlinSetter(parent) else ""
|
||||
}
|
||||
|
||||
val Interface.wasmTypedReceiverArgs get() =
|
||||
if (isGlobal) emptyList()
|
||||
else listOf("arena: Int", "index: Int")
|
||||
|
||||
fun Member.wasmTypedReceiverArgs(parent: Interface) =
|
||||
if (isStatic) emptyList() else parent.wasmTypedReceiverArgs
|
||||
|
||||
fun Operation.generateWasmStub(parent: Interface): String {
|
||||
val wasmName = wasmFunctionName(this.name, parent.name)
|
||||
val allArgs = (wasmTypedReceiverArgs(parent) + args.toList().wasmTypedMapping() + wasmTypedReturnMapping).joinToString(", ")
|
||||
return "@SymbolName(\"$wasmName\")\n" +
|
||||
"external public fun $wasmName($allArgs): ${returnType.wasmReturnTypeMapping()}\n\n"
|
||||
}
|
||||
fun Attribute.generateWasmSetterStub(parent: Interface): String {
|
||||
val wasmSetter = wasmSetterName(this.name, parent.name)
|
||||
val allArgs = (wasmTypedReceiverArgs(parent) + Arg("value", this.type).wasmTypedMapping()).joinToString(", ")
|
||||
return "@SymbolName(\"$wasmSetter\")\n" +
|
||||
"external public fun $wasmSetter($allArgs): Unit\n\n"
|
||||
}
|
||||
fun Attribute.generateWasmGetterStub(parent: Interface): String {
|
||||
val wasmGetter = wasmGetterName(this.name, parent.name)
|
||||
val allArgs = (wasmTypedReceiverArgs(parent) + wasmTypedReturnMapping).joinToString(", ")
|
||||
return "@SymbolName(\"$wasmGetter\")\n" +
|
||||
"external public fun $wasmGetter($allArgs): Int\n\n"
|
||||
}
|
||||
fun Attribute.generateWasmStubs(parent: Interface) =
|
||||
generateWasmGetterStub(parent) +
|
||||
if (!readOnly) generateWasmSetterStub(parent) else ""
|
||||
|
||||
// TODO: consider using virtual methods
|
||||
fun Member.generateKotlin(parent: Interface): String = when (this) {
|
||||
is Operation -> this.generateKotlin(parent)
|
||||
is Attribute -> this.generateKotlin(parent)
|
||||
else -> error("Unexpected member")
|
||||
}
|
||||
|
||||
// TODO: consider using virtual methods
|
||||
fun Member.generateWasmStub(parent: Interface) =
|
||||
when (this) {
|
||||
is Operation -> this.generateWasmStub(parent)
|
||||
is Attribute -> this.generateWasmStubs(parent)
|
||||
else -> error("Unexpected member")
|
||||
|
||||
}
|
||||
|
||||
fun Arg.wasmTypedMapping()
|
||||
= this.wasmArgNames().map { "$it: Int" } .joinToString(", ")
|
||||
|
||||
// TODO: Optimize for simple types.
|
||||
fun Type.wasmTypedReturnMapping(): String = "resultArena: Int"
|
||||
|
||||
val Operation.wasmTypedReturnMapping get() = returnType.wasmTypedReturnMapping()
|
||||
|
||||
val Attribute.wasmTypedReturnMapping get() = type.wasmTypedReturnMapping()
|
||||
|
||||
fun List<Arg>.wasmTypedMapping()
|
||||
= this.map(Arg::wasmTypedMapping)
|
||||
|
||||
// TODO: more complex return types, such as returning a pair of Ints
|
||||
// will require a more complex approach.
|
||||
fun Type.wasmReturnTypeMapping()
|
||||
= if (this == idlVoid) "Unit" else "Int"
|
||||
|
||||
fun Interface.generateMemberWasmStubs() =
|
||||
members.map {
|
||||
it.generateWasmStub(this)
|
||||
}.joinToString("")
|
||||
|
||||
fun Interface.generateKotlinMembers() =
|
||||
members.filterNot { it.isStatic } .map {
|
||||
it.generateKotlin(this)
|
||||
}.joinToString("")
|
||||
|
||||
fun Interface.generateKotlinCompanion() =
|
||||
" companion object {\n" +
|
||||
members.filter { it.isStatic } .map {
|
||||
it.generateKotlin(this)
|
||||
}.joinToString("") +
|
||||
" }\n"
|
||||
|
||||
fun Interface.generateKotlinClassHeader() =
|
||||
"open class $name(arena: Int, index: Int): JsValue(arena, index) {\n" +
|
||||
" constructor(jsValue: JsValue): this(jsValue.arena, jsValue.index)\n"
|
||||
|
||||
fun Interface.generateKotlinClassFooter() =
|
||||
"}\n"
|
||||
|
||||
fun Interface.generateKotlinClassConverter() =
|
||||
"val JsValue.as$name: $name\n" +
|
||||
" get() {\n" +
|
||||
" return $name(this.arena, this.index)\n"+
|
||||
" }\n"
|
||||
|
||||
fun Interface.generateKotlin(): String {
|
||||
fun unlessGlobal(value: () -> String): String {
|
||||
return if (this.isGlobal) "" else value()
|
||||
}
|
||||
|
||||
return generateMemberWasmStubs() +
|
||||
unlessGlobal { generateKotlinClassHeader() } +
|
||||
generateKotlinMembers() +
|
||||
unlessGlobal {
|
||||
generateKotlinCompanion() +
|
||||
generateKotlinClassFooter() +
|
||||
generateKotlinClassConverter()
|
||||
}
|
||||
}
|
||||
|
||||
fun generateKotlin(pkg: String, interfaces: List<Interface>) =
|
||||
kotlinHeader(pkg) +
|
||||
interfaces.map {
|
||||
it.generateKotlin()
|
||||
}.joinToString("\n") +
|
||||
if (pkg == "kotlinx.interop.wasm.dom") // TODO: make it a general solution.
|
||||
"fun <R> setInterval(interval: Int, lambda: KtFunction<R>) = setInterval(lambda, interval)\n"
|
||||
else ""
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
fun Arg.composeWasmArgs(): String = when (type) {
|
||||
is idlVoid -> error("An arg can not be idlVoid")
|
||||
is idlInt -> ""
|
||||
is idlFloat -> ""
|
||||
is idlDouble ->
|
||||
" var $name = twoIntsToDouble(${name}Upper, ${name}Lower);\n"
|
||||
is idlString ->
|
||||
" var $name = toUTF16String(${name}Ptr, ${name}Len);\n"
|
||||
is idlObject -> TODO("implement me")
|
||||
is idlFunction ->
|
||||
" var $name = konan_dependencies.env.Konan_js_wrapLambda(lambdaResultArena, ${name}Index);\n"
|
||||
|
||||
is idlInterfaceRef -> TODO("Implement me")
|
||||
else -> error("Unexpected type")
|
||||
}
|
||||
|
||||
val Interface.receiver get() =
|
||||
if (isGlobal) "" else "kotlinObject(arena, obj)."
|
||||
|
||||
fun Member.receiver(parent: Interface) =
|
||||
if (isStatic) "${parent.name}." else parent.receiver
|
||||
|
||||
val Interface.wasmReceiverArgName get() =
|
||||
if (isGlobal) emptyList() else listOf("arena", "obj")
|
||||
|
||||
fun Member.wasmReceiverArgName(parent: Interface) =
|
||||
if (isStatic) emptyList() else parent.wasmReceiverArgName
|
||||
|
||||
val Operation.wasmReturnArgName get() =
|
||||
returnType.wasmReturnArgName
|
||||
|
||||
val Attribute.wasmReturnArgName get() =
|
||||
type.wasmReturnArgName
|
||||
|
||||
val Type.wasmReturnArgName get() =
|
||||
when (this) {
|
||||
is idlVoid -> emptyList()
|
||||
is idlInt -> emptyList()
|
||||
is idlFloat -> emptyList()
|
||||
is idlDouble -> emptyList()
|
||||
is idlString -> listOf("resultArena")
|
||||
is idlObject -> listOf("resultArena")
|
||||
is idlInterfaceRef -> listOf("resultArena")
|
||||
else -> error("Unexpected type: $this")
|
||||
}
|
||||
|
||||
val Type.wasmReturnExpression get() =
|
||||
when(this) {
|
||||
is idlVoid -> ""
|
||||
is idlInt -> "result"
|
||||
is idlFloat -> "result" // TODO: can we really pass floats as is?
|
||||
is idlDouble -> "doubleToReturnSlot(result)"
|
||||
is idlString -> "toArena(resultArena, result)"
|
||||
is idlObject -> "toArena(resultArena, result)"
|
||||
is idlInterfaceRef -> "toArena(resultArena, result)"
|
||||
else -> error("Unexpected type: $this")
|
||||
}
|
||||
|
||||
fun Operation.generateJs(parent: Interface): String {
|
||||
val allArgs = wasmReceiverArgName(parent) + args.map { it.wasmArgNames() }.flatten() + wasmReturnArgName
|
||||
val wasmMapping = allArgs.joinToString(", ")
|
||||
val argList = args.map { it.name }. joinToString(", ")
|
||||
val composedArgsList = args.map { it.composeWasmArgs() }. joinToString("")
|
||||
|
||||
return "\n ${wasmFunctionName(this.name, parent.name)}: function($wasmMapping) {\n" +
|
||||
composedArgsList +
|
||||
" var result = ${receiver(parent)}$name($argList);\n" +
|
||||
" return ${returnType.wasmReturnExpression};\n" +
|
||||
" }"
|
||||
}
|
||||
|
||||
fun Attribute.generateJsSetter(parent: Interface): String {
|
||||
val valueArg = Arg("value", type)
|
||||
val allArgs = wasmReceiverArgName(parent) + valueArg.wasmArgNames()
|
||||
val wasmMapping = allArgs.joinToString(", ")
|
||||
return "\n ${wasmSetterName(name, parent.name)}: function($wasmMapping) {\n" +
|
||||
valueArg.composeWasmArgs() +
|
||||
" ${receiver(parent)}$name = value;\n" +
|
||||
" }"
|
||||
}
|
||||
|
||||
fun Attribute.generateJsGetter(parent: Interface): String {
|
||||
val allArgs = wasmReceiverArgName(parent) + wasmReturnArgName
|
||||
val wasmMapping = allArgs.joinToString(", ")
|
||||
return "\n ${wasmGetterName(name, parent.name)}: function($wasmMapping) {\n" +
|
||||
" var result = ${receiver(parent)}$name;\n" +
|
||||
" return ${type.wasmReturnExpression};\n" +
|
||||
" }"
|
||||
}
|
||||
|
||||
fun Attribute.generateJs(parent: Interface) =
|
||||
generateJsGetter(parent) +
|
||||
if (!readOnly) ",\n${generateJsSetter(parent)}" else ""
|
||||
|
||||
fun Member.generateJs(parent: Interface): String = when (this) {
|
||||
is Operation -> this.generateJs(parent)
|
||||
is Attribute -> this.generateJs(parent)
|
||||
else -> error("Unexpected member")
|
||||
}
|
||||
|
||||
fun generateJs(interfaces: List<Interface>): String =
|
||||
"konan.libraries.push ({\n" +
|
||||
interfaces.map { interf ->
|
||||
interf.members.map { member ->
|
||||
member.generateJs(interf)
|
||||
}
|
||||
}.flatten() .joinToString(",\n") +
|
||||
"\n})\n"
|
||||
|
||||
const val idlMathPackage = "kotlinx.interop.wasm.math"
|
||||
const val idlDomPackage = "kotlinx.interop.wasm.dom"
|
||||
|
||||
fun processIdlLib(args: Array<String>): Array<String> {
|
||||
val arguments = parseCommandLine(args, CommonInteropArguments())
|
||||
// TODO: Refactor me.
|
||||
val userDir = System.getProperty("user.dir")
|
||||
val ktGenRoot = File(arguments.generated ?: userDir).mkdirs()
|
||||
val nativeLibsDir = File(arguments.natives ?: userDir).mkdirs()
|
||||
|
||||
val idl = when (arguments.pkg) {
|
||||
idlMathPackage-> idlMath
|
||||
idlDomPackage -> idlDom
|
||||
else -> throw IllegalArgumentException("Please choose either $idlMathPackage or $idlDomPackage for -pkg argument")
|
||||
}
|
||||
|
||||
File(ktGenRoot, "kotlin_stubs.kt").writeText(generateKotlin(arguments.pkg!!, idl))
|
||||
File(nativeLibsDir, "js_stubs.js").writeText(generateJs(idl))
|
||||
File(arguments.manifest!!).writeText("") // The manifest is currently unused for wasm.
|
||||
return argsToCompiler(arguments.staticLibrary, arguments.libraryPath)
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.wasm.idl
|
||||
|
||||
// This shall be an output of Web IDL parser.
|
||||
val idlDom = listOf(
|
||||
Interface("Context",
|
||||
Attribute("lineWidth", idlInt),
|
||||
Attribute("fillStyle", idlString),
|
||||
Attribute("strokeStyle", idlString),
|
||||
|
||||
Operation("lineTo", idlVoid, Arg("x", idlInt), Arg("y", idlInt)),
|
||||
Operation("moveTo", idlVoid, Arg("x", idlInt), Arg("y", idlInt)),
|
||||
Operation("beginPath", idlVoid),
|
||||
Operation("stroke", idlVoid),
|
||||
Operation("fillRect", idlVoid, Arg("x", idlInt), Arg("y", idlInt), Arg("width", idlInt), Arg("height", idlInt)),
|
||||
Operation("fillText", idlVoid, Arg("test", idlString), Arg("x", idlInt), Arg("y", idlInt), Arg("maxWidth", idlInt)),
|
||||
Operation("fill", idlVoid),
|
||||
Operation("closePath", idlVoid)
|
||||
),
|
||||
Interface("DOMRect",
|
||||
Attribute("left", idlInt),
|
||||
Attribute("right", idlInt),
|
||||
Attribute("top", idlInt),
|
||||
Attribute("bottom", idlInt)
|
||||
),
|
||||
Interface("Canvas",
|
||||
Operation("getContext", idlInterfaceRef("Context"), Arg("context", idlString)),
|
||||
Operation("getBoundingClientRect", idlInterfaceRef("DOMRect"))
|
||||
),
|
||||
Interface("Document",
|
||||
Operation("getElementById", idlObject, Arg("id", idlString))
|
||||
),
|
||||
Interface("MouseEvent",
|
||||
Attribute("clientX", idlInt, readOnly = true),
|
||||
Attribute("clientY", idlInt, readOnly = true)
|
||||
),
|
||||
Interface("Response",
|
||||
Operation("json", idlObject)
|
||||
),
|
||||
Interface("Promise",
|
||||
Operation("then", idlInterfaceRef("Promise"), Arg("lambda", idlFunction))
|
||||
),
|
||||
Interface("__Global",
|
||||
Attribute("document", idlInterfaceRef("Document"), readOnly = true),
|
||||
|
||||
Operation("fetch", idlInterfaceRef("Promise"), Arg("url", idlString)),
|
||||
Operation("setInterval", idlVoid, Arg("lambda", idlFunction), Arg("interval", idlInt))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.wasm.idl
|
||||
|
||||
// This is (as of now) a poor man's IDL representation.
|
||||
|
||||
interface Type
|
||||
interface Member {
|
||||
val isStatic: Boolean get() = false
|
||||
}
|
||||
|
||||
object idlVoid: Type
|
||||
object idlInt: Type
|
||||
object idlFloat: Type
|
||||
object idlDouble: Type
|
||||
object idlString: Type
|
||||
object idlObject: Type
|
||||
object idlFunction: Type
|
||||
|
||||
data class Attribute(val name: String, val type: Type,
|
||||
val readOnly: Boolean = false,
|
||||
override val isStatic: Boolean = false): Member
|
||||
|
||||
data class Arg(val name: String, val type: Type)
|
||||
|
||||
class Operation(val name: String, val returnType: Type,
|
||||
override val isStatic: Boolean = false,
|
||||
vararg val args: Arg): Member {
|
||||
|
||||
constructor(name: String, returnType: Type, vararg args: Arg) :
|
||||
this(name, returnType, false, *args)
|
||||
}
|
||||
|
||||
data class idlInterfaceRef(val name: String): Type
|
||||
class Interface(val name: String, vararg val members: Member) {
|
||||
val isGlobal = (name == "__Global")
|
||||
}
|
||||
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen.wasm.idl
|
||||
|
||||
// There are no WebIDL descriptions of Math,
|
||||
// so in any case this one will be a part of the project.
|
||||
// Although, may be in the form of our own WebIDL source.
|
||||
|
||||
val idlMath = listOf(
|
||||
Interface("Math",
|
||||
Attribute("E", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("LN2", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("LN10", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("LOG2E", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("LOG10E", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("PI", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("SQRT1_2", idlDouble, readOnly = true, isStatic = true),
|
||||
Attribute("SQRT2", idlDouble, readOnly = true, isStatic = true),
|
||||
|
||||
Operation("abs", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("acos", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("acosh", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("asin", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("asinh", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("atan", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("atanh", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("atan2", idlDouble, true, Arg("y", idlDouble), Arg("x", idlDouble)),
|
||||
Operation("cbrt", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("ceil", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("clz32", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("cos", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("cosh", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("exp", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("expm1", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("floor", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("fround", idlDouble, true, Arg("x", idlDouble)),
|
||||
//Operation("hypot([x[, y[, …]]]),
|
||||
//Operation("imul(x, y),
|
||||
Operation("log", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("log1p", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("log10", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("log2", idlDouble, true, Arg("x", idlDouble)),
|
||||
//Operation("max([x[, y[, …]]]), // TODO: Support varargs.
|
||||
//Operation("min([x[, y[, …]]]),
|
||||
Operation("pow", idlDouble, true, Arg("x", idlDouble), Arg("y", idlDouble)),
|
||||
Operation("random", idlDouble, true),
|
||||
Operation("round", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("sign", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("sin", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("sinh", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("sqrt", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("tan", idlDouble, true, Arg("x", idlDouble)),
|
||||
Operation("tanh", idlDouble, true, Arg("x", idlDouble)),
|
||||
//Operation("toSource(),
|
||||
Operation("trunc", idlDouble, true, Arg("x", idlDouble))
|
||||
)
|
||||
)
|
||||
+9
-2
@@ -278,13 +278,20 @@ internal open class WasmPlatform(distribution: Distribution)
|
||||
private fun javaScriptLink(jsFiles: List<String>, executable: String): String {
|
||||
val linkedJavaScript = File("$executable.js")
|
||||
|
||||
val linkerStub = "var konan = { libraries: [] };\n"
|
||||
val linkerHeader = "var konan = { libraries: [] };\n"
|
||||
val linkerFooter = """|if (isBrowser()) {
|
||||
| konan.moduleEntry([]);
|
||||
|} else {
|
||||
| konan.moduleEntry(arguments);
|
||||
|}""".trimMargin()
|
||||
|
||||
linkedJavaScript.writeBytes(linkerStub.toByteArray());
|
||||
linkedJavaScript.writeBytes(linkerHeader.toByteArray());
|
||||
|
||||
jsFiles.forEach {
|
||||
linkedJavaScript.appendBytes(File(it).readBytes())
|
||||
}
|
||||
|
||||
linkedJavaScript.appendBytes(linkerFooter.toByteArray());
|
||||
return linkedJavaScript.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2370,6 +2370,18 @@ if (isMac()) {
|
||||
}
|
||||
}
|
||||
|
||||
task jsinterop_math(type: RunStandaloneKonanTest) {
|
||||
doFirst {
|
||||
// TODO: We probably need a NativeInteropPlugin for jsinterop?
|
||||
"jsinterop -pkg kotlinx.interop.wasm.math -o $buildDir/jsmath -target wasm32".execute()
|
||||
|
||||
}
|
||||
disabled = (project.testTarget != 'wasm32')
|
||||
goldValue = "e = 2.718281828459045, pi = 3.141592653589793, sin(pi) = 1.2246467991473532E-16, sin(pi/2) = 1.0, ln(1) = 0.0, ln(e) = 1.0\n"
|
||||
source = "jsinterop/math.kt"
|
||||
flags = ["-r", "$buildDir", "-l", "jsmath"]
|
||||
}
|
||||
|
||||
task produce_dynamic(type: DynamicKonanTest) {
|
||||
disabled = (project.testTarget != null && project.testTarget != project.hostName)
|
||||
source = "produce_dynamic/simple/hello.kt"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import kotlinx.interop.wasm.math.*
|
||||
import kotlinx.wasm.jsinterop.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
val e = Math.E
|
||||
val pi = Math.PI
|
||||
|
||||
val sin_pi = Math.sin(pi)
|
||||
val sin_pi_2 = Math.sin(pi/2)
|
||||
val ln_1 = Math.log(1.0)
|
||||
val ln_e = Math.log(e)
|
||||
|
||||
println("e = $e, pi = $pi, sin(pi) = $sin_pi, sin(pi/2) = $sin_pi_2, ln(1) = $ln_1, ln(e) = $ln_e")
|
||||
}
|
||||
|
||||
@@ -311,6 +311,9 @@ targetList.each { target ->
|
||||
|
||||
task("${target}PlatformLibs") {
|
||||
dependsOn ":platformLibs:${target}Install"
|
||||
if (target == 'wasm32') {
|
||||
dependsOn 'Interop:JsRuntime:installJsRuntime'
|
||||
}
|
||||
}
|
||||
|
||||
task("${target}CrossDist") {
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2010-2017 JetBrains s.r.o.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
DIR="${BASH_SOURCE[0]%/*}"
|
||||
: ${DIR:="."}
|
||||
|
||||
"${DIR}"/run_konan jsinterop "$@"
|
||||
@@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
|
||||
rem Copyright 2010-2017 JetBrains s.r.o.
|
||||
rem
|
||||
rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
rem you may not use this file except in compliance with the License.
|
||||
rem You may obtain a copy of the License at
|
||||
rem
|
||||
rem http://www.apache.org/licenses/LICENSE-2.0
|
||||
rem
|
||||
rem Unless required by applicable law or agreed to in writing, software
|
||||
rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
rem See the License for the specific language governing permissions and
|
||||
rem limitations under the License.
|
||||
|
||||
call %~dps0run_konan.bat jsinterop %*
|
||||
@@ -2,7 +2,6 @@ import org.jetbrains.kotlin.KlibInstall
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
import org.jetbrains.kotlin.konan.util.DefFile
|
||||
|
||||
|
||||
import static org.jetbrains.kotlin.konan.util.VisibleNamedKt.getVisibleName
|
||||
|
||||
apply plugin: 'konan'
|
||||
|
||||
@@ -95,6 +95,24 @@ function toUTF16String(pointer, size) {
|
||||
return string;
|
||||
}
|
||||
|
||||
function twoIntsToDouble(upper, lower) {
|
||||
var buffer = new ArrayBuffer(8);
|
||||
var ints = new Int32Array(buffer);
|
||||
var doubles = new Float64Array(buffer);
|
||||
ints[1] = upper;
|
||||
ints[0] = lower;
|
||||
return doubles[0];
|
||||
}
|
||||
|
||||
function doubleToTwoInts(value) {
|
||||
var buffer = new ArrayBuffer(8);
|
||||
var ints = new Int32Array(buffer);
|
||||
var doubles = new Float64Array(buffer);
|
||||
doubles[0] = value;
|
||||
var twoInts = {upper: ints[1], lower: ints[0]};
|
||||
return twoInts
|
||||
}
|
||||
|
||||
function int32ToHeap(value, pointer) {
|
||||
heap[pointer] = value & 0xff;
|
||||
heap[pointer+1] = (value & 0xff00) >>> 8;
|
||||
@@ -102,6 +120,11 @@ function int32ToHeap(value, pointer) {
|
||||
heap[pointer+3] = (value & 0xff000000) >>> 24;
|
||||
}
|
||||
|
||||
function doubleToReturnSlot(value) {
|
||||
var twoInts = doubleToTwoInts(value);
|
||||
instance.exports.ReturnSlot_setDouble(twoInts.upper, twoInts.lower);
|
||||
}
|
||||
|
||||
function stackTop() {
|
||||
// Read the value module's `__stack_pointer` is initialized with.
|
||||
// It is the very first static in .data section.
|
||||
@@ -239,17 +262,22 @@ function instantiateAndRunSync(arraybuffer, args) {
|
||||
return invokeModule(instance, args)
|
||||
}
|
||||
|
||||
if (isBrowser()) {
|
||||
var filename = document.currentScript.getAttribute("wasm");
|
||||
fetch(filename).then( function(response) {
|
||||
return response.arrayBuffer();
|
||||
}).then(function(arraybuffer) {
|
||||
instantiateAndRun(arraybuffer, [filename]);
|
||||
});
|
||||
} else {
|
||||
// Invoke from d8.
|
||||
var arrayBuffer = readbuffer(arguments[0]);
|
||||
var exitStatus = instantiateAndRunSync(arrayBuffer, arguments);
|
||||
quit(exitStatus);
|
||||
konan.moduleEntry = function (args) {
|
||||
if (isBrowser()) {
|
||||
if (!document.currentScript.hasAttribute("wasm")) {
|
||||
throw new Error('Could not find the wasm attribute pointing to the WebAssembly binary.') ;
|
||||
}
|
||||
var filename = document.currentScript.getAttribute("wasm");
|
||||
fetch(filename).then( function(response) {
|
||||
return response.arrayBuffer();
|
||||
}).then(function(arraybuffer) {
|
||||
instantiateAndRun(arraybuffer, [filename]);
|
||||
});
|
||||
} else {
|
||||
// Invoke from d8.
|
||||
var arrayBuffer = readbuffer(args[0]);
|
||||
var exitStatus = instantiateAndRunSync(arrayBuffer, args);
|
||||
quit(exitStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ReturnSlot.h"
|
||||
|
||||
#ifdef KONAN_WASM
|
||||
namespace {
|
||||
THREAD_LOCAL_VARIABLE long long storage;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
KDouble ReturnSlot_getDouble() {
|
||||
return *reinterpret_cast<KDouble*>(&::storage);
|
||||
}
|
||||
|
||||
void ReturnSlot_setDouble(KInt upper, KInt lower) {
|
||||
reinterpret_cast<KInt*>(&::storage)[0] = lower;
|
||||
reinterpret_cast<KInt*>(&::storage)[1] = upper;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
#ifdef KONAN_WASM
|
||||
extern "C" {
|
||||
KDouble ReturnSlot_getDouble();
|
||||
void ReturnSlot_setDouble(KInt upper, KInt lower);
|
||||
}
|
||||
#endif
|
||||
@@ -12,25 +12,12 @@ if defined KONAN_HOME (
|
||||
if not exist build\bin\ (mkdir build\bin)
|
||||
if not exist build\klib\ (mkdir build\klib)
|
||||
|
||||
call konanc %DIR%\src\jsinterop\kotlin ^
|
||||
-includeBinary %DIR%\src\jsinterop\js\jsinterop.js ^
|
||||
-p library -o %DIR%\build\klib\jsinterop -target wasm32 || exit /b
|
||||
|
||||
call konanc %DIR%\src\stubGenerator\kotlin ^
|
||||
-e org.jetbrains.kotlin.konan.jsinterop.tool.main ^
|
||||
-o %DIR%\build\bin\generator || exit /b
|
||||
|
||||
::: TODO: make a couple of args to name the result, for example.
|
||||
%DIR%\build\bin\generator.exe || exit /b
|
||||
|
||||
::: TODO: use the proper path and names
|
||||
call konanc kotlin_stubs.kt ^
|
||||
-includeBinary js_stubs.js ^
|
||||
-l %DIR%\build\klib\jsinterop ^
|
||||
-p library -o %DIR%\build\klib\canvas -target wasm32 || exit /b
|
||||
call jsinterop -pkg kotlinx.interop.wasm.dom ^
|
||||
-o %DIR%\build\klib\dom -target wasm32 || exit /b
|
||||
|
||||
call konanc %DIR%\src\main\kotlin ^
|
||||
-r %DIR%\build\klib -l jsinterop -l canvas ^
|
||||
-r %DIR%\build\klib -l dom ^
|
||||
-o %DIR%\build\bin\html5Canvas -target wasm32 || exit 1
|
||||
|
||||
echo "Artifact path is %DIR%\build\bin\html5Canvas.wasm"
|
||||
|
||||
@@ -20,25 +20,11 @@ COMPILER_ARGS=${!var} # add -opt for an optimized build.
|
||||
mkdir -p $DIR/build/bin
|
||||
mkdir -p $DIR/build/klib
|
||||
|
||||
konanc $DIR/src/jsinterop/kotlin \
|
||||
-includeBinary $DIR/src/jsinterop/js/jsinterop.js \
|
||||
-p library -o $DIR/build/klib/jsinterop -target wasm32 || exit 1
|
||||
|
||||
konanc $DIR/src/stubGenerator/kotlin \
|
||||
-e org.jetbrains.kotlin.konan.jsinterop.tool.main \
|
||||
-o $DIR/build/bin/generator || exit 1
|
||||
|
||||
# TODO: make a couple of args to name the result, for example.
|
||||
$DIR/build/bin/generator.kexe || exit 1
|
||||
|
||||
# TODO: use the proper path and names
|
||||
konanc kotlin_stubs.kt \
|
||||
-includeBinary js_stubs.js \
|
||||
-l $DIR/build/klib/jsinterop \
|
||||
-p library -o $DIR/build/klib/canvas -target wasm32 || exit 1
|
||||
jsinterop -pkg kotlinx.interop.wasm.dom \
|
||||
-o $DIR/build/klib/dom -target wasm32 || exit 1
|
||||
|
||||
konanc $DIR/src/main/kotlin \
|
||||
-r $DIR/build/klib -l jsinterop -l canvas \
|
||||
-r $DIR/build/klib -l dom \
|
||||
-o $DIR/build/bin/html5Canvas -target wasm32 || exit 1
|
||||
|
||||
echo "Artifact path is $DIR/build/bin/html5Canvas.wasm"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import html5.minimal.*
|
||||
import kotlinx.interop.wasm.dom.*
|
||||
import kotlinx.wasm.jsinterop.*
|
||||
import konan.internal.ExportForCppRuntime
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
include ':dependencies'
|
||||
include ':Interop:Indexer'
|
||||
include ':Interop:JsRuntime'
|
||||
include ':Interop:StubGenerator'
|
||||
include ':Interop:Runtime'
|
||||
include ':llvmDebugInfoC'
|
||||
|
||||
@@ -118,6 +118,8 @@ data class File constructor(internal val javaPath: Path) {
|
||||
Files.write(javaPath, lines)
|
||||
}
|
||||
|
||||
fun writeText(text: String): Unit = writeLines(listOf(text))
|
||||
|
||||
fun forEachLine(action: (String) -> Unit) {
|
||||
Files.lines(javaPath).use { lines ->
|
||||
lines.forEach { action(it) }
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.utilities
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.library.defaultResolver
|
||||
import org.jetbrains.kotlin.backend.konan.library.impl.KonanLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.library.resolveLibrariesRecursive
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.properties.loadProperties
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
private val NODEFAULTLIBS = "-nodefaultlibs"
|
||||
private val PURGE_USER_LIBS = "--purge_user_libs"
|
||||
|
||||
// TODO: this function should eventually be eliminated from 'utilities'.
|
||||
// The interaction of interop and the compler should be streamlined.
|
||||
|
||||
fun invokeInterop(flavor: String, args: Array<String>): Array<String> {
|
||||
val cinteropArgFilter = listOf(NODEFAULTLIBS, PURGE_USER_LIBS)
|
||||
|
||||
var outputFileName = "nativelib"
|
||||
var target = "host"
|
||||
val libraries = mutableListOf<String>()
|
||||
val repos = mutableListOf<String>()
|
||||
var noDefaultLibs = false
|
||||
var purgeUserLibs = false
|
||||
for (i in args.indices) {
|
||||
val arg = args[i]
|
||||
val nextArg = args.getOrNull(i + 1)
|
||||
if (arg.startsWith("-o"))
|
||||
outputFileName = nextArg ?: outputFileName
|
||||
if (arg == "-target")
|
||||
target = nextArg ?: target
|
||||
if (arg == "-library")
|
||||
libraries.addIfNotNull(nextArg)
|
||||
if (arg == "-r" || arg == "-repo")
|
||||
repos.addIfNotNull(nextArg)
|
||||
if (arg == NODEFAULTLIBS)
|
||||
noDefaultLibs = true
|
||||
if (arg == PURGE_USER_LIBS)
|
||||
purgeUserLibs = true
|
||||
}
|
||||
|
||||
|
||||
val buildDir = File("$outputFileName-build")
|
||||
val generatedDir = File(buildDir, "kotlin")
|
||||
val nativesDir = File(buildDir, "natives")
|
||||
val cstubsName ="cstubs"
|
||||
val manifest = File(buildDir, "manifest.properties")
|
||||
|
||||
val targetManager = TargetManager(target)
|
||||
val resolver = defaultResolver(repos, targetManager)
|
||||
val allLibraries = resolver.resolveLibrariesRecursive(
|
||||
libraries, targetManager.target, noStdLib = true, noDefaultLibs = noDefaultLibs
|
||||
)
|
||||
|
||||
val importArgs = allLibraries.flatMap {
|
||||
val library = KonanLibrary(it.libraryFile)
|
||||
val manifestProperties = library.manifestFile.loadProperties()
|
||||
// TODO: handle missing properties?
|
||||
manifestProperties["package"]?.let {
|
||||
val pkg = it as String
|
||||
val headerIds = (manifestProperties["includedHeaders"] as String).split(' ')
|
||||
val arg = "$pkg:${headerIds.joinToString(";")}"
|
||||
listOf("-import", arg)
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
val additionalArgs = listOf<String>(
|
||||
"-generated", generatedDir.path,
|
||||
"-natives", nativesDir.path,
|
||||
"-cstubsname", cstubsName,
|
||||
"-manifest", manifest.path,
|
||||
"-flavor", flavor
|
||||
) + importArgs
|
||||
|
||||
val cinteropArgs = (additionalArgs + args.filter { it !in cinteropArgFilter }).toTypedArray()
|
||||
|
||||
val cinteropArgsToCompiler = interop(flavor, cinteropArgs) ?: emptyArray<String>()
|
||||
|
||||
val nativeStubs =
|
||||
if (flavor == "wasm")
|
||||
arrayOf("-includeBinary", File(nativesDir, "js_stubs.js").path)
|
||||
else
|
||||
arrayOf("-nativelibrary",File(nativesDir, "$cstubsName.bc").path)
|
||||
|
||||
val konancArgs = arrayOf(
|
||||
generatedDir.path,
|
||||
"-produce", "library",
|
||||
"-o", outputFileName,
|
||||
"-target", target,
|
||||
"-manifest", manifest.path) +
|
||||
nativeStubs +
|
||||
cinteropArgsToCompiler +
|
||||
libraries.flatMap { listOf("-library", it) } +
|
||||
repos.flatMap { listOf("-repo", it) } +
|
||||
(if (noDefaultLibs) arrayOf(NODEFAULTLIBS) else emptyArray()) +
|
||||
(if (purgeUserLibs) arrayOf(PURGE_USER_LIBS) else emptyArray())
|
||||
|
||||
return konancArgs
|
||||
}
|
||||
|
||||
|
||||
@@ -16,105 +16,23 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.utilities
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.library.defaultResolver
|
||||
import org.jetbrains.kotlin.backend.konan.library.impl.KonanLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.library.resolveLibrariesRecursive
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.properties.loadProperties
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.cli.bc.main as konancMain
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
||||
import org.jetbrains.kotlin.cli.klib.main as klibMain
|
||||
|
||||
private val NODEFAULTLIBS = "-nodefaultlibs"
|
||||
private val PURGE_USER_LIBS = "--purge_user_libs"
|
||||
|
||||
fun invokeCinterop(args: Array<String>) {
|
||||
val cinteropArgFilter = listOf(NODEFAULTLIBS, PURGE_USER_LIBS)
|
||||
|
||||
var outputFileName = "nativelib"
|
||||
var target = "host"
|
||||
val libraries = mutableListOf<String>()
|
||||
val repos = mutableListOf<String>()
|
||||
var noDefaultLibs = false
|
||||
var purgeUserLibs = false
|
||||
for (i in args.indices) {
|
||||
val arg = args[i]
|
||||
val nextArg = args.getOrNull(i + 1)
|
||||
if (arg.startsWith("-o"))
|
||||
outputFileName = nextArg ?: outputFileName
|
||||
if (arg == "-target")
|
||||
target = nextArg ?: target
|
||||
if (arg == "-library")
|
||||
libraries.addIfNotNull(nextArg)
|
||||
if (arg == "-r" || arg == "-repo")
|
||||
repos.addIfNotNull(nextArg)
|
||||
if (arg == NODEFAULTLIBS)
|
||||
noDefaultLibs = true
|
||||
if (arg == PURGE_USER_LIBS)
|
||||
purgeUserLibs = true
|
||||
}
|
||||
|
||||
|
||||
val buildDir = File("$outputFileName-build")
|
||||
val generatedDir = File(buildDir, "kotlin")
|
||||
val nativesDir = File(buildDir, "natives")
|
||||
val cstubsName ="cstubs"
|
||||
val manifest = File(buildDir, "manifest.properties")
|
||||
|
||||
val targetManager = TargetManager(target)
|
||||
val resolver = defaultResolver(repos, targetManager)
|
||||
val allLibraries = resolver.resolveLibrariesRecursive(
|
||||
libraries, targetManager.target, noStdLib = true, noDefaultLibs = noDefaultLibs
|
||||
)
|
||||
|
||||
val importArgs = allLibraries.flatMap {
|
||||
val library = KonanLibrary(it.libraryFile)
|
||||
val manifestProperties = library.manifestFile.loadProperties()
|
||||
// TODO: handle missing properties?
|
||||
manifestProperties["package"]?.let {
|
||||
val pkg = it as String
|
||||
val headerIds = (manifestProperties["includedHeaders"] as String).split(' ')
|
||||
val arg = "$pkg:${headerIds.joinToString(";")}"
|
||||
listOf("-import", arg)
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
val additionalArgs = listOf<String>(
|
||||
"-generated", generatedDir.path,
|
||||
"-natives", nativesDir.path,
|
||||
"-cstubsname", cstubsName,
|
||||
"-manifest", manifest.path,
|
||||
"-flavor", "native"
|
||||
) + importArgs
|
||||
|
||||
val cinteropArgs = (additionalArgs + args.filter { it !in cinteropArgFilter }).toTypedArray()
|
||||
val cinteropArgsToCompiler = mutableListOf<String>()
|
||||
interop(cinteropArgs, cinteropArgsToCompiler)
|
||||
|
||||
val konancArgs = arrayOf(
|
||||
generatedDir.path,
|
||||
"-produce", "library",
|
||||
"-nativelibrary", File(nativesDir, "$cstubsName.bc").path,
|
||||
"-o", outputFileName,
|
||||
"-target", target,
|
||||
"-manifest", manifest.path
|
||||
) + cinteropArgsToCompiler + libraries.flatMap { listOf("-library", it) } + repos.flatMap { listOf("-repo", it) } +
|
||||
(if (noDefaultLibs) arrayOf(NODEFAULTLIBS) else emptyArray()) +
|
||||
(if (purgeUserLibs) arrayOf(PURGE_USER_LIBS) else emptyArray())
|
||||
|
||||
konancMain(konancArgs)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val utilityName = args[0]
|
||||
val utilityArgs = args.drop(1).toTypedArray()
|
||||
when (utilityName) {
|
||||
"konanc" ->
|
||||
konancMain(utilityArgs)
|
||||
"cinterop" ->
|
||||
invokeCinterop(utilityArgs)
|
||||
"cinterop" -> {
|
||||
val konancArgs = invokeInterop("native", utilityArgs)
|
||||
konancMain(konancArgs)
|
||||
}
|
||||
"jsinterop" -> {
|
||||
val konancArgs = invokeInterop("wasm", utilityArgs)
|
||||
konancMain(konancArgs)
|
||||
}
|
||||
"klib" ->
|
||||
klibMain(utilityArgs)
|
||||
else ->
|
||||
|
||||
Reference in New Issue
Block a user