在上一篇文章“ WebAssembly简介”中,我介绍了使用Emscripten处理WebAssembly模块的一些基本知识。
本文是一些研究和实验的结果,以查看是否可以使用Emscripten构建WebAssembly模块,但没有任何管道代码。
例如,如果我们使用下面的命令行构建下面的C文件,结果将是一个101 KB的HTML文件,一个80.2 KB的JS文件和一个9.4 KB的wasm文件!
#include <stdio.h>
#include "../emscripten/emscripten.h"
int main() { return 0; }
int EMSCRIPTEN_KEEPALIVE add(int x, int y) { return x + y; }
拥有所有的管道是非常方便的,因为它可以让你立刻开始使用WebAssembly模块,但是如果我们只想要最低限度的,并且会自己处理HTML和JavaScript呢?
幸运的是,有一种方法可以告诉Emscripten只输出裸骨文件。
我们首先将C文件剥离到最低限度。在这种情况下,我们只需要添加方法:
int add(int x,int y){ return x + y ; }
如果我们使用下面的命令行,我们将得到只是wasm文件,它只有202个字节!
emcc test.c -s WASM=1 -s SIDE_MODULE=1 -O1 -o test.wasm
该SIDE_MODULE标志告诉Emscripten编译只有我们的方法,并没有别的意思,你也无法获得的东西像printf或者malloc的。
如果未指定,则使用的默认优化标志是-O0 (大写字母o和数字0),但在尝试加载模块时会导致以下错误:
LinkError: import object field 'DYNAMICTOP_PTR' is not a Number
添加任何优化标志-O0 将解决问题,所以我们用-O1 (大写字母o和数字1)来代替这个例子。
但是有一点需要注意,O似乎区分大小写。可用的各种优化标志可以在这里找到。
我们还需要在命令行中指定输出文件的名称,因为如果我们不这样做,Emscripten将输出名称为a.out.wasm的文件。
因为我们决定不使用Emscripten的管道代码,所以我们需要编写自己的HTML和JavaScript。以下是在模块中加载的一些HTML和JavaScript示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="button" value="test" onclick="javascript:OnClickTest();" />
<script type="text/javascript">
var gModule = null;
var importObject = {
'env': {
'memoryBase': 0,
'tableBase': 0,
'memory': new WebAssembly.Memory({initial: 256}),
'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'})
}
};
fetch('test.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
gModule = results.instance; // Hold onto the module's instance so that we can reuse it
});
function OnClickTest(){
alert(gModule.exports._add(1, 2));
}
</script>
</body>
</html>
有一件事你可能已经注意到了,与我们之前的博客文章中所做的相比,我们对C方法的调用不同,是因为我们没有使用Module.ccall或Module.cwrap。这些是Emscripten帮助方法。在这里,我们直接调用C方法。
还有一点需要注意的是,你的JavaScript需要在方法名前包含一个下划线字符。例如,在我们的例子中,我们的方法叫做add。在JavaScript中调用add方法时,可以使用_add(1,2); 而不是加(1,2);
虽然这种方法可能不是一个可以在任何情况下都能正常工作的解决方案,但是如果你不能访问像malloc这样的东西,那么如果你正在进行数字运算,不需要Emscripten的所有管理费用。