A little more stubs for html demo.

Tweaked arena management a little.
This commit is contained in:
Alexander Gorshenev
2017-09-15 13:50:55 +03:00
committed by alexander-gorshenev
parent 6511340eb3
commit 3b1428597b
6 changed files with 268 additions and 82 deletions
+5 -4
View File
@@ -17,18 +17,19 @@ var=COMPILER_ARGS_${TARGET}
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/jsinterop -target wasm32 || exit 1
-p library -o $DIR/build/klib/jsinterop -target wasm32 || exit 1
konanc $DIR/src/canvas/kotlin \
-includeBinary $DIR/src/canvas/js/canvas.js \
-l $DIR/build/jsinterop \
-p library -o $DIR/build/canvas -target wasm32 || exit 1
-l $DIR/build/klib/jsinterop \
-p library -o $DIR/build/klib/canvas -target wasm32 || exit 1
konanc $DIR/src/main/kotlin \
-l $DIR/build/jsinterop -l $DIR/build/canvas \
-r $DIR/build -l jsinterop -l canvas \
-o $DIR/build/bin/html5Canvas -target wasm32 || exit 1
echo "Artifact path is $DIR/build/bin/html5Canvas.wasm"
+50 -15
View File
@@ -2,32 +2,67 @@
konan.libraries.push ({ // This one will be auto generated.
knjs_addDocumentToArena: function(arena) {
// Should be resolved by jsinterop.
return konan_dependencies.env.Konan_js_addObjectToArena(arena, document);
return toArena(arena, document);
},
knjs_setInterval: function (func, interval) {
setInterval(konan_dependencies.env.Konan_js_wrapLambda(func), interval);
knjs_setInterval: function (arena, func, interval) {
setInterval(konan_dependencies.env.Konan_js_wrapLambda(arena, func), interval);
},
knjs_getElementById: function (arena, obj, id, idLength) {
knjs_getElementById: function (arena, obj, id, idLength, resultArena) {
var name = toUTF16String(id, idLength);
var result = konan_dependencies.env.arenas[arena][obj].getElementById(name);
return konan_dependencies.env.Konan_js_addObjectToArena(arena, result);
var result = kotlinObject(arena, obj).getElementById(name);
return toArena(resultArena, result);
},
knjs_getContext: function (arena, obj, context, contextLength) {
knjs_getContext: function (arena, obj, context, contextLength, resultArena) {
var name = toUTF16String(context, contextLength);
var result = konan_dependencies.env.arenas[arena][obj].getContext(name);
return konan_dependencies.env.Konan_js_addObjectToArena(arena, result);
var result = kotlinObject(arena, obj).getContext(name);
return toArena(resultArena, result);
},
knjs_getBoundingClientRect: function (arena, obj) {
var result = konan_dependencies.env.arenas[arena][obj].getBoundingClientRect();
return konan_dependencies.env.Konan_js_addObjectToArena(arena, result);
knjs_getBoundingClientRect: function (arena, obj, resultArena) {
var result = kotlinObject(arena, obj).getBoundingClientRect();
return toArena(resultArena, result);
},
knjs_moveTo: function(arena, obj, x, y) {
konan_dependencies.env.arenas[arena][obj].moveTo(x, y);
kotlinObject(arena, obj).moveTo(x, y);
},
knjs_lineTo: function(arena, obj, x, y) {
konan_dependencies.env.arenas[arena][obj].lineTo(x, y);
kotlinObject(arena, obj).lineTo(x, y);
},
knjs_fillRect: function(arena, obj, x1, y1, width, height) {
kotlinObject(arena, obj).fillRect(x1, y1, width, height);
},
knjs_fill: function(arena, obj) {
kotlinObject(arena, obj).fill();
},
knjs_closePath: function(arena, obj) {
kotlinObject(arena, obj).closePath();
},
knjs_setLineWidth: function(arena, obj, value) {
kotlinObject(arena, obj).lineWidth = value;
},
knjs_setFillStyle: function(arena, obj, value_ptr, value_length) {
var value = toUTF16String(value_ptr, value_length);
kotlinObject(arena, obj).fillStyle = value;
},
knjs_beginPath: function(arena, obj) {
kotlinObject(arena, obj).beginPath();
},
knjs_stroke: function(arena, obj) {
konan_dependencies.env.arenas[arena][obj].stroke();
kotlinObject(arena, obj).stroke();
},
knjs_fetch: function(arena, urlPtr, urlLength, resultArena) {
var url = toUTF16String(urlPtr, urlLength);
var result = fetch(url);
return toArena(resultArena, result);
},
knjs_then: function(arena, obj, func, resultArena) {
var value = konan_dependencies.env.Konan_js_wrapLambda(arena, func);
var result = kotlinObject(arena, obj).then(value);
var index = toArena(resultArena, result);
return index;
},
knjs_json: function(arena, obj, resultArena) {
var object = kotlinObject(arena, obj);
result = object.json();
return toArena(resultArena, result);
}
});
+87 -18
View File
@@ -8,13 +8,16 @@ import kotlinx.wasm.jsinterop.*
external public fun addDocumentToArena(arena: Arena): Int
@SymbolName("knjs_getElementById")
external public fun knjs_getElementById(arena: Int, obj: Int, idPtr: Int, idLen: Int): Int;
external public fun knjs_getElementById(arena: Int, obj: Int, idPtr: Int, idLen: Int, resultArena: Int): Int;
@SymbolName("knjs_getContext")
external public fun knjs_getContext(arena: Int, obj: Int, idPtr: Int, idLen: Int): Int;
external public fun knjs_getContext(arena: Int, obj: Int, idPtr: Int, idLen: Int, resultArena: Int): Int;
@SymbolName("knjs_getBoundingClientRect")
external public fun knjs_getBoundingClientRect(arena: Int, obj: Int): Int;
external public fun knjs_getBoundingClientRect(arena: Int, obj: Int, resultArena: Int): Int;
@SymbolName("knjs_beginPath")
external public fun knjs_beginPath(arena: Int, obj: Int);
@SymbolName("knjs_stroke")
external public fun knjs_stroke(arena: Int, obj: Int);
@@ -25,13 +28,37 @@ external public fun knjs_moveTo(arena: Int, obj: Int, x: Int, y: Int): Int;
@SymbolName("knjs_lineTo")
external public fun knjs_lineTo(arena: Int, obj: Int, x: Int, y: Int): Int;
@SymbolName("knjs_fillRect")
external public fun knjs_fillRect(arena: Int, obj: Int, x1: Int, y1: Int, width: Int, height: Int)
@SymbolName("knjs_fill")
external public fun knjs_fill(arena: Int, obj: Int)
@SymbolName("knjs_closePath")
external public fun knjs_closePath(arena: Int, obj: Int)
@SymbolName("knjs_setLineWidth")
external public fun knjs_setLineWidth(thisArena: Int, thisIndex: Int, value: Int)
@SymbolName("knjs_setFillStyle")
external public fun knjs_setFillStyle(thisArena: Int, thisIndex: Int, valuePtr: Int, valueLength: Int)
@SymbolName("knjs_setInterval")
external public fun knjs_setInterval(functionIndex: Int, interval: Int);
external public fun knjs_setInterval(arenaIndex: Int, functionIndex: Int, interval: Int);
@SymbolName("knjs_fetch")
external public fun knjs_fetch(arena: Int, urlPtr: Int, urlLen: Int, resultArena: Int): Int;
@SymbolName("knjs_then")
external public fun knjs_then(thisArena: Int, thisIndex: Int, functionIndex: Int, resultArena: Int): Int
@SymbolName("knjs_json")
external public fun knjs_json(thisArena: Int, thisIndex: Int, resultArena: Int): Int
open class Document(arena: Int, index: Int): JsValue(arena, index) {
constructor(value: JsValue): this(value.arena, value.index)
fun getElementById(id: String): JsValue {
val resultIndex = knjs_getElementById(this.arena, this.index, stringPointer(id), stringLengthBytes(id))
return JsValue(this.arena, resultIndex)
val resultIndex = knjs_getElementById(this.arena, this.index, stringPointer(id), stringLengthBytes(id), ArenaManager.currentArena)
return JsValue(ArenaManager.currentArena, resultIndex)
}
}
@@ -40,11 +67,11 @@ open class DOMRect(arena: Int, index: Int): JsValue(arena, index) {
open class Canvas(arena: Int, index: Int): JsValue(arena, index) {
fun getContext(context: String): Context {
val resultIndex = knjs_getContext(this.arena, this.index, stringPointer(context), stringLengthBytes(context))
return Context(this.arena, resultIndex)
val resultIndex = knjs_getContext(this.arena, this.index, stringPointer(context), stringLengthBytes(context), ArenaManager.currentArena)
return Context(ArenaManager.currentArena, resultIndex)
}
fun getBoundingClientRect(): DOMRect {
return DOMRect(this.arena, knjs_getBoundingClientRect(this.arena, this.index))
return DOMRect(ArenaManager.currentArena, knjs_getBoundingClientRect(this.arena, this.index, ArenaManager.currentArena))
}
}
@@ -61,20 +88,62 @@ open class Context(arena: Int, index: Int): JsValue(arena, index) {
fun moveTo(x: Int, y: Int) {
knjs_moveTo(this.arena, this.index, x, y)
}
fun beginPath() {
knjs_beginPath(this.arena, this.index);
}
fun stroke() {
knjs_stroke(this.arena, this.index);
}
}
fun setInterval(lambda: (ArrayList<JsValue>)->Unit, interval: Int) {
knjs_setInterval(wrapFunction(lambda), interval);
fun fillRect(x: Int, y: Int, width: Int, height: Int) {
knjs_fillRect(this.arena, this.index, x, y, width, height)
}
fun fill() {
knjs_fill(this.arena, this.index)
}
fun closePath() {
knjs_closePath(this.arena, this.index)
}
var lineWidth: Int
get() = TODO("implement me")
set(value: Int) {
knjs_setLineWidth(this.arena, this.index, value)
}
var fillStyle: String
get() = TODO("implement me")
set(value: String) {
knjs_setFillStyle(this.arena, this.index, stringPointer(value), stringLengthBytes(value))
}
}
// TODO: The arguments are flipped to have lambda within the last position.
fun setInterval(interval: Int, lambda: (ArrayList<JsValue>)->Unit)
= setInterval(lambda, interval)
class html5 {
val arena = allocateArena()
val document = Document(arena, addDocumentToArena(arena))
class Response(arena: Int, index: Int): JsValue(arena, index) {
constructor(value: JsValue): this(value.arena, value.index)
fun json(): JsValue {
return JsValue(ArenaManager.currentArena, knjs_json(this.arena, this.index, ArenaManager.currentArena))
}
}
class Html5() {
val document = Document(ArenaManager.currentArena, addDocumentToArena(ArenaManager.currentArena))
fun fetch(url: String): Promise {
return Promise (ArenaManager.currentArena, knjs_fetch(ArenaManager.currentArena, stringPointer(url), stringLengthBytes(url), ArenaManager.currentArena))
}
fun setInterval(lambda: KtFunction<Unit>, interval: Int) {
knjs_setInterval(ArenaManager.globalArena, wrapFunction(lambda), interval);
}
// TODO: The arguments are flipped to have lambda within the last position.
fun setInterval(interval: Int, lambda: KtFunction<Unit>)
= setInterval(lambda, interval)
}
open class Promise (arena: Int, index: Int): JsValue(arena, index) {
fun <R> then(lambda: KtFunction<R>): Promise {
return Promise(ArenaManager.currentArena, knjs_then(arena, index, wrapFunction<R>(lambda), ArenaManager.currentArena))
}
}
@@ -1,41 +1,82 @@
konan.libraries.push ({
arenas: [],
arenas: new Map(),
nextArena: 0,
Konan_js_allocateArena: function (array) {
konan_dependencies.env.arenas.push(array || []);
return konan_dependencies.env.arenas.length - 1;
var index = konan_dependencies.env.nextArena++;
konan_dependencies.env.arenas.set(index, array || []);
return index;
},
Konan_js_freeArena: function(arenaIndex) {
var arena = konan_dependencies.env.arenas[arenaIndex];
var arena = konan_dependencies.env.arenas.get(arenaIndex);
arena.forEach(function(element, index) {
arena[index] = null;
});
konan_dependencies.env.arenas[arenaIndex] = null;
konan_dependencies.env.arenas.delete(arenaIndex);
},
Konan_js_pushIntToArena: function (arenaIndex, value) {
var arena = konan_dependencies.env.arenas.get(arenaIndex);
arena.push(value);
return arena.length - 1;
},
Konan_js_addObjectToArena: function (arenaIndex, object) {
var arena = konan_dependencies.env.arenas[arenaIndex];
var arena = konan_dependencies.env.arenas.get(arenaIndex);
arena.push(object);
return arena.length - 1;
},
Konan_js_wrapLambda: function (index) {
Konan_js_wrapLambda: function (functionArenaIndex, index) {
return (function () {
var functionArena = konan_dependencies.env.arenas.get(functionArenaIndex);
// convert Arguments to an array
// to be provided by launcher.js
var arenaIndex = konan_dependencies.env.Konan_js_allocateArena(Array.prototype.slice.call(arguments));
// To be provided by Kotlin runtime.
instance.exports.Konan_js_runLambda(index, arenaIndex, arguments.length);
konan_dependencies.env.Konan_js_freeArena(arenaIndex);
var argumentArenaIndex = konan_dependencies.env.Konan_js_allocateArena(Array.prototype.slice.call(arguments));
var resultIndex = instance.exports.Konan_js_runLambda(index, argumentArenaIndex, arguments.length);
var result = kotlinObject(argumentArenaIndex, resultIndex);
konan_dependencies.env.Konan_js_freeArena(argumentArenaIndex);
return result;
});
},
Konan_js_getInt: function(arenaIndex, objIndex, propertyNamePtr, propertyNameLength) {
// TODO: The toUTF16String() is to be resolved by launcher.js runtime.
var property = toUTF16String(propertyNamePtr, propertyNameLength);
var value = konan_dependencies.env.arenas[arenaIndex][objIndex][property];
var value = kotlinObject(arenaIndex, objIndex)[property];
return value;
},
Konan_js_getProperty: function(arenaIndex, objIndex, propertyNamePtr, propertyNameLength) {
// TODO: The toUTF16String() is to be resolved by launcher.js runtime.
var property = toUTF16String(propertyNamePtr, propertyNameLength);
var arena = konan_dependencies.env.arenas.get(arenaIndex);
var value = arena[objIndex][property];
arena.push(value);
return arena.length - 1;
},
Konan_js_setFunction: function (arena, obj, propertyName, propertyNameLength, func) {
var name = toUTF16String(propertyName, propertyNameLength);
konan_dependencies.env.arenas[arena][obj][name] = konan_dependencies.env.Konan_js_wrapLambda(func);
kotlinObject(arena, obj)[name] = konan_dependencies.env.Konan_js_wrapLambda(arena, func);
},
Konan_js_setString: function (arena, obj, propertyName, propertyNameLength, stringPtr, stringLength) {
var name = toUTF16String(propertyName, propertyNameLength);
var string = toUTF16String(stringPtr, stringLength);
kotlinObject(arena, obj)[name] = string;
},
});
// TODO: This is just a shorthand notation.
function kotlinObject(arenaIndex, objectIndex) {
var arena = konan_dependencies.env.arenas.get(arenaIndex);
if (typeof arena == "undefined") {
console.log("No arena index " + arenaIndex + "for object" + objectIndex);
console.trace()
}
return arena[objectIndex]
}
function toArena(arenaIndex, object) {
return konan_dependencies.env.Konan_js_addObjectToArena(arenaIndex, object);
}
@@ -13,49 +13,87 @@ external public fun allocateArena(): Arena
@SymbolName("Konan_js_freeArena")
external public fun freeArena(arena: Arena)
@SymbolName("Konan_js_pushIntToArena")
external public fun pushIntToArena(arena: Arena, value: Int)
@SymbolName("Kotlin_String_utf16pointer")
external public fun stringPointer(message: String): Pointer
@SymbolName("Kotlin_String_utf16length")
external public fun stringLengthBytes(message: String): Int
typealias KtFunction = (ArrayList<JsValue>)->Unit
typealias KtFunction <R> = ((ArrayList<JsValue>)->R)
fun wrapFunction(func: KtFunction): Int {
fun <R> wrapFunction(func: KtFunction<R>): Int {
val ptr: Long = StableObjPtr.create(func).value.toLong()
return ptr.toInt() // TODO: LP64 unsafe.
}
@ExportForCppRuntime("Konan_js_runLambda")
fun runLambda(userData: Int, arena: Arena, arenaSize: Int) {
fun runLambda(pointer: Int, argumentsArena: Arena, argumentsArenaSize: Int): Int {
val arguments = arrayListOf<JsValue>()
for (i in 0..arenaSize-1) {
arguments.add(JsValue(arena, i));
for (i in 0 until argumentsArenaSize) {
arguments.add(JsValue(argumentsArena, i));
}
val previousArena = ArenaManager.currentArena
ArenaManager.currentArena = argumentsArena
// TODO: LP64 unsafe: wasm32 passes Int, not Long.
val func = StableObjPtr.fromValue(userData.toLong().toCPointer()!!).get() as KtFunction
func(arguments);
val func = StableObjPtr.fromValue(pointer.toLong().toCPointer()!!).get() as KtFunction<JsValue>
val result = func(arguments)
ArenaManager.currentArena = previousArena
return result.index
}
open class JsValue(val arena: Arena, val index: Object) {
fun getInt(property: String): Int {
return getInt(arena, index, stringPointer(property), stringLengthBytes(property))
return getInt(ArenaManager.currentArena, index, stringPointer(property), stringLengthBytes(property))
}
fun getProperty(property: String): JsValue {
return JsValue(ArenaManager.currentArena, Konan_js_getProperty(ArenaManager.currentArena, index, stringPointer(property), stringLengthBytes(property)))
}
}
open class JsArray(arena: Arena, index: Object): JsValue(arena, index) {
constructor(jsValue: JsValue): this(jsValue.arena, jsValue.index)
operator fun get(index: Int): JsValue {
// TODO: we could pass an integer index to index arrays.
return getProperty(index.toString())
}
val size: Int
get() = this.getInt("length")
}
@SymbolName("Konan_js_getInt")
external public fun getInt(arena: Arena, obj: Object, propertyPtr: Pointer, propertyLen: Int): Int;
@SymbolName("Konan_js_getProperty")
external public fun Konan_js_getProperty(arena: Arena, obj: Object, propertyPtr: Pointer, propertyLen: Int): Int;
@SymbolName("Konan_js_setFunction")
external public fun setFunction(arena: Arena, obj: Object, propertyName: Pointer, propertyLength: Int , function: Int)
fun setter(obj: JsValue, property: String, lambda: (arguments: ArrayList<JsValue>) -> Unit) {
val index = wrapFunction(lambda);
setFunction(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), index)
@SymbolName("Konan_js_setString")
external public fun setString(arena: Arena, obj: Object, propertyName: Pointer, propertyLength: Int, stringPtr: Pointer, stringLength: Int )
fun setter(obj: JsValue, property: String, string: String) {
setString(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), stringPointer(string), stringLengthBytes(string))
}
fun JsValue.setter(property: String, lambda: (ArrayList<JsValue>) -> Unit) {
fun setter(obj: JsValue, property: String, lambda: KtFunction<Unit>) {
val pointer = wrapFunction(lambda);
setFunction(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), pointer)
}
fun JsValue.setter(property: String, lambda: KtFunction<Unit>) {
setter(this, property, lambda)
}
fun JsValue.setter(property: String, string: String) {
setter(this, property, string)
}
object ArenaManager {
val globalArena = allocateArena()
var currentArena = globalArena
}
+21 -19
View File
@@ -3,44 +3,46 @@ import kotlinx.wasm.jsinterop.*
import konan.internal.ExportForCppRuntime
fun main(args: Array<String>) {
val html5 = html5()
val html5 = Html5()
val document = html5.document
val canvas = document.getElementById("myCanvas").asCanvas;
val ctx = canvas.getContext("2d");
val rect = canvas.getBoundingClientRect();
val canvas = document.getElementById("myCanvas").asCanvas
val ctx = canvas.getContext("2d")
val rect = canvas.getBoundingClientRect()
val rectLeft = rect.getInt("left")
val rectTop = rect.getInt("top")
var mouseX: Int = 0;
var mouseY: Int = 0;
var draw: Boolean = false;
var mouseX: Int = 0
var mouseY: Int = 0
var draw: Boolean = false
document.setter("onmousemove") { args: ArrayList<JsValue> ->
val event = args[0];
val event = args[0]
mouseX = event.getInt("clientX") - rectLeft
mouseY = event.getInt("clientY") - rectTop
if (mouseX < 0) mouseX = 0;
if (mouseX > 639) mouseX = 639;
if (mouseY < 0) mouseY = 0;
if (mouseY > 479) mouseY = 479;
if (mouseX < 0) mouseX = 0
if (mouseX > 639) mouseX = 639
if (mouseY < 0) mouseY = 0
if (mouseY > 479) mouseY = 479
}
document.setter("onmousedown") {
draw = true;
draw = true
}
document.setter("onmouseup") {
draw = false;
draw = false
}
setInterval(10) {
html5.setInterval(10) {
if (draw) {
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
ctx.setter("strokeStyle", "#222222")
ctx.lineTo(mouseX, mouseY)
ctx.stroke()
} else {
ctx.moveTo(mouseX, mouseY);
ctx.stroke();
ctx.moveTo(mouseX, mouseY)
ctx.stroke()
}
}
}