将JavaScript数组作为参数传递给WebAssembly函数
我想测试WebAssembly以进行一些复杂的数组计算 <> p>我编写了一个简单的C++函数,添加了两个代码> int <代码>数组,每个数组包含3个元素:将JavaScript数组作为参数传递给WebAssembly函数,javascript,c++,browser,emscripten,webassembly,Javascript,C++,Browser,Emscripten,Webassembly,我想测试WebAssembly以进行一些复杂的数组计算 p>我编写了一个简单的C++函数,添加了两个代码> int 数组,每个数组包含3个元素: // hello.cpp extern "C" { void array_add(int * summed, int* a, int* b) { for (int i=0; i < 3; i++) { summed[i] = a[i] + b[i]; } } } 但不知何故,结果数组总是[0,0,0] 我尝试了很多方法,包
// hello.cpp
extern "C" {
void array_add(int * summed, int* a, int* b) {
for (int i=0; i < 3; i++) {
summed[i] = a[i] + b[i];
}
}
}
但不知何故,结果
数组总是[0,0,0]
我尝试了很多方法,包括使用ccall()
(请参阅)调用函数,但似乎无法将数组作为wasm编译函数的参数传递
例如,使用以下C++函数:
extern "C" {
int first(int * arr) {
return arr[0];
}
}
在JavaScript中调用时,结果是一个随机整数,而不是作为参数传递的数组中的预期值
我错过了什么
关于C++的问题,我知道的很清楚,所以如果这是一个与我的C++无知有关的初学者问题,那么所有的道歉……< /p> < p>你的问题非常类似:WebSalm只支持<代码> i32 < /> >代码> i64 < /代码> />代码> f32 < /> >代码> f64 < /> >以及<>代码> i8>代码> /<代码> i16<代码>存储。 这意味着您不能传入指针。当你来自C++的观点时,你所做的一切都是理智的(不需要为无知道歉),但这并不是WebAssembly的边界如何运作。这对于C++专家来说也是令人惊讶的。 与字符串问题一样,您需要:
- 通过对每个条目调用一次导出,一次复制一个数组(例如
)set(size\t index,int-value)
- 将WebAssembly实例的堆作为
公开给JavaScript,并将所需的值直接写入ArrayBuffer
中ArrayBuffer
const bin = ...; // WebAssembly binary, I assume below that it imports a memory from module "imports", field "memory".
const module = new WebAssembly.Module(bin);
const memory = new WebAssembly.Memory({ initial: 2 }); // Size is in pages.
const instance = new WebAssembly.Instance(module, { imports: { memory: memory } });
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);
< C++ >你可能会想:“指针怎么工作?”上面我解释了WebAssembly↔ JavaScript你不能到处传递指针!内部WebAssembly指针表示为简单的
i32
值。Empscripten依赖LLVM来完成这项工作,由于WebAssembly以ILP32的形式出现,最大堆大小为4GiB,所以它可以正常工作
它确实对间接函数调用和函数指针有着有趣的含义!我把这个留给另一个问题;-)
然而,这确实意味着JavaScript可以“谈论”指向WebAssembly的指针:i32
是i32
。如果知道某个值在堆中的某个位置,那么可以将该值传递给JavaScript,JavaScript可以修改该值并将其传递回WebAssembly。如果JavaScript访问堆的代码< ArrayBuffer < /Cord>,那么有一个<代码> i32 允许你知道堆中的什么地方,并像C++那样修改堆。
WebSaseBead堆与大多数C++堆不同:它没有访问可执行页的能力,也没有访问调用栈的权限(或者更确切地说,大多数调用堆栈):编译器(例如LLVM)可能“将某些地址取值溢出到堆中,而不是使用WebSasppLISH本地语言”。这基本上就是哈佛体系结构所做的(与冯·诺依曼相反)
那么你的
你好。_array\u add(result,a,b)
在做什么?使用ToInteger
从数组强制执行a
和b
。这将成为0
,它在WebAssembly中是一个有效的堆位置!您正在访问堆中非常意外的部分 多亏了其他类似的问题:
和API文件:
我已经弄明白了。为了举例说明如何将数组传递给WASM函数/获取数组,我在C++中实现了一个简单的数组复制:
#include <stdint.h>
extern "C" {
int* copy_array(int* in_array, int length) {
int out_array[length];
for (int i=0; i<length; i++) {
out_array[i] = in_array[i];
}
return out_array;
}
}
请注意此处的
arrayToPtr
和ptrToArray
函数。。。他们正在做传递/返回数组的工作。我尝试了一种基于MessagePack的解决方案,将任意对象序列化到WASM内存数组中。这似乎相当灵活,允许我在JavaScript和C++之间来回传递对象。以下是一篇包含更多详细信息的博文:
#include <stdint.h>
extern "C" {
int* copy_array(int* in_array, int length) {
int out_array[length];
for (int i=0; i<length; i++) {
out_array[i] = in_array[i];
}
return out_array;
}
}
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="build/wasm_dsp.js"></script>
<script type="text/javascript">
function reqListener () {
// Loading wasm module
var arrayBuffer = oReq.response
WasmDsp['wasmBinary'] = arrayBuffer
wasmDsp = WasmDsp({ wasmBinary: WasmDsp.wasmBinary })
var inArray = new Int32Array([22, 44, 66, 999])
var nByte = 4
copyArray = wasmDsp.cwrap('copy_array', null, ['number', 'number']);
// Takes an Int32Array, copies it to the heap and returns a pointer
function arrayToPtr(array) {
var ptr = wasmDsp._malloc(array.length * nByte)
wasmDsp.HEAP32.set(array, ptr / nByte)
return ptr
}
// Takes a pointer and array length, and returns a Int32Array from the heap
function ptrToArray(ptr, length) {
var array = new Int32Array(length)
var pos = ptr / nByte
array.set(wasmDsp.HEAP32.subarray(pos, pos + length))
return array
}
var copiedArray = ptrToArray(
copyArray(arrayToPtr(inArray), inArray.length)
, inArray.length)
console.log(copiedArray)
}
var oReq = new XMLHttpRequest();
oReq.responseType = "arraybuffer";
oReq.addEventListener("load", reqListener);
oReq.open("GET", "build/wasm_dsp.wasm");
oReq.send();
</script>
</head>
<body>
</body>
</html>