tor-browser

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

WasmDump.cpp (28325B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "wasm/WasmDump.h"
      8 
      9 #include <cinttypes>
     10 
     11 #include "wasm/WasmValidate.h"
     12 
     13 using namespace js;
     14 using namespace js::wasm;
     15 
     16 static void DumpTypeDefIndex(const TypeDef* typeDef, GenericPrinter& out,
     17                             const TypeContext* types) {
     18  if (types) {
     19    out.printf("%" PRIu32, types->indexOf(*typeDef));
     20  } else {
     21    out.printf("? (;0x%" PRIxPTR ";)", (uintptr_t)typeDef);
     22  }
     23 }
     24 
     25 static void DumpFuncParamsAndResults(const FuncType& funcType,
     26                                     StructuredPrinter& out,
     27                                     const TypeContext* types) {
     28  if (funcType.args().length() > 0) {
     29    out.brk(" ", "\n");
     30    out.printf("(param");
     31    for (ValType arg : funcType.args()) {
     32      out.printf(" ");
     33      DumpValType(arg, out, types);
     34    }
     35    out.printf(")");
     36  }
     37  if (funcType.results().length() > 0) {
     38    out.brk(" ", "\n");
     39    out.printf("(result");
     40    for (ValType result : funcType.results()) {
     41      out.printf(" ");
     42      DumpValType(result, out, types);
     43    }
     44    out.printf(")");
     45  }
     46 }
     47 
     48 template <size_t MinInlineCapacity, class AllocPolicy>
     49 static void DumpBytesAsString(
     50    const mozilla::Vector<uint8_t, MinInlineCapacity, AllocPolicy>& bytes,
     51    GenericPrinter& out) {
     52  WATStringEscape esc;
     53  EscapePrinter o(out, esc);
     54 
     55  out.put("\"");
     56  for (uint8_t b : bytes) {
     57    o.putChar(b);
     58  }
     59  out.put("\"");
     60 }
     61 
     62 static void DumpName(const CacheableName& name, GenericPrinter& out) {
     63  WATStringEscape esc;
     64  EscapePrinter o(out, esc);
     65 
     66  out.put("\"");
     67  o.put(name.utf8Bytes());
     68  out.put("\"");
     69 }
     70 
     71 void wasm::DumpModule(const Module& module) {
     72  Fprinter out(stdout);
     73  wasm::DumpModule(module, out);
     74  out.printf("\n");
     75 }
     76 
     77 void wasm::DumpModule(const Module& module, GenericPrinter& out) {
     78  StructuredPrinter o(out);
     79 
     80  o.printf("(module");
     81  {
     82    StructuredPrinter::Scope _(o);
     83 
     84    // Type section
     85    const TypeContext& types = *module.moduleMeta().codeMeta->types.get();
     86    if (types.length()) {
     87      o.printf("\n");
     88      DumpTypeContext(types, o);
     89      o.printf("\n");
     90    }
     91 
     92    // Import section
     93    size_t numFuncImports = 0;
     94    size_t numTableImports = 0;
     95    size_t numMemoryImports = 0;
     96    size_t numGlobalImports = 0;
     97    size_t numTagImports = 0;
     98    for (const Import& import : module.moduleMeta().imports) {
     99      o.printf("\n");
    100      o.printf("(import ");
    101 
    102      DumpName(import.module, o);
    103      o.put(" ");
    104      DumpName(import.field, o);
    105      o.put(" ");
    106 
    107      switch (import.kind) {
    108        case DefinitionKind::Function: {
    109          size_t funcIndex = numFuncImports++;
    110          const TypeDef& funcTypeDef =
    111              module.codeMeta().getFuncTypeDef(funcIndex);
    112          o.printf("(func (;%zu;) (type ", funcIndex);
    113          DumpTypeDefIndex(&funcTypeDef, o, &types);
    114          o.printf("))");
    115        } break;
    116        case DefinitionKind::Table: {
    117          size_t tableIndex = numTableImports++;
    118          const TableDesc& tableDesc = module.codeMeta().tables[tableIndex];
    119          DumpTableDesc(tableDesc, module.codeMeta(), /*includeInitExpr=*/false,
    120                        o, int32_t(tableIndex));
    121        } break;
    122        case DefinitionKind::Memory: {
    123          size_t memIndex = numMemoryImports++;
    124          const MemoryDesc& memDesc = module.codeMeta().memories[memIndex];
    125          DumpMemoryDesc(memDesc, o, int32_t(memIndex));
    126        } break;
    127        case DefinitionKind::Global: {
    128          size_t globalIndex = numGlobalImports++;
    129          const GlobalDesc& globalDesc = module.codeMeta().globals[globalIndex];
    130          DumpGlobalDesc(globalDesc, module.codeMeta(), false, o,
    131                         int32_t(globalIndex));
    132        } break;
    133        case DefinitionKind::Tag: {
    134          size_t tagIndex = numTagImports++;
    135          const TagDesc& tagDesc = module.codeMeta().tags[tagIndex];
    136          DumpTagDesc(tagDesc, o, int32_t(tagIndex), &types);
    137        } break;
    138        default: {
    139          o.printf("(; unknown import kind ;)");
    140        } break;
    141      }
    142 
    143      o.printf(")");
    144    }
    145    if (module.moduleMeta().imports.length() > 0) {
    146      o.printf("\n");
    147    }
    148 
    149    // We skip the function section because the function types show up later in
    150    // the dump of the code section.
    151 
    152    // Table section
    153    for (size_t i = numTableImports; i < module.codeMeta().tables.length();
    154         i++) {
    155      const TableDesc& tableDesc = module.codeMeta().tables[i];
    156      o.printf("\n");
    157      DumpTableDesc(tableDesc, module.codeMeta(), /*includeInitExpr=*/true, o,
    158                    int32_t(i));
    159    }
    160    if (module.codeMeta().tables.length() - numTableImports > 0) {
    161      o.printf("\n");
    162    }
    163 
    164    // Memory section
    165    for (size_t i = numMemoryImports; i < module.codeMeta().memories.length();
    166         i++) {
    167      const MemoryDesc& memDesc = module.codeMeta().memories[i];
    168      o.printf("\n");
    169      DumpMemoryDesc(memDesc, o, int32_t(i));
    170    }
    171    if (module.codeMeta().memories.length() - numMemoryImports > 0) {
    172      o.printf("\n");
    173    }
    174 
    175    // Tag section
    176    for (size_t i = numTagImports; i < module.codeMeta().tags.length(); i++) {
    177      const TagDesc& tagDesc = module.codeMeta().tags[i];
    178      o.printf("\n");
    179      DumpTagDesc(tagDesc, o, int32_t(i), &types);
    180    }
    181    if (module.codeMeta().tags.length() - numTagImports > 0) {
    182      o.printf("\n");
    183    }
    184 
    185    // Global section
    186    for (size_t i = numGlobalImports; i < module.codeMeta().globals.length();
    187         i++) {
    188      const GlobalDesc& globalDesc = module.codeMeta().globals[i];
    189      o.printf("\n");
    190      DumpGlobalDesc(globalDesc, module.codeMeta(), /*includeInitExpr=*/true, o,
    191                     int32_t(i));
    192    }
    193    if (module.codeMeta().globals.length() - numGlobalImports > 0) {
    194      o.printf("\n");
    195    }
    196 
    197    // Export section
    198    for (const Export& exp : module.moduleMeta().exports) {
    199      o.printf("\n");
    200      o.printf("(export ");
    201      DumpName(exp.fieldName(), o);
    202      o.printf(" ");
    203      switch (exp.kind()) {
    204        case DefinitionKind::Function: {
    205          o.printf("(func %" PRIu32 ")", exp.funcIndex());
    206        } break;
    207        case DefinitionKind::Table: {
    208          o.printf("(table %" PRIu32 ")", exp.tableIndex());
    209        } break;
    210        case DefinitionKind::Memory: {
    211          o.printf("(memory %" PRIu32 ")", exp.memoryIndex());
    212        } break;
    213        case DefinitionKind::Global: {
    214          o.printf("(global %" PRIu32 ")", exp.globalIndex());
    215        } break;
    216        case DefinitionKind::Tag: {
    217          o.printf("(tag %" PRIu32 ")", exp.tagIndex());
    218        } break;
    219        default: {
    220          o.printf("(; unknown export kind ;)");
    221        } break;
    222      }
    223      o.printf(")");
    224    }
    225    if (module.moduleMeta().exports.length() > 0) {
    226      o.printf("\n");
    227    }
    228 
    229    // Start section
    230    if (module.codeMeta().startFuncIndex.isSome()) {
    231      o.printf("\n");
    232      o.printf("(start %" PRIu32 ")", module.codeMeta().startFuncIndex.value());
    233      o.printf("\n");
    234    }
    235 
    236    // Element section
    237    for (size_t i = 0; i < module.moduleMeta().elemSegments.length(); i++) {
    238      const ModuleElemSegment& elem = module.moduleMeta().elemSegments[i];
    239      o.printf("\n");
    240      o.printf("(elem (;%zu;)", i);
    241 
    242      bool typeExpanded = false;
    243      {
    244        StructuredPrinter::Scope _(o);
    245        if (elem.active()) {
    246          o.brk(" ", "\n");
    247          o.printf("(table %" PRIu32 ")", elem.tableIndex);
    248          o.brk(" ", "\n");
    249          o.printf("(offset");
    250          {
    251            StructuredPrinter::Scope _(o);
    252            DumpInitExpr(elem.offset(), module.codeMeta(), o);
    253            o.brk("", "\n");
    254          }
    255          o.printf(")");
    256        } else if (elem.kind == ModuleElemSegment::Kind::Declared) {
    257          o.brk(" ", "\n");
    258          o.printf("declare");
    259        }
    260        if (elem.encoding == ModuleElemSegment::Encoding::Expressions) {
    261          o.brk(" ", "\n");
    262          DumpRefType(elem.elemType, o, &types);
    263        }
    264        o.brk("", "\n");
    265        typeExpanded = o.isExpanded();
    266      }
    267      {
    268        StructuredPrinter::Scope _(o);
    269        if (typeExpanded) {
    270          o.expand();
    271        }
    272 
    273        switch (elem.encoding) {
    274          case ModuleElemSegment::Encoding::Indices: {
    275            o.brk(" ", "\n");
    276            o.printf("func");
    277            for (uint32_t idx : elem.elemIndices) {
    278              o.printf(" %" PRIu32, idx);
    279            }
    280          } break;
    281          case ModuleElemSegment::Encoding::Expressions: {
    282            UniqueChars error;
    283            Decoder d(elem.elemExpressions.exprBytes.begin(),
    284                      elem.elemExpressions.exprBytes.end(), /* dummy */ 0,
    285                      &error);
    286            ValTypeVector locals;
    287            ValidatingOpIter iter(module.codeMeta(), d, locals,
    288                                  ValidatingOpIter::Kind::InitExpr);
    289            for (uint32_t i = 0; i < elem.numElements(); i++) {
    290              o.brk(" ", "\n");
    291              o.printf("(item");
    292              {
    293                StructuredPrinter::Scope _(o);
    294 
    295                OpDumper visitor(o, &types);
    296                if (!iter.startInitExpr(elem.elemType)) {
    297                  out.printf("(; bad expression ;)");
    298                  return;
    299                }
    300                if (!ValidateOps(iter, visitor, module.codeMeta())) {
    301                  out.printf("(; bad expression: %s ;)", d.error()->get());
    302                  return;
    303                }
    304                if (!iter.endInitExpr()) {
    305                  out.printf("(; bad expression ;)");
    306                  return;
    307                }
    308                o.brk("", "\n");
    309              }
    310              o.printf(")");
    311            }
    312 
    313            if (elem.numElements() > 1) {
    314              o.expand();
    315            }
    316          } break;
    317          default: {
    318            out.printf("(; unknown encoding ;)");
    319          } break;
    320        }
    321        o.brk("", "\n");
    322      }
    323      o.printf(")");
    324    }
    325    if (module.moduleMeta().elemSegments.length() > 0) {
    326      o.printf("\n");
    327    }
    328 
    329    // Code section
    330    for (size_t i = numFuncImports; i < module.codeMeta().numFuncs(); i++) {
    331      o.printf("\n");
    332      DumpFunction(module.codeMeta(), module.codeTailMeta(), i, o);
    333    }
    334    if (module.codeMeta().numFuncs() - numFuncImports > 0) {
    335      o.printf("\n");
    336    }
    337 
    338    // Data section
    339    for (size_t i = 0; i < module.moduleMeta().dataSegments.length(); i++) {
    340      RefPtr<const DataSegment> seg = module.moduleMeta().dataSegments[i];
    341 
    342      o.printf("\n");
    343      o.printf("(data (;%zu;)", i);
    344      {
    345        StructuredPrinter::Scope _(o);
    346        if (seg->active()) {
    347          o.brk(" ", "\n");
    348          o.printf("(memory %" PRIu32 ")", seg->memoryIndex);
    349          o.brk(" ", "\n");
    350          o.printf("(offset");
    351          {
    352            StructuredPrinter::Scope _(o);
    353            DumpInitExpr(seg->offset(), module.codeMeta(), o);
    354            o.brk("", "\n");
    355          }
    356          o.printf(")");
    357        }
    358        o.brk(" ", "\n");
    359        DumpBytesAsString(seg->bytes, o);
    360        o.brk("", "\n");
    361      }
    362      o.printf(")");
    363    }
    364  }
    365  o.brk("", "\n");
    366  o.printf(")");
    367 }
    368 
    369 void wasm::DumpValType(ValType type, const TypeContext* types) {
    370  Fprinter out(stdout);
    371  wasm::DumpValType(type, out, types);
    372  out.printf("\n");
    373 }
    374 
    375 void wasm::DumpValType(ValType type, GenericPrinter& out,
    376                       const TypeContext* types) {
    377  DumpStorageType(type.storageType(), out, types);
    378 }
    379 
    380 void wasm::DumpStorageType(StorageType type, const TypeContext* types) {
    381  Fprinter out(stdout);
    382  wasm::DumpStorageType(type, out, types);
    383  out.printf("\n");
    384 }
    385 
    386 void wasm::DumpStorageType(StorageType type, GenericPrinter& out,
    387                           const TypeContext* types) {
    388  const char* literal = nullptr;
    389  switch (type.kind()) {
    390    case StorageType::I8:
    391      literal = "i8";
    392      break;
    393    case StorageType::I16:
    394      literal = "i16";
    395      break;
    396    case StorageType::I32:
    397      literal = "i32";
    398      break;
    399    case StorageType::I64:
    400      literal = "i64";
    401      break;
    402    case StorageType::V128:
    403      literal = "v128";
    404      break;
    405    case StorageType::F32:
    406      literal = "f32";
    407      break;
    408    case StorageType::F64:
    409      literal = "f64";
    410      break;
    411    case StorageType::Ref:
    412      return DumpRefType(type.refType(), out, types);
    413    default:
    414      MOZ_CRASH("unexpected storage type");
    415  }
    416  out.put(literal);
    417 }
    418 
    419 void wasm::DumpRefType(RefType type, const TypeContext* types) {
    420  Fprinter out(stdout);
    421  wasm::DumpRefType(type, out, types);
    422  out.printf("\n");
    423 }
    424 
    425 void wasm::DumpRefType(RefType type, GenericPrinter& out,
    426                       const TypeContext* types) {
    427  if (type.isNullable() && !type.isTypeRef()) {
    428    const char* literal = nullptr;
    429    switch (type.kind()) {
    430      case RefType::Func:
    431        literal = "funcref";
    432        break;
    433      case RefType::Extern:
    434        literal = "externref";
    435        break;
    436      case RefType::Any:
    437        literal = "anyref";
    438        break;
    439      case RefType::NoFunc:
    440        literal = "nullfuncref";
    441        break;
    442      case RefType::NoExn:
    443        literal = "nullexn";
    444        break;
    445      case RefType::NoExtern:
    446        literal = "nullexternref";
    447        break;
    448      case RefType::None:
    449        literal = "nullref";
    450        break;
    451      case RefType::Eq:
    452        literal = "eqref";
    453        break;
    454      case RefType::I31:
    455        literal = "i31ref";
    456        break;
    457      case RefType::Struct:
    458        literal = "structref";
    459        break;
    460      case RefType::Array:
    461        literal = "arrayref";
    462        break;
    463      case RefType::Exn:
    464        literal = "exnref";
    465        break;
    466      case RefType::TypeRef: {
    467        MOZ_CRASH("type ref should not be possible here");
    468      }
    469    }
    470    out.put(literal);
    471    return;
    472  }
    473 
    474  // Emit the full reference type with heap type
    475  out.printf("(ref %s", type.isNullable() ? "null " : "");
    476  DumpHeapType(type, out, types);
    477  out.printf(")");
    478 }
    479 
    480 void wasm::DumpHeapType(RefType type, const TypeContext* types) {
    481  Fprinter out(stdout);
    482  wasm::DumpHeapType(type, out, types);
    483  out.printf("\n");
    484 }
    485 
    486 void wasm::DumpHeapType(RefType type, GenericPrinter& out,
    487                        const TypeContext* types) {
    488  switch (type.kind()) {
    489    case RefType::Func:
    490      out.put("func");
    491      return;
    492    case RefType::Extern:
    493      out.put("extern");
    494      return;
    495    case RefType::Any:
    496      out.put("any");
    497      return;
    498    case RefType::NoFunc:
    499      out.put("nofunc");
    500      return;
    501    case RefType::NoExn:
    502      out.put("noexn");
    503      return;
    504    case RefType::NoExtern:
    505      out.put("noextern");
    506      return;
    507    case RefType::None:
    508      out.put("none");
    509      return;
    510    case RefType::Eq:
    511      out.put("eq");
    512      return;
    513    case RefType::I31:
    514      out.put("i31");
    515      return;
    516    case RefType::Struct:
    517      out.put("struct");
    518      return;
    519    case RefType::Array:
    520      out.put("array");
    521      return;
    522    case RefType::Exn:
    523      out.put("exn");
    524      return;
    525    case RefType::TypeRef: {
    526      DumpTypeDefIndex(type.typeDef(), out, types);
    527      return;
    528    }
    529  }
    530 }
    531 
    532 void wasm::DumpFuncType(const FuncType& funcType, const TypeContext* types) {
    533  Fprinter fileOut(stdout);
    534  StructuredPrinter out(fileOut);
    535  wasm::DumpFuncType(funcType, out, types);
    536  out.printf("\n");
    537 }
    538 
    539 void wasm::DumpFuncType(const FuncType& funcType, StructuredPrinter& out,
    540                        const TypeContext* types) {
    541  out.printf("(func");
    542  {
    543    StructuredPrinter::Scope _(out);
    544    DumpFuncParamsAndResults(funcType, out, types);
    545    out.brk("", "\n");
    546 
    547    if (funcType.args().length() + funcType.results().length() > 10) {
    548      out.expand();
    549    }
    550  }
    551  out.printf(")");
    552 }
    553 
    554 void wasm::DumpStructType(const StructType& structType,
    555                          const TypeContext* types) {
    556  Fprinter fileOut(stdout);
    557  StructuredPrinter out(fileOut);
    558  wasm::DumpStructType(structType, out, types);
    559  out.printf("\n");
    560 }
    561 
    562 void wasm::DumpStructType(const StructType& structType, StructuredPrinter& out,
    563                          const TypeContext* types) {
    564  out.printf("(struct");
    565  {
    566    StructuredPrinter::Scope _(out);
    567 
    568    for (const FieldType& field : structType.fields_) {
    569      out.brk(" ", "\n");
    570      out.printf("(field ");
    571      if (field.isMutable) {
    572        out.printf("(mut ");
    573      }
    574      DumpStorageType(field.type, out, types);
    575      if (field.isMutable) {
    576        out.printf(")");
    577      }
    578      out.printf(")");
    579    }
    580    out.brk("", "\n");
    581 
    582    if (structType.fields_.length() > 1) {
    583      out.expand();
    584    }
    585  }
    586  out.printf(")");
    587 }
    588 
    589 void wasm::DumpArrayType(const ArrayType& arrayType, const TypeContext* types) {
    590  Fprinter fileOut(stdout);
    591  StructuredPrinter out(fileOut);
    592  wasm::DumpArrayType(arrayType, out, types);
    593  out.printf("\n");
    594 }
    595 
    596 void wasm::DumpArrayType(const ArrayType& arrayType, StructuredPrinter& out,
    597                         const TypeContext* types) {
    598  out.printf("(array ");
    599  if (arrayType.isMutable()) {
    600    out.printf("(mut ");
    601  }
    602  DumpStorageType(arrayType.elementType(), out, types);
    603  if (arrayType.isMutable()) {
    604    out.printf(")");
    605  }
    606  out.printf(")");
    607 }
    608 
    609 void wasm::DumpTypeDef(const TypeDef& typeDef, int32_t index,
    610                       const TypeContext* types) {
    611  Fprinter fileOut(stdout);
    612  StructuredPrinter out(fileOut);
    613  wasm::DumpTypeDef(typeDef, out, index, types);
    614  out.printf("\n");
    615 }
    616 
    617 void wasm::DumpTypeDef(const TypeDef& typeDef, StructuredPrinter& out,
    618                       int32_t index, const TypeContext* types) {
    619  out.printf("(type ");
    620  if (index >= 0) {
    621    out.printf("(;%" PRIi32 ";) ", index);
    622  }
    623  if (types && int32_t(types->indexOf(typeDef)) != index) {
    624    out.printf("(;canonicalized;) ");
    625  }
    626 
    627  // Somewhat counterintuitively, the text format works like so:
    628  //
    629  // (type (struct)):                       no parent, final
    630  // (type (sub (struct))):                 no parent, open
    631  // (type (sub $parent (struct))):         has parent, open
    632  // (type (sub $parent final (struct))):   has parent, final
    633  bool printSub = typeDef.superTypeDef() || !typeDef.isFinal();
    634 
    635  if (printSub) {
    636    out.printf("(sub ");
    637    if (typeDef.isFinal()) {
    638      out.printf("final ");
    639    }
    640    if (typeDef.superTypeDef()) {
    641      DumpTypeDefIndex(typeDef.superTypeDef(), out, types);
    642      out.printf(" ");
    643    }
    644  }
    645 
    646  switch (typeDef.kind()) {
    647    case TypeDefKind::Func:
    648      DumpFuncType(typeDef.funcType(), out, types);
    649      break;
    650    case TypeDefKind::Struct:
    651      DumpStructType(typeDef.structType(), out, types);
    652      break;
    653    case TypeDefKind::Array:
    654      DumpArrayType(typeDef.arrayType(), out, types);
    655      break;
    656    case TypeDefKind::None:
    657      out.printf("(; TypeDefKind::None ;)\n");
    658      break;
    659  }
    660 
    661  if (printSub) {
    662    out.printf(")");
    663  }
    664 
    665  out.printf(")");
    666 }
    667 
    668 void wasm::DumpRecGroup(const RecGroup& recGroup, int32_t startTypeIndex,
    669                        const TypeContext* types) {
    670  Fprinter fileOut(stdout);
    671  StructuredPrinter out(fileOut);
    672  wasm::DumpRecGroup(recGroup, out, startTypeIndex, types);
    673  out.printf("\n");
    674 }
    675 
    676 void wasm::DumpRecGroup(const RecGroup& recGroup, StructuredPrinter& out,
    677                        int32_t startTypeIndex, const TypeContext* types) {
    678  if (recGroup.numTypes() > 1) {
    679    out.printf("(rec\n");
    680    {
    681      StructuredPrinter::Scope _(out);
    682      for (uint32_t i = 0; i < recGroup.numTypes(); i++) {
    683        if (i > 0) {
    684          out.printf("\n");
    685        }
    686        DumpTypeDef(recGroup.type(i), out,
    687                    startTypeIndex < 0 ? -1 : startTypeIndex + int32_t(i),
    688                    types);
    689      }
    690      out.printf("\n");
    691    }
    692    out.printf(")");
    693  } else {
    694    DumpTypeDef(recGroup.type(0), out, startTypeIndex < 0 ? -1 : startTypeIndex,
    695                types);
    696  }
    697 }
    698 
    699 void wasm::DumpTableDesc(const TableDesc& tableDesc,
    700                         const CodeMetadata& codeMeta, bool includeInitExpr,
    701                         int32_t index) {
    702  Fprinter fileOut(stdout);
    703  StructuredPrinter out(fileOut);
    704  wasm::DumpTableDesc(tableDesc, codeMeta, includeInitExpr, out, index);
    705  out.printf("\n");
    706 }
    707 
    708 void wasm::DumpTableDesc(const TableDesc& tableDesc,
    709                         const CodeMetadata& codeMeta, bool includeInitExpr,
    710                         StructuredPrinter& out, int32_t index) {
    711  out.printf("(table ");
    712  if (index >= 0) {
    713    out.printf("(;%" PRIi32 ";) ", index);
    714  }
    715  if (tableDesc.addressType() == AddressType::I64) {
    716    out.printf("i64 ");
    717  }
    718  out.printf("%" PRIu64 " ", tableDesc.initialLength());
    719  if (tableDesc.maximumLength().isSome()) {
    720    out.printf("%" PRIu64 " ", tableDesc.maximumLength().value());
    721  }
    722  DumpRefType(tableDesc.elemType, out, codeMeta.types);
    723  if (includeInitExpr && tableDesc.initExpr) {
    724    StructuredPrinter::Scope _(out);
    725    DumpInitExpr(tableDesc.initExpr.ref(), codeMeta, out);
    726    out.brk("", "\n");
    727  }
    728  out.printf(")");
    729 }
    730 
    731 void wasm::DumpMemoryDesc(const MemoryDesc& memDesc, int32_t index) {
    732  Fprinter fileOut(stdout);
    733  StructuredPrinter out(fileOut);
    734  wasm::DumpMemoryDesc(memDesc, out, index);
    735  out.printf("\n");
    736 }
    737 
    738 void wasm::DumpMemoryDesc(const MemoryDesc& memDesc, StructuredPrinter& out,
    739                          int32_t index) {
    740  out.printf("(memory ");
    741  if (index >= 0) {
    742    out.printf("(;%" PRIi32 ";) ", index);
    743  }
    744  if (memDesc.addressType() == AddressType::I64) {
    745    out.printf("i64 ");
    746  }
    747  out.printf("%" PRIu64, memDesc.initialPages().pageCount());
    748  if (memDesc.maximumPages().isSome()) {
    749    out.printf(" %" PRIu64, memDesc.maximumPages().value().pageCount());
    750  }
    751  if (memDesc.initialPages().pageSize() != PageSize::Standard) {
    752    out.printf(" (pagesize %d)",
    753               PageSizeInBytes(memDesc.initialPages().pageSize()));
    754  }
    755  out.printf(")");
    756 }
    757 
    758 void wasm::DumpGlobalDesc(const GlobalDesc& globalDesc,
    759                          const CodeMetadata& codeMeta, bool includeInitExpr,
    760                          int32_t index) {
    761  Fprinter fileOut(stdout);
    762  StructuredPrinter out(fileOut);
    763  wasm::DumpGlobalDesc(globalDesc, codeMeta, includeInitExpr, out, index);
    764  out.printf("\n");
    765 }
    766 
    767 void wasm::DumpGlobalDesc(const GlobalDesc& globalDesc,
    768                          const CodeMetadata& codeMeta, bool includeInitExpr,
    769                          StructuredPrinter& out, int32_t index) {
    770  out.printf("(global ");
    771  if (index >= 0) {
    772    out.printf("(;%" PRIi32 ";) ", index);
    773  }
    774  if (globalDesc.isMutable()) {
    775    out.printf("(mut ");
    776  }
    777  DumpValType(globalDesc.type(), out, codeMeta.types);
    778  if (globalDesc.isMutable()) {
    779    out.printf(")");
    780  }
    781  if (includeInitExpr) {
    782    StructuredPrinter::Scope _(out);
    783    DumpInitExpr(globalDesc.initExpr(), codeMeta, out);
    784    out.brk("", "\n");
    785  }
    786  out.printf(")");
    787 }
    788 
    789 void wasm::DumpTagDesc(const TagDesc& tagDesc, int32_t index,
    790                       const TypeContext* types) {
    791  Fprinter fileOut(stdout);
    792  StructuredPrinter out(fileOut);
    793  wasm::DumpTagDesc(tagDesc, out, index, types);
    794  out.printf("\n");
    795 }
    796 
    797 void wasm::DumpTagDesc(const TagDesc& tagDesc, StructuredPrinter& out,
    798                       int32_t index, const TypeContext* types) {
    799  out.printf("(tag ");
    800  if (index >= 0) {
    801    out.printf("(;%" PRIi32 ";) ", index);
    802  }
    803  out.printf("(type ");
    804  DumpTypeDefIndex(&tagDesc.type->type(), out, types);
    805  out.printf("))");
    806 }
    807 
    808 void wasm::DumpInitExpr(const InitExpr& initExpr,
    809                        const CodeMetadata& codeMeta) {
    810  Fprinter fileOut(stdout);
    811  StructuredPrinter out(fileOut);
    812  wasm::DumpInitExpr(initExpr, codeMeta, out);
    813  out.printf("\n");
    814 }
    815 
    816 void wasm::DumpInitExpr(const InitExpr& initExpr, const CodeMetadata& codeMeta,
    817                        StructuredPrinter& out) {
    818  UniqueChars error;
    819  Decoder d(initExpr.bytecode().begin(), initExpr.bytecode().end(),
    820            /* dummy */ 0, &error);
    821  ValTypeVector locals;
    822  ValidatingOpIter iter(codeMeta, d, locals, ValidatingOpIter::Kind::InitExpr);
    823  OpDumper visitor(out, codeMeta.types);
    824 
    825  if (!iter.startInitExpr(initExpr.type())) {
    826    out.brk(" ", "\n");
    827    out.printf("(; bad expression ;)");
    828    return;
    829  }
    830  if (!ValidateOps(iter, visitor, codeMeta)) {
    831    out.brk(" ", "\n");
    832    out.printf("(; bad expression: %s ;)", d.error()->get());
    833    return;
    834  }
    835  if (!iter.endInitExpr()) {
    836    out.brk(" ", "\n");
    837    out.printf("(; bad expression ;)");
    838    return;
    839  }
    840 }
    841 
    842 void wasm::DumpTypeContext(const TypeContext& typeContext) {
    843  Fprinter fileOut(stdout);
    844  StructuredPrinter out(fileOut);
    845  wasm::DumpTypeContext(typeContext, out);
    846  out.printf("\n");
    847 }
    848 
    849 void wasm::DumpTypeContext(const TypeContext& typeContext,
    850                           StructuredPrinter& out) {
    851  uint32_t numTypesSoFar = 0;
    852  for (size_t i = 0; i < typeContext.groups().length(); i++) {
    853    if (i > 0) {
    854      out.printf("\n");
    855    }
    856    const RecGroup& group = *typeContext.groups()[i];
    857    DumpRecGroup(group, out, int32_t(numTypesSoFar), &typeContext);
    858    numTypesSoFar += group.numTypes();
    859  }
    860 }
    861 
    862 void wasm::DumpFunction(const CodeMetadata& codeMeta,
    863                        const CodeTailMetadata& codeTailMeta,
    864                        uint32_t funcIndex) {
    865  Fprinter fileOut(stdout);
    866  StructuredPrinter out(fileOut);
    867  wasm::DumpFunction(codeMeta, codeTailMeta, funcIndex, out);
    868  out.printf("\n");
    869 }
    870 
    871 void wasm::DumpFunction(const CodeMetadata& codeMeta,
    872                        const CodeTailMetadata& codeTailMeta,
    873                        uint32_t funcIndex, StructuredPrinter& out) {
    874  const FuncDesc& f = codeMeta.funcs[funcIndex];
    875  const TypeDef& typeDef = codeMeta.getFuncTypeDef(funcIndex);
    876  const FuncType& funcType = typeDef.funcType();
    877 
    878  out.printf("(func (;%" PRIu32 ";) (type %" PRIu32 ")", funcIndex,
    879             f.typeIndex);
    880 
    881  bool typeExpanded = false;
    882  {
    883    StructuredPrinter::Scope _(out);
    884 
    885    DumpFuncParamsAndResults(funcType, out, codeMeta.types);
    886    out.brk("", "\n");
    887    if (funcType.args().length() + funcType.results().length() > 8) {
    888      out.expand();
    889    }
    890 
    891    typeExpanded = out.isExpanded();
    892  }
    893  {
    894    StructuredPrinter::Scope _(out);
    895    if (typeExpanded) {
    896      out.expand();
    897    }
    898 
    899    if (codeTailMeta.codeSectionBytecode) {
    900      UniqueChars error;
    901      BytecodeSpan funcBytecode = codeTailMeta.funcDefBody(funcIndex);
    902      DumpFunctionBody(codeMeta, funcIndex, funcBytecode.data(),
    903                       funcBytecode.size(), out);
    904    } else {
    905      out.brk(" ", "\n");
    906      out.printf("(; no bytecode available ;)");
    907    }
    908    out.brk("", "\n");
    909  }
    910  out.printf(")");
    911 }
    912 
    913 void wasm::DumpFunctionBody(const CodeMetadata& codeMeta, uint32_t funcIndex,
    914                            const uint8_t* bodyStart, uint32_t bodySize) {
    915  Fprinter fileOut(stdout);
    916  StructuredPrinter out(fileOut);
    917  wasm::DumpFunctionBody(codeMeta, funcIndex, bodyStart, bodySize, out);
    918  out.printf("\n");
    919 }
    920 
    921 void wasm::DumpFunctionBody(const CodeMetadata& codeMeta, uint32_t funcIndex,
    922                            const uint8_t* bodyStart, uint32_t bodySize,
    923                            StructuredPrinter& out) {
    924  UniqueChars error;
    925 
    926  const uint8_t* bodyEnd = bodyStart + bodySize;
    927  Decoder d(bodyStart, bodyEnd, 0, &error);
    928 
    929  ValTypeVector locals;
    930  if (!DecodeLocalEntriesWithParams(d, codeMeta, funcIndex, &locals)) {
    931    out.brk(" ", "\n");
    932    out.printf("(; error: %s ;)", error.get());
    933    return;
    934  }
    935  uint32_t numArgs = codeMeta.getFuncType(funcIndex).args().length();
    936  if (locals.length() - numArgs > 0) {
    937    out.printf("\n(local");
    938    for (size_t i = numArgs; i < locals.length(); i++) {
    939      ValType local = locals[i];
    940      out.printf(" ");
    941      DumpValType(local, out, codeMeta.types);
    942    }
    943    out.printf(")\n");
    944  }
    945 
    946  ValidatingOpIter iter(codeMeta, d, locals);
    947  if (!iter.startFunction(funcIndex)) {
    948    out.brk(" ", "\n");
    949    out.printf("(; error: %s ;)", error.get());
    950    return;
    951  }
    952 
    953  OpDumper visitor(out, codeMeta.types);
    954  if (!ValidateOps(iter, visitor, codeMeta)) {
    955    out.brk(" ", "\n");
    956    out.printf("(; error: %s ;)", error.get());
    957    return;
    958  }
    959 
    960  if (!iter.endFunction(bodyEnd)) {
    961    out.brk(" ", "\n");
    962    out.printf("(; error: %s ;)", error.get());
    963    return;
    964  }
    965 }