WASM和以往的脚本语言调用本地代码方式很不相同。之前脚本语言是虚拟的,本地代码是操作系统原生的。这次js才是主人,C代码是虚拟运行的。
JS和C交互
- 不出所料只能调用C,C++接口需要自己封装成C的。
- JS和C调用的参数能只能是number类型,指针也当成number类型来处理。
- JS可以任意访问C的内存,而C不能访问任何JS内存。这种内存访问是单向的。
- C语言ma行后不退出,可以继续调用其他方法。函数名前都需要加下划线,例如Module._my_free(xx);来调用my_free()函数。
- C不能调用任何系统接口,只能通过JS来中转。因为C的运行环境是虚拟的,浏览器也只提供标准的JS API。
- WASM接口导出函数不支持int64,数据需要内存对齐。
- WASM使用的内存默认比较小。可以编译器时使用-s TOTAL_MEMORY=67108864来指定,或者ALLOW_MEMORY_GROWTH=1来动态扩容。
传递数据
- JS想传递除数字以外的参数,需要先用statckAlloc或者malloc函数分配内存,然后填充数据,再把指针传过去。
- C直接返回内存指针给JS,JS通过访问C的虚拟内存(Module.HEAPX)来获取指针指向的数据。
编译
-sEXPORTED_FUNCTIONS=_main,_other_function或者宏EMSCRIPTEN_KEEPALIVE,来标记函数需要导出。 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap编译选项表示启用ccall和cwrap功能,这两个帮助函数能自动做类型转换。
-sMODULARIZE编译参数可以让模块化导入:
var factory = require('./api_example.js');
factory().then((instance) => {
instance._sayHi(); // direct calling works
instance.ccall("sayHi"); // using ccall etc. also work
console.log(instance._daysInWeek()); // values can be returned, etc.
});
--preload-file test/hello_world_file.txt 预加载文件
JS调用C
1.ccall, 2.cwrap 3.加‘_’直接调用
C调用JS
1.eval调用
emscripten_run_script("alert('hi')");
2.EM_JS和EM_ASM
#include <emscripten.h>
EM_JS(void, call_alert, (), {
alert('hello world!');
throw 'all done';
});
int main() {
call_alert();
return 0;
}
int main() {
EM_ASM(
alert('hello world!');
throw 'all done';
);
return 0;
}
3.JS实现C函数
extern "C" {
extern void my_js();
}
mergeInto(LibraryManager.library, {
my_js: function() {
alert('hi');
},
});
4.JS添加C回调
addFunction(your_function, 'vi');
其他
环境变量
Module.preRun.push(function() {ENV.MY_FILE_ROOT = "/usr/lib/test"})