iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 9
1
Modern Web

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

[練習 08] 使用Table來分享資源

  • 分享至 

  • xImage
  •  

Table的使用細節

簡單列一下一些Table的重點:

  1. 他是一個透過索引存取的元素表
  2. 目前可使用的元素類型只有anyfunc一種
  3. anyfunc是在WebAssembly中定義的函數
  4. 在WebAssembly端,可以透過Element區段設定Table,把要放入Table的函數填入
  5. 在Javascript端,可透過WebAssembly.Table()這個Constructor建立Table,讓WebAssembly程式輸入
  6. 在Javascript端,可透過Table實例的get/set方法存取,但是set只能設定由WebAssembly輸出的函數
  7. 在WebAssembly端,需要使用call_indirect指令呼叫Table中的函數

在WebAssembly程式中使用Table

在Table區段宣告之後,在Element區段把要塞入Table的函數放進去就可以。像這樣:

(module
	(table (export "table") 2 2 anyfunc)
	(func $getone (result i32)
		i32.const 1
	)
	(func $gettwo (result i32)
		i32.const 2
	)
	(elem (i32.const 0) $getone $gettwo)
)

輸出的Table,在Javascript中,可以透過索引來取得並執行:

<html>
<head>
	<meta charset="UTF-8">
</head>
<body>
	<script src="../wasm_util.js"></script>
	<script>
	let s = new Wasm('test007.wasm').getInstance();
	s.then(instance => {
		console.log(instance.exports.table.get(0)());
		console.log(instance.exports.table.get(1)());
	});
	</script>
</body>
</html>

上面這段程式,透過WebAssembly程式輸出的Table索引0,就可以在Javascript環境呼叫$getone函數,同理可以透過索引1呼叫$gettwo函數。

執行的結果:

Imgur

進一步的使用

Table方便的地方,在於提供一個不需要import,也可以呼叫位於其他WebAssembly模組中的函數的方法。(但是需要知道函數的特徵,也就是參數的型別及返回值型別)類似動態連結。

要測試的話,會需要同時使用兩個WebAssembly模組,不過測試時發現之前寫的載入WebAssembly的程式有問題,所以改了一下:

(function(global) {
	global.Wasm = Wasm;
	function Wasm(_url) {
		let module = null;
		let url = _url;
		this.getModule = getModule;
		this.getInstance = getInstance;
		function getModule() {
			if(module === null) {
				return new Promise((resolve, reject) => {
					WebAssembly.compileStreaming(fetch(url))
					.then(_module => {
						module = _module;
						resolve(_module)
					})
					.catch(reason => reject(reason))
				});
			} else {
				return new Promise((resolve, reject) => resolve(module));
			}
		};
		function getInstance(importObjects) {
			if(module === null) {
				return new Promise((resolve, reject) => {
					getModule()
					.then(_module => {
						if(!!importObjects) resolve(new WebAssembly.Instance(_module, importObjects));
						else  resolve(new WebAssembly.Instance(_module));
					})
					.catch(reason => reject(reason))
				});
			} else {
				return new Promise((resolve, reject) => {
					if(!!importObjects) resolve(new WebAssembly.Instance(module, importObjects));
					else resolve(new WebAssembly.Instance(module));
				});
			}
		}
	}
})(window);

然後就可以開始做簡單的測試。這次Table是在Javascript端產生,並且由兩個WebAssembly模組共享,第一個模組把函數透過Element區段放進Table,第二個模組則在輸出的函數中呼叫位於Table中的函數。先來看看網頁端:

<html>
<body>
	<script src="../wasm_util.js"></script>
	<script>
	let importObjects = {
		js: {
			tbl: new WebAssembly.Table({initial: 2, element: 'anyfunc'})
		}
	};
	new Wasm('test008a.wasm')
	.getInstance(importObjects)
	.then(instance => new Wasm('test008b.wasm').getInstance(importObjects))
	.then(instance => {
		console.log(instance.exports.getone());
		console.log(instance.exports.gettwo());
	});
	</script>
</body>
</html>

因為要讓模組a先寫入Table,然後讓模組b呼叫,所以先後順序是很重要的。

第一個WebAssembly模組(test008a):

(module
	(table (import "js" "tbl") 2 anyfunc)
	(func $getone (result i32)
		i32.const 1
	)
	(func $gettwo (result i32)
		i32.const 2
	)
	(elem (i32.const 0) $getone $gettwo)
)

要呼叫Table中的函數,需要知道他的特徵。在這裡會使用Type區段來宣告呼叫的函數特徵。另外,呼叫Table中的函數時,會使用call_indirect函數,他需要兩個參數,一個是要呼叫函數的索引,另外一個是特徵:

(test008b)

(module
	(import "js" "tbl" (table 2 anyfunc))
	(type $remote (func (result i32)))
	(func (export "getone") (result i32)
		i32.const 0
		call_indirect (type $remote)
	)
	(func (export "gettwo") (result i32)
		i32.const 1
		call_indirect (type $remote)
	)
)

在Javascript環境呼叫模組b輸出的getone函數,就會透過Table呼叫在模組a中定義的函數,gettwo也一樣。執行結果:

Imgur


除了透過Memory說Hello的例子,目前練習的都只是基本的區塊使用方式。接下來就要更進一步來看比較複雜的程式流程,所以會開始使用到更多流程控制的指令,就從明天開始吧。


上一篇
[練習 07] 使用Memory來雙向溝通
下一篇
[練習 09] 流程控制指令
系列文
謙虛,踏實的Web Assembly練習20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言