tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

wasm-binary.js (26837B)


      1 // MagicNumber = 0x6d736100;
      2 const magic0 = 0x00;  // '\0'
      3 const magic1 = 0x61;  // 'a'
      4 const magic2 = 0x73;  // 's'
      5 const magic3 = 0x6d;  // 'm'
      6 
      7 // EncodingVersion
      8 const encodingVersion = 0x1;
      9 const ver0 = (encodingVersion >>>  0) & 0xff;
     10 const ver1 = (encodingVersion >>>  8) & 0xff;
     11 const ver2 = (encodingVersion >>> 16) & 0xff;
     12 const ver3 = (encodingVersion >>> 24) & 0xff;
     13 
     14 // Section opcodes
     15 const userDefinedId    = 0;
     16 const typeId           = 1;
     17 const importId         = 2;
     18 const functionId       = 3;
     19 const tableId          = 4;
     20 const memoryId         = 5;
     21 const globalId         = 6;
     22 const exportId         = 7;
     23 const startId          = 8;
     24 const elemId           = 9;
     25 const codeId           = 10;
     26 const dataId           = 11;
     27 const dataCountId      = 12;
     28 const tagId            = 13;
     29 
     30 // User-defined section names
     31 const nameName         = "name";
     32 
     33 // Name section name types
     34 const nameTypeModule    = 0;
     35 const nameTypeFunction  = 1;
     36 const nameTypeLocal     = 2;
     37 const nameTypeTag       = 3;
     38 
     39 // Type codes
     40 const I32Code          = 0x7f;
     41 const I64Code          = 0x7e;
     42 const F32Code          = 0x7d;
     43 const F64Code          = 0x7c;
     44 const V128Code         = 0x7b;
     45 const FuncRefCode      = 0x70;
     46 const ExternRefCode    = 0x6f;
     47 const AnyRefCode       = 0x6e;
     48 const EqRefCode        = 0x6d;
     49 const OptRefCode       = 0x63; // (ref null $t), needs heap type immediate
     50 const RefCode          = 0x64; // (ref $t), needs heap type immediate
     51 const FuncCode         = 0x60;
     52 const StructCode       = 0x5f;
     53 const ArrayCode        = 0x5e;
     54 const VoidCode         = 0x40;
     55 const BadType          = 0x79; // reserved for testing
     56 const RecGroupCode     = 0x4e;
     57 const SubFinalTypeCode = 0x4f;
     58 const SubNoFinalTypeCode = 0x50;
     59 
     60 // Opcodes
     61 const UnreachableCode  = 0x00
     62 const BlockCode        = 0x02;
     63 const TryCode          = 0x06;
     64 const CatchCode        = 0x07;
     65 const ThrowCode        = 0x08;
     66 const RethrowCode      = 0x09;
     67 const EndCode          = 0x0b;
     68 const ReturnCode       = 0x0f;
     69 const CallCode         = 0x10;
     70 const CallIndirectCode = 0x11;
     71 const ReturnCallCode   = 0x12;
     72 const ReturnCallIndirectCode = 0x13;
     73 const ReturnCallRefCode      = 0x15;
     74 const DelegateCode     = 0x18;
     75 const DropCode         = 0x1a;
     76 const SelectCode       = 0x1b;
     77 const LocalGetCode     = 0x20;
     78 const I32Load          = 0x28;
     79 const I64Load          = 0x29;
     80 const F32Load          = 0x2a;
     81 const F64Load          = 0x2b;
     82 const I32Load8S        = 0x2c;
     83 const I32Load8U        = 0x2d;
     84 const I32Load16S       = 0x2e;
     85 const I32Load16U       = 0x2f;
     86 const I64Load8S        = 0x30;
     87 const I64Load8U        = 0x31;
     88 const I64Load16S       = 0x32;
     89 const I64Load16U       = 0x33;
     90 const I64Load32S       = 0x34;
     91 const I64Load32U       = 0x35;
     92 const I32Store         = 0x36;
     93 const I64Store         = 0x37;
     94 const F32Store         = 0x38;
     95 const F64Store         = 0x39;
     96 const I32Store8        = 0x3a;
     97 const I32Store16       = 0x3b;
     98 const I64Store8        = 0x3c;
     99 const I64Store16       = 0x3d;
    100 const I64Store32       = 0x3e;
    101 const GrowMemoryCode   = 0x40;
    102 const I32ConstCode     = 0x41;
    103 const I64ConstCode     = 0x42;
    104 const F32ConstCode     = 0x43;
    105 const F64ConstCode     = 0x44;
    106 const I32AddCode       = 0x6a;
    107 const I32DivSCode      = 0x6d;
    108 const I32DivUCode      = 0x6e;
    109 const I32RemSCode      = 0x6f;
    110 const I32RemUCode      = 0x70;
    111 const I32TruncSF32Code = 0xa8;
    112 const I32TruncUF32Code = 0xa9;
    113 const I32TruncSF64Code = 0xaa;
    114 const I32TruncUF64Code = 0xab;
    115 const I64TruncSF32Code = 0xae;
    116 const I64TruncUF32Code = 0xaf;
    117 const I64TruncSF64Code = 0xb0;
    118 const I64TruncUF64Code = 0xb1;
    119 const I64DivSCode      = 0x7f;
    120 const I64DivUCode      = 0x80;
    121 const I64RemSCode      = 0x81;
    122 const I64RemUCode      = 0x82;
    123 const RefNullCode      = 0xd0;
    124 const RefIsNullCode    = 0xd1;
    125 const RefFuncCode      = 0xd2;
    126 
    127 // SIMD opcodes
    128 const V128LoadCode = 0x00;
    129 const V128StoreCode = 0x0b;
    130 
    131 // Relaxed SIMD opcodes.
    132 const I8x16RelaxedSwizzleCode = 0x100;
    133 const I32x4RelaxedTruncSSatF32x4Code = 0x101;
    134 const I32x4RelaxedTruncUSatF32x4Code = 0x102;
    135 const I32x4RelaxedTruncSatF64x2SZeroCode = 0x103;
    136 const I32x4RelaxedTruncSatF64x2UZeroCode = 0x104;
    137 const F32x4RelaxedMaddCode = 0x105;
    138 const F32x4RelaxedNmaddCode = 0x106;
    139 const F64x2RelaxedMaddCode = 0x107;
    140 const F64x2RelaxedNmaddCode = 0x108;
    141 const I8x16RelaxedLaneSelectCode = 0x109;
    142 const I16x8RelaxedLaneSelectCode = 0x10a;
    143 const I32x4RelaxedLaneSelectCode = 0x10b;
    144 const I64x2RelaxedLaneSelectCode = 0x10c;
    145 const F32x4RelaxedMinCode = 0x10d;
    146 const F32x4RelaxedMaxCode = 0x10e;
    147 const F64x2RelaxedMinCode = 0x10f;
    148 const F64x2RelaxedMaxCode = 0x110;
    149 const I16x8RelaxedQ15MulrSCode = 0x111;
    150 const I16x8RelaxedDotI8x16I7x16SCode = 0x112;
    151 const I32x4RelaxedDotI8x16I7x16AddSCode = 0x113;
    152 
    153 const FirstInvalidOpcode = 0xc5;
    154 const LastInvalidOpcode = 0xfa;
    155 const GcPrefix = 0xfb;
    156 const MiscPrefix = 0xfc;
    157 const SimdPrefix = 0xfd;
    158 const ThreadPrefix = 0xfe;
    159 const MozPrefix = 0xff;
    160 
    161 // See WasmConstants.h for documentation.
    162 // Limit this to a group of 8 per line.
    163 
    164 const definedOpcodes =
    165    [0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    166     0x06, 0x07, 0x08, 0x09,
    167     0x0a,
    168     0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    169     0x10, 0x11,
    170     0x12, 0x13,
    171     0x14,
    172     0x15,
    173     0x18, 0x19,
    174     0x1a, 0x1b, 0x1c,
    175     0x1f,
    176     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
    177     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    178     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    179     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    180     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    181     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    182     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
    183     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
    184     0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
    185     0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    186     0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
    187     0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
    188     0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    189     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    190     0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    191     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    192     0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    193     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    194     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    195     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    196     0xc0, 0xc1, 0xc2, 0xc3, 0xc4,
    197     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
    198     0xf0,
    199     0xfb, 0xfc, 0xfd, 0xfe, 0xff ];
    200 
    201 const undefinedOpcodes = (function () {
    202    let a = [];
    203    let j = 0;
    204    let i = 0;
    205    while (i < 256) {
    206        while (definedOpcodes[j] > i)
    207            a.push(i++);
    208        assertEq(definedOpcodes[j], i);
    209        i++;
    210        j++;
    211    }
    212    assertEq(definedOpcodes.length + a.length, 256);
    213    return a;
    214 })();
    215 
    216 // Secondary opcode bytes for misc prefix
    217 const MemoryInitCode = 0x08;    // Pending
    218 const DataDropCode = 0x09;      // Pending
    219 const MemoryCopyCode = 0x0a;    // Pending
    220 const MemoryFillCode = 0x0b;    // Pending
    221 const TableInitCode = 0x0c;     // Pending
    222 const ElemDropCode = 0x0d;      // Pending
    223 const TableCopyCode = 0x0e;     // Pending
    224 
    225 const StructNew = 0x00;         // UNOFFICIAL
    226 const StructNewDefault = 0x01;  // UNOFFICIAL
    227 const StructGet = 0x03;         // UNOFFICIAL
    228 const StructSet = 0x06;         // UNOFFICIAL
    229 
    230 // External type codes (used for imports/exports)
    231 const ExternFuncCode   = 0x00;
    232 const ExternTableCode  = 0x01;
    233 const ExternMemCode    = 0x02;
    234 const ExternGlobalCode = 0x03;
    235 const ExternTagCode    = 0x04;
    236 
    237 // ResizableFlags
    238 const HasMaximumFlag   = 0x1;
    239 
    240 function toU8(array) {
    241    for (const [i, b] of array.entries()) {
    242        assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`);
    243    }
    244    return Uint8Array.from(array);
    245 }
    246 
    247 function toSharedU8(array) {
    248    let sab = new SharedArrayBuffer(array.length);
    249    let view = new Uint8Array(sab);
    250    for (const [i, b] of array.entries()) {
    251        assertEq(b < 256, true, `expected byte at index ${i} but got ${b}`);
    252        view[i] = b;
    253    }
    254    return view;
    255 }
    256 
    257 function varU32(u32) {
    258    assertEq(u32 >= 0, true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`);
    259    assertEq(u32 < Math.pow(2,32), true, `varU32 input must be number between 0 and 2^32-1, got ${u32}`);
    260    var bytes = [];
    261    do {
    262        var byte = u32 & 0x7f;
    263        u32 >>>= 7;
    264        if (u32 != 0)
    265            byte |= 0x80;
    266        bytes.push(byte);
    267    } while (u32 != 0);
    268    return bytes;
    269 }
    270 
    271 function varS32(s32) {
    272    assertEq(s32 >= -Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`);
    273    assertEq(s32 < Math.pow(2,31), true, `varS32 input must be number between -2^31 and 2^31-1, got ${s32}`);
    274    var bytes = [];
    275    do {
    276        var byte = s32 & 0x7f;
    277        s32 >>= 7;
    278        if (s32 != 0 && s32 != -1)
    279            byte |= 0x80;
    280        bytes.push(byte);
    281    } while (s32 != 0 && s32 != -1);
    282    return bytes;
    283 }
    284 
    285 function varU64(u64) {
    286    u64 = BigInt(u64);
    287    assertEq(u64 >= 0n, true, `varU64 input must be number between 0 and 2^64-1, got ${u64}`);
    288    assertEq(u64 < 2n**64n, true, `varU64 input must be number between 0 and 2^64-1, got ${u64}`);
    289    var bytes = [];
    290    do {
    291        var byte = Number(u64 & 0x7fn);
    292        u64 >>= 7n;
    293        if (u64 !== 0n)
    294            byte |= 0x80;
    295        bytes.push(byte);
    296    } while (u64 !== 0n);
    297    return bytes;
    298 }
    299 
    300 function moduleHeaderThen(...rest) {
    301    return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
    302 }
    303 
    304 function string(name) {
    305    var nameBytes = name.split('').map(c => {
    306        var code = c.charCodeAt(0);
    307        assertEq(code < 128, true); // TODO
    308        return code
    309    });
    310    return varU32(nameBytes.length).concat(nameBytes);
    311 }
    312 
    313 function encodedString(name, len) {
    314    var name = unescape(encodeURIComponent(name)); // break into string of utf8 code points
    315    var nameBytes = name.split('').map(c => c.charCodeAt(0)); // map to array of numbers
    316    return varU32(len === undefined ? nameBytes.length : len).concat(nameBytes);
    317 }
    318 
    319 function moduleWithSections(sections) {
    320    const bytes = moduleHeaderThen();
    321    for (const section of sections) {
    322        bytes.push(section.name);
    323        bytes.push(...varU32(section.length ?? section.body.length));
    324        for (let byte of section.body) {
    325            bytes.push(byte);
    326        }
    327    }
    328    return toU8(bytes);
    329 }
    330 
    331 /**
    332 * Creates a type section for a module. Example:
    333 *
    334 *     typeSection([
    335 *         // (type (func (param i32 i64)))
    336 *         { kind: FuncCode, args: [I32Code, I64Code], ret: [] },
    337 *         // (type (func (result (ref 123))))
    338 *         { kind: FuncCode, args: [], ret: [[RefCode, ...varS32(123)]] },
    339 *
    340 *         // GC types are supported:
    341 *         { kind: StructCode, fields: [I32Code, { mut: true, type: [RefCode, ...varS32(123)] }] },
    342 *         { kind: ArrayCode, elem: { mut: true, type: I32Code } }] },
    343 *         { kind: ArrayCode, elem: { mut: true, type: [RefCode, ...varS32(123)] } }] },
    344 *
    345 *         // Recursion groups can be created with the recGroup function
    346 *         recGroup([
    347 *             { kind: StructCode, fields: [I32Code, I64Code] },
    348 *             { kind: StructCode, sub: 5, fields: [I32Code, I64Code, I32Code] },
    349 *         ]),
    350 *     ])
    351 *
    352 * ## Full documentation
    353 *
    354 * This function takes an array of type objects in one of the following formats:
    355 *
    356 *     { kind: FuncCode, args: <ResultType>, ret: <ResultType> }
    357 *     { kind: StructCode, fields: [<FieldType>] }
    358 *     { kind: ArrayCode, elem: <FieldType> }
    359 *
    360 * Each type object can also have the following optional fields:
    361 *
    362 *   - `sub: <number>`: Makes the type a subtype of the given type index.
    363 *     By default it will not have any parent types.
    364 *   - `final: <boolean>`: Controls whether the type is final. Default `true`.
    365 *
    366 * And finally, types can be placed in a recursion group by wrapping them
    367 * with the `recGroup` function.
    368 *
    369 * ### ResultType
    370 *
    371 * A result type is a vector of value types. You provide this as an array
    372 * where each entry is the bytes for the type. For example, for a function
    373 * with `(return i32 (ref 123))`, you might provide:
    374 *
    375 *     [[I32Code], [RefCode, ...varS32(123)]]
    376 *
    377 * If a value type is only a single byte, you can pass it directly instead of
    378 * passing an array:
    379 *
    380 *     [I32Code, [RefCode, ...varS32(123)]]
    381 *
    382 * If there is only a single value type, you can omit the outer array too:
    383 *
    384 *     I32Code // same as [I32Code], same as [[I32Code]]
    385 *
    386 * And finally, `VoidCode` is a special case that results in an empty vector.
    387 *
    388 *     VoidCode // same as []
    389 *
    390 * Note that if you want to encode a single type, but that type has multiple
    391 * bytes, you will need to keep the outermost array.
    392 *
    393 *     [I32Code, I64Code]        // sugar for [[I32Code], [I64Code]], so two types
    394 *     [RefCode, ...varS32(123)] // will be interpreted as [[RefCode], [123]],
    395 *                               // i.e. two types - not what you want
    396 *
    397 * ### FieldType
    398 *
    399 * A field type is used for struct and array values, and is a value type plus
    400 * mutability info. The general form looks like:
    401 *
    402 *     { mut: <boolean>, type: <bytes> }
    403 *
    404 * For example, `(mut i32)` would look like:
    405 *
    406 *     { mut: true, type: [I32Code] }
    407 *
    408 * If the type is a single byte, you can omit the array:
    409 *
    410 *     { mut: true, type: I32Code }
    411 *
    412 * And if you wish for the field to be immutable, you can provide the type only:
    413 *
    414 *     I32Code // same as { mut: false, type: I32Code }
    415 *
    416 */
    417 function typeSection(types) {
    418    var body = [];
    419    body.push(...varU32(types.length)); // technically a count of recursion groups
    420    for (const type of types) {
    421        if (type.isRecursionGroup) {
    422            body.push(RecGroupCode);
    423            body.push(...varU32(type.types.length));
    424            for (const t of type.types) {
    425                for (const byte of _encodeType(t)) {
    426                    body.push(byte);
    427                }
    428            }
    429        } else {
    430            for (const byte of _encodeType(type)) {
    431                body.push(byte);
    432            }
    433        }
    434    }
    435    return { name: typeId, body };
    436 }
    437 
    438 function recGroup(types) {
    439    return { isRecursionGroup: true, types };
    440 }
    441 
    442 /**
    443 * Returns a "normalized" version of all the ResultType stuff from `typeSection`,
    444 * i.e. an array of array of bytes for each value type.
    445 */
    446 function _resultType(input) {
    447    if (input === VoidCode) {
    448        return [];
    449    }
    450    if (typeof input === "number") {
    451        input = [input];
    452    }
    453    input = input.map(valType => Array.isArray(valType) ? valType : [valType]);
    454    return input;
    455 }
    456 
    457 /**
    458 * Returns a "normalized" version of FieldType from `typeSection`, i.e. an object
    459 * of the form `{ mut: <boolean>, type: <bytes> }`.
    460 */
    461 function _fieldType(input) {
    462    if (typeof input !== "object" || Array.isArray(input)) {
    463        input = { mut: false, type: input };
    464    }
    465    if (!Array.isArray(input.type)) {
    466        input.type = [input.type];
    467    }
    468    return input;
    469 }
    470 
    471 /**
    472 * Encodes a type object from `typeSection`. This basically corresponds to `subtypeDef`
    473 * in the GC spec doc.
    474 */
    475 function _encodeType(typeObj) {
    476    const typeBytes = [];
    477    // Types are now final by default.
    478    const final = typeObj.final ?? true;
    479    if (typeObj.sub !== undefined) {
    480        typeBytes.push(final ? SubFinalTypeCode : SubNoFinalTypeCode);
    481        typeBytes.push(...varU32(1), ...varU32(typeObj.sub));
    482    }
    483    else if (final == false) {
    484        // This type is extensible even if no supertype is defined.
    485        typeBytes.push(SubNoFinalTypeCode);
    486        typeBytes.push(0x00);
    487    }
    488    typeBytes.push(typeObj.kind);
    489    switch (typeObj.kind) {
    490    case FuncCode: {
    491        const args = _resultType(typeObj.args);
    492        const ret = _resultType(typeObj.ret);
    493        typeBytes.push(...varU32(args.length));
    494        for (const t of args) {
    495            typeBytes.push(...t);
    496        }
    497        typeBytes.push(...varU32(ret.length));
    498        for (const t of ret) {
    499            typeBytes.push(...t);
    500        }
    501    } break;
    502    case StructCode: {
    503        // fields
    504        typeBytes.push(...varU32(typeObj.fields.length));
    505        for (const f of typeObj.fields) {
    506            typeBytes.push(..._encodeFieldType(f));
    507        }
    508    } break;
    509    case ArrayCode: {
    510        // elem
    511        typeBytes.push(..._encodeFieldType(typeObj.elem));
    512    } break;
    513    default:
    514        throw new Error(`unknown type kind ${typeObj.kind} in type section`);
    515    }
    516    return typeBytes;
    517 }
    518 
    519 function _encodeFieldType(fieldTypeObj) {
    520    fieldTypeObj = _fieldType(fieldTypeObj);
    521    return [...fieldTypeObj.type, fieldTypeObj.mut ? 0x01 : 0x00];
    522 }
    523 
    524 /**
    525 * A convenience function to create a type section containing only function
    526 * types. This is basically sugar for `typeSection`, although you do not have
    527 * to provide `kind: FuncCode` on each definition as you would there.
    528 *
    529 * Example:
    530 *
    531 *     sigSection([
    532 *         // (type (func (param i32 i64)))
    533 *         { args: [I32Code, I64Code], ret: [] },
    534 *         // (type (func (result (ref 123))))
    535 *         { args: [], ret: [[RefCode, ...varS32(123)]] },
    536 *     ])
    537 *
    538 */
    539 function sigSection(sigs) {
    540    return typeSection(sigs.map(sig => ({ kind: FuncCode, ...sig })));
    541 }
    542 
    543 function declSection(decls) {
    544    var body = [];
    545    body.push(...varU32(decls.length));
    546    for (let decl of decls)
    547        body.push(...varU32(decl));
    548    return { name: functionId, body };
    549 }
    550 
    551 function funcBody(func, withEndCode=true) {
    552    var body = varU32(func.locals.length);
    553    for (let local of func.locals)
    554        body.push(...varU32(local));
    555    for (let byte of func.body) {
    556        body.push(byte);
    557    }
    558    if (withEndCode)
    559        body.push(EndCode);
    560    body.splice(0, 0, ...varU32(body.length));
    561    return body;
    562 }
    563 
    564 function bodySection(bodies) {
    565    var body = varU32(bodies.length).concat(...bodies);
    566    return { name: codeId, body };
    567 }
    568 
    569 function importSection(imports) {
    570    var body = [];
    571    body.push(...varU32(imports.length));
    572    for (let imp of imports) {
    573        body.push(...string(imp.module));
    574        body.push(...string(imp.item));
    575        if (imp.hasOwnProperty("funcTypeIndex")) {
    576            body.push(ExternFuncCode);
    577            body.push(...varU32(imp.funcTypeIndex));
    578        } else if (imp.hasOwnProperty("tableType")) {
    579            body.push(ExternTableCode);
    580            body.push(...imp.tableType);
    581        } else if (imp.hasOwnProperty("memType")) {
    582            body.push(ExternMemCode);
    583            body.push(...imp.memType);
    584        } else if (imp.hasOwnProperty("globalType")) {
    585            body.push(ExternGlobalCode);
    586            body.push(...imp.globalType);
    587        } else if (imp.hasOwnProperty("tagType")) {
    588            body.push(ExternTagCode);
    589            body.push(...imp.tagType);
    590        } else {
    591            throw new Error(`unknown import type for "${imp.module}" "${imp.name}"`);
    592        }
    593    }
    594    return { name: importId, body };
    595 }
    596 
    597 function exportSection(exports) {
    598    var body = [];
    599    body.push(...varU32(exports.length));
    600    for (let exp of exports) {
    601        body.push(...string(exp.name));
    602        if (exp.hasOwnProperty("funcIndex")) {
    603            body.push(...varU32(ExternFuncCode));
    604            body.push(...varU32(exp.funcIndex));
    605        } else if (exp.hasOwnProperty("memIndex")) {
    606            body.push(...varU32(ExternMemCode));
    607            body.push(...varU32(exp.memIndex));
    608        } else if (exp.hasOwnProperty("tagIndex")) {
    609            body.push(...varU32(ExternTagCode));
    610            body.push(...varU32(exp.tagIndex));
    611        } else {
    612            throw "Bad export " + exp;
    613        }
    614    }
    615    return { name: exportId, body };
    616 }
    617 
    618 /**
    619 * Encode a table section:
    620 * https://wasm-dsl.github.io/spectec/core/binary/modules.html#table-section
    621 *
    622 *     tableSection([
    623 *         // (table 10 100 funcref), two ways
    624 *         { type: tableType(FuncRefCode, { min: 10, max: 100 }) },
    625 *         { elemType: FuncRefCode, min: 10, max: 100 },
    626 *
    627 *         // (table i64 10 (ref func) ref.func 123), two ways
    628 *         {
    629 *             type: tableType([RefCode, FuncRefCode], { addrType: "i64", min: 10n }),
    630 *             init: [RefFuncCode, ...varU32(123), EndCode],
    631 *         },
    632 *         {
    633 *             elemType: [RefCode, FuncRefCode],
    634 *             addrType: "i64", min: 10n,
    635 *             init: [RefFuncCode, ...varU32(123), EndCode],
    636 *         },
    637 *     ]);
    638 */
    639 function tableSection(tables) {
    640    var body = [];
    641    body.push(...varU32(tables.length));
    642    for (const table of tables) {
    643        if (table.init) {
    644            body.push(0x40, 0x00);
    645        }
    646        if (table.type) {
    647            body.push(...table.type);
    648        } else {
    649            body.push(...tableType(table.elemType, limits({
    650                addrType: table.addrType ?? "i32",
    651                min: table.min, max: table.max,
    652            })));
    653        }
    654        if (table.init) {
    655            body.push(...table.init);
    656        }
    657    }
    658    return { name: tableId, body };
    659 }
    660 
    661 /**
    662 * Encode a table section of funcs with the given initial size (and no max).
    663 * Useful for the typical wasm 1.0 use of tables.
    664 */
    665 function defaultTableSection(initialSize) {
    666    return tableSection([{ elemType: FuncRefCode, min: initialSize }]);
    667 }
    668 
    669 /**
    670 * Create limits:
    671 * https://wasm-dsl.github.io/spectec/core/binary/types.html#binary-limits
    672 *
    673 *     limits({ min: 0 })
    674 *     limits({ addrType: "i64", min: 0n, max: 10n })
    675 */
    676 function limits({ addrType = "i32", min, max }) {
    677    var body = [];
    678    body.push((addrType === "i64" ? 0x04 : 0x00) & (max === undefined ? 0x00 : 0x01));
    679    body.push(...varU64(min));
    680    if (max !== undefined) {
    681        body.push(...varU64(max));
    682    }
    683    return body;
    684 }
    685 
    686 /**
    687 * Create a table type:
    688 * https://wasm-dsl.github.io/spectec/core/binary/types.html#table-types
    689 *
    690 *     tableType(FuncRefCode, limits({ min: 0 }))
    691 *     tableType([RefCode, ...varS32(123)], limits({ addrType: "i64", min: 0n, max: 10n }))
    692 */
    693 function tableType(elemType, limits) {
    694    var body = [];
    695    if (typeof elemType === "number") {
    696        elemType = [elemType];
    697    }
    698    body.push(...elemType);
    699    body.push(...limits);
    700    return body;
    701 }
    702 
    703 function memorySection(initialSize) {
    704    var body = [];
    705    body.push(...varU32(1));           // number of memories
    706    body.push(...varU32(0x0));         // for now, no maximum
    707    body.push(...varU32(initialSize));
    708    return { name: memoryId, body };
    709 }
    710 
    711 function tagSection(tags) {
    712    var body = [];
    713    body.push(...varU32(tags.length));
    714    for (let tag of tags) {
    715        body.push(...varU32(0)); // exception attribute
    716        body.push(...varU32(tag.type));
    717    }
    718    return { name: tagId, body };
    719 }
    720 
    721 function dataSection(segmentArrays) {
    722    var body = [];
    723    body.push(...varU32(segmentArrays.length));
    724    for (let array of segmentArrays) {
    725        body.push(...varU32(0)); // memory index
    726        body.push(...varU32(I32ConstCode));
    727        body.push(...varS32(array.offset));
    728        body.push(...varU32(EndCode));
    729        body.push(...varU32(array.elems.length));
    730        for (let elem of array.elems)
    731            body.push(...varU32(elem));
    732    }
    733    return { name: dataId, body };
    734 }
    735 
    736 function dataCountSection(count) {
    737    var body = [];
    738    body.push(...varU32(count));
    739    return { name: dataCountId, body };
    740 }
    741 
    742 function globalSection(globalArray) {
    743    var body = [];
    744    body.push(...varU32(globalArray.length));
    745    for (let globalObj of globalArray) {
    746        // Value type
    747        body.push(...varU32(globalObj.valType));
    748        // Flags
    749        body.push(globalObj.flags & 255);
    750        // Initializer expression
    751        body.push(...globalObj.initExpr);
    752    }
    753    return { name: globalId, body };
    754 }
    755 
    756 function elemSection(elemArrays) {
    757    var body = [];
    758    body.push(...varU32(elemArrays.length));
    759    for (let array of elemArrays) {
    760        body.push(...varU32(0)); // table index
    761        body.push(...varU32(I32ConstCode));
    762        body.push(...varS32(array.offset));
    763        body.push(...varU32(EndCode));
    764        body.push(...varU32(array.elems.length));
    765        for (let elem of array.elems)
    766            body.push(...varU32(elem));
    767    }
    768    return { name: elemId, body };
    769 }
    770 
    771 const ActiveFuncIdxTable0 = 0;
    772 const PassiveFuncIdx = 1;
    773 const ActiveFuncIdx = 2;
    774 const DeclaredFuncIdx = 3;
    775 const ActiveElemExprTable0 = 4;
    776 const PassiveElemExpr = 5;
    777 const ActiveElemExpr = 6;
    778 const DeclaredElemExpr = 7;
    779 
    780 function generalElemSection(elemObjs) {
    781    let body = [];
    782    body.push(...varU32(elemObjs.length));
    783    for (let elemObj of elemObjs) {
    784        body.push(elemObj.flag);
    785        if ((elemObj.flag & 3) == 2)
    786            body.push(...varU32(elemObj.table));
    787        // TODO: This is not very flexible
    788        if ((elemObj.flag & 1) == 0) {
    789            body.push(...varU32(I32ConstCode));
    790            body.push(...varS32(elemObj.offset));
    791            body.push(...varU32(EndCode));
    792        }
    793        if (elemObj.flag & 4) {
    794            if (elemObj.flag & 3)
    795                body.push(elemObj.typeCode & 255);
    796            // Each element is an array of bytes
    797            body.push(...varU32(elemObj.elems.length));
    798            for (let elemBytes of elemObj.elems)
    799                body.push(...elemBytes);
    800        } else {
    801            if (elemObj.flag & 3)
    802                body.push(elemObj.externKind & 255);
    803            // Each element is a putative function index
    804            body.push(...varU32(elemObj.elems.length));
    805            for (let elem of elemObj.elems)
    806                body.push(...varU32(elem));
    807        }
    808    }
    809    return { name: elemId, body };
    810 }
    811 
    812 function moduleNameSubsection(moduleName) {
    813    var body = [];
    814    body.push(...varU32(nameTypeModule));
    815 
    816    var subsection = encodedString(moduleName);
    817    body.push(...varU32(subsection.length));
    818    body.push(...subsection);
    819 
    820    return body;
    821 }
    822 
    823 function funcNameSubsection(funcNames, subsectionLen = null) {
    824    var body = [];
    825    body.push(...varU32(nameTypeFunction));
    826 
    827    var subsection = varU32(funcNames.length);
    828 
    829    var funcIndex = 0;
    830    for (let f of funcNames) {
    831        subsection.push(...varU32(f.index ? f.index : funcIndex));
    832        subsection.push(...encodedString(f.name, f.nameLen));
    833        funcIndex++;
    834    }
    835 
    836    body.push(...varU32(subsectionLen ?? subsection.length));
    837    body.push(...subsection);
    838    return body;
    839 }
    840 
    841 function nameSection(subsections, sectionLength = null) {
    842    var body = [];
    843    body.push(...string(nameName));
    844 
    845    for (let ss of subsections)
    846        body.push(...ss);
    847 
    848    return { name: userDefinedId, length: sectionLength, body };
    849 }
    850 
    851 function customSection(name, ...body) {
    852    return { name: userDefinedId, body: [...string(name), ...body] };
    853 }
    854 
    855 function tableSection0() {
    856    var body = [];
    857    body.push(...varU32(0));           // number of tables
    858    return { name: tableId, body };
    859 }
    860 
    861 function memorySection0() {
    862    var body = [];
    863    body.push(...varU32(0));           // number of memories
    864    return { name: memoryId, body };
    865 }