diff --git a/__wasm/js-wasm/README.md b/__wasm/js-wasm/README.md
new file mode 100644
index 0000000..2a373f8
--- /dev/null
+++ b/__wasm/js-wasm/README.md
@@ -0,0 +1,5 @@
+
+
+Reference:
+https://github.com/richardanaya/js-wasm
+
diff --git a/__wasm/js-wasm/helloworld/Cargo.lock b/__wasm/js-wasm/helloworld/Cargo.lock
new file mode 100644
index 0000000..4de72af
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/Cargo.lock
@@ -0,0 +1,56 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "callback"
+version = "0.5.4"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "helloworld"
+version = "0.1.0"
+dependencies = [
+ "js",
+]
+
+[[package]]
+name = "js"
+version = "0.4.3"
+dependencies = [
+ "callback",
+ "spin",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "spin"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
+dependencies = [
+ "lock_api",
+]
diff --git a/__wasm/js-wasm/helloworld/Cargo.toml b/__wasm/js-wasm/helloworld/Cargo.toml
new file mode 100644
index 0000000..fc46643
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "helloworld"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+crate-type = ['cdylib']
+
+[profile.release]
+lto = true
+
+[dependencies]
+js = "0.4.3"
+
+[patch.crates-io]
+callback = { path = "./js-wasm/crates/callback" }
+js = { path = "./js-wasm/crates/js" }
+
diff --git a/__wasm/js-wasm/helloworld/index.html b/__wasm/js-wasm/helloworld/index.html
new file mode 100644
index 0000000..c741529
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ Open my console.
+
+ https://github.com/richardanaya/js-wasm
+
+
diff --git a/__wasm/js-wasm/helloworld/js-wasm b/__wasm/js-wasm/helloworld/js-wasm
new file mode 160000
index 0000000..56faaf5
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/js-wasm
@@ -0,0 +1 @@
+Subproject commit 56faaf58b4e118f57b46ceeebcd7887a337e1db9
diff --git a/__wasm/js-wasm/helloworld/js-wasm.js b/__wasm/js-wasm/helloworld/js-wasm.js
new file mode 100644
index 0000000..5ab7765
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/js-wasm.js
@@ -0,0 +1,244 @@
+"use strict";
+class Index {
+ constructor(index, generation) {
+ this.index = index;
+ this.generation = generation;
+ }
+ toNum() {
+ return Number((BigInt(this.generation) << BigInt(32)) | BigInt(this.index));
+ }
+ static fromNum(n) {
+ const i = Number(((BigInt(n) & BigInt(0xffffffff00000000)) >> BigInt(32)) &
+ BigInt(0xffffffff));
+ const g = n & 0xffffffff;
+ return new Index(g, i);
+ }
+}
+class GenerationalArena {
+ constructor() {
+ this.items = [];
+ this.generation = 0;
+ this.free_list_head = undefined;
+ this.length = 0;
+ }
+ insert(v) {
+ // lets use the first free entry if we have one
+ if (this.free_list_head !== undefined) {
+ let i = this.free_list_head;
+ this.free_list_head = this.items[i].nextFree;
+ this.items[i] = {
+ generation: this.generation,
+ value: v,
+ };
+ this.length += 1;
+ return new Index(i, this.generation);
+ }
+ this.items.push({
+ generation: this.generation,
+ value: v,
+ });
+ const idx = new Index(this.items.length - 1, this.generation);
+ this.length += 1;
+ return idx;
+ }
+ contains(idx) {
+ return this.get(idx) !== undefined;
+ }
+ get(i) {
+ let e = this.items[i.index];
+ if (e && e.generation === i.generation) {
+ return e.value;
+ }
+ return undefined;
+ }
+ remove(idx) {
+ if (idx.index >= this.items.length) {
+ return undefined;
+ }
+ let e = this.items[idx.index];
+ if (e.generation !== undefined && e.generation == idx.generation) {
+ this.generation += 1;
+ this.items[idx.index] = {
+ nextFree: this.free_list_head,
+ };
+ this.free_list_head = idx.index;
+ this.length -= 1;
+ return e.value;
+ }
+ return undefined;
+ }
+ *[Symbol.iterator]() {
+ for (let i = 0; i < this.items.length; i++) {
+ const x = this.items[i];
+ if (x.generation !== undefined) {
+ yield { index: new Index(i, x.generation), value: x.value };
+ }
+ }
+ }
+ indices() {
+ return {
+ items: this.items,
+ [Symbol.iterator]: function* iter() {
+ for (let i = 0; i < this.items.length; i++) {
+ const x = this.items[i];
+ if (x.generation !== undefined) {
+ yield new Index(i, x.generation);
+ }
+ }
+ },
+ };
+ }
+ values() {
+ return {
+ items: this.items,
+ [Symbol.iterator]: function* iter() {
+ for (let i = 0; i < this.items.length; i++) {
+ const x = this.items[i];
+ if (x.generation !== undefined) {
+ yield x.value;
+ }
+ }
+ },
+ };
+ }
+}
+const JsWasm = {
+ createEnvironment() {
+ const arena = new GenerationalArena();
+ arena.insert(undefined);
+ arena.insert(null);
+ arena.insert(self);
+ arena.insert(typeof document != "undefined" ? document : null);
+ arena.insert(typeof document != "undefined" ? document.body : null);
+ const context = {
+ functions: [
+ function () {
+ debugger;
+ return 0;
+ },
+ ],
+ objects: arena,
+ utf8dec: new TextDecoder("utf-8"),
+ utf8enc: new TextEncoder(),
+ utf16dec: new TextDecoder("utf-16"),
+ toCallbackArg: function (arg) {
+ if (typeof arg === "object") {
+ return context.storeObject(arg);
+ }
+ return arg;
+ },
+ createCallback: function (cb) {
+ if (!this.module) {
+ throw new Error("module not set");
+ }
+ const fnHandleCallback = this.module.instance.exports.handle_callback;
+ return function () {
+ const arg = arguments;
+ fnHandleCallback(cb, context.toCallbackArg(arg[0]), context.toCallbackArg(arg[1]), context.toCallbackArg(arg[2]), context.toCallbackArg(arg[3]), context.toCallbackArg(arg[4]), context.toCallbackArg(arg[5]), context.toCallbackArg(arg[6]), context.toCallbackArg(arg[7]), context.toCallbackArg(arg[8]), context.toCallbackArg(arg[9]));
+ };
+ },
+ readUtf8FromMemory: function (start, len) {
+ const text = this.utf8dec.decode(this.getMemory().subarray(start, start + len));
+ return text;
+ },
+ createAllocation: function (size) {
+ if (!this.module) {
+ throw new Error("module not set");
+ }
+ const allocationId = this.module.instance.exports.create_allocation(size);
+ const allocationPtr = this.module.instance.exports.allocation_ptr(allocationId);
+ return [allocationId, allocationPtr];
+ },
+ writeUtf8ToMemory: function (str) {
+ const bytes = this.utf8enc.encode(str);
+ const len = bytes.length;
+ const [id, start] = this.createAllocation(len);
+ this.getMemory().set(bytes, start);
+ return id;
+ },
+ readUtf16FromMemory: function (start, len) {
+ const text = this.utf16dec.decode(this.getMemory().subarray(start, start + len));
+ return text;
+ },
+ readUint8ArrayFromMemory(start) {
+ if (!this.module) {
+ throw new Error("module not set");
+ }
+ const data32 = new Uint32Array(this.module.instance.exports.memory.buffer);
+ const ptr = data32[start / 4];
+ const length = data32[ptr / 4];
+ let b = this.getMemory().slice(ptr + 4, ptr + 4 + length);
+ return new Uint8Array(b);
+ },
+ storeObject: function (obj) {
+ const index = this.objects.insert(obj);
+ return index.toNum();
+ },
+ getObject: function (handle) {
+ return this.objects.get(Index.fromNum(handle));
+ },
+ releaseObject: function (handle) {
+ this.objects.remove(Index.fromNum(handle));
+ },
+ getMemory: function () {
+ if (!this.module) {
+ throw new Error("module not set");
+ }
+ return new Uint8Array(this.module.instance.exports.memory.buffer);
+ },
+ };
+ return [{
+ abort() {
+ throw new Error("WebAssembly module aborted");
+ },
+ js_release(obj) {
+ context.releaseObject(obj);
+ },
+ js_register_function(start, len, utfByteLen) {
+ let functionBody;
+ if (utfByteLen === 16) {
+ functionBody = context.readUtf16FromMemory(start, len);
+ }
+ else {
+ functionBody = context.readUtf8FromMemory(start, len);
+ }
+ let id = context.functions.length;
+ context.functions.push(Function(`"use strict";return(${functionBody})`)());
+ return id;
+ },
+ js_invoke_function(funcHandle, a, b, c, d, e, f, g, h, i, j) {
+ return context.functions[funcHandle].call(context, a, b, c, d, e, f, g, h, i, j);
+ },
+ }, context];
+ },
+ async loadAndRunWasm(wasmURL) {
+ const [env, context] = JsWasm.createEnvironment();
+ const response = await fetch(wasmURL);
+ const bytes = await response.arrayBuffer();
+ const module = await WebAssembly.instantiate(bytes, {
+ env,
+ });
+ context.module = module;
+ module.instance.exports.main();
+ },
+};
+document.addEventListener("DOMContentLoaded", function () {
+ const wasmScripts = document.querySelectorAll("script[type='application/wasm']");
+ for (let i = 0; i < wasmScripts.length; i++) {
+ const src = wasmScripts[i].src;
+ if (src) {
+ JsWasm.loadAndRunWasm(src);
+ }
+ else {
+ console.error("Script tag must have 'src' property.");
+ }
+ }
+});
+if (window.WasmScriptComponents) {
+ window.WasmScriptComponents["js-wasm"] = function (e) {
+ return {
+ ...e,
+ ...JsWasm.createEnvironment(),
+ };
+ };
+}
diff --git a/__wasm/js-wasm/helloworld/justfile b/__wasm/js-wasm/helloworld/justfile
new file mode 100644
index 0000000..7d00442
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/justfile
@@ -0,0 +1,13 @@
+_:
+ @just --list
+
+build:
+ cargo build --target wasm32-unknown-unknown --release
+
+serve-py2:
+ python -m SimpleHTTPServer
+
+serve-py3:
+ python -m http.server
+
+
diff --git a/__wasm/js-wasm/helloworld/src/lib.rs b/__wasm/js-wasm/helloworld/src/lib.rs
new file mode 100644
index 0000000..804892c
--- /dev/null
+++ b/__wasm/js-wasm/helloworld/src/lib.rs
@@ -0,0 +1,16 @@
+use js::*;
+
+#[no_mangle]
+pub fn main() {
+ // let's dynamically create a javascript function we can invoke to write logs
+ let fn_log = js!(r##"function(strPtr, strLen) {
+ console.log(this.readUtf8FromMemory(strPtr, strLen));
+ }"##);
+ // let fn_alert = js!(r##"function(strPtr, strLen) {
+ // alert(this.readUtf8FromMemory(strPtr, strLen));
+ // }"##);
+ let msg = "Hello World!";
+ fn_log.invoke_2(msg.as_ptr() as u32, msg.len() as u32);
+ // fn_alert.invoke_2(msg.as_ptr() as u32, msg.len() as u32);
+}
+