Skip to content

Instantly share code, notes, and snippets.

@ABCxFF
Created February 25, 2024 03:44
Show Gist options
  • Save ABCxFF/9cad88c17d2d3452ddc259fabcd121dc to your computer and use it in GitHub Desktop.
Save ABCxFF/9cad88c17d2d3452ddc259fabcd121dc to your computer and use it in GitHub Desktop.
async WebAssembly.Function polyfill
// For Firefox or other browsers that don't provide WebAssembly.Function APIs,
// this polyfill, albeit async, can be used as a substitute.
//
// Using WASM, this polyfill is accepted by WebAssembly.Tables, and can be inserted
// into them without failure
if (!WebAssembly.Function) {
const vu32 = (x) => {
let out = [];
do {
let byte = x & 0x7F;
x >>= 7;
if (x) byte |= 0x80
out.push(byte);
} while (x);
return out;
}
const deepFlat = arr => arr.flatMap(e => Array.isArray(e) ? deepFlat(e.flat()) : e);
const vector = (arr) => vu32(arr.length).concat(deepFlat(arr));
const string = (str) => vector(Array.from(new TextEncoder().encode(str)))
const types = {
"i32": 0x7F,
"i64": 0x7E,
"f32": 0x7D,
"f64": 0x7C,
"v128": 0x7B,
"funcref": 0x70,
"externref": 0x6F
}
class WebAssemblyFunction {
constructor({ parameters, results }, callable) {
return new Promise(async (resolve, reject) => {
for (let i = 0; i < parameters.length; ++i) {
if (!types.hasOwnProperty(parameters[i])) {
return reject(new TypeError(`Argument 0 parameter type at index #${i} must be a value type`));
}
}
for (let i = 0; i < results.length; ++i) {
if (!types.hasOwnProperty(results[i])) {
return reject(new TypeError(`Argument 0 result type at index #${i} must be a value type`));
}
}
if (typeof callable !== "function") {
return reject(new TypeError("Argument 1 must be a function"));
}
const w = await WebAssembly.instantiate(new Uint8Array(deepFlat([
[0x00, 0x61, 0x73, 0x6d], // magic
[0x01, 0x00, 0x00, 0x00], // version
[
0x1, // type section
vector(deepFlat(vector([
[
0x60,
vector(parameters.map(n => types[n])),
vector(results.map(n => types[n]))
]
])))
],
[
0x2, // import section
vector(deepFlat(vector([
[
string("m"),
string("c"),
0,
vu32(0)
]
])))
],
[
0x3, // function section
vector(deepFlat(vector([
[
vu32(0)
]
])))
],
[
0x7, // export section
vector(deepFlat(vector([
[
string("e"),
0,
vu32(1)
]
])))
],
[
0xA, // function section
vector(deepFlat(vector([
vector(deepFlat([
vector([]),
...parameters.flatMap((_,i) => [0x20, ...vu32(i)]),
0x10, vu32(0), // call $0
0x0B // end
]))
])))
],
])), { m: { c: callable } });
resolve(w.instance.exports.e.bind());
});
}
}
WebAssembly.Function = WebAssemblyFunction;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment