iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 7
0
Modern Web

謙虛,踏實的Web Assembly練習系列 第 7

[練習 06] 使用Memory來實做Hello, World.

關於Memory

寫程式時的幾個重點:

  • Memory是一個線性地址的空間,可把他當作以byte為單位的陣列
  • 可以從WebAssembly程式端設定及存取
  • 也可以從Javascript API端設定及存取
  • 在WebAssembly程式中,使用<型別>.load(8|16|32)_(u|s) index指令可以從index指定的位置把資料從Memory載入
  • 在WebAssembly程式中,使用<型別>.store(8|16|32) index指令可以把資料寫回Memory中index指定的位置
  • 在Javascript API端,則是透過ArrayBuffer來操作
  • 如果初始的大小不夠用,還可以動態增加大小

設定Memory

Memory可以直接在WebAssembly程式中設定後輸出:

(module
    (memory (export "out") 1)
)

這樣透過Javascript API裡面Instance的exports.out,就可以取得他的ArrayBuffer:

...
.then(instance => {
    let view = new Uint8Array(instance.exports.out.buffer);
    ...
})

或是在Javascript端先定義好:

let out = new WebAssembly.Memory({initial: 1});
let exportObjects = {
    js: {
        out: out
    }
};
let view = new Uint8Array(out.buffer);
view[0] = 0x61;

然後在WebAssembly程式端輸入:

(module
    (memory (import "js" "out") 1)
)

寫一個Hello, World.

WebAssembly程式:

(module
	(memory (export "out") 1)
	(func $space (result i32) i32.const 32)
	(func $comma (result i32) i32.const 44)
	(func $period (result i32) i32.const 46)
	(func $H (result i32) i32.const 72)
	(func $W (result i32) i32.const 87)
	(func $d (result i32) i32.const 100)
	(func $e (result i32) i32.const 101)
	(func $l (result i32) i32.const 108)
	(func $o (result i32) i32.const 111)
	(func $r (result i32) i32.const 114)
	(func (export "hello")
		i32.const 0
		call $H
		i32.store8
		i32.const 1
		call $e
		i32.store8
		i32.const 2
		call $l
		i32.store8
		i32.const 3
		call $l
		i32.store8
		i32.const 4
		call $o
		i32.store8
		i32.const 5
		call $comma
		i32.store8
		i32.const 6
		call $space
		i32.store8
		i32.const 7
		call $W
		i32.store8
		i32.const 8
		call $o
		i32.store8
		i32.const 9
		call $r
		i32.store8
		i32.const 10
		call $l
		i32.store8
		i32.const 11
		call $d
		i32.store8
		i32.const 12
		call $period
		i32.store8
	)
)

簡單說,就是依序把Hello, World.的ASCII Code(其實應該是UTF-8,但是在英文這段,資料是一樣的)填入Memory。

然後在Javascript端透過ArrayBuffer取出,透過View來轉成字串:

<html>
<body>
	<div id="panel"></div>
	<script src="../wasm_util.js"></script>
	<script>
	let m = new Wasm('test003.wasm');
	m.getInstance()
	.then(instance => {
		let buffer = instance.exports.out.buffer;
		instance.exports.hello();
		document.getElementById('panel').innerHTML = String.fromCharCode.apply(null, new Uint8Array(buffer));
	})
	</script>
</body>
</html>

這樣就會在網頁輸出Hello, World.:
Imgur

使用Data區段填入預設資料

上面這樣的作法,的確可以輸出Hello, World.,但是一個一個輸出ASCII Code實在很費事。實際上並不需要這麼麻煩,透過Data區段,就可以把資料字串預先填入到Memory來使用。例如:

(module
    (memory (export "out") 1)
    (data (i32.const 1024) "Hello, World.")
)

這樣就會從Memory的1024這個索引位置開始,填入Hello, World.字串。

用這個方式改寫一下之前的例子,同時嘗試由Javascript端設定Memory:

(module
	(func $log (import "js" "log") (param i32) (param i32))
	(memory (import "js" "out") 1)
	(data (i32.const 1024) "Hello, World.")
	(func (export "hello")
		i32.const 1024
		i32.const 13
		call $log
	)
)

程式的作用就是輸出一個hello函數,當他被呼叫時,就呼叫傳入的log函數,把資料的起點及長度傳給他,這樣他才知道從哪裡取出結果字串:

<html>
<body>
	<script src="../wasm_util.js"></script>
	<script>
	let out = new WebAssembly.Memory({initial: 1});
	let importObjects = {
		js: {
			out: out,
			log: log
		}
	}
	let m = new Wasm('test004.wasm');
	m.getModule()
	.then(module => {
		console.log('exports', WebAssembly.Module.exports(module));
		console.log('imports', WebAssembly.Module.imports(module));
		return m.getInstance(importObjects)
	})
	.then(instance => {
		instance.exports.hello();
	});
	function log(offset, length) {
		let view = new Uint8Array(out.buffer, offset, length);
		console.log(view);
		console.log(String.fromCharCode.apply(null, view));
	}
	</script>
</body>
</html>

程式裡面順便觀察了一下,怎麼使用Module.exports()以及Module.imports()來取得輸出入的資訊。頁面同樣會看到Hello, World.

Imgur

更進一步...

下一次的主題還是Memory,只是會同時從Javascript端以及WebAssembly程式端寫入資料,然後取得結果。


上一篇
[練習 05] 透過Javascript API取得Module及Instance物件
下一篇
[練習 07] 使用Memory來雙向溝通
系列文
謙虛,踏實的Web Assembly練習20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
海綿寶寶
iT邦大神 1 級 ‧ 2017-12-26 10:25:50

真的假的
看到我都想寫

INT 21H

/images/emoticon/emoticon06.gif

一個小小 typo 更正建議
Memroy 可能是「Memory」之誤

fillano iT邦超人 1 級 ‧ 2017-12-27 07:33:06 檢舉

...改了XD

我要留言

立即登入留言