diff --git a/samples/html5Canvas/src/jsinterop_tool/kotlin/stubGenerator.kt b/samples/html5Canvas/src/jsinterop_tool/kotlin/stubGenerator.kt index 78624b097a4..02f39dcf2a2 100644 --- a/samples/html5Canvas/src/jsinterop_tool/kotlin/stubGenerator.kt +++ b/samples/html5Canvas/src/jsinterop_tool/kotlin/stubGenerator.kt @@ -56,6 +56,8 @@ fun Arg.wasmMapping(): String { } } +fun Interface.wasmReturnArg(): String = "ArenaManager.currentArena" + fun Arg.wasmArgNames(): List { return when (type) { is Void -> error("An arg can not be Void") @@ -69,16 +71,13 @@ fun Arg.wasmArgNames(): List { } } -fun Arg.wasmTypedMapping() - = this.wasmArgNames().map { "$it: Int" } .joinToString(", ") - fun Type.wasmReturnMapping(value: String): String { return when (this) { is Void -> "" is Integer -> value is Floating -> value is idlString -> TODO("Implement me") - is Object -> "JsValue(this.arena, $value)" + is Object -> "JsValue(ArenaManager.currentArena, $value)" is Function -> TODO("Implement me") is InterfaceRef -> "$name(ArenaManager.currentArena, $value)" else -> error("Unexpected type") @@ -100,22 +99,24 @@ val Operation.kotlinTypeParameters: String get() { return if (lambdaRetTypes == "") "" else "<$lambdaRetTypes>" } +val Interface.wasmReceiverArgs get() = + if (this.name != "__Global") + listOf("this.arena", "this.index") + else emptyList() -fun Operation.generateKotlin(parentInterface: Interface): String { +fun Operation.generateKotlin(parent: Interface): String { val argList = args.map { "${it.name}: ${it.type.toKotlinType(it.name)}" }.joinToString(",") - val wasmArgList = args.map{it.wasmMapping()}.joinToString(",") + + val wasmArgList = (parent.wasmReceiverArgs + args.map{it.wasmMapping()} + parent.wasmReturnArg()).joinToString(",") // TODO: there can be multiple Rs. return "\tfun $kotlinTypeParameters $name(" + argList + "): ${returnType.toKotlinType()} {\n" + - "\t\tval wasmRetVal = ${wasmFunctionName(name, parentInterface.name)}(" + - "this.arena, this.index" + - (if (wasmArgList == "") "" else ", $wasmArgList") + - ")\n" + + "\t\tval wasmRetVal = ${wasmFunctionName(name, parent.name)}($wasmArgList)\n" + "\t\treturn ${returnType.wasmReturnMapping("wasmRetVal")}\n"+ "\t}\n" @@ -125,8 +126,7 @@ fun Attribute.generateKotlinSetter(parent: Interface): String { val kotlinType = type.toKotlinType(name) return "\t\tset(value: $kotlinType) {\n" + "\t\t\t${wasmSetterName(name, parent.name)}(" + - "this.arena, this.index, " + - Arg("value", type).wasmMapping() + + (parent.wasmReceiverArgs + Arg("value", type).wasmMapping()).joinToString(", ") + ")\n" + "\t\t}\n" } @@ -134,7 +134,7 @@ fun Attribute.generateKotlinSetter(parent: Interface): String { fun Attribute.generateKotlinGetter(parent: Interface): String { //val kotlinType = type.toKotlinType() return "\t\tget() {\n" + - "\t\t\tval wasmRetVal = ${wasmGetterName(name, parent.name)}(this.arena, this.index)\n" + + "\t\t\tval wasmRetVal = ${wasmGetterName(name, parent.name)}(${(parent.wasmReceiverArgs + parent.wasmReturnArg()).joinToString(", ")})\n" + "\t\t\treturn ${type.wasmReturnMapping("wasmRetVal")}\n"+ "\t\t}\n" + "\t\n" @@ -143,24 +143,32 @@ fun Attribute.generateKotlinGetter(parent: Interface): String { fun Attribute.generateKotlin(parent: Interface): String { val kotlinType = type.toKotlinType(name) return "\tvar $name: $kotlinType\n" + - (if (hasGetter) generateKotlinGetter(parent) else "get() = error(\"There is not getter for $name\")\n") + - (if (hasSetter) generateKotlinSetter(parent) else "set(_) = error(\"There is not setter for $name\")\n") + (if (hasGetter) generateKotlinGetter(parent) else "\t\tget() = error(\"There is no getter for $name\")\n") + + (if (hasSetter) generateKotlinSetter(parent) else "\t\tset(_) = error(\"There is no setter for $name\")\n") } +val Interface.wasmTypedReceiverArgs get() = + if (this.name != "__Global") + listOf("arena: Int", "index: Int") + else emptyList() + fun Operation.generateWasmStub(parent: Interface): String { val wasmName = wasmFunctionName(this.name, parent.name) + val allArgs = (parent.wasmTypedReceiverArgs + args.toList().wasmTypedMapping() + parent.wasmTypedReturnMapping()).joinToString(", ") return "@SymbolName(\"$wasmName\")\n" + - "external public fun $wasmName(arena: Int, index: Int ${args.toList().wasmTypedMapping()}): ${returnType.wasmReturnTypeMapping()}\n\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 = (parent.wasmTypedReceiverArgs + Arg("value", this.type).wasmTypedMapping()).joinToString(", ") return "@SymbolName(\"$wasmSetter\")\n" + - "external public fun $wasmSetter(arena: Int, index: Int, ${Arg("value", this.type).wasmTypedMapping()}): Unit\n\n" + "external public fun $wasmSetter($allArgs): Unit\n\n" } fun Attribute.generateWasmGetterStub(parent: Interface): String { val wasmGetter = wasmGetterName(this.name, parent.name) + val allArgs = (parent.wasmTypedReceiverArgs + parent.wasmTypedReturnMapping()).joinToString(", ") return "@SymbolName(\"$wasmGetter\")\n" + - "external public fun $wasmGetter(arena: Int, index: Int): Int\n\n" + "external public fun $wasmGetter($allArgs): Int\n\n" } fun Attribute.generateWasmStubs(parent: Interface) = (if (hasGetter) generateWasmGetterStub(parent) else "") + @@ -184,38 +192,62 @@ fun Member.generateWasmStub(parent: Interface) = } +fun Arg.wasmTypedMapping() + = this.wasmArgNames().map { "$it: Int" } .joinToString(", ") + +fun Interface.wasmTypedReturnMapping() + = "resultArena: Int" + fun List.wasmTypedMapping() - = this.map{ ", " + it.wasmTypedMapping() }.joinToString("") + = this.map{ it.wasmTypedMapping() } // TODO: more complex return types, such as returning a pair of Ints // will require a more complex approach. fun Type.wasmReturnTypeMapping() = if (this == Void) "Unit" else "Int" -fun Interface.generateKotlin(): String { - - return members.map { +fun Interface.generateMemberWasmStubs() = + members.map { it.generateWasmStub(this) - }.joinToString("") + + }.joinToString("") - "open class $name(arena: Int, index: Int): JsValue(arena, index) {\n" + +fun Interface.generateKotlinMembers() = members.map { it.generateKotlin(this) - }.joinToString("") + - "}\n" + + }.joinToString("") + +fun Interface.generateKotlinClassHeader() = + "open class $name(arena: Int, index: Int): JsValue(arena, index) {\n" + + "\tconstructor(jsValue: JsValue): this(jsValue.arena, jsValue.index)\n" + +fun Interface.generateKotlinClassFooter() = + "}\n" + +fun Interface.generateKotlinClassConverter() = "val JsValue.as$name: $name\n" + "\tget() {\n" + "\t\treturn $name(this.arena, this.index)\n"+ "\t}\n" + +fun Interface.generateKotlin(): String { + + fun String.skipForGlobal() = + if (this@generateKotlin.name != "__Global") this + else "" + + return generateMemberWasmStubs() + + generateKotlinClassHeader().skipForGlobal() + + generateKotlinMembers() + + generateKotlinClassFooter().skipForGlobal() + + generateKotlinClassConverter().skipForGlobal() } -fun generateKotlin(interfaces: List) = +fun generateKotlin(interfaces: List) = kotlinHeader() + interfaces.map { it.generateKotlin() }.joinToString("\n") + - "val html5 = __Global(ArenaManager.currentArena, -1)\n" + - "fun __Global.setInterval(interval: Int, lambda: KtFunction) = setInterval(lambda, interval)\n" + "fun setInterval(interval: Int, lambda: KtFunction) = setInterval(lambda, interval)\n" ///////////////////////////////////////////////////////// @@ -226,7 +258,7 @@ fun Arg.composeWasmArgs(): String { is Floating -> "" is idlString -> "\t\t$name = toUTF16String(${name}Ptr, ${name}Len);\n" is Object -> TODO("implement me") - is Function -> "\t\t$name = konan_dependencies.env.Konan_js_wrapLambda(arena, $name);\n" + is Function -> "\t\t$name = konan_dependencies.env.Konan_js_wrapLambda(lambdaResultArena, ${name}Index);\n" is InterfaceRef -> TODO("Implement me") else -> error("Unexpected type") @@ -237,33 +269,43 @@ val Interface.receiver get() = if (this.name == "__Global") "" else "kotlinObject(arena, obj)." fun Operation.generateJs(parent: Interface): String { - val wasmArgList = args.map { it.wasmArgNames() } - .flatten() - .map { ", $it" } - .joinToString("") + val allArgs = parent.wasmReceiverArgName + 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\t${wasmFunctionName(this.name, parent.name)}: function(arena, obj$wasmArgList) {\n" + + return "\n\t${wasmFunctionName(this.name, parent.name)}: function($wasmMapping) {\n" + composedArgsList + "\t\tvar result = ${parent.receiver}$name($argList);\n" + - "\t\treturn toArena(resultArena, result);\n" + + (if (returnType == Void) "" else "\t\treturn toArena(resultArena, result);\n") + "\t}" } +val Interface.wasmReceiverArgName get() = + if (this.name != "__Global") listOf("arena", "obj") else emptyList() + +val Operation.wasmReturnArgName get() = + if (this.returnType != Void) listOf("resultArena") else emptyList() + +val Attribute.wasmReturnArgName get() = + if (this.type != Void) listOf("resultArena") else emptyList() + fun Attribute.generateJsSetter(parent: Interface): String { val valueArg = Arg("value", type) - val wasmMapping = valueArg.wasmArgNames().joinToString(", ") - return "\n\t${wasmSetterName(name, parent.name)}: function(arena, obj, $wasmMapping) {\n" + + val allArgs = parent.wasmReceiverArgName + valueArg.wasmArgNames() + val wasmMapping = allArgs.joinToString(", ") + return "\n\t${wasmSetterName(name, parent.name)}: function($wasmMapping) {\n" + valueArg.composeWasmArgs() + "\t\t${parent.receiver}$name = value;\n" + "\t}" } fun Attribute.generateJsGetter(parent: Interface): String { - return "\n\t${wasmGetterName(name, parent.name)}: function(arena, obj) {\n" + + val allArgs = parent.wasmReceiverArgName + wasmReturnArgName + val wasmMapping = allArgs.joinToString(", ") + return "\n\t${wasmGetterName(name, parent.name)}: function($wasmMapping) {\n" + "\t\tvar result = ${parent.receiver}$name;\n" + - "\t\treturn toArena(arena, result);\n" + + "\t\treturn toArena(resultArena, result);\n" + "\t}" } diff --git a/samples/html5Canvas/src/main/kotlin/main.kt b/samples/html5Canvas/src/main/kotlin/main.kt index ff7f30bd4d6..e77d366d8e2 100644 --- a/samples/html5Canvas/src/main/kotlin/main.kt +++ b/samples/html5Canvas/src/main/kotlin/main.kt @@ -4,9 +4,6 @@ import konan.internal.ExportForCppRuntime fun main(args: Array) { - //val html5 = Html5() - val document = html5.document - val canvas = document.getElementById("myCanvas").asCanvas val ctx = canvas.getContext("2d") val rect = canvas.getBoundingClientRect() @@ -35,7 +32,7 @@ fun main(args: Array) { draw = false } - html5.setInterval(10) { + setInterval(10) { if (draw) { ctx.setter("strokeStyle", "#222222") ctx.lineTo(mouseX, mouseY)