feat: add js-wasm
This commit is contained in:
5
__wasm/js-wasm/README.md
Normal file
5
__wasm/js-wasm/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
https://github.com/richardanaya/js-wasm
|
||||||
|
|
||||||
56
__wasm/js-wasm/helloworld/Cargo.lock
generated
Normal file
56
__wasm/js-wasm/helloworld/Cargo.lock
generated
Normal file
@@ -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",
|
||||||
|
]
|
||||||
20
__wasm/js-wasm/helloworld/Cargo.toml
Normal file
20
__wasm/js-wasm/helloworld/Cargo.toml
Normal file
@@ -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" }
|
||||||
|
|
||||||
13
__wasm/js-wasm/helloworld/index.html
Normal file
13
__wasm/js-wasm/helloworld/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<!-- <script src="https://unpkg.com/js-wasm/js-wasm.js"></script>-->
|
||||||
|
<script src="js-wasm.js"></script>
|
||||||
|
<script type="application/wasm" src="target/wasm32-unknown-unknown/release/helloworld.wasm"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Open my console.
|
||||||
|
<br><br><br>
|
||||||
|
<div style="font-size:11px;"><i>https://github.com/richardanaya/js-wasm</i></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
__wasm/js-wasm/helloworld/js-wasm
Submodule
1
__wasm/js-wasm/helloworld/js-wasm
Submodule
Submodule __wasm/js-wasm/helloworld/js-wasm added at 56faaf58b4
244
__wasm/js-wasm/helloworld/js-wasm.js
Normal file
244
__wasm/js-wasm/helloworld/js-wasm.js
Normal file
@@ -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(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
13
__wasm/js-wasm/helloworld/justfile
Normal file
13
__wasm/js-wasm/helloworld/justfile
Normal file
@@ -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
|
||||||
|
|
||||||
|
|
||||||
16
__wasm/js-wasm/helloworld/src/lib.rs
Normal file
16
__wasm/js-wasm/helloworld/src/lib.rs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user