UNPKG

watr

Version:

Light & fast WAT compiler – WebAssembly Text to binary, parse, print, transform

1,800 lines (1,794 loc) 187 kB
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 ? ( // ((