title: [WebAssembly 入門] 第二次的 Hello, world!javascript
date: 2018-3-29 14:45:00java
categories: WebAssembly, 筆記web
tags: WebAssembly, JavaScript, Rust, LLVM toolchainapi
Reference link: 參考連接數組
auther: Yiniauapp
上一次Hello,world作了最基礎的實現,主要的代碼以下dom
src/lib.rside
//! a WebAssembly module with Rust
extern {
fn hello_insert_dom();
}
/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn hello_call_js() { // equal pub extern "C" fn ...
unsafe {
hello_insert_dom();
}
}
複製代碼
main.js函數
const imports = {
env: {
hello_insert_dom: () => {
const h1 = document.createElement('h1');
h1.innerHTML = 'Hello, world';
const body = document.querySelector('body');
body.appendChild(h1);
},
}
};
fetch('build/hello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports))
.then(results => {
console.log(results);
const exports = results.instance.exports;
exports.hello_call_js();
});
複製代碼
ok, 如今讓咱們來換一個方式實現這個hello,world ———— 經過在WebAssembly中向JS函數傳遞參數來實現測試
可是傳參在 MVP 版本的WebAssebly中並非一件容易的事, WebAssembly只支持整數或浮點數 (支持 i32/i64/f32/f64值類型以及 i8/i16 存儲。), 並無對String的原生支持
想要與實例交互, 能夠經過如下屬性
exports
- JS調用WebAssembly函數的地方, WebAssembly返回的值imports
- 當WebAssembly調用JS時, 具備任意數量的值類型(請注意: 這即不是數組也不是可變參數, 長度必須在Module編譯時已知)Memory.buffer
- 這是一個ArrayBuffer,可使用Uint8Array(等等)進行序列化全部的方法中彷佛直接訪問緩衝區是最簡單的: 首先咱們要在JS中建立一個內存對象並序列化它:
// create a memory, Size is in pages.
const memory = new WebAssembly.Memory({ initial: 2 }); // 建立了2頁的內存,每一個page 65536B -> 64KiB
const buffer = new Uint8Array(memory.buffer); // index arrayBuffer
// 當字符所有是英文時u8足夠使用
// 即只有0~255
複製代碼
而後在env屬性中建立存值函數:
env: {
...
save_char_to_memory(num, index) {
buffer[index] = num;
},
...
}
複製代碼
以及hello函數:
env: {
save_char_to_memory(num, index) {
buffer[index] = num;
},
log_string(size, index) {
let s = "";
for (let i = index; i < index + size; ++i) {
s += String.fromCodePoint(buffer[i]);
}
console.log(s);
}
}
複製代碼
接下來在rust中完成函數
extern {
fn save_char_to_memory(num: u8, index: usize);
fn log_string(size: usize, index: usize);
}
/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn pass_str() { // equal pub extern "C" fn ...
let s = b"Hello, world";
unsafe {
for i in 0..s.len() {
save_char_to_memory(s[i], i);
}
log_string(s.len(), 0);
}
}
複製代碼
測試以前咱們的代碼應該長這樣:
src/lib.rs
:
//! a WebAssembly module with Rust
extern {
fn save_char_to_memory(num: u8, index: usize);
fn log_string(size: usize, index: usize);
}
/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn pass_str() { // equal pub extern "C" fn ...
let s = b"Hello, world";
unsafe {
for i in 0..s.len() {
save_char_to_memory(s[i], i);
}
log_string(s.len(), 0);
}
}
複製代碼
main.js
:
// create a memory, Size is in pages.
const memory = new WebAssembly.Memory({ initial: 2 });
const buffer = new Uint8Array(memory.buffer); // index arrayBuffer
const imports = {
memory: memory,
env: {
save_char_to_memory(num, index) {
buffer[index] = num;
},
log_string(size, index) {
let s = "";
for (let i = index; i < index + size; i++) {
s += String.fromCodePoint(buffer[i]);
}
console.log(s);
}
}
};
fetch('build/hello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes, imports))
.then(results => {
console.log(results);
const exports = results.instance.exports;
exports.pass_str();
});
複製代碼
編譯運行吧!
結果顯而易見,以前的文章中有講到WebAssembly也可以使用memory,可是沒法接觸到memory範圍以外的數據。
那麼讓咱們先打印一下WebAssembly實例的內容
main.js:
fetch('build/hello.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiateStreaming(bytes, imports))
.then(results => {
console.log(results);
});
複製代碼
src/lib.rs:
//! a WebAssembly module with Rust
extern {
fn log_string(ptr: *const u8, len: u16);
}
/// pass str | slice to js side
#[no_mangle]
pub extern fn pass_str() { // equal pub extern "C" fn ...
...
}
複製代碼
result:
嗯??,memory? 看來這個就是WebAssembly自動建立的Memory實例,而且在附加在exports.instance
中傳遞到了JS端
那麼咱們就可以天然地假設js直接從WebAssembly中獲取字符串的裸指針和長度就可以從memory中截取一段buffer並使用Uint8Array
序列化,再使用String.fromCharCode
翻譯爲js String
try:
main.js:
...
const imports = {
memory: memory,
env: {
store_string_to_buffer(ptr, len) {
// buffer slice
let buf = new Uint16Array(mem.buffer, ptr, len);
let msg = new TextDecoder('utf8').decode(buf);
// 這是個新的api,你也能夠用下面的方法
// let msg = String.fromCharCode(...buf);
console.log('msg: ', msg);
console.log('mbuf: ', buf);
}
}
};
...
複製代碼
src/lib.rs:
//! a WebAssembly module with Rust
extern {
fn log_string(ptr: *const u8, len: u16);
}
/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn pass_str() { // equal pub extern "C" fn ...
let s = "Hello, world";
unsafe {
log_string(s.as_ptr(), s.len() as u16);
}
}
複製代碼
Second attempt:
yeap! 一切如本身所料的感受真不錯!😙
可是!
memory對象應該是共享的纔對!這樣JS端也可以向WebAssembly傳值...
算了,仍是留待下回分解吧 😋