单文件 WebAssembly html 演示
A single-file WebAssembly html demo
我在哪里可以找到最简单的单文件演示,展示 html 中 WebAssembly 的用法?
JavaScript 例子很简单:
<script>
function hw() { console.log("Hello, world."); }
</script>
<button onclick="hw()">HW</button>
是否有类似的 WebAssembly?
我希望它有类似带有 wasm 二进制文件的硬编码字节缓冲区的东西,它被加载并执行它的一些微不足道的功能。以下是我的预期概要:
<script>
function hw() {
var wasm_code = [255, 0, 128, ..., whatever, ...];
var magic = give_me_wasm(wasm_code);
var x = magic.my_add(2,2);
console.log("2 + 2 = ", x);
}
</script>
<button onclick="hw()">HW</button>
这样的demo可以做吗,简单的表格,不用架设任何框架和工具,直接贴在Developer Console里试试?
自己完成:
<script>
var wasm_base64;
var wasm_buffer;
var wasm;
var wasm_instance;
function hw() {
wasm_base64 = "AGFzbQEAAAABBwFgAnx8AXwDAgEABwoBBm15X2FkZAAACgkBBwAgACABoAs=";
wasm_buffer = Uint8Array.from(atob(wasm_base64), c => c.charCodeAt(0)).buffer;
WebAssembly.compile(wasm_buffer).then(x => {
wasm = x;
wasm_instance = new WebAssembly.Instance(wasm);
var x = wasm_instance.exports.my_add(2,2);
console.log("2+2 = ",x);
});
}
</script>
<button onclick="hw()">HW</button>
这里是嵌入的WebAssembly文本形式(q.wat
):
(module
(type (;0;) (func (param f64 f64) (result f64)))
(func $myadd (type 0) (param f64 f64) (result f64)
get_local 0
get_local 1
f64.add)
(export "my_add" (func $myadd))
)
以下是生成该 base64 缓冲区的命令行:
$ wat2wasm q.wat -o w.wasm
$ base64 -w0 w.wasm ;echo
AGFzbQEAAAABBwFgAnx8AXwDAgEABwoBBm15X2FkZAAACgkBBwAgACABoAs=
wasm
和 wasm_instance
对象可以使用 Developer Console 进行探索。
已在 Firefox 63.0b9 中检查。
This 是我在 Gist 上找到的一个评论很好的例子。将代码嵌入 <script>
标签中,您将获得 "A single-file WebAssembly html demo"
为了方便复制在这里:
// this code is from a simple add function in c:
//
// // hello.c
// int add(int a, int b) {
// return a + b;
// }
//
// compiles it with emcc (http://webassembly.org/getting-started/developers-guide/)
// $ git clone https://github.com/juj/emsdk.git
// $ cd emsdk
// $ ./emsdk install latest
// $ ./emsdk activate latest
// $ source ./emsdk_env.sh --build=Release
//
// once installed:
//
// $ emcc hello.c -s ONLY_MY_CODE=1 -s WASM=1 -s EXPORTED_FUNCTIONS="['_add']" -o hello.js
//
// yes, not sure why we have to add '_' in front of the exported function.
// take the hello.wasm and serialize it to base64, e.g. using node
// const code = fs.readFileSync('./hello.wasm')
// code.toString('base64')
// AGFzbQEAAAABCwJgAX8AYAJ/fwF/An4HA2VudgZtZW1vcnkCAYACgAIDZW52BXRhYmxlAXABAAADZW52Cm1lbW9yeUJhc2UDfwADZW52CXRhYmxlQmFzZQN/AANlbnYIU1RBQ0tUT1ADfwADZW52CVNUQUNLX01BWAN/AANlbnYSYWJvcnRTdGFja092ZXJmbG93AAADAgEBBhMDfwEjAgt/ASMDC30BQwAAAAALBwgBBF9hZGQAAQkBAAo7ATkBB38jBCEIIwRBEGokBCMEIwVOBEBBEBAACyAAIQIgASEDIAIhBCADIQUgBCAFaiEGIAgkBCAGDws=
// https://www.npmjs.com/package/base64-arraybuffer
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Use a lookup table to find the index.
const lookup = new Uint8Array(256);
for (let i = 0; i < chars.length; i++) {
lookup[chars.charCodeAt(i)] = i;
}
function decode(base64) {
var bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
if (base64[base64.length - 1] === "=") {
bufferLength--;
if (base64[base64.length - 2] === "=") {
bufferLength--;
}
}
var arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i+=4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i+1)];
encoded3 = lookup[base64.charCodeAt(i+2)];
encoded4 = lookup[base64.charCodeAt(i+3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
}
// this is the serialized code.
const code = 'AGFzbQEAAAABCwJgAX8AYAJ/fwF/An4HA2VudgZtZW1vcnkCAYACgAIDZW52BXRhYmxlAXABAAADZW52Cm1lbW9yeUJhc2UDfwADZW52CXRhYmxlQmFzZQN/AANlbnYIU1RBQ0tUT1ADfwADZW52CVNUQUNLX01BWAN/AANlbnYSYWJvcnRTdGFja092ZXJmbG93AAADAgEBBhMDfwEjAgt/ASMDC30BQwAAAAALBwgBBF9hZGQAAQkBAAo7ATkBB38jBCEIIwRBEGokBCMEIwVOBEBBEBAACyAAIQIgASEDIAIhBCADIQUgBCAFaiEGIAgkBCAGDws='
const buffer = decode(code)
// bootstrap the env.
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
const importObj = {
env: {
abortWhosebug: () => { throw new Error('overflow'); },
table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' }),
tableBase: 0,
memory: memory,
memoryBase: 1024,
STACKTOP: 0,
STACK_MAX: memory.buffer.byteLength,
}
}
// instantiate
WebAssembly.instantiate(buffer, importObj)
// take a look at that _add.
.then(({module, instance}) => { console.log(instance.exports._add(1, 2)) })
.catch((err) => { console.log(err.message) })
我在哪里可以找到最简单的单文件演示,展示 html 中 WebAssembly 的用法?
JavaScript 例子很简单:
<script>
function hw() { console.log("Hello, world."); }
</script>
<button onclick="hw()">HW</button>
是否有类似的 WebAssembly?
我希望它有类似带有 wasm 二进制文件的硬编码字节缓冲区的东西,它被加载并执行它的一些微不足道的功能。以下是我的预期概要:
<script>
function hw() {
var wasm_code = [255, 0, 128, ..., whatever, ...];
var magic = give_me_wasm(wasm_code);
var x = magic.my_add(2,2);
console.log("2 + 2 = ", x);
}
</script>
<button onclick="hw()">HW</button>
这样的demo可以做吗,简单的表格,不用架设任何框架和工具,直接贴在Developer Console里试试?
自己完成:
<script>
var wasm_base64;
var wasm_buffer;
var wasm;
var wasm_instance;
function hw() {
wasm_base64 = "AGFzbQEAAAABBwFgAnx8AXwDAgEABwoBBm15X2FkZAAACgkBBwAgACABoAs=";
wasm_buffer = Uint8Array.from(atob(wasm_base64), c => c.charCodeAt(0)).buffer;
WebAssembly.compile(wasm_buffer).then(x => {
wasm = x;
wasm_instance = new WebAssembly.Instance(wasm);
var x = wasm_instance.exports.my_add(2,2);
console.log("2+2 = ",x);
});
}
</script>
<button onclick="hw()">HW</button>
这里是嵌入的WebAssembly文本形式(q.wat
):
(module
(type (;0;) (func (param f64 f64) (result f64)))
(func $myadd (type 0) (param f64 f64) (result f64)
get_local 0
get_local 1
f64.add)
(export "my_add" (func $myadd))
)
以下是生成该 base64 缓冲区的命令行:
$ wat2wasm q.wat -o w.wasm
$ base64 -w0 w.wasm ;echo
AGFzbQEAAAABBwFgAnx8AXwDAgEABwoBBm15X2FkZAAACgkBBwAgACABoAs=
wasm
和 wasm_instance
对象可以使用 Developer Console 进行探索。
已在 Firefox 63.0b9 中检查。
This 是我在 Gist 上找到的一个评论很好的例子。将代码嵌入 <script>
标签中,您将获得 "A single-file WebAssembly html demo"
为了方便复制在这里:
// this code is from a simple add function in c:
//
// // hello.c
// int add(int a, int b) {
// return a + b;
// }
//
// compiles it with emcc (http://webassembly.org/getting-started/developers-guide/)
// $ git clone https://github.com/juj/emsdk.git
// $ cd emsdk
// $ ./emsdk install latest
// $ ./emsdk activate latest
// $ source ./emsdk_env.sh --build=Release
//
// once installed:
//
// $ emcc hello.c -s ONLY_MY_CODE=1 -s WASM=1 -s EXPORTED_FUNCTIONS="['_add']" -o hello.js
//
// yes, not sure why we have to add '_' in front of the exported function.
// take the hello.wasm and serialize it to base64, e.g. using node
// const code = fs.readFileSync('./hello.wasm')
// code.toString('base64')
// AGFzbQEAAAABCwJgAX8AYAJ/fwF/An4HA2VudgZtZW1vcnkCAYACgAIDZW52BXRhYmxlAXABAAADZW52Cm1lbW9yeUJhc2UDfwADZW52CXRhYmxlQmFzZQN/AANlbnYIU1RBQ0tUT1ADfwADZW52CVNUQUNLX01BWAN/AANlbnYSYWJvcnRTdGFja092ZXJmbG93AAADAgEBBhMDfwEjAgt/ASMDC30BQwAAAAALBwgBBF9hZGQAAQkBAAo7ATkBB38jBCEIIwRBEGokBCMEIwVOBEBBEBAACyAAIQIgASEDIAIhBCADIQUgBCAFaiEGIAgkBCAGDws=
// https://www.npmjs.com/package/base64-arraybuffer
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Use a lookup table to find the index.
const lookup = new Uint8Array(256);
for (let i = 0; i < chars.length; i++) {
lookup[chars.charCodeAt(i)] = i;
}
function decode(base64) {
var bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
if (base64[base64.length - 1] === "=") {
bufferLength--;
if (base64[base64.length - 2] === "=") {
bufferLength--;
}
}
var arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i+=4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i+1)];
encoded3 = lookup[base64.charCodeAt(i+2)];
encoded4 = lookup[base64.charCodeAt(i+3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}
return arraybuffer;
}
// this is the serialized code.
const code = 'AGFzbQEAAAABCwJgAX8AYAJ/fwF/An4HA2VudgZtZW1vcnkCAYACgAIDZW52BXRhYmxlAXABAAADZW52Cm1lbW9yeUJhc2UDfwADZW52CXRhYmxlQmFzZQN/AANlbnYIU1RBQ0tUT1ADfwADZW52CVNUQUNLX01BWAN/AANlbnYSYWJvcnRTdGFja092ZXJmbG93AAADAgEBBhMDfwEjAgt/ASMDC30BQwAAAAALBwgBBF9hZGQAAQkBAAo7ATkBB38jBCEIIwRBEGokBCMEIwVOBEBBEBAACyAAIQIgASEDIAIhBCADIQUgBCAFaiEGIAgkBCAGDws='
const buffer = decode(code)
// bootstrap the env.
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
const importObj = {
env: {
abortWhosebug: () => { throw new Error('overflow'); },
table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' }),
tableBase: 0,
memory: memory,
memoryBase: 1024,
STACKTOP: 0,
STACK_MAX: memory.buffer.byteLength,
}
}
// instantiate
WebAssembly.instantiate(buffer, importObj)
// take a look at that _add.
.then(({module, instance}) => { console.log(instance.exports._add(1, 2)) })
.catch((err) => { console.log(err.message) })