watr
Version:
Light & fast WAT compiler – WebAssembly Text to binary, parse, print, transform
1,800 lines (1,794 loc) • 187 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
// src/encode.js
var encode_exports = {};
__export(encode_exports, {
f32: () => f32,
f64: () => f64,
i16: () => i16,
i32: () => i32,
i64: () => i64,
i8: () => i8,
uleb: () => uleb,
uleb5: () => uleb5,
v128: () => v128
});
// src/util.js
var err = (text, pos = err.loc) => {
if (pos != null && err.src) {
let line = 1, col = 1;
for (let i = 0; i < pos && i < err.src.length; i++) {
if (err.src[i] === "\n") line++, col = 1;
else col++;
}
text += ` at ${line}:${col}`;
}
throw Error(text);
};
var sepRE = /^_|_$|[^\da-f]_|_[^\da-f]/i;
var intRE = /^[+-]?(?:0x[\da-f]+|\d+)$/i;
var tenc = new TextEncoder();
var tdec = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
var escape = { n: 10, r: 13, t: 9, '"': 34, "'": 39, "\\": 92 };
var str = (s) => {
let bytes = [], i = 1, code, c, buf = "";
const commit = () => (buf && bytes.push(...tenc.encode(buf)), buf = "");
while (i < s.length - 1) {
c = s[i++], code = null;
if (c === "\\") {
if (s[i] === "u") {
i++, i++;
c = String.fromCodePoint(parseInt(s.slice(i, i = s.indexOf("}", i)), 16));
i++;
} else if (escape[s[i]]) code = escape[s[i++]];
else if (!isNaN(code = parseInt(s[i] + s[i + 1], 16))) i++, i++;
else c += s[i];
}
code != null ? (commit(), bytes.push(code)) : buf += c;
}
commit();
bytes.valueOf = () => s;
return bytes;
};
var unescape = (s) => tdec.decode(new Uint8Array(str(s)));
var clone = (node) => Array.isArray(node) ? node.map(clone) : node;
var walk = (node, fn, parent, idx) => {
fn(node, parent, idx);
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walk(node[i], fn, node, i);
};
var walkPost = (node, fn, parent, idx) => {
if (Array.isArray(node)) for (let i = 0; i < node.length; i++) walkPost(node[i], fn, node, i);
const result = fn(node, parent, idx);
if (result !== void 0 && parent) parent[idx] = result;
return result !== void 0 ? result : node;
};
// src/encode.js
var uleb = (n, buffer = []) => {
if (n == null) return buffer;
if (typeof n === "string") n = /[_x]/i.test(n) ? BigInt(n.replaceAll("_", "")) : i32.parse(n);
if (typeof n === "bigint") {
while (true) {
const byte2 = Number(n & 0x7Fn);
n >>= 7n;
if (n === 0n) {
buffer.push(byte2);
break;
}
buffer.push(byte2 | 128);
}
return buffer;
}
let byte = n & 127;
n >>>= 7;
if (n === 0) {
buffer.push(byte);
return buffer;
}
buffer.push(byte | 128);
return uleb(n, buffer);
};
function uleb5(value) {
const result = [];
for (let i = 0; i < 5; i++) {
let byte = value & 127;
value >>>= 7;
if (i < 4) {
byte |= 128;
}
result.push(byte);
}
return result;
}
function i32(n, buffer = []) {
if (typeof n === "string") n = i32.parse(n);
while (true) {
const byte = Number(n & 127);
n >>= 7;
if (n === 0 && (byte & 64) === 0 || n === -1 && (byte & 64) !== 0) {
buffer.push(byte);
break;
}
buffer.push(byte | 128);
}
return buffer;
}
var cleanInt = (v) => !sepRE.test(v) && intRE.test(v = v.replaceAll("_", "")) ? v : err(`Bad int ${v}`);
var i8 = i32;
var i16 = i32;
i32.parse = (n) => {
n = parseInt(cleanInt(n));
if (n < -2147483648 || n > 4294967295) err(`i32 constant out of range`);
return n;
};
function i64(n, buffer = []) {
if (typeof n === "string") n = i64.parse(n);
else if (typeof n === "number") n = BigInt(n);
if (typeof n === "bigint" && n > 0x7fffffffffffffffn) {
n = n - 0x10000000000000000n;
}
while (true) {
const byte = Number(n & 0x7Fn);
n >>= 7n;
if (n === 0n && (byte & 64) === 0 || n === -1n && (byte & 64) !== 0) {
buffer.push(byte);
break;
}
buffer.push(byte | 128);
}
return buffer;
}
var _buf = new ArrayBuffer(8);
var _u8 = new Uint8Array(_buf);
var _i32 = new Int32Array(_buf);
var _f32 = new Float32Array(_buf);
var _f64 = new Float64Array(_buf);
var _i64 = new BigInt64Array(_buf);
i64.parse = (n) => {
n = cleanInt(n);
const neg = n[0] === "-";
const body = neg || n[0] === "+" ? n.slice(1) : n;
let max;
if (body[0] === "0" && (body[1] === "x" || body[1] === "X")) {
const hex = body.slice(2).replace(/^0+/, "") || "0";
max = neg ? "8000000000000000" : "ffffffffffffffff";
if (hex.length > 16 || hex.length === 16 && hex.toLowerCase() > max) err(`i64 constant out of range`);
} else {
const dec = body.replace(/^0+/, "") || "0";
max = neg ? "9223372036854775808" : "18446744073709551615";
if (dec.length > max.length || dec.length === max.length && dec > max) err(`i64 constant out of range`);
}
let bi = BigInt(body);
if (neg) bi = 0n - bi;
_i64[0] = bi;
return _i64[0];
};
var F32_SIGN = 2147483648;
var F32_NAN = 2139095040;
var F32_QUIET = 4194304;
function f32(input, value, idx) {
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
if (input[idx + 3] === ":") {
const tail = input.slice(idx + 4);
value = tail === "canonical" || tail === "arithmetic" ? F32_QUIET : i32.parse(tail);
} else value = F32_QUIET;
value = (value | F32_NAN) >>> 0;
if (input[0] === "-") value = (value | F32_SIGN) >>> 0;
_i32[0] = value | 0;
} else {
value = typeof input === "string" ? f32.parse(input) : input;
_f32[0] = value;
}
return [_u8[0], _u8[1], _u8[2], _u8[3]];
}
var F64_SIGN = 0x8000000000000000n;
var F64_NAN = 0x7ff0000000000000n;
var F64_QUIET = 0x8000000000000n;
function f64(input, value, idx) {
if (typeof input === "string" && (idx = input.indexOf("nan")) >= 0) {
if (input[idx + 3] === ":") {
const tail = input.slice(idx + 4);
value = tail === "canonical" || tail === "arithmetic" ? F64_QUIET : i64.parse(tail);
} else value = F64_QUIET;
value |= F64_NAN;
if (input[0] === "-") value |= F64_SIGN;
_i64[0] = value;
} else {
value = typeof input === "string" ? f64.parse(input) : input;
_f64[0] = value;
}
return [_u8[0], _u8[1], _u8[2], _u8[3], _u8[4], _u8[5], _u8[6], _u8[7]];
}
f64.parse = (input, max = Number.MAX_VALUE) => {
input = input.replaceAll("_", "");
let sign = 1;
if (input[0] === "-") sign = -1, input = input.slice(1);
else if (input[0] === "+") input = input.slice(1);
if (input[1] === "x") {
let [sig, exp = "0"] = input.split(/p/i);
let [int, fract = ""] = sig.split(".");
let flen = fract.length ?? 0;
let intVal = 0;
for (let i = int.length - 1; i >= 2; i--) {
let digit = parseInt(int[i], 16);
intVal += digit * 16 ** (int.length - 1 - i);
}
let fractVal = fract ? parseInt("0x" + fract) / 16 ** flen : 0;
exp = parseInt(exp, 10);
let value = sign * (intVal + fractVal) * 2 ** exp;
value = Math.max(-max, Math.min(max, value));
return value;
}
if (input.includes("nan")) return sign < 0 ? NaN : NaN;
if (input.includes("inf")) return sign * Infinity;
return sign * parseFloat(input);
};
f32.parse = (input) => f64.parse(input, 34028234663852886e22);
var v128 = (input) => {
let n = typeof input === "string" ? BigInt(input.replaceAll("_", "")) : BigInt(input);
let arr = new Uint8Array(16);
for (let i = 0; i < 16; i++) arr[i] = Number(n & 0xffn), n >>= 8n;
return [...arr];
};
// src/const.js
var INSTR = [
// 0x00-0x0a: control
"unreachable",
"nop",
"block block",
"loop block",
"if block",
"else null",
"then null",
,
"throw tagidx",
,
"throw_ref",
// 0x0b-0x19: control
"end end",
"br labelidx",
"br_if labelidx",
"br_table br_table",
"return",
"call funcidx",
"call_indirect call_indirect",
"return_call funcidx",
"return_call_indirect call_indirect",
"call_ref typeidx",
"return_call_ref typeidx",
,
,
,
,
// 0x1a-0x1f: parametric
"drop",
"select select",
"",
,
,
"try_table try_table",
// 0x20-0x27: variable
"local.get localidx",
"local.set localidx",
"local.tee localidx",
"global.get globalidx",
"global.set globalidx",
"table.get tableidx",
"table.set tableidx",
,
// 0x28-0x3e: memory
"i32.load memarg",
"i64.load memarg",
"f32.load memarg",
"f64.load memarg",
"i32.load8_s memarg",
"i32.load8_u memarg",
"i32.load16_s memarg",
"i32.load16_u memarg",
"i64.load8_s memarg",
"i64.load8_u memarg",
"i64.load16_s memarg",
"i64.load16_u memarg",
"i64.load32_s memarg",
"i64.load32_u memarg",
"i32.store memarg",
"i64.store memarg",
"f32.store memarg",
"f64.store memarg",
"i32.store8 memarg",
"i32.store16 memarg",
"i64.store8 memarg",
"i64.store16 memarg",
"i64.store32 memarg",
// 0x3f-0x40: memory size/grow
"memory.size opt_memory",
"memory.grow opt_memory",
// 0x41-0x44: const
"i32.const i32",
"i64.const i64",
"f32.const f32",
"f64.const f64",
// 0x45-0x4f: i32 comparison
"i32.eqz",
"i32.eq",
"i32.ne",
"i32.lt_s",
"i32.lt_u",
"i32.gt_s",
"i32.gt_u",
"i32.le_s",
"i32.le_u",
"i32.ge_s",
"i32.ge_u",
// 0x50-0x5a: i64 comparison
"i64.eqz",
"i64.eq",
"i64.ne",
"i64.lt_s",
"i64.lt_u",
"i64.gt_s",
"i64.gt_u",
"i64.le_s",
"i64.le_u",
"i64.ge_s",
"i64.ge_u",
// 0x5b-0x60: f32 comparison
"f32.eq",
"f32.ne",
"f32.lt",
"f32.gt",
"f32.le",
"f32.ge",
// 0x61-0x66: f64 comparison
"f64.eq",
"f64.ne",
"f64.lt",
"f64.gt",
"f64.le",
"f64.ge",
// 0x67-0x78: i32 arithmetic
"i32.clz",
"i32.ctz",
"i32.popcnt",
"i32.add",
"i32.sub",
"i32.mul",
"i32.div_s",
"i32.div_u",
"i32.rem_s",
"i32.rem_u",
"i32.and",
"i32.or",
"i32.xor",
"i32.shl",
"i32.shr_s",
"i32.shr_u",
"i32.rotl",
"i32.rotr",
// 0x79-0x8a: i64 arithmetic
"i64.clz",
"i64.ctz",
"i64.popcnt",
"i64.add",
"i64.sub",
"i64.mul",
"i64.div_s",
"i64.div_u",
"i64.rem_s",
"i64.rem_u",
"i64.and",
"i64.or",
"i64.xor",
"i64.shl",
"i64.shr_s",
"i64.shr_u",
"i64.rotl",
"i64.rotr",
// 0x8b-0x98: f32 arithmetic
"f32.abs",
"f32.neg",
"f32.ceil",
"f32.floor",
"f32.trunc",
"f32.nearest",
"f32.sqrt",
"f32.add",
"f32.sub",
"f32.mul",
"f32.div",
"f32.min",
"f32.max",
"f32.copysign",
// 0x99-0xa6: f64 arithmetic
"f64.abs",
"f64.neg",
"f64.ceil",
"f64.floor",
"f64.trunc",
"f64.nearest",
"f64.sqrt",
"f64.add",
"f64.sub",
"f64.mul",
"f64.div",
"f64.min",
"f64.max",
"f64.copysign",
// 0xa7-0xc4: conversions (no immediates)
"i32.wrap_i64",
"i32.trunc_f32_s",
"i32.trunc_f32_u",
"i32.trunc_f64_s",
"i32.trunc_f64_u",
"i64.extend_i32_s",
"i64.extend_i32_u",
"i64.trunc_f32_s",
"i64.trunc_f32_u",
"i64.trunc_f64_s",
"i64.trunc_f64_u",
"f32.convert_i32_s",
"f32.convert_i32_u",
"f32.convert_i64_s",
"f32.convert_i64_u",
"f32.demote_f64",
"f64.convert_i32_s",
"f64.convert_i32_u",
"f64.convert_i64_s",
"f64.convert_i64_u",
"f64.promote_f32",
"i32.reinterpret_f32",
"i64.reinterpret_f64",
"f32.reinterpret_i32",
"f64.reinterpret_i64",
// 0xc0-0xc4: sign extension
"i32.extend8_s",
"i32.extend16_s",
"i64.extend8_s",
"i64.extend16_s",
"i64.extend32_s",
,
,
,
,
,
,
,
,
,
,
,
// 0xd0-0xd6: reference
"ref.null ref_null",
"ref.is_null",
"ref.func funcidx",
"ref.eq",
"ref.as_non_null",
"br_on_null labelidx",
"br_on_non_null labelidx",
,
,
,
,
,
,
,
,
,
// 0xe0-0xe6: stack switching (Phase 3)
"cont.new typeidx",
"cont.bind cont_bind",
"suspend tagidx",
"resume resume",
"resume_throw resume_throw",
"resume_throw_ref resume_throw_ref",
"switch switch_cont",
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
// 0xfb: GC instructions (nested array for multi-byte opcodes)
[
"struct.new typeidx",
"struct.new_default typeidx",
"struct.get typeidx_field",
"struct.get_s typeidx_field",
"struct.get_u typeidx_field",
"struct.set typeidx_field",
"array.new typeidx",
"array.new_default typeidx",
"array.new_fixed typeidx_multi",
"array.new_data typeidx_dataidx",
"array.new_elem typeidx_elemidx",
"array.get typeidx",
"array.get_s typeidx",
"array.get_u typeidx",
"array.set typeidx",
"array.len",
"array.fill typeidx",
"array.copy typeidx_typeidx",
"array.init_data typeidx_dataidx",
"array.init_elem typeidx_elemidx",
"ref.test reftype",
"ref.test_null reftype",
"ref.cast reftype",
"ref.cast_null reftype",
"br_on_cast reftype2",
"br_on_cast_fail reftype2",
"any.convert_extern",
"extern.convert_any",
"ref.i31",
"i31.get_s",
"i31.get_u",
,
"struct.new_desc typeidx",
"struct.new_default_desc typeidx",
"ref.get_desc typeidx",
"ref.cast_desc_eq reftype",
,
"br_on_cast_desc_eq reftype2",
"br_on_cast_desc_eq_fail reftype2",
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
// stringref: 0xFB 0x80-0xB7
"string.new_utf8 memoryidx?",
"string.new_wtf16 memoryidx?",
"string.const stringidx",
"string.measure_utf8",
"string.measure_wtf8",
"string.measure_wtf16",
"string.encode_utf8 memoryidx?",
"string.encode_wtf16 memoryidx?",
"string.concat",
"string.eq",
"string.is_usv_sequence",
"string.new_lossy_utf8 memoryidx?",
"string.new_wtf8 memoryidx?",
"string.encode_lossy_utf8 memoryidx?",
"string.encode_wtf8 memoryidx?",
,
// 0x8F
"string.as_wtf8",
"stringview_wtf8.advance",
"stringview_wtf8.encode_utf8 memoryidx?",
"stringview_wtf8.slice",
"stringview_wtf8.encode_lossy_utf8 memoryidx?",
"stringview_wtf8.encode_wtf8 memoryidx?",
,
,
// 0x96-0x97
"string.as_wtf16",
"stringview_wtf16.length",
"stringview_wtf16.get_codeunit",
"stringview_wtf16.encode memoryidx?",
"stringview_wtf16.slice",
,
,
,
// 0x9D-0x9F
"string.as_iter",
"stringview_iter.next",
"stringview_iter.advance",
"stringview_iter.rewind",
"stringview_iter.slice",
,
,
,
,
,
,
,
,
,
,
,
// 0xA5-0xAF
"string.new_utf8_array",
"string.new_wtf16_array",
"string.encode_utf8_array",
"string.encode_wtf16_array",
"string.new_lossy_utf8_array",
"string.new_wtf8_array",
"string.encode_lossy_utf8_array",
"string.encode_wtf8_array"
],
// 0xfc: Bulk memory/table operations + rounding mode control (nested array)
[
"i32.trunc_sat_f32_s",
"i32.trunc_sat_f32_u",
"i32.trunc_sat_f64_s",
"i32.trunc_sat_f64_u",
"i64.trunc_sat_f32_s",
"i64.trunc_sat_f32_u",
"i64.trunc_sat_f64_s",
"i64.trunc_sat_f64_u",
"memory.init dataidx_memoryidx",
"data.drop dataidx",
"memory.copy memoryidx_memoryidx",
"memory.fill memoryidx?",
"table.init reversed",
"elem.drop elemidx",
"table.copy tableidx_tableidx",
"table.grow tableidx",
"table.size tableidx",
"table.fill tableidx",
,
"i64.add128",
"i64.sub128",
"i64.mul_wide_s",
"i64.mul_wide_u",
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
// 0x80-0xBB: rounding mode control
"f32.sqrt_ceil",
"f32.add_ceil",
"f32.sub_ceil",
"f32.mul_ceil",
"f32.div_ceil",
"f64.sqrt_ceil",
"f64.add_ceil",
"f64.sub_ceil",
"f64.mul_ceil",
"f64.div_ceil",
"f32.convert_ceil_i32_s",
"f32.convert_ceil_i32_u",
"f32.convert_ceil_i64_s",
"f32.convert_ceil_i64_u",
"f32.demote_ceil_f64",
"f64.convert_ceil_i32_s",
"f64.convert_ceil_i32_u",
"f64.convert_ceil_i64_s",
"f64.convert_ceil_i64_u",
"f64.promote_ceil_f32",
"f32.sqrt_floor",
"f32.add_floor",
"f32.sub_floor",
"f32.mul_floor",
"f32.div_floor",
"f64.sqrt_floor",
"f64.add_floor",
"f64.sub_floor",
"f64.mul_floor",
"f64.div_floor",
"f32.convert_floor_i32_s",
"f32.convert_floor_i32_u",
"f32.convert_floor_i64_s",
"f32.convert_floor_i64_u",
"f32.demote_floor_f64",
"f64.convert_floor_i32_s",
"f64.convert_floor_i32_u",
"f64.convert_floor_i64_s",
"f64.convert_floor_i64_u",
"f64.promote_floor_f32",
"f32.sqrt_trunc",
"f32.add_trunc",
"f32.sub_trunc",
"f32.mul_trunc",
"f32.div_trunc",
"f64.sqrt_trunc",
"f64.add_trunc",
"f64.sub_trunc",
"f64.mul_trunc",
"f64.div_trunc",
"f32.convert_trunc_i32_s",
"f32.convert_trunc_i32_u",
"f32.convert_trunc_i64_s",
"f32.convert_trunc_i64_u",
"f32.demote_trunc_f64",
"f64.convert_trunc_i32_s",
"f64.convert_trunc_i32_u",
"f64.convert_trunc_i64_s",
"f64.convert_trunc_i64_u",
"f64.promote_trunc_f32"
],
// 0xfd: SIMD instructions (nested array)
[
"v128.load memarg",
"v128.load8x8_s memarg",
"v128.load8x8_u memarg",
"v128.load16x4_s memarg",
"v128.load16x4_u memarg",
"v128.load32x2_s memarg",
"v128.load32x2_u memarg",
"v128.load8_splat memarg",
"v128.load16_splat memarg",
"v128.load32_splat memarg",
"v128.load64_splat memarg",
"v128.store memarg",
"v128.const v128const",
"i8x16.shuffle shuffle",
"i8x16.swizzle",
"i8x16.splat",
"i16x8.splat",
"i32x4.splat",
"i64x2.splat",
"f32x4.splat",
"f64x2.splat",
"i8x16.extract_lane_s laneidx",
"i8x16.extract_lane_u laneidx",
"i8x16.replace_lane laneidx",
"i16x8.extract_lane_s laneidx",
"i16x8.extract_lane_u laneidx",
"i16x8.replace_lane laneidx",
"i32x4.extract_lane laneidx",
"i32x4.replace_lane laneidx",
"i64x2.extract_lane laneidx",
"i64x2.replace_lane laneidx",
"f32x4.extract_lane laneidx",
"f32x4.replace_lane laneidx",
"f64x2.extract_lane laneidx",
"f64x2.replace_lane laneidx",
"i8x16.eq",
"i8x16.ne",
"i8x16.lt_s",
"i8x16.lt_u",
"i8x16.gt_s",
"i8x16.gt_u",
"i8x16.le_s",
"i8x16.le_u",
"i8x16.ge_s",
"i8x16.ge_u",
"i16x8.eq",
"i16x8.ne",
"i16x8.lt_s",
"i16x8.lt_u",
"i16x8.gt_s",
"i16x8.gt_u",
"i16x8.le_s",
"i16x8.le_u",
"i16x8.ge_s",
"i16x8.ge_u",
"i32x4.eq",
"i32x4.ne",
"i32x4.lt_s",
"i32x4.lt_u",
"i32x4.gt_s",
"i32x4.gt_u",
"i32x4.le_s",
"i32x4.le_u",
"i32x4.ge_s",
"i32x4.ge_u",
"f32x4.eq",
"f32x4.ne",
"f32x4.lt",
"f32x4.gt",
"f32x4.le",
"f32x4.ge",
"f64x2.eq",
"f64x2.ne",
"f64x2.lt",
"f64x2.gt",
"f64x2.le",
"f64x2.ge",
"v128.not",
"v128.and",
"v128.andnot",
"v128.or",
"v128.xor",
"v128.bitselect",
"v128.any_true",
"v128.load8_lane memlane",
"v128.load16_lane memlane",
"v128.load32_lane memlane",
"v128.load64_lane memlane",
"v128.store8_lane memlane",
"v128.store16_lane memlane",
"v128.store32_lane memlane",
"v128.store64_lane memlane",
"v128.load32_zero memarg",
"v128.load64_zero memarg",
"f32x4.demote_f64x2_zero",
"f64x2.promote_low_f32x4",
"i8x16.abs",
"i8x16.neg",
"i8x16.popcnt",
"i8x16.all_true",
"i8x16.bitmask",
"i8x16.narrow_i16x8_s",
"i8x16.narrow_i16x8_u",
"f32x4.ceil",
"f32x4.floor",
"f32x4.trunc",
"f32x4.nearest",
"i8x16.shl",
"i8x16.shr_s",
"i8x16.shr_u",
"i8x16.add",
"i8x16.add_sat_s",
"i8x16.add_sat_u",
"i8x16.sub",
"i8x16.sub_sat_s",
"i8x16.sub_sat_u",
"f64x2.ceil",
"f64x2.floor",
"i8x16.min_s",
"i8x16.min_u",
"i8x16.max_s",
"i8x16.max_u",
"f64x2.trunc",
"i8x16.avgr_u",
"i16x8.extadd_pairwise_i8x16_s",
"i16x8.extadd_pairwise_i8x16_u",
"i32x4.extadd_pairwise_i16x8_s",
"i32x4.extadd_pairwise_i16x8_u",
"i16x8.abs",
"i16x8.neg",
"i16x8.q15mulr_sat_s",
"i16x8.all_true",
"i16x8.bitmask",
"i16x8.narrow_i32x4_s",
"i16x8.narrow_i32x4_u",
"i16x8.extend_low_i8x16_s",
"i16x8.extend_high_i8x16_s",
"i16x8.extend_low_i8x16_u",
"i16x8.extend_high_i8x16_u",
"i16x8.shl",
"i16x8.shr_s",
"i16x8.shr_u",
"i16x8.add",
"i16x8.add_sat_s",
"i16x8.add_sat_u",
"i16x8.sub",
"i16x8.sub_sat_s",
"i16x8.sub_sat_u",
"f64x2.nearest",
"i16x8.mul",
"i16x8.min_s",
"i16x8.min_u",
"i16x8.max_s",
"i16x8.max_u",
,
"i16x8.avgr_u",
"i16x8.extmul_low_i8x16_s",
"i16x8.extmul_high_i8x16_s",
"i16x8.extmul_low_i8x16_u",
"i16x8.extmul_high_i8x16_u",
"i32x4.abs",
"i32x4.neg",
,
"i32x4.all_true",
"i32x4.bitmask",
,
,
"i32x4.extend_low_i16x8_s",
"i32x4.extend_high_i16x8_s",
"i32x4.extend_low_i16x8_u",
"i32x4.extend_high_i16x8_u",
"i32x4.shl",
"i32x4.shr_s",
"i32x4.shr_u",
"i32x4.add",
,
,
"i32x4.sub",
,
,
,
"i32x4.mul",
"i32x4.min_s",
"i32x4.min_u",
"i32x4.max_s",
"i32x4.max_u",
"i32x4.dot_i16x8_s",
,
"i32x4.extmul_low_i16x8_s",
"i32x4.extmul_high_i16x8_s",
"i32x4.extmul_low_i16x8_u",
"i32x4.extmul_high_i16x8_u",
"i64x2.abs",
"i64x2.neg",
,
"i64x2.all_true",
"i64x2.bitmask",
,
,
"i64x2.extend_low_i32x4_s",
"i64x2.extend_high_i32x4_s",
"i64x2.extend_low_i32x4_u",
"i64x2.extend_high_i32x4_u",
"i64x2.shl",
"i64x2.shr_s",
"i64x2.shr_u",
"i64x2.add",
,
,
"i64x2.sub",
,
,
,
"i64x2.mul",
"i64x2.eq",
"i64x2.ne",
"i64x2.lt_s",
"i64x2.gt_s",
"i64x2.le_s",
"i64x2.ge_s",
"i64x2.extmul_low_i32x4_s",
"i64x2.extmul_high_i32x4_s",
"i64x2.extmul_low_i32x4_u",
"i64x2.extmul_high_i32x4_u",
"f32x4.abs",
"f32x4.neg",
,
"f32x4.sqrt",
"f32x4.add",
"f32x4.sub",
"f32x4.mul",
"f32x4.div",
"f32x4.min",
"f32x4.max",
"f32x4.pmin",
"f32x4.pmax",
"f64x2.abs",
"f64x2.neg",
,
"f64x2.sqrt",
"f64x2.add",
"f64x2.sub",
"f64x2.mul",
"f64x2.div",
"f64x2.min",
"f64x2.max",
"f64x2.pmin",
"f64x2.pmax",
"i32x4.trunc_sat_f32x4_s",
"i32x4.trunc_sat_f32x4_u",
"f32x4.convert_i32x4_s",
"f32x4.convert_i32x4_u",
"i32x4.trunc_sat_f64x2_s_zero",
"i32x4.trunc_sat_f64x2_u_zero",
"f64x2.convert_low_i32x4_s",
"f64x2.convert_low_i32x4_u",
"i8x16.relaxed_swizzle",
"i32x4.relaxed_trunc_f32x4_s",
"i32x4.relaxed_trunc_f32x4_u",
"i32x4.relaxed_trunc_f64x2_s_zero",
"i32x4.relaxed_trunc_f64x2_u_zero",
"f32x4.relaxed_madd",
"f32x4.relaxed_nmadd",
"f64x2.relaxed_madd",
"f64x2.relaxed_nmadd",
"i8x16.relaxed_laneselect",
"i16x8.relaxed_laneselect",
"i32x4.relaxed_laneselect",
"i64x2.relaxed_laneselect",
"f32x4.relaxed_min",
"f32x4.relaxed_max",
"f64x2.relaxed_min",
"f64x2.relaxed_max",
"i16x8.relaxed_q15mulr_s",
"i16x8.relaxed_dot_i8x16_i7x16_s",
"i32x4.relaxed_dot_i8x16_i7x16_add_s"
],
// 0xfe: atomic/thread instructions
[
"memory.atomic.notify memarg",
"memory.atomic.wait32 memarg",
"memory.atomic.wait64 memarg",
"atomic.fence opt_memory",
,
,
,
,
,
,
,
,
,
,
,
,
"i32.atomic.load memarg",
"i64.atomic.load memarg",
"i32.atomic.load8_u memarg",
"i32.atomic.load16_u memarg",
"i64.atomic.load8_u memarg",
"i64.atomic.load16_u memarg",
"i64.atomic.load32_u memarg",
"i32.atomic.store memarg",
"i64.atomic.store memarg",
"i32.atomic.store8 memarg",
"i32.atomic.store16 memarg",
"i64.atomic.store8 memarg",
"i64.atomic.store16 memarg",
"i64.atomic.store32 memarg",
"i32.atomic.rmw.add memarg",
"i64.atomic.rmw.add memarg",
"i32.atomic.rmw8.add_u memarg",
"i32.atomic.rmw16.add_u memarg",
"i64.atomic.rmw8.add_u memarg",
"i64.atomic.rmw16.add_u memarg",
"i64.atomic.rmw32.add_u memarg",
"i32.atomic.rmw.sub memarg",
"i64.atomic.rmw.sub memarg",
"i32.atomic.rmw8.sub_u memarg",
"i32.atomic.rmw16.sub_u memarg",
"i64.atomic.rmw8.sub_u memarg",
"i64.atomic.rmw16.sub_u memarg",
"i64.atomic.rmw32.sub_u memarg",
"i32.atomic.rmw.and memarg",
"i64.atomic.rmw.and memarg",
"i32.atomic.rmw8.and_u memarg",
"i32.atomic.rmw16.and_u memarg",
"i64.atomic.rmw8.and_u memarg",
"i64.atomic.rmw16.and_u memarg",
"i64.atomic.rmw32.and_u memarg",
"i32.atomic.rmw.or memarg",
"i64.atomic.rmw.or memarg",
"i32.atomic.rmw8.or_u memarg",
"i32.atomic.rmw16.or_u memarg",
"i64.atomic.rmw8.or_u memarg",
"i64.atomic.rmw16.or_u memarg",
"i64.atomic.rmw32.or_u memarg",
"i32.atomic.rmw.xor memarg",
"i64.atomic.rmw.xor memarg",
"i32.atomic.rmw8.xor_u memarg",
"i32.atomic.rmw16.xor_u memarg",
"i64.atomic.rmw8.xor_u memarg",
"i64.atomic.rmw16.xor_u memarg",
"i64.atomic.rmw32.xor_u memarg",
"i32.atomic.rmw.xchg memarg",
"i64.atomic.rmw.xchg memarg",
"i32.atomic.rmw8.xchg_u memarg",
"i32.atomic.rmw16.xchg_u memarg",
"i64.atomic.rmw8.xchg_u memarg",
"i64.atomic.rmw16.xchg_u memarg",
"i64.atomic.rmw32.xchg_u memarg",
"i32.atomic.rmw.cmpxchg memarg",
"i64.atomic.rmw.cmpxchg memarg",
"i32.atomic.rmw8.cmpxchg_u memarg",
"i32.atomic.rmw16.cmpxchg_u memarg",
"i64.atomic.rmw8.cmpxchg_u memarg",
"i64.atomic.rmw16.cmpxchg_u memarg",
"i64.atomic.rmw32.cmpxchg_u memarg"
]
];
var resultType = (op) => {
if (typeof op !== "string") return null;
const dot = op.indexOf(".");
if (dot < 0) return null;
const prefix = op.slice(0, dot);
const scalar = prefix === "i32" || prefix === "i64" || prefix === "f32" || prefix === "f64";
if (scalar && /^(eqz?|ne|[lg][te])(_[su])?$/.test(op.slice(dot + 1))) return "i32";
if (scalar || prefix === "v128") return prefix;
if (op === "memory.size" || op === "memory.grow") return "i32";
return null;
};
var SECTION = { custom: 0, type: 1, import: 2, func: 3, table: 4, memory: 5, tag: 13, strings: 14, global: 6, export: 7, start: 8, elem: 9, datacount: 12, code: 10, data: 11 };
var TYPE = {
// Value types
i8: 120,
i16: 119,
i32: 127,
i64: 126,
f32: 125,
f64: 124,
void: 64,
v128: 123,
// Heap types
exn: 105,
noexn: 116,
nofunc: 115,
noextern: 114,
none: 113,
func: 112,
extern: 111,
any: 110,
eq: 109,
i31: 108,
struct: 107,
array: 106,
data: 107,
cont: 104,
nocont: 117,
// stack switching (Phase 3)
string: 103,
stringview_wtf8: 102,
stringview_wtf16: 96,
stringview_iter: 97,
// stringref
// Reference type abbreviations (absheaptype abbrs)
nullfuncref: 115,
nullexternref: 114,
nullexnref: 116,
nullref: 113,
funcref: 112,
externref: 111,
exnref: 105,
anyref: 110,
eqref: 109,
i31ref: 108,
structref: 107,
arrayref: 106,
contref: 104,
nocontref: 117,
// stack switching abbreviations
stringref: 103,
// stringref abbreviation
// ref, refnull
ref: 100,
// -0x1c
refnull: 99,
// -0x1d
// Recursion group / type definition opcodes
sub: 80,
subfinal: 79,
rec: 78
};
var DEFTYPE = { func: 96, struct: 95, array: 94, cont: 93, sub: 80, subfinal: 79, rec: 78 };
var KIND = { func: 0, table: 1, memory: 2, global: 3, tag: 4 };
// src/parse.js
var parse_default = (str2) => {
let i = 0, level = [], buf = "", q = 0, depth = 0;
const commit = () => buf && (level.push(buf), buf = "");
const parseLevel = (pos) => {
level.loc = pos;
for (let c, root, p; i < str2.length; ) {
c = str2.charCodeAt(i);
if (q === 34) buf += str2[i++], c === 92 ? buf += str2[i++] : c === 34 && (commit(), q = 0);
else if (q > 59) c === 40 && str2.charCodeAt(i + 1) === 59 ? (q++, buf += str2[i++] + str2[i++]) : (
// nested (;
c === 59 && str2.charCodeAt(i + 1) === 41 ? (buf += str2[i++] + str2[i++], --q === 59 && (commit(), q = 0)) : (
// ;)
buf += str2[i++]
)
);
else if (q < 0) c === 10 || c === 13 ? (buf += str2[i++], commit(), q = 0) : q === -2 && c === 41 ? (commit(), q = 0) : buf += str2[i++];
else if (c === 34) buf !== "$" && commit(), q = 34, buf += str2[i++];
else if (c === 40 && str2.charCodeAt(i + 1) === 59) commit(), q = 60, buf = str2[i++] + str2[i++];
else if (c === 59 && str2.charCodeAt(i + 1) === 59) commit(), q = str2.indexOf("\n", i) < 0 ? -2 : -1, buf = str2[i++] + str2[i++];
else if (c === 40 && str2.charCodeAt(i + 1) === 64) commit(), p = i, i += 2, buf = "@", depth++, (root = level).push(level = []), parseLevel(p), level = root;
else if (c === 40) commit(), p = i++, depth++, (root = level).push(level = []), parseLevel(p), level = root;
else if (c === 41) return commit(), i++, depth--;
else if (c <= 32) commit(), i++;
else buf += str2[i++];
}
q < 0 && commit();
commit();
};
parseLevel(0);
if (q === 34) err(`Unclosed quote`, i);
if (q > 59) err(`Unclosed block comment`, i);
if (depth > 0) err(`Unclosed parenthesis`, i);
if (i < str2.length) err(`Unexpected closing parenthesis`, i);
return level.length > 1 ? level : level[0] || [];
};
// src/compile.js
var isDroppable = (n) => typeof n === "string" && (n[0] === ";" || n[1] === ";") || Array.isArray(n) && n[0]?.[0] === "@" && n[0] !== "@custom" && !n[0]?.startsWith?.("@metadata.code.");
var cleanup = (node, result) => {
if (typeof node === "string") return (
// normalize quoted ids: $"name" -> $name (if no escapes), else $unescaped
node[0] === "$" && node[1] === '"' ? node.includes("\\") ? "$" + unescape(node.slice(1)) : "$" + node.slice(2, -1) : (
// convert string literals to byte arrays with valueOf
node[0] === '"' ? str(node) : node
)
);
if (!Array.isArray(node)) return node;
result = node.filter((c) => !isDroppable(c)).map(cleanup);
result.loc = node.loc;
return result.length === 1 && result[0]?.[0] === "module" ? result[0] : result;
};
function compile(nodes) {
if (typeof nodes === "string") err.src = nodes, nodes = parse_default(nodes) || [];
else err.src = "";
err.loc = 0;
nodes = isDroppable(nodes) ? [] : cleanup(nodes) ?? [];
let idx = 0;
if (nodes[0] === "module") idx++, isId(nodes[idx]) && idx++;
else if (typeof nodes[0] === "string") nodes = [nodes];
if (nodes[idx] === "binary") return Uint8Array.from(nodes.slice(++idx).flat());
if (nodes[idx] === "quote") return compile(nodes.slice(++idx).map((v) => v.valueOf().slice(1, -1)).flat().join(""));
nodes = nodes.flatMap((n, i) => {
if (i < idx || !Array.isArray(n) || n[0] !== "import") return [n];
const [, mod, ...rest] = n;
if (!rest.some((r) => Array.isArray(r) && r[0] === "item")) return [n];
const lastIsType = Array.isArray(rest.at(-1)) && rest.at(-1)[0] !== "item";
if (lastIsType) {
const type = rest.at(-1);
return rest.slice(0, -1).filter((r) => r[0] === "item").map(([, nm]) => ["import", mod, nm, type]);
}
return rest.filter((r) => r[0] === "item").map(([, nm, type]) => ["import", mod, nm, type]);
});
const ctx = [];
for (let kind in SECTION) (ctx[SECTION[kind]] = ctx[kind] = []).name = kind;
ctx.metadata = {};
nodes.slice(idx).filter((n) => {
if (!Array.isArray(n)) {
let pos = err.loc, src = err.src, c;
while ((pos = src.indexOf(n, pos)) >= 0) {
c = src.charCodeAt(pos - 1);
if (pos > 0 && (c > 47 && c < 58 || c > 64 && c < 91 || c > 96 && c < 123 || c === 95 || c === 36)) {
pos++;
continue;
}
c = src.charCodeAt(pos + n.length);
if (c > 47 && c < 58 || c > 64 && c < 91 || c > 96 && c < 123 || c === 95) {
pos++;
continue;
}
break;
}
if (pos >= 0) err.loc = pos;
err(`Unexpected token ${n}`);
}
let [kind, ...node] = n;
err.loc = n.loc;
if (kind === "@custom") {
ctx.custom.push(node);
} else if (kind === "rec") {
for (let i = 0; i < node.length; i++) {
let [, ...subnode] = node[i];
name(subnode, ctx.type);
const tdesc = [];
while (subnode[0]?.[0] === "descriptor" || subnode[0]?.[0] === "describes") tdesc.push(subnode.shift());
(subnode = typedef(subnode, ctx)).push(i ? true : [ctx.type.length, node.length]);
if (tdesc.length) subnode.desc = subnode.desc ? [...tdesc, ...subnode.desc] : tdesc;
ctx.type.push(subnode);
}
} else if (kind === "type") {
name(node, ctx.type);
const tdesc = [];
while (node[0]?.[0] === "descriptor" || node[0]?.[0] === "describes") tdesc.push(node.shift());
const td = typedef(node, ctx);
if (tdesc.length) td.desc = td.desc ? [...tdesc, ...td.desc] : tdesc;
ctx.type.push(td);
} else if (kind === "start" || kind === "export") ctx[kind].push(node);
else return true;
}).forEach((n) => {
let [kind, ...node] = n;
err.loc = n.loc;
let imported;
if (kind === "import") [kind, ...node] = (imported = node).pop();
let items = ctx[kind];
if (!items) err(`Unknown section ${kind}`);
name(node, items);
while (node[0]?.[0] === "export") ctx.export.push([node.shift()[1], [kind, items?.length]]);
if (node[0]?.[0] === "import") [, ...imported] = node.shift();
if (kind === "table") {
const is64 = node[0] === "i64", idx2 = is64 ? 1 : 0;
if (node[idx2 + 1]?.[0] === "elem") {
let [reftype2, [, ...els]] = [node[idx2], node[idx2 + 1]];
node = is64 ? ["i64", els.length, els.length, reftype2] : [els.length, els.length, reftype2];
ctx.elem.push([["table", items.length], ["offset", [is64 ? "i64.const" : "i32.const", is64 ? 0n : 0]], reftype2, ...els]);
}
} else if (kind === "memory") {
const is64 = node[0] === "i64", idx2 = is64 ? 1 : 0;
if (node[idx2]?.[0] === "data") {
const ps = node.find((n2) => Array.isArray(n2) && n2[0] === "pagesize")?.[1] ?? 65536;
let [, ...data] = node.splice(idx2, 1)[0], m = "" + Math.ceil(data.reduce((s, d) => s + d.length, 0) / ps);
ctx.data.push([["memory", items.length], [is64 ? "i64.const" : "i32.const", is64 ? 0n : 0], ...data]);
node = is64 ? ["i64", m, m] : [m, m];
}
} else if (kind === "func") {
let [idx2, param, result] = typeuse(node, ctx);
idx2 ??= regtype(param, result, ctx);
!imported && ctx.code.push([[idx2, param, result], ...normalize(node, ctx)]);
node = [["type", idx2]];
} else if (kind === "tag") {
let [idx2, param] = typeuse(node, ctx);
idx2 ??= regtype(param, [], ctx);
node = [["type", idx2]];
}
if (imported) ctx.import.push([...imported, [kind, ...node]]), node = null;
items.push(node);
});
const bin = (kind, count = true) => {
const items = ctx[kind].filter(Boolean).map((item) => build[kind](item, ctx)).filter(Boolean);
if (kind === SECTION.custom) return items.flatMap((content) => [kind, ...vec(content)]);
return !items.length ? [] : [kind, ...vec(count ? vec(items) : items.flat())];
};
const binMeta = () => {
const sections = [];
for (const type in ctx.metadata) {
const name2 = vec(str(`"metadata.code.${type}"`));
const content = vec(ctx.metadata[type].map(
([funcIdx, instances]) => [...uleb(funcIdx), ...vec(instances.map(([pos, data]) => [...uleb(pos), ...vec(data)]))]
));
sections.push(0, ...vec([...name2, ...content]));
}
return sections;
};
const globalSection = bin(SECTION.global);
const elemSection = bin(SECTION.elem);
const codeSection = bin(SECTION.code);
const metaSection = binMeta();
const dataSection = bin(SECTION.data);
const stringsSection = ctx.strings.length ? [SECTION.strings, ...vec([0, ...vec(ctx.strings.map((s) => vec(s)))])] : [];
return Uint8Array.from([
0,
97,
115,
109,
// magic
1,
0,
0,
0,
// version
...bin(SECTION.custom),
...bin(SECTION.type),
...bin(SECTION.import),
...bin(SECTION.func),
...bin(SECTION.table),
...bin(SECTION.memory),
...bin(SECTION.tag),
...stringsSection,
...globalSection,
...bin(SECTION.export),
...bin(SECTION.start, false),
...elemSection,
...bin(SECTION.datacount, false),
...codeSection,
...metaSection,
...dataSection
]);
}
var isIdx = (n) => n?.[0] === "$" || !isNaN(n);
var isId = (n) => n?.[0] === "$";
var isMemParam = (n) => n?.[0] === "a" || n?.[0] === "o";
function normalize(nodes, ctx) {
const out = [];
nodes = [...nodes];
while (nodes.length) {
let node = nodes.shift();
if (typeof node === "string") {
out.push(node);
if (node === "block" || node === "if" || node === "loop") {
if (isId(nodes[0])) out.push(nodes.shift());
out.push(blocktype(nodes, ctx));
} else if (node === "else" || node === "end") {
if (isId(nodes[0])) nodes.shift();
} else if (node === "select") out.push(paramres(nodes)[1]);
else if (node.endsWith("call_indirect")) {
let tableidx = isIdx(nodes[0]) ? nodes.shift() : 0, [idx, param, result] = typeuse(nodes, ctx);
out.push(tableidx, ["type", idx ?? regtype(param, result, ctx)]);
} else if (node === "table.init") out.push(isIdx(nodes[1]) ? nodes.shift() : 0, nodes.shift());
else if (node === "table.copy" || node === "memory.copy") out.push(isIdx(nodes[0]) ? nodes.shift() : 0, isIdx(nodes[0]) ? nodes.shift() : 0);
else if (node.startsWith("table.")) out.push(isIdx(nodes[0]) ? nodes.shift() : 0);
else if (node === "memory.init") {
out.push(...isIdx(nodes[1]) ? [nodes.shift(), nodes.shift()].reverse() : [nodes.shift(), 0]);
ctx.datacount && (ctx.datacount[0] = true);
} else if (node === "data.drop" || node === "array.new_data" || node === "array.init_data") {
node === "data.drop" && out.push(nodes.shift());
ctx.datacount && (ctx.datacount[0] = true);
} else if ((node.startsWith("memory.") || node.endsWith("load") || node.endsWith("store")) && isIdx(nodes[0])) out.push(nodes.shift());
} else if (Array.isArray(node)) {
let op = node[0];
node.loc != null && (err.loc = node.loc);
if (op?.startsWith?.("@metadata.code.")) {
let type = op.slice(15);
out.push(["@metadata", type, node[1]]);
continue;
}
if (typeof op !== "string" || !Array.isArray(INSTR[op])) {
out.push(node);
continue;
}
const parts = node.slice(1);
if (op === "block" || op === "loop") {
out.push(op);
if (isId(parts[0])) out.push(parts.shift());
out.push(blocktype(parts, ctx), ...normalize(parts, ctx), "end");
} else if (op === "if") {
let then = [], els = [];
if (parts.at(-1)?.[0] === "else") els = normalize(parts.pop().slice(1), ctx);
if (parts.at(-1)?.[0] === "then") then = normalize(parts.pop().slice(1), ctx);
let immed = [op];
if (isId(parts[0])) immed.push(parts.shift());
immed.push(blocktype(parts, ctx));
out.push(...normalize(parts, ctx), ...immed, ...then);
els.length && out.push("else", ...els);
out.push("end");
} else if (op === "try_table") {
out.push(op);
if (isId(parts[0])) out.push(parts.shift());
out.push(blocktype(parts, ctx));
while (parts[0]?.[0] === "catch" || parts[0]?.[0] === "catch_ref" || parts[0]?.[0] === "catch_all" || parts[0]?.[0] === "catch_all_ref") {
out.push(parts.shift());
}
out.push(...normalize(parts, ctx), "end");
} else if (op === "ref.test" || op === "ref.cast") {
const type = parts[0];
const isNullable = !Array.isArray(type) || type[1] === "null" || type[0] !== "ref";
if (isNullable) op += "_null";
out.push(...normalize(parts.slice(1), ctx), op, type);
nodes.unshift(...out.splice(out.length - 2));
} else {
const imm = [];
while (parts.length && (!Array.isArray(parts[0]) || parts[0].valueOf !== Array.prototype.valueOf || "type,param,result,ref,exact,on".includes(parts[0][0]))) imm.push(parts.shift());
out.push(...normalize(parts, ctx), op, ...imm);
nodes.unshift(...out.splice(out.length - 1 - imm.length));
}
} else out.push(node);
}
return out;
}
var regtype = (param, result, ctx, idx = "$" + param + ">" + result) => (ctx.type[idx] ??= ctx.type.push(["func", [param, result]]) - 1, idx);
var fieldseq = (nodes, field) => {
let seq = [];
while (nodes[0]?.[0] === field) {
let [, ...args] = nodes.shift(), nm = isId(args[0]) && args.shift();
if (nm) nm in seq ? (() => {
throw Error(`Duplicate ${field} ${nm}`);
})() : seq[nm] = seq.length;
seq.push(...args);
}
return seq;
};
var paramres = (nodes) => {
let param = fieldseq(nodes, "param"), result = fieldseq(nodes, "result");
if (nodes[0]?.[0] === "param") throw Error("Unexpected param");
return [param, result];
};
var typeuse = (nodes, ctx) => {
if (nodes[0]?.[0] !== "type") return [, ...paramres(nodes)];
let [, idx] = nodes.shift(), [param, result] = paramres(nodes);
const entry = ctx.type[typeof idx === "string" && isNaN(idx) ? ctx.type[idx] : +idx];
if (!entry) throw Error(`Unknown type ${idx}`);
if ((param.length || result.length) && entry[1].join(">") !== param + ">" + result) throw Error(`Type ${idx} mismatch`);
return [idx, ...entry[1]];
};
var blocktype = (nodes, ctx) => {
let [idx, param, result] = typeuse(nodes, ctx);
if (!param.length && !result.length) return;
if (!param.length && result.length === 1) return ["result", ...result];
return ["type", idx ?? regtype(param, result, ctx)];
};
var name = (node, list) => {
let nm = isId(node[0]) && node.shift();
if (nm) nm in list ? err(`Duplicate ${list.name} ${nm}`) : list[nm] = list.length;
return nm;
};
var typedef = ([dfn], ctx) => {
let subkind = "subfinal", supertypes = [], compkind, desc = [];
if (dfn[0] === "sub") {
subkind = dfn.shift(), dfn[0] === "final" && (subkind += dfn.shift());
dfn = (supertypes = dfn).pop();
supertypes = supertypes.filter((n) => Array.isArray(n) && (n[0] === "descriptor" || n[0] === "describes") ? (desc.push(n), false) : true);
}
[compkind, ...dfn] = dfn;
if (compkind === "func") dfn = paramres(dfn), ctx.type["$" + dfn.join(">")] ??= ctx.type.length;
else if (compkind === "struct") dfn = fieldseq(dfn, "field");
else if (compkind === "array") [dfn] = dfn;
const result = [compkind, dfn, subkind, supertypes];
if (desc.length) result.desc = desc;
return result;
};
var build = [
// (@custom "name" placement? data) - custom section builder
([name2, ...rest], ctx) => {
let data = rest;
if (rest[0]?.[0] === "before" || rest[0]?.[0] === "after") {
data = rest.slice(1);
}
return [...vec(name2), ...data.flat()];
},
// type kinds
// (func params result)
// (array i8)
// (struct ...fields)
// (cont $ft) - stack switching (Phase 3)
(node, ctx) => {
const [kind, fields, subkind, supertypes, rec] = node;
if (rec === true) return;
const descPfx = (node.desc ?? []).flatMap(([clause, ref]) => [clause === "descriptor" ? 77 : 76, ...uleb(id(ref, ctx.type))]);
const comptype = (k, f) => {
if (k === "func") return [DEFTYPE.func, ...vec(f[0].map((t) => reftype(t, ctx))), ...vec(f[1].map((t) => reftype(t, ctx)))];
if (k === "array") return [DEFTYPE.array, ...fieldtype(f, ctx)];
if (k === "struct") return [DEFTYPE.struct, ...vec(f.map((t) => fieldtype(t, ctx)))];
if (k === "cont") return [DEFTYPE.cont, ...uleb(id(f[0] ?? f, ctx.type))];
return [DEFTYPE[k]];
};
if (rec) {
let [from, length] = rec;
const subtypes = Array.from({ length }, (_, i) => {
const t = ctx.type[from + i], sub = t.slice(0, 4);
if (t.desc) sub.desc = t.desc;
return build[SECTION.type](sub, ctx);
});
return [DEFTYPE.rec, ...vec(subtypes)];
} else if (subkind === "sub" || supertypes?.length) {
return [DEFTYPE[subkind], ...vec(supertypes.map((n) => id(n, ctx.type))), ...descPfx, ...comptype(kind, fields)];
}
return [...descPfx, ...comptype(kind, fields)];
},
// (import "math" "add" (func|table|global|memory|tag dfn?))
([mod, field, [kind, ...dfn]], ctx) => {
let details, kindByte = KIND[kind];
if (kind === "func") {
const isExact = dfn[0] === "exact" && dfn.shift();
if (isExact) kindByte = 32;
let [[, typeidx]] = dfn;
details = uleb(id(typeidx, ctx.type));
} else if (kind === "tag") {
let [[, typeidx]] = dfn;
details = [0, ...uleb(id(typeidx, ctx.type))];
} else if (kind === "memory") {
details = limits(dfn);
} else if (kind === "global") {
details = fieldtype(dfn[0], ctx);
} else if (kind === "table") {
details = [...reftype(dfn.pop(), ctx), ...limits(dfn)];
} else err(`Unknown kind ${kind}`);
return [...vec(mod), ...vec(field), kindByte, ...details];
},
// (func $name? ...params result ...body)
([[, typeidx]], ctx) => uleb(id(typeidx, ctx.type)),
// (table 1 2 funcref)
(node, ctx) => {
let lims = limits(node), t = reftype(node.shift(), ctx), [init] = node;
return init ? [64, 0, ...t, ...lims, ...expr(init, ctx)] : [...t, ...lims];
},
// (memory id? export* min max shared)
(node, ctx) => limits(node),
// (global $id? (mut i32) (i32.const 42))
([t, init], ctx) => [...fieldtype(t, ctx), ...expr(init, ctx)],
// (export "name" (func|table|mem $name|idx))
([nm, [kind, l]], ctx) => [...vec(nm), KIND[kind], ...uleb(id(l, ctx[kind]))],
// (start $main)
([l], ctx) => uleb(id(l, ctx.func)),
// (elem elem*) - passive
// (elem declare elem*) - declarative
// (elem (table idx)? (offset expr)|(expr) elem*) - active
// ref: https://webassembly.github.io/spec/core/binary/modules.html#element-section
(parts, ctx) => {
let passive = 0, declare = 0, elexpr = 0, nofunc = 0, tabidx, offset2, rt;
if (parts[0] === "declare") parts.shift(), declare = 1;
if (parts[0]?.[0] === "table") {
[, tabidx] = parts.shift();
tabidx = id(tabidx, ctx.table);
} else if ((typeof parts[0] === "string" || typeof parts[0] === "number") && (parts[1]?.[0] === "offset" || Array.isArray(parts[1]) && parts[1][0] !== "item" && !parts[1][0]?.startsWith("ref"))) {
tabidx = id(parts.shift(), ctx.table);
}
if (parts[0]?.[0] === "offset" || Array.isArray(parts[0]) && parts[0][0] !== "item" && !parts[0][0].startsWith("ref")) {
offset2 = parts.shift();
if (offset2[0] === "offset") [, offset2] = offset2;
offset2 = expr(offset2, ctx);
} else if (!declare) passive = 1;
if (TYPE[parts[0]] || parts[0]?.[0] === "ref") rt = reftype(parts.shift(), ctx);
else if (parts[0] === "func") rt = [TYPE[parts.shift()]];
else rt = [TYPE.func];
parts = parts.map((el) => {
if (el[0] === "item") el = el.length === 3 && el[1] === "ref.func" ? el[2] : el[1];
if (el[0] === "ref.func") [, el] = el;
if (typeof el !== "string") elexpr = 1;
return el;
});
if (rt[0] !== TYPE.funcref) nofunc = 1, elexpr = 1;
let mode = elexpr << 2 | (passive || declare ? declare : !!tabidx || nofunc) << 1 | (passive || declare);
return [
mode,
...// 0b000 e:expr y*:vec(funcidx) | type=(ref func), init ((ref.func y)end)*, active (table=0,offset=e)
mode === 0 ? offset2 : (
// 0b001 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive
mode === 1 ? [0] : (
// 0b010 x:tabidx e:expr et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, active (table=x,offset=e)
mode === 2 ? [...uleb(tabidx || 0), ...offset2, 0] : (
// 0b011 et:elkind y*:vec(funcidx) | type=0x00, init ((ref.func y)end)*, passive declare
mode === 3 ? [0] : (
// 0b100 e:expr el*:vec(expr) | type=(ref null func), init el*, active (table=0, offset=e)
mode === 4 ? offset2 : (
// 0b101 et:reftype el*:vec(expr) | type=et, init el*, passive
mode === 5 ? rt : (
// 0b110 x:tabidx e:expr et:reftype el*:vec(expr) | type=et, init el*, active (table=x, offset=e)
mode === 6 ? [...uleb(tabidx || 0), ...offset2, ...rt] : (
// 0b111 et:reftype el*:vec(expr) | type=et, init el*, passive declare
rt
)
)
)
)
)
)
),
...vec(
parts.map(
elexpr ? (
// ((