Подтвердить что ты не робот

Как я могу вернуть строку JavaScript из функции WebAssembly

Как я могу вернуть строку JavaScript из функции WebAssembly?

Можно ли записать следующий модуль в C (++)?

export function foo() {
  return 'Hello World!';
}

Также: могу ли я передать это движку JS для сбора мусора?

4b9b3361

Ответ 1

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 в будущем.

Ответ 2

Там более простой способ сделать это. Во-первых, вам нужен экземпляр вашего двоичного файла:

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. Передайте свою функцию из С++, которая возвращает строку, и вы увидите результат. Для этого случая проверить эту библиотеку.