Как я могу вернуть строку JavaScript из функции WebAssembly?
Можно ли записать следующий модуль в C (++)?
export function foo() {
return 'Hello World!';
}
Также: могу ли я передать это движку JS для сбора мусора?
Как я могу вернуть строку JavaScript из функции WebAssembly?
Можно ли записать следующий модуль в C (++)?
export function foo() {
return 'Hello World!';
}
Также: могу ли я передать это движку JS для сбора мусора?
WebAssembly не поддерживает строковый тип, он скорее поддерживает i32
/i64
/f32
/f64
типы значений, а также i8
/i16
для хранения.
Вы можете взаимодействовать с экземпляром WebAssembly, используя:
exports
, где из JavaScript вы вызываете в WebAssembly, а WebAssembly возвращает один тип значения.imports
, где WebAssembly вызывает JavaScript, с таким количеством типов значений, сколько вы хотите (обратите внимание: счет должен быть известен при компиляции модуля время, это не массив и не является вариационным).Memory.buffer
, который является ArrayBuffer
, который может быть проиндексирован с использованием (среди прочих) Uint8Array
.Это зависит от того, что вы хотите сделать, но похоже, что доступ к буфере напрямую самый простой:
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);
Если в вашем модуле была функция start
, то она была выполнена во время создания экземпляра. В противном случае вы, вероятно, будете иметь экспорт, который вы вызываете, например. instance.exports.doIt()
.
После этого вам нужно получить индекс строки + index в памяти, который вы также можете экспортировать через экспорт:
const size = instance.exports.myStringSize();
const index = instance.exports.myStringIndex();
Затем вы прочитали его из буфера:
let s = "";
for (let i = index; i < index + size; ++i)
s += String.fromCharCode(buffer[i]);
Обратите внимание, что я читаю 8-битные значения из буфера, поэтому я предполагаю, что строки были ASCII. То, что даст вам std::string
(индекс в памяти будет тем, что возвращает .c_str()
), но для того, чтобы выставить что-то еще, например UTF-8, вам нужно будет использовать библиотеку С++, поддерживающую UTF-8, а затем прочитать UTF- 8 из JavaScript, получить коды и использовать String.fromCodePoint
.
Вы также можете полагаться на строку с нулевым завершением, чего я здесь не делал.
Вы также можете использовать TextDecoder
API, когда он будет доступен более широко в браузерах, создав ArrayBufferView
в WebAssembly.Memory
buffer
(который является ArrayBuffer
).
Если вместо этого вы делаете что-то вроде ведения журнала из WebAssembly на JavaScript, вы можете открыть Memory
, как указано выше, а затем из WebAssembly объявить импорт, который вызывает JavaScript с размером + position. Вы можете создать экземпляр своего модуля как:
const memory = new WebAssembly.Memory({ initial: 2 });
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);
const instance = new WebAssembly.Instance(module, {
imports: {
memory: memory,
logString: (size, index) => {
let s = "";
for (let i = index; i < index + size; ++i)
s += String.fromCharCode(buffer[i]);
console.log(s);
}
});
Это означает, что если вы когда-либо вырасти память (либо с помощью JavaScript с помощью Memory.prototype.grow
, либо с помощью кода операции grow_memory
), то ArrayBuffer
получает стерилизацию, и вам нужно создать ее заново.
В сборке мусора: WebAssembly.Module
/WebAssembly.Instance
/WebAssembly.Memory
- все мусор, собранный движком JavaScript, но это довольно большой молот. Вероятно, вы захотите использовать GC-строки, и это невозможно для объектов, которые живут внутри WebAssembly.Memory
. Мы обсудили добавление поддержки GC в будущем.
Там более простой способ сделать это. Во-первых, вам нужен экземпляр вашего двоичного файла:
const module = new WebAssembly.Module(bin);
const memory = new WebAssembly.Memory({ initial: 2 });
const instance = new WebAssembly.Instance(module, { imports: { memory: memory } });
Затем, если вы запустите console.log(instance)
, почти в верхней части этого объекта, вы увидите функцию AsciiToString
. Передайте свою функцию из С++, которая возвращает строку, и вы увидите результат. Для этого случая проверить эту библиотеку.