diff --git a/samples/html5Canvas/build.sh b/samples/html5Canvas/build.sh
index 384d0304d57..7460bf83da7 100755
--- a/samples/html5Canvas/build.sh
+++ b/samples/html5Canvas/build.sh
@@ -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"
diff --git a/samples/html5Canvas/src/canvas/js/canvas.js b/samples/html5Canvas/src/canvas/js/canvas.js
index 22758a5d37d..cc9daa99701 100644
--- a/samples/html5Canvas/src/canvas/js/canvas.js
+++ b/samples/html5Canvas/src/canvas/js/canvas.js
@@ -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);
+ }
});
diff --git a/samples/html5Canvas/src/canvas/kotlin/canvas.kt b/samples/html5Canvas/src/canvas/kotlin/canvas.kt
index cb355282f3c..8d9ee020bf9 100644
--- a/samples/html5Canvas/src/canvas/kotlin/canvas.kt
+++ b/samples/html5Canvas/src/canvas/kotlin/canvas.kt
@@ -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)->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)->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, 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)
+ = setInterval(lambda, interval)
+
+}
+
+open class Promise (arena: Int, index: Int): JsValue(arena, index) {
+ fun then(lambda: KtFunction): Promise {
+ return Promise(ArenaManager.currentArena, knjs_then(arena, index, wrapFunction(lambda), ArenaManager.currentArena))
+ }
+}
+
+
diff --git a/samples/html5Canvas/src/jsinterop/js/jsinterop.js b/samples/html5Canvas/src/jsinterop/js/jsinterop.js
index 221b12f4efb..81a72e3c8e6 100644
--- a/samples/html5Canvas/src/jsinterop/js/jsinterop.js
+++ b/samples/html5Canvas/src/jsinterop/js/jsinterop.js
@@ -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);
+}
+
diff --git a/samples/html5Canvas/src/jsinterop/kotlin/jsinterop.kt b/samples/html5Canvas/src/jsinterop/kotlin/jsinterop.kt
index 03be07b7a05..86e7296d1f0 100644
--- a/samples/html5Canvas/src/jsinterop/kotlin/jsinterop.kt
+++ b/samples/html5Canvas/src/jsinterop/kotlin/jsinterop.kt
@@ -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)->Unit
+typealias KtFunction = ((ArrayList)->R)
-fun wrapFunction(func: KtFunction): Int {
+fun wrapFunction(func: KtFunction): 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()
- 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
+ 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) -> 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) -> Unit) {
+fun setter(obj: JsValue, property: String, lambda: KtFunction) {
+ val pointer = wrapFunction(lambda);
+ setFunction(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), pointer)
+}
+
+fun JsValue.setter(property: String, lambda: KtFunction) {
setter(this, property, lambda)
}
+
+fun JsValue.setter(property: String, string: String) {
+ setter(this, property, string)
+}
+
+object ArenaManager {
+ val globalArena = allocateArena()
+ var currentArena = globalArena
+}
diff --git a/samples/html5Canvas/src/main/kotlin/main.kt b/samples/html5Canvas/src/main/kotlin/main.kt
index f232ec8aa69..c2d67d1d0a5 100644
--- a/samples/html5Canvas/src/main/kotlin/main.kt
+++ b/samples/html5Canvas/src/main/kotlin/main.kt
@@ -3,44 +3,46 @@ import kotlinx.wasm.jsinterop.*
import konan.internal.ExportForCppRuntime
fun main(args: Array) {
- 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 ->
- 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()
}
}
}