Я застрял на этом некоторое время и не могу найти хорошие ресурсы для своей проблемы. Я пришел из "только C", поэтому большинство вещей для веб-разработчиков для меня совершенно новые.
Я написал функцию C float editDistance(char *str1, char *str2)
, которая возвращает расстояние редактирования двух массивов символов. Сейчас цель состоит в том, чтобы успешно вызвать эту функцию из среды JS.
Убедившись, что код работает с рекомендованным методом Emscipten ccall
, я решил двигаться дальше. Сейчас
Я использую Emscripten для компиляции кода C с флагами -O3
, -s WASM=1
, -s EXPORTED_FUNCTIONS="['_editDistance']"
и -s SIDE_MODULE=1 -s
для Wasm. Код JS, который я пытаюсь обернуть вокруг своей WebAssembly:
// Allocate memory for the wasm module to run in. (65536*256 bit)
let wasmMemory = new WebAssembly.Memory({
initial: 256
});
let info = {
env: {
abort: function() {},
memoryBase: 0,
tableBase: 0,
memory: wasmMemory,
table: new WebAssembly.Table({initial: 2, element: 'anyfunc'}),
}
}
// Define the strings
let str1 = "abcd";
let str2 = "abcd";
// Allocate memory on the wasm partition for the HEAPU8
let HEAPU8 = new Uint8Array(wasmMemory.buffer);
// Create the char arrays on the heap from the strings
let stackPtr = 0;
let str1Ptr = stackPtr;
stackPtr = stringToASCIIArray(str1, HEAPU8, stackPtr);
let str2Ptr = stackPtr;
stackPtr = stringToASCIIArray(str2, HEAPU8, stackPtr);
// Read the wasm file and instantiate it with the above environment setup. Then
// call the exported function with the string pointers.
let wasmBinaryFile = 'bin/edit_distanceW.wasm';
fetch(wasmBinaryFile, {credentials:"same-origin"})
.then((response) => response.arrayBuffer())
.then((binary) => WebAssembly.instantiate(binary,info))
.then((wa) => alert(wa.instance.exports._editDistance(str1Ptr, str2Ptr)));
// Converts a string to an ASCII byte array on the specified memory
function stringToASCIIArray(str, outU8Array, idx){
let length = str.length + 1;
let i;
for(i=0; i<length; i++){
outU8Array[idx+i] = str.charCodeAt(i);
}
outU8Array[idx+i]=0;
return (idx + length);
}
Сгенерированный файл wasm
при преобразовании в wat
требует следующих импортов:
(import "env" "abort" (func (;0;) (type 0)))
(import "env" "memoryBase" (global (;0;) i32))
(import "env" "tableBase" (global (;1;) i32))
(import "env" "memory" (memory (;0;) 256))
(import "env" "table" (table (;0;) 2 anyfunc))
.. и экспортирует эти:
(export "__post_instantiate" (func 7))
(export "_editDistance" (func 9))
(export "runPostSets" (func 6))
(elem (;0;) (get_global 1) 8 1))
Теперь, когда я тестирую код, строки без проблем передаются в модуль C. Несколько вызовов функций даже сделаны на них (strLen), прежде чем дела пойдут на юг. В функции C есть этот неприятный вложенный цикл, который выполняет основные вычисления, итерируя по двумерному массиву, читая символы из строк (код C только что был перенесен с бумаги с уродливым псевдокодом, так что извините за имена переменных) :
do{
for(p=0; p<editDistance; p++){
// Do stuff
}
// Do more stuff
editDistance++;
} while(fkp[len2*2-len1][editDistance] != len1);
До того, как функция входит в цикл for()
, в модуле по-прежнему хранятся строки в памяти str1Ptr=0x00
и str2Ptr=0x05
с правильной длиной и содержанием. Напротив, сразу после входа в цикл for()
память перезаписывается мусором (в основном 0 с), что приводит к повреждению конечного результата. Я подозреваю, что некоторые проблемы с сохранением и восстановлением стека при изменении области видимости, так как тот же код, скомпилированный на моем ПК с использованием gcc, работает как чудо.
Есть идеи, какие настройки мне не хватает, что мешает правильному завершению функции C?