Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/wasm2c/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 105 kB image not shown  

Quelle  binary-reader.cc

  Sprache: C
 

/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "wabt/binary-reader.h"

#include <cassert>
#include <cinttypes>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>

#include "wabt/config.h"

#include "wabt/binary-reader-logging.h"
#include "wabt/binary.h"
#include "wabt/leb128.h"
#include "wabt/stream.h"
#include "wabt/utf8.h"

#if HAVE_ALLOCA
#include <alloca.h>
#endif

#define ERROR_IF(expr, ...)    \
  do {                         \
    if (expr) {                \
      PrintError(__VA_ARGS__); \
      return Result::Error;    \
    }                          \
  } while (0)

#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__)

#define ERROR_UNLESS_OPCODE_ENABLED(opcode)     \
  do {                                          \
    if (!opcode.IsEnabled(options_.features)) { \
      return ReportUnexpectedOpcode(opcode);    \
    }                                           \
  } while (0)

#define CALLBACK0(member) \
  ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed")

#define CALLBACK(member, ...)                             \
  ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \
               #member " callback failed")

namespace wabt {

namespace {

class BinaryReader {
 public:
  struct ReadModuleOptions {
    bool stop_on_first_error;
  };

  BinaryReader(const void* data,
               size_t size,
               BinaryReaderDelegate* delegate,
               const ReadBinaryOptions& options);

  Result ReadModule(const ReadModuleOptions& options);

 private:
  template <typename T, T BinaryReader::*member>
  struct ValueRestoreGuard {
    explicit ValueRestoreGuard(BinaryReader* this_)
        : this_(this_), previous_value_(this_->*member) {}
    ~ValueRestoreGuard() { this_->*member = previous_value_; }

    BinaryReader* this_;
    T previous_value_;
  };

  struct ReadSectionsOptions {
    bool stop_on_first_error;
  };

  void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
  [[nodiscard]] Result ReadOpcode(Opcode* out_value, const char* desc);
  template <typename T>
  [[nodiscard]] Result ReadT(T* out_value,
                             const char* type_name,
                             const char* desc);
  [[nodiscard]] Result ReadU8(uint8_t* out_value, const char* desc);
  [[nodiscard]] Result ReadU32(uint32_t* out_value, const char* desc);
  [[nodiscard]] Result ReadF32(uint32_t* out_value, const char* desc);
  [[nodiscard]] Result ReadF64(uint64_t* out_value, const char* desc);
  [[nodiscard]] Result ReadV128(v128* out_value, const char* desc);
  [[nodiscard]] Result ReadU32Leb128(uint32_t* out_value, const char* desc);
  [[nodiscard]] Result ReadU64Leb128(uint64_t* out_value, const char* desc);
  [[nodiscard]] Result ReadS32Leb128(uint32_t* out_value, const char* desc);
  [[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc);
  [[nodiscard]] Result ReadType(Type* out_value, const char* desc);
  [[nodiscard]] Result ReadRefType(Type* out_value, const char* desc);
  [[nodiscard]] Result ReadExternalKind(ExternalKind* out_value,
                                        const char* desc);
  [[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc);
  [[nodiscard]] Result ReadBytes(const void** out_data,
                                 Address* out_data_size,
                                 const char* desc);
  [[nodiscard]] Result ReadBytesWithSize(const void** out_data,
                                         Offset size,
                                         const char* desc);
  [[nodiscard]] Result ReadIndex(Index* index, const char* desc);
  [[nodiscard]] Result ReadOffset(Offset* offset, const char* desc);
  [[nodiscard]] Result ReadAlignment(Address* align_log2, const char* desc);
  [[nodiscard]] Result CheckAlignment(Address* align_log2, const char* desc);
  [[nodiscard]] Result TakeHasMemidx(Address* align_log2, bool* has_memidx);
  [[nodiscard]] Result ReadMemidx(Index* memidx, const char* desc);
  [[nodiscard]] Result ReadMemLocation(Address* alignment_log2,
                                       Index* memidx,
                                       Address* offset,
                                       const char* desc_align,
                                       const char* desc_memidx,
                                       const char* desc_offset,
                                       uint8_t* lane_val = nullptr);
  [[nodiscard]] Result CallbackMemLocation(const Address* alignment_log2,
                                           const Index* memidx,
                                           const Address* offset,
                                           const uint8_t* lane_val = nullptr);
  [[nodiscard]] Result ReadCount(Index* index, const char* desc);
  [[nodiscard]] Result ReadField(TypeMut* out_value);

  bool IsConcreteType(Type);
  bool IsBlockType(Type);

  Index NumTotalFuncs();

  [[nodiscard]] Result ReadInitExpr(Index index);
  [[nodiscard]] Result ReadTable(Type* out_elem_type, Limits* out_elem_limits);
  [[nodiscard]] Result ReadMemory(Limits* out_page_limits,
                                  uint32_t* out_page_size);
  [[nodiscard]] Result ReadGlobalHeader(Type* out_type, bool* out_mutable);
  [[nodiscard]] Result ReadTagType(Index* out_sig_index);
  [[nodiscard]] Result ReadAddress(Address* out_value,
                                   Index memory,
                                   const char* desc);
  [[nodiscard]] Result ReadFunctionBody(Offset end_offset);
  // ReadInstructions reads until end_offset or the nesting depth reaches zero.
  [[nodiscard]] Result ReadInstructions(Offset end_offset, const char* context);
  [[nodiscard]] Result ReadNameSection(Offset section_size);
  [[nodiscard]] Result ReadRelocSection(Offset section_size);
  [[nodiscard]] Result ReadDylinkSection(Offset section_size);
  [[nodiscard]] Result ReadGenericCustomSection(std::string_view name,
                                                Offset section_size);
  [[nodiscard]] Result ReadDylink0Section(Offset section_size);
  [[nodiscard]] Result ReadTargetFeaturesSections(Offset section_size);
  [[nodiscard]] Result ReadLinkingSection(Offset section_size);
  [[nodiscard]] Result ReadCodeMetadataSection(std::string_view name,
                                               Offset section_size);
  [[nodiscard]] Result ReadCustomSection(Index section_index,
                                         Offset section_size);
  [[nodiscard]] Result ReadTypeSection(Offset section_size);
  [[nodiscard]] Result ReadImportSection(Offset section_size);
  [[nodiscard]] Result ReadFunctionSection(Offset section_size);
  [[nodiscard]] Result ReadTableSection(Offset section_size);
  [[nodiscard]] Result ReadMemorySection(Offset section_size);
  [[nodiscard]] Result ReadGlobalSection(Offset section_size);
  [[nodiscard]] Result ReadExportSection(Offset section_size);
  [[nodiscard]] Result ReadStartSection(Offset section_size);
  [[nodiscard]] Result ReadElemSection(Offset section_size);
  [[nodiscard]] Result ReadCodeSection(Offset section_size);
  [[nodiscard]] Result ReadDataSection(Offset section_size);
  [[nodiscard]] Result ReadDataCountSection(Offset section_size);
  [[nodiscard]] Result ReadTagSection(Offset section_size);
  [[nodiscard]] Result ReadSections(const ReadSectionsOptions& options);
  Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr);

  size_t read_end_ = 0;  // Either the section end or data_size.
  BinaryReaderDelegate::State state_;
  BinaryReaderLogging logging_delegate_;
  BinaryReaderDelegate* delegate_ = nullptr;
  TypeVector param_types_;
  TypeVector result_types_;
  TypeMutVector fields_;
  std::vector<Index> target_depths_;
  const ReadBinaryOptions& options_;
  BinarySection last_known_section_ = BinarySection::Invalid;
  bool did_read_names_section_ = false;
  bool reading_custom_section_ = false;
  Index num_func_imports_ = 0;
  Index num_table_imports_ = 0;
  Index num_memory_imports_ = 0;
  Index num_global_imports_ = 0;
  Index num_tag_imports_ = 0;
  Index num_function_signatures_ = 0;
  Index num_function_bodies_ = 0;
  Index num_data_segments_ = 0;
  Index data_count_ = kInvalidIndex;

  using ReadEndRestoreGuard =
      ValueRestoreGuard<size_t, &BinaryReader::read_end_>;
};

BinaryReader::BinaryReader(const void* data,
                           size_t size,
                           BinaryReaderDelegate* delegate,
                           const ReadBinaryOptions& options)
    : read_end_(size),
      state_(static_cast<const uint8_t*>(data), size),
      logging_delegate_(options.log_stream, delegate),
      delegate_(options.log_stream ? &logging_delegate_ : delegate),
      options_(options),
      last_known_section_(BinarySection::Invalid) {
  delegate->OnSetState(&state_);
}

void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format,
                                                       ...) {
  ErrorLevel error_level =
      reading_custom_section_ && !options_.fail_on_custom_section_error
          ? ErrorLevel::Warning
          : ErrorLevel::Error;

  WABT_SNPRINTF_ALLOCA(buffer, length, format);
  Error error(error_level, Location(state_.offset), buffer);
  bool handled = delegate_->OnError(error);

  if (!handled) {
    // Not great to just print, but we don't want to eat the error either.
    fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset,
            GetErrorLevelName(error_level), buffer);
  }
}

Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) {
  std::string message = "unexpected opcode";
  if (where) {
    message += ' ';
    message += where;
  }

  message += ":";

  std::vector<uint8_t> bytes = opcode.GetBytes();
  assert(bytes.size() > 0);

  for (uint8_t byte : bytes) {
    message += StringPrintf(" 0x%x", byte);
  }

  PrintError("%s", message.c_str());
  return Result::Error;
}

Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) {
  uint8_t value = 0;
  CHECK_RESULT(ReadU8(&value, desc));

  if (Opcode::IsPrefixByte(value)) {
    uint32_t code;
    CHECK_RESULT(ReadU32Leb128(&code, desc));
    *out_value = Opcode::FromCode(value, code);
  } else {
    *out_value = Opcode::FromCode(value);
  }
  return Result::Ok;
}

template <typename T>
Result BinaryReader::ReadT(T* out_value,
                           const char* type_name,
                           const char* desc) {
  if (state_.offset + sizeof(T) > read_end_) {
    PrintError("unable to read %s: %s", type_name, desc);
    return Result::Error;
  }
#if WABT_BIG_ENDIAN
  uint8_t tmp[sizeof(T)];
  memcpy(tmp, state_.data + state_.offset, sizeof(tmp));
  SwapBytesSized(tmp, sizeof(tmp));
  memcpy(out_value, tmp, sizeof(T));
#else
  memcpy(out_value, state_.data + state_.offset, sizeof(T));
#endif
  state_.offset += sizeof(T);
  return Result::Ok;
}

Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) {
  return ReadT(out_value, "uint8_t", desc);
}

Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) {
  return ReadT(out_value, "uint32_t", desc);
}

Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) {
  return ReadT(out_value, "float", desc);
}

Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) {
  return ReadT(out_value, "double", desc);
}

Result BinaryReader::ReadV128(v128* out_value, const char* desc) {
  return ReadT(out_value, "v128", desc);
}

Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) {
  const uint8_t* p = state_.data + state_.offset;
  const uint8_t* end = state_.data + read_end_;
  size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value);
  ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc);
  state_.offset += bytes_read;
  return Result::Ok;
}

Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) {
  const uint8_t* p = state_.data + state_.offset;
  const uint8_t* end = state_.data + read_end_;
  size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value);
  ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc);
  state_.offset += bytes_read;
  return Result::Ok;
}

Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) {
  const uint8_t* p = state_.data + state_.offset;
  const uint8_t* end = state_.data + read_end_;
  size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value);
  ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc);
  state_.offset += bytes_read;
  return Result::Ok;
}

Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) {
  const uint8_t* p = state_.data + state_.offset;
  const uint8_t* end = state_.data + read_end_;
  size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value);
  ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc);
  state_.offset += bytes_read;
  return Result::Ok;
}

Result BinaryReader::ReadType(Type* out_value, const char* desc) {
  uint32_t type = 0;
  CHECK_RESULT(ReadS32Leb128(&type, desc));
  if (static_cast<Type::Enum>(type) == Type::Reference) {
    uint32_t heap_type = 0;
    CHECK_RESULT(ReadS32Leb128(&heap_type, desc));
    *out_value = Type(Type::Reference, heap_type);
  } else {
    *out_value = static_cast<Type>(type);
  }
  return Result::Ok;
}

Result BinaryReader::ReadRefType(Type* out_value, const char* desc) {
  uint32_t type = 0;
  CHECK_RESULT(ReadS32Leb128(&type, desc));
  *out_value = static_cast<Type>(type);
  ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc);
  return Result::Ok;
}

Result BinaryReader::ReadExternalKind(ExternalKind* out_value,
                                      const char* desc) {
  uint8_t value = 0;
  CHECK_RESULT(ReadU8(&value, desc));
  ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d",
               value);
  *out_value = static_cast<ExternalKind>(value);
  return Result::Ok;
}

Result BinaryReader::ReadStr(std::string_view* out_str, const char* desc) {
  uint32_t str_len = 0;
  CHECK_RESULT(ReadU32Leb128(&str_len, "string length"));

  ERROR_UNLESS(state_.offset + str_len <= read_end_,
               "unable to read string: %s", desc);

  *out_str = std::string_view(
      reinterpret_cast<const char*>(state_.data) + state_.offset, str_len);
  state_.offset += str_len;

  ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()),
               "invalid utf-8 encoding: %s", desc);
  return Result::Ok;
}

Result BinaryReader::ReadBytes(const void** out_data,
                               Address* out_data_size,
                               const char* desc) {
  uint32_t data_size = 0;
  CHECK_RESULT(ReadU32Leb128(&data_size, "data size"));
  CHECK_RESULT(ReadBytesWithSize(out_data, data_size, desc));
  *out_data_size = data_size;
  return Result::Ok;
}

Result BinaryReader::ReadBytesWithSize(const void** out_data,
                                       Offset size,
                                       const char* desc) {
  ERROR_UNLESS(state_.offset + size <= read_end_, "unable to read data: %s",
               desc);

  *out_data = static_cast<const uint8_t*>(state_.data) + state_.offset;
  state_.offset += size;
  return Result::Ok;
}

Result BinaryReader::ReadIndex(Index* index, const char* desc) {
  uint32_t value;
  CHECK_RESULT(ReadU32Leb128(&value, desc));
  *index = value;
  return Result::Ok;
}

Result BinaryReader::ReadOffset(Offset* offset, const char* desc) {
  uint32_t value;
  CHECK_RESULT(ReadU32Leb128(&value, desc));
  *offset = value;
  return Result::Ok;
}

Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) {
  uint32_t value;
  CHECK_RESULT(ReadU32Leb128(&value, desc));
  *alignment_log2 = value;
  return Result::Ok;
}

Result BinaryReader::CheckAlignment(Address* align_log2, const char* desc) {
  uint32_t value = *align_log2;
  if (value >= 32) {
    PrintError("invalid %s: %" PRIu32, desc, value);
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReader::TakeHasMemidx(Address* align_log2, bool* has_memidx) {
  // extract the has_memidx flag
  *has_memidx = (*align_log2 >> 6) & 1;
  // then clear it
  *align_log2 &= ~(1 << 6);
  return Result::Ok;
}

Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) {
  CHECK_RESULT(ReadIndex(memidx, desc));
  return Result::Ok;
}

Result BinaryReader::ReadMemLocation(Address* alignment_log2,
                                     Index* memidx,
                                     Address* offset,
                                     const char* desc_align,
                                     const char* desc_memidx,
                                     const char* desc_offset,
                                     uint8_t* lane_val) {
  bool has_memidx = false;
  CHECK_RESULT(ReadAlignment(alignment_log2, desc_align));
  CHECK_RESULT(TakeHasMemidx(alignment_log2, &has_memidx));
  CHECK_RESULT(CheckAlignment(alignment_log2, desc_align));
  *memidx = 0;
  if (has_memidx) {
    ERROR_IF(!options_.features.multi_memory_enabled(),
             "multi_memory not allowed");
    CHECK_RESULT(ReadMemidx(memidx, desc_memidx));
  }
  CHECK_RESULT(ReadAddress(offset, 0, desc_offset));

  if (lane_val) {
    CHECK_RESULT(ReadU8(lane_val, "Lane idx"));
  }

  return Result::Ok;
}

Result BinaryReader::CallbackMemLocation(const Address* alignment_log2,
                                         const Index* memidx,
                                         const Address* offset,
                                         const uint8_t* lane_val) {
  if (lane_val) {
    if (*memidx) {
      CALLBACK(OnOpcodeUint32Uint32Uint32Uint32, *alignment_log2, *memidx,
               *offset, *lane_val);
    } else {
      CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *offset, *lane_val);
    }
  } else {
    if (*memidx) {
      CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *memidx, *offset);
    } else {
      CALLBACK(OnOpcodeUint32Uint32, *alignment_log2, *offset);
    }
  }

  return Result::Ok;
}

Result BinaryReader::ReadCount(Index* count, const char* desc) {
  CHECK_RESULT(ReadIndex(count, desc));

  // This check assumes that each item follows in this section, and takes at
  // least 1 byte. It's possible that this check passes but reading fails
  // later. It is still useful to check here, though, because it early-outs
  // when an erroneous large count is used, before allocating memory for it.
  size_t section_remaining = read_end_ - state_.offset;
  if (*count > section_remaining) {
    PrintError("invalid %s %" PRIindex ", only %" PRIzd
               " bytes left in section",
               desc, *count, section_remaining);
    return Result::Error;
  }
  return Result::Ok;
}

Result BinaryReader::ReadField(TypeMut* out_value) {
  // TODO: Reuse for global header too?
  Type field_type;
  CHECK_RESULT(ReadType(&field_type, "field type"));
  ERROR_UNLESS(IsConcreteType(field_type),
               "expected valid field type (got " PRItypecode ")",
               WABT_PRINTF_TYPE_CODE(field_type));

  uint8_t mutable_ = 0;
  CHECK_RESULT(ReadU8(&mutable_, "field mutability"));
  ERROR_UNLESS(mutable_ <= 1, "field mutability must be 0 or 1");
  out_value->type = field_type;
  out_value->mutable_ = mutable_;
  return Result::Ok;
}

bool BinaryReader::IsConcreteType(Type type) {
  switch (type) {
    case Type::I32:
    case Type::I64:
    case Type::F32:
    case Type::F64:
      return true;

    case Type::V128:
      return options_.features.simd_enabled();

    case Type::FuncRef:
    case Type::ExternRef:
      return options_.features.reference_types_enabled();

    case Type::ExnRef:
      return options_.features.exceptions_enabled();

    case Type::Reference:
      return options_.features.function_references_enabled();

    default:
      return false;
  }
}

bool BinaryReader::IsBlockType(Type type) {
  if (IsConcreteType(type) || type == Type::Void) {
    return true;
  }

  if (!(options_.features.multi_value_enabled() && type.IsIndex())) {
    return false;
  }

  return true;
}

Index BinaryReader::NumTotalFuncs() {
  return num_func_imports_ + num_function_signatures_;
}

Result BinaryReader::ReadInitExpr(Index index) {
  CHECK_RESULT(ReadInstructions(read_end_, "init expression"));
  assert(state_.offset <= read_end_);
  return Result::Ok;
}

Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) {
  CHECK_RESULT(ReadRefType(out_elem_type, "table elem type"));

  uint8_t flags;
  uint32_t initial;
  uint32_t max = 0;
  CHECK_RESULT(ReadU8(&flags, "table flags"));
  bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
  bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
  bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
  const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_TABLE_FLAGS;
  ERROR_IF(is_shared, "tables may not be shared");
  ERROR_IF(is_64 && !options_.features.memory64_enabled(),
           "memory64 not allowed");
  ERROR_UNLESS(unknown_flags == 0, "malformed table limits flag: %d", flags);
  CHECK_RESULT(ReadU32Leb128(&initial, "table initial elem count"));
  if (has_max) {
    CHECK_RESULT(ReadU32Leb128(&max, "table max elem count"));
  }

  out_elem_limits->has_max = has_max;
  out_elem_limits->is_64 = is_64;
  out_elem_limits->initial = initial;
  out_elem_limits->max = max;
  return Result::Ok;
}

Result BinaryReader::ReadMemory(Limits* out_page_limits,
                                uint32_t* out_page_size) {
  uint8_t flags;
  uint64_t initial;
  uint64_t max = 0;
  CHECK_RESULT(ReadU8(&flags, "memory flags"));
  bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
  bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
  bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
  bool has_custom_page_size =
      flags & WABT_BINARY_LIMITS_HAS_CUSTOM_PAGE_SIZE_FLAG;
  const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_MEMORY_FLAGS;
  ERROR_UNLESS(unknown_flags == 0, "malformed memory limits flag: %d", flags);
  ERROR_IF(is_shared && !options_.features.threads_enabled(),
           "memory may not be shared: threads not allowed");
  ERROR_IF(is_64 && !options_.features.memory64_enabled(),
           "memory64 not allowed");
  ERROR_IF(
      has_custom_page_size && !options_.features.custom_page_sizes_enabled(),
      "custom page sizes not allowed");
  if (options_.features.memory64_enabled()) {
    CHECK_RESULT(ReadU64Leb128(&initial, "memory initial page count"));
    if (has_max) {
      CHECK_RESULT(ReadU64Leb128(&max, "memory max page count"));
    }
  } else {
    uint32_t initial32;
    CHECK_RESULT(ReadU32Leb128(&initial32, "memory initial page count"));
    initial = initial32;
    if (has_max) {
      uint32_t max32;
      CHECK_RESULT(ReadU32Leb128(&max32, "memory max page count"));
      max = max32;
    }
  }
  if (has_custom_page_size) {
    uint32_t page_size_log2;
    CHECK_RESULT(ReadU32Leb128(&page_size_log2, "memory page size"));
    ERROR_IF(page_size_log2 > 16, "malformed memory page size");
    *out_page_size = 1 << page_size_log2;
  } else {
    *out_page_size = WABT_DEFAULT_PAGE_SIZE;
  }

  out_page_limits->has_max = has_max;
  out_page_limits->is_shared = is_shared;
  out_page_limits->is_64 = is_64;
  out_page_limits->initial = initial;
  out_page_limits->max = max;

  return Result::Ok;
}

Result BinaryReader::ReadGlobalHeader(Type* out_type, bool* out_mutable) {
  Type global_type = Type::Void;
  uint8_t mutable_ = 0;
  CHECK_RESULT(ReadType(&global_type, "global type"));
  ERROR_UNLESS(IsConcreteType(global_type), "invalid global type: %#x",
               static_cast<int>(global_type));

  CHECK_RESULT(ReadU8(&mutable_, "global mutability"));
  ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1");

  *out_type = global_type;
  *out_mutable = mutable_;
  return Result::Ok;
}

Result BinaryReader::ReadAddress(Address* out_value,
                                 Index memory,
                                 const char* desc) {
  if (options_.features.memory64_enabled()) {
    return ReadU64Leb128(out_value, desc);
  } else {
    uint32_t val;
    Result res = ReadU32Leb128(&val, desc);
    *out_value = val;
    return res;
  }
}

Result BinaryReader::ReadFunctionBody(Offset end_offset) {
  CHECK_RESULT(ReadInstructions(end_offset, "function body"));
  ERROR_UNLESS(state_.offset == end_offset,
               "function body shorter than given size");
  return Result::Ok;
}

Result BinaryReader::ReadInstructions(Offset end_offset, const char* context) {
  std::stack<Opcode> nested_blocks;
  while (state_.offset < end_offset) {
    Opcode opcode;
    CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
    CALLBACK(OnOpcode, opcode);
    ERROR_UNLESS_OPCODE_ENABLED(opcode);

    switch (opcode) {
      case Opcode::Unreachable:
        CALLBACK0(OnUnreachableExpr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::Block: {
        nested_blocks.push(opcode);
        Type sig_type;
        CHECK_RESULT(ReadType(&sig_type, "block signature type"));
        ERROR_UNLESS(IsBlockType(sig_type),
                     "expected valid block signature type");
        CALLBACK(OnBlockExpr, sig_type);
        CALLBACK(OnOpcodeBlockSig, sig_type);
        break;
      }

      case Opcode::Loop: {
        nested_blocks.push(opcode);
        Type sig_type;
        CHECK_RESULT(ReadType(&sig_type, "loop signature type"));
        ERROR_UNLESS(IsBlockType(sig_type),
                     "expected valid block signature type");
        CALLBACK(OnLoopExpr, sig_type);
        CALLBACK(OnOpcodeBlockSig, sig_type);
        break;
      }

      case Opcode::If: {
        nested_blocks.push(opcode);
        Type sig_type;
        CHECK_RESULT(ReadType(&sig_type, "if signature type"));
        ERROR_UNLESS(IsBlockType(sig_type),
                     "expected valid block signature type");
        CALLBACK(OnIfExpr, sig_type);
        CALLBACK(OnOpcodeBlockSig, sig_type);
        break;
      }

      case Opcode::Else:
        ERROR_IF(nested_blocks.empty() || (nested_blocks.top() != Opcode::If),
                 "else outside if block");
        CALLBACK0(OnElseExpr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::SelectT: {
        Index num_results;
        CHECK_RESULT(ReadCount(&num_results, "num result types"));

        result_types_.resize(num_results);
        for (Index i = 0; i < num_results; ++i) {
          Type result_type;
          CHECK_RESULT(ReadType(&result_type, "select result type"));
          ERROR_UNLESS(IsConcreteType(result_type),
                       "expected valid select result type (got " PRItypecode
                       ")",
                       WABT_PRINTF_TYPE_CODE(result_type));
          result_types_[i] = result_type;
        }

        if (num_results) {
          CALLBACK(OnSelectExpr, num_results, result_types_.data());
          CALLBACK(OnOpcodeType, result_types_[0]);
        } else {
          CALLBACK(OnSelectExpr, 0, NULL);
          CALLBACK0(OnOpcodeBare);
        }
        break;
      }

      case Opcode::Select:
        CALLBACK(OnSelectExpr, 0, nullptr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::Br: {
        Index depth;
        CHECK_RESULT(ReadIndex(&depth, "br depth"));
        CALLBACK(OnBrExpr, depth);
        CALLBACK(OnOpcodeIndex, depth);
        break;
      }

      case Opcode::BrIf: {
        Index depth;
        CHECK_RESULT(ReadIndex(&depth, "br_if depth"));
        CALLBACK(OnBrIfExpr, depth);
        CALLBACK(OnOpcodeIndex, depth);
        break;
      }

      case Opcode::BrTable: {
        Index num_targets;
        CHECK_RESULT(ReadCount(&num_targets, "br_table target count"));
        target_depths_.resize(num_targets);

        for (Index i = 0; i < num_targets; ++i) {
          Index target_depth;
          CHECK_RESULT(ReadIndex(&target_depth, "br_table target depth"));
          target_depths_[i] = target_depth;
        }

        Index default_target_depth;
        CHECK_RESULT(
            ReadIndex(&default_target_depth, "br_table default target depth"));

        Index* target_depths = num_targets ? target_depths_.data() : nullptr;

        CALLBACK(OnBrTableExpr, num_targets, target_depths,
                 default_target_depth);
        break;
      }

      case Opcode::Return:
        CALLBACK0(OnReturnExpr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::Nop:
        CALLBACK0(OnNopExpr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::Drop:
        CALLBACK0(OnDropExpr);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::End:
        CALLBACK0(OnEndExpr);
        if (nested_blocks.empty()) {
          return Result::Ok;
        }
        nested_blocks.pop();
        break;

      case Opcode::I32Const: {
        uint32_t value;
        CHECK_RESULT(ReadS32Leb128(&value, "i32.const value"));
        CALLBACK(OnI32ConstExpr, value);
        CALLBACK(OnOpcodeUint32, value);
        break;
      }

      case Opcode::I64Const: {
        uint64_t value;
        CHECK_RESULT(ReadS64Leb128(&value, "i64.const value"));
        CALLBACK(OnI64ConstExpr, value);
        CALLBACK(OnOpcodeUint64, value);
        break;
      }

      case Opcode::F32Const: {
        uint32_t value_bits = 0;
        CHECK_RESULT(ReadF32(&value_bits, "f32.const value"));
        CALLBACK(OnF32ConstExpr, value_bits);
        CALLBACK(OnOpcodeF32, value_bits);
        break;
      }

      case Opcode::F64Const: {
        uint64_t value_bits = 0;
        CHECK_RESULT(ReadF64(&value_bits, "f64.const value"));
        CALLBACK(OnF64ConstExpr, value_bits);
        CALLBACK(OnOpcodeF64, value_bits);
        break;
      }

      case Opcode::V128Const: {
        v128 value_bits;
        ZeroMemory(value_bits);
        CHECK_RESULT(ReadV128(&value_bits, "v128.const value"));
        CALLBACK(OnV128ConstExpr, value_bits);
        CALLBACK(OnOpcodeV128, value_bits);
        break;
      }

      case Opcode::GlobalGet: {
        Index global_index;
        CHECK_RESULT(ReadIndex(&global_index, "global.get global index"));
        CALLBACK(OnGlobalGetExpr, global_index);
        CALLBACK(OnOpcodeIndex, global_index);
        break;
      }

      case Opcode::LocalGet: {
        Index local_index;
        CHECK_RESULT(ReadIndex(&local_index, "local.get local index"));
        CALLBACK(OnLocalGetExpr, local_index);
        CALLBACK(OnOpcodeIndex, local_index);
        break;
      }

      case Opcode::GlobalSet: {
        Index global_index;
        CHECK_RESULT(ReadIndex(&global_index, "global.set global index"));
        CALLBACK(OnGlobalSetExpr, global_index);
        CALLBACK(OnOpcodeIndex, global_index);
        break;
      }

      case Opcode::LocalSet: {
        Index local_index;
        CHECK_RESULT(ReadIndex(&local_index, "local.set local index"));
        CALLBACK(OnLocalSetExpr, local_index);
        CALLBACK(OnOpcodeIndex, local_index);
        break;
      }

      case Opcode::Call: {
        Index func_index;
        CHECK_RESULT(ReadIndex(&func_index, "call function index"));
        CALLBACK(OnCallExpr, func_index);
        CALLBACK(OnOpcodeIndex, func_index);
        break;
      }

      case Opcode::CallIndirect: {
        Index sig_index;
        CHECK_RESULT(ReadIndex(&sig_index, "call_indirect signature index"));
        Index table_index = 0;
        if (options_.features.reference_types_enabled()) {
          CHECK_RESULT(ReadIndex(&table_index, "call_indirect table index"));
        } else {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "call_indirect reserved"));
          ERROR_UNLESS(reserved == 0, "call_indirect reserved value must be 0");
        }
        CALLBACK(OnCallIndirectExpr, sig_index, table_index);
        CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
        break;
      }

      case Opcode::ReturnCall: {
        Index func_index;
        CHECK_RESULT(ReadIndex(&func_index, "return_call"));
        CALLBACK(OnReturnCallExpr, func_index);
        CALLBACK(OnOpcodeIndex, func_index);
        break;
      }

      case Opcode::ReturnCallIndirect: {
        Index sig_index;
        CHECK_RESULT(ReadIndex(&sig_index, "return_call_indirect"));
        Index table_index = 0;
        if (options_.features.reference_types_enabled()) {
          CHECK_RESULT(
              ReadIndex(&table_index, "return_call_indirect table index"));
        } else {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "return_call_indirect reserved"));
          ERROR_UNLESS(reserved == 0,
                       "return_call_indirect reserved value must be 0");
        }
        CALLBACK(OnReturnCallIndirectExpr, sig_index, table_index);
        CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
        break;
      }

      case Opcode::LocalTee: {
        Index local_index;
        CHECK_RESULT(ReadIndex(&local_index, "local.tee local index"));
        CALLBACK(OnLocalTeeExpr, local_index);
        CALLBACK(OnOpcodeIndex, local_index);
        break;
      }

      case Opcode::I32Load8S:
      case Opcode::I32Load8U:
      case Opcode::I32Load16S:
      case Opcode::I32Load16U:
      case Opcode::I64Load8S:
      case Opcode::I64Load8U:
      case Opcode::I64Load16S:
      case Opcode::I64Load16U:
      case Opcode::I64Load32S:
      case Opcode::I64Load32U:
      case Opcode::I32Load:
      case Opcode::I64Load:
      case Opcode::F32Load:
      case Opcode::F64Load:
      case Opcode::V128Load:
      case Opcode::V128Load8X8S:
      case Opcode::V128Load8X8U:
      case Opcode::V128Load16X4S:
      case Opcode::V128Load16X4U:
      case Opcode::V128Load32X2S:
      case Opcode::V128Load32X2U: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "load alignment""load memidx",
                                     "load offset"));
        CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::I32Store8:
      case Opcode::I32Store16:
      case Opcode::I64Store8:
      case Opcode::I64Store16:
      case Opcode::I64Store32:
      case Opcode::I32Store:
      case Opcode::I64Store:
      case Opcode::F32Store:
      case Opcode::F64Store:
      case Opcode::V128Store: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "store alignment""store memidx",
                                     "store offset"));
        CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::MemorySize: {
        Index memidx = 0;
        if (!options_.features.multi_memory_enabled()) {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
          ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
        } else {
          CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx"));
        }
        CALLBACK(OnMemorySizeExpr, memidx);
        CALLBACK(OnOpcodeUint32, memidx);
        break;
      }

      case Opcode::MemoryGrow: {
        Index memidx = 0;
        if (!options_.features.multi_memory_enabled()) {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
          ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
        } else {
          CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx"));
        }
        CALLBACK(OnMemoryGrowExpr, memidx);
        CALLBACK(OnOpcodeUint32, memidx);
        break;
      }

      case Opcode::I32Add:
      case Opcode::I32Sub:
      case Opcode::I32Mul:
      case Opcode::I32DivS:
      case Opcode::I32DivU:
      case Opcode::I32RemS:
      case Opcode::I32RemU:
      case Opcode::I32And:
      case Opcode::I32Or:
      case Opcode::I32Xor:
      case Opcode::I32Shl:
      case Opcode::I32ShrU:
      case Opcode::I32ShrS:
      case Opcode::I32Rotr:
      case Opcode::I32Rotl:
      case Opcode::I64Add:
      case Opcode::I64Sub:
      case Opcode::I64Mul:
      case Opcode::I64DivS:
      case Opcode::I64DivU:
      case Opcode::I64RemS:
      case Opcode::I64RemU:
      case Opcode::I64And:
      case Opcode::I64Or:
      case Opcode::I64Xor:
      case Opcode::I64Shl:
      case Opcode::I64ShrU:
      case Opcode::I64ShrS:
      case Opcode::I64Rotr:
      case Opcode::I64Rotl:
      case Opcode::F32Add:
      case Opcode::F32Sub:
      case Opcode::F32Mul:
      case Opcode::F32Div:
      case Opcode::F32Min:
      case Opcode::F32Max:
      case Opcode::F32Copysign:
      case Opcode::F64Add:
      case Opcode::F64Sub:
      case Opcode::F64Mul:
      case Opcode::F64Div:
      case Opcode::F64Min:
      case Opcode::F64Max:
      case Opcode::F64Copysign:
      case Opcode::I8X16Add:
      case Opcode::I16X8Add:
      case Opcode::I32X4Add:
      case Opcode::I64X2Add:
      case Opcode::I8X16Sub:
      case Opcode::I16X8Sub:
      case Opcode::I32X4Sub:
      case Opcode::I64X2Sub:
      case Opcode::I16X8Mul:
      case Opcode::I32X4Mul:
      case Opcode::I64X2Mul:
      case Opcode::I8X16AddSatS:
      case Opcode::I8X16AddSatU:
      case Opcode::I16X8AddSatS:
      case Opcode::I16X8AddSatU:
      case Opcode::I8X16SubSatS:
      case Opcode::I8X16SubSatU:
      case Opcode::I16X8SubSatS:
      case Opcode::I16X8SubSatU:
      case Opcode::I8X16MinS:
      case Opcode::I16X8MinS:
      case Opcode::I32X4MinS:
      case Opcode::I8X16MinU:
      case Opcode::I16X8MinU:
      case Opcode::I32X4MinU:
      case Opcode::I8X16MaxS:
      case Opcode::I16X8MaxS:
      case Opcode::I32X4MaxS:
      case Opcode::I8X16MaxU:
      case Opcode::I16X8MaxU:
      case Opcode::I32X4MaxU:
      case Opcode::I8X16Shl:
      case Opcode::I16X8Shl:
      case Opcode::I32X4Shl:
      case Opcode::I64X2Shl:
      case Opcode::I8X16ShrS:
      case Opcode::I8X16ShrU:
      case Opcode::I16X8ShrS:
      case Opcode::I16X8ShrU:
      case Opcode::I32X4ShrS:
      case Opcode::I32X4ShrU:
      case Opcode::I64X2ShrS:
      case Opcode::I64X2ShrU:
      case Opcode::V128And:
      case Opcode::V128Or:
      case Opcode::V128Xor:
      case Opcode::F32X4Min:
      case Opcode::F32X4PMin:
      case Opcode::F64X2Min:
      case Opcode::F64X2PMin:
      case Opcode::F32X4Max:
      case Opcode::F32X4PMax:
      case Opcode::F64X2Max:
      case Opcode::F64X2PMax:
      case Opcode::F32X4Add:
      case Opcode::F64X2Add:
      case Opcode::F32X4Sub:
      case Opcode::F64X2Sub:
      case Opcode::F32X4Div:
      case Opcode::F64X2Div:
      case Opcode::F32X4Mul:
      case Opcode::F64X2Mul:
      case Opcode::I8X16Swizzle:
      case Opcode::I8X16NarrowI16X8S:
      case Opcode::I8X16NarrowI16X8U:
      case Opcode::I16X8NarrowI32X4S:
      case Opcode::I16X8NarrowI32X4U:
      case Opcode::V128Andnot:
      case Opcode::I8X16AvgrU:
      case Opcode::I16X8AvgrU:
      case Opcode::I16X8ExtmulLowI8X16S:
      case Opcode::I16X8ExtmulHighI8X16S:
      case Opcode::I16X8ExtmulLowI8X16U:
      case Opcode::I16X8ExtmulHighI8X16U:
      case Opcode::I32X4ExtmulLowI16X8S:
      case Opcode::I32X4ExtmulHighI16X8S:
      case Opcode::I32X4ExtmulLowI16X8U:
      case Opcode::I32X4ExtmulHighI16X8U:
      case Opcode::I64X2ExtmulLowI32X4S:
      case Opcode::I64X2ExtmulHighI32X4S:
      case Opcode::I64X2ExtmulLowI32X4U:
      case Opcode::I64X2ExtmulHighI32X4U:
      case Opcode::I16X8Q15mulrSatS:
      case Opcode::I32X4DotI16X8S:
      case Opcode::I8X16RelaxedSwizzle:
      case Opcode::F32X4RelaxedMin:
      case Opcode::F32X4RelaxedMax:
      case Opcode::F64X2RelaxedMin:
      case Opcode::F64X2RelaxedMax:
      case Opcode::I16X8RelaxedQ15mulrS:
      case Opcode::I16X8DotI8X16I7X16S:
        CALLBACK(OnBinaryExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::I32Eq:
      case Opcode::I32Ne:
      case Opcode::I32LtS:
      case Opcode::I32LeS:
      case Opcode::I32LtU:
      case Opcode::I32LeU:
      case Opcode::I32GtS:
      case Opcode::I32GeS:
      case Opcode::I32GtU:
      case Opcode::I32GeU:
      case Opcode::I64Eq:
      case Opcode::I64Ne:
      case Opcode::I64LtS:
      case Opcode::I64LeS:
      case Opcode::I64LtU:
      case Opcode::I64LeU:
      case Opcode::I64GtS:
      case Opcode::I64GeS:
      case Opcode::I64GtU:
      case Opcode::I64GeU:
      case Opcode::F32Eq:
      case Opcode::F32Ne:
      case Opcode::F32Lt:
      case Opcode::F32Le:
      case Opcode::F32Gt:
      case Opcode::F32Ge:
      case Opcode::F64Eq:
      case Opcode::F64Ne:
      case Opcode::F64Lt:
      case Opcode::F64Le:
      case Opcode::F64Gt:
      case Opcode::F64Ge:
      case Opcode::I8X16Eq:
      case Opcode::I16X8Eq:
      case Opcode::I32X4Eq:
      case Opcode::I64X2Eq:
      case Opcode::F32X4Eq:
      case Opcode::F64X2Eq:
      case Opcode::I8X16Ne:
      case Opcode::I16X8Ne:
      case Opcode::I32X4Ne:
      case Opcode::I64X2Ne:
      case Opcode::F32X4Ne:
      case Opcode::F64X2Ne:
      case Opcode::I8X16LtS:
      case Opcode::I8X16LtU:
      case Opcode::I16X8LtS:
      case Opcode::I16X8LtU:
      case Opcode::I32X4LtS:
      case Opcode::I32X4LtU:
      case Opcode::I64X2LtS:
      case Opcode::F32X4Lt:
      case Opcode::F64X2Lt:
      case Opcode::I8X16LeS:
      case Opcode::I8X16LeU:
      case Opcode::I16X8LeS:
      case Opcode::I16X8LeU:
      case Opcode::I32X4LeS:
      case Opcode::I32X4LeU:
      case Opcode::I64X2LeS:
      case Opcode::F32X4Le:
      case Opcode::F64X2Le:
      case Opcode::I8X16GtS:
      case Opcode::I8X16GtU:
      case Opcode::I16X8GtS:
      case Opcode::I16X8GtU:
      case Opcode::I32X4GtS:
      case Opcode::I32X4GtU:
      case Opcode::I64X2GtS:
      case Opcode::F32X4Gt:
      case Opcode::F64X2Gt:
      case Opcode::I8X16GeS:
      case Opcode::I8X16GeU:
      case Opcode::I16X8GeS:
      case Opcode::I16X8GeU:
      case Opcode::I32X4GeS:
      case Opcode::I32X4GeU:
      case Opcode::I64X2GeS:
      case Opcode::F32X4Ge:
      case Opcode::F64X2Ge:
        CALLBACK(OnCompareExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::I32Clz:
      case Opcode::I32Ctz:
      case Opcode::I32Popcnt:
      case Opcode::I64Clz:
      case Opcode::I64Ctz:
      case Opcode::I64Popcnt:
      case Opcode::F32Abs:
      case Opcode::F32Neg:
      case Opcode::F32Ceil:
      case Opcode::F32Floor:
      case Opcode::F32Trunc:
      case Opcode::F32Nearest:
      case Opcode::F32Sqrt:
      case Opcode::F64Abs:
      case Opcode::F64Neg:
      case Opcode::F64Ceil:
      case Opcode::F64Floor:
      case Opcode::F64Trunc:
      case Opcode::F64Nearest:
      case Opcode::F64Sqrt:
      case Opcode::I8X16Splat:
      case Opcode::I16X8Splat:
      case Opcode::I32X4Splat:
      case Opcode::I64X2Splat:
      case Opcode::F32X4Splat:
      case Opcode::F64X2Splat:
      case Opcode::I8X16Neg:
      case Opcode::I16X8Neg:
      case Opcode::I32X4Neg:
      case Opcode::I64X2Neg:
      case Opcode::V128Not:
      case Opcode::V128AnyTrue:
      case Opcode::I8X16Bitmask:
      case Opcode::I16X8Bitmask:
      case Opcode::I32X4Bitmask:
      case Opcode::I64X2Bitmask:
      case Opcode::I8X16AllTrue:
      case Opcode::I16X8AllTrue:
      case Opcode::I32X4AllTrue:
      case Opcode::I64X2AllTrue:
      case Opcode::F32X4Ceil:
      case Opcode::F64X2Ceil:
      case Opcode::F32X4Floor:
      case Opcode::F64X2Floor:
      case Opcode::F32X4Trunc:
      case Opcode::F64X2Trunc:
      case Opcode::F32X4Nearest:
      case Opcode::F64X2Nearest:
      case Opcode::F32X4Neg:
      case Opcode::F64X2Neg:
      case Opcode::F32X4Abs:
      case Opcode::F64X2Abs:
      case Opcode::F32X4Sqrt:
      case Opcode::F64X2Sqrt:
      case Opcode::I16X8ExtendLowI8X16S:
      case Opcode::I16X8ExtendHighI8X16S:
      case Opcode::I16X8ExtendLowI8X16U:
      case Opcode::I16X8ExtendHighI8X16U:
      case Opcode::I32X4ExtendLowI16X8S:
      case Opcode::I32X4ExtendHighI16X8S:
      case Opcode::I32X4ExtendLowI16X8U:
      case Opcode::I32X4ExtendHighI16X8U:
      case Opcode::I64X2ExtendLowI32X4S:
      case Opcode::I64X2ExtendHighI32X4S:
      case Opcode::I64X2ExtendLowI32X4U:
      case Opcode::I64X2ExtendHighI32X4U:
      case Opcode::I8X16Abs:
      case Opcode::I16X8Abs:
      case Opcode::I32X4Abs:
      case Opcode::I64X2Abs:
      case Opcode::I8X16Popcnt:
      case Opcode::I16X8ExtaddPairwiseI8X16S:
      case Opcode::I16X8ExtaddPairwiseI8X16U:
      case Opcode::I32X4ExtaddPairwiseI16X8S:
      case Opcode::I32X4ExtaddPairwiseI16X8U:
      case Opcode::I32X4RelaxedTruncF32X4S:
      case Opcode::I32X4RelaxedTruncF32X4U:
      case Opcode::I32X4RelaxedTruncF64X2SZero:
      case Opcode::I32X4RelaxedTruncF64X2UZero:
        CALLBACK(OnUnaryExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::V128BitSelect:
      case Opcode::F32X4RelaxedMadd:
      case Opcode::F32X4RelaxedNmadd:
      case Opcode::F64X2RelaxedMadd:
      case Opcode::F64X2RelaxedNmadd:
      case Opcode::I8X16RelaxedLaneSelect:
      case Opcode::I16X8RelaxedLaneSelect:
      case Opcode::I32X4RelaxedLaneSelect:
      case Opcode::I64X2RelaxedLaneSelect:
      case Opcode::I32X4DotI8X16I7X16AddS:
        CALLBACK(OnTernaryExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::I8X16ExtractLaneS:
      case Opcode::I8X16ExtractLaneU:
      case Opcode::I16X8ExtractLaneS:
      case Opcode::I16X8ExtractLaneU:
      case Opcode::I32X4ExtractLane:
      case Opcode::I64X2ExtractLane:
      case Opcode::F32X4ExtractLane:
      case Opcode::F64X2ExtractLane:
      case Opcode::I8X16ReplaceLane:
      case Opcode::I16X8ReplaceLane:
      case Opcode::I32X4ReplaceLane:
      case Opcode::I64X2ReplaceLane:
      case Opcode::F32X4ReplaceLane:
      case Opcode::F64X2ReplaceLane: {
        uint8_t lane_val;
        CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
        CALLBACK(OnSimdLaneOpExpr, opcode, lane_val);
        CALLBACK(OnOpcodeUint64, lane_val);
        break;
      }

      case Opcode::I8X16Shuffle: {
        v128 value;
        CHECK_RESULT(ReadV128(&value, "Lane idx [16]"));
        CALLBACK(OnSimdShuffleOpExpr, opcode, value);
        CALLBACK(OnOpcodeV128, value);
        break;
      }

      case Opcode::V128Load8Splat:
      case Opcode::V128Load16Splat:
      case Opcode::V128Load32Splat:
      case Opcode::V128Load64Splat: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "load alignment""load memidx",
                                     "load offset"));
        CALLBACK(OnLoadSplatExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }
      case Opcode::V128Load8Lane:
      case Opcode::V128Load16Lane:
      case Opcode::V128Load32Lane:
      case Opcode::V128Load64Lane: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        uint8_t lane_val;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "load alignment""load memidx",
                                     "load offset", &lane_val));
        CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset,
                 lane_val);
        CHECK_RESULT(
            CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
        break;
      }
      case Opcode::V128Store8Lane:
      case Opcode::V128Store16Lane:
      case Opcode::V128Store32Lane:
      case Opcode::V128Store64Lane: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        uint8_t lane_val;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "store alignment""store memidx",
                                     "store offset", &lane_val));
        CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset,
                 lane_val);
        CHECK_RESULT(
            CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
        break;
      }
      case Opcode::V128Load32Zero:
      case Opcode::V128Load64Zero: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "load alignment""load memidx",
                                     "load offset"));
        CALLBACK(OnLoadZeroExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }
      case Opcode::I32TruncF32S:
      case Opcode::I32TruncF64S:
      case Opcode::I32TruncF32U:
      case Opcode::I32TruncF64U:
      case Opcode::I32WrapI64:
      case Opcode::I64TruncF32S:
      case Opcode::I64TruncF64S:
      case Opcode::I64TruncF32U:
      case Opcode::I64TruncF64U:
      case Opcode::I64ExtendI32S:
      case Opcode::I64ExtendI32U:
      case Opcode::F32ConvertI32S:
      case Opcode::F32ConvertI32U:
      case Opcode::F32ConvertI64S:
      case Opcode::F32ConvertI64U:
      case Opcode::F32DemoteF64:
      case Opcode::F32ReinterpretI32:
      case Opcode::F64ConvertI32S:
      case Opcode::F64ConvertI32U:
      case Opcode::F64ConvertI64S:
      case Opcode::F64ConvertI64U:
      case Opcode::F64PromoteF32:
      case Opcode::F64ReinterpretI64:
      case Opcode::I32ReinterpretF32:
      case Opcode::I64ReinterpretF64:
      case Opcode::I32Eqz:
      case Opcode::I64Eqz:
      case Opcode::F32X4ConvertI32X4S:
      case Opcode::F32X4ConvertI32X4U:
      case Opcode::I32X4TruncSatF32X4S:
      case Opcode::I32X4TruncSatF32X4U:
      case Opcode::F32X4DemoteF64X2Zero:
      case Opcode::F64X2PromoteLowF32X4:
      case Opcode::I32X4TruncSatF64X2SZero:
      case Opcode::I32X4TruncSatF64X2UZero:
      case Opcode::F64X2ConvertLowI32X4S:
      case Opcode::F64X2ConvertLowI32X4U:
        CALLBACK(OnConvertExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::Try: {
        nested_blocks.push(opcode);
        Type sig_type;
        CHECK_RESULT(ReadType(&sig_type, "try signature type"));
        ERROR_UNLESS(IsBlockType(sig_type),
                     "expected valid block signature type");
        CALLBACK(OnTryExpr, sig_type);
        CALLBACK(OnOpcodeBlockSig, sig_type);
        break;
      }

      case Opcode::Catch: {
        Index index;
        CHECK_RESULT(ReadIndex(&index, "tag index"));
        CALLBACK(OnCatchExpr, index);
        CALLBACK(OnOpcodeIndex, index);
        break;
      }

      case Opcode::CatchAll: {
        CALLBACK(OnCatchAllExpr);
        CALLBACK(OnOpcodeBare);
        break;
      }

      case Opcode::Delegate: {
        ERROR_IF(nested_blocks.empty() || (nested_blocks.top() != Opcode::Try),
                 "delegate outside try block");
        nested_blocks.pop();
        Index index;
        CHECK_RESULT(ReadIndex(&index, "depth"));
        CALLBACK(OnDelegateExpr, index);
        CALLBACK(OnOpcodeIndex, index);
        break;
      }

      case Opcode::Rethrow: {
        Index depth;
        CHECK_RESULT(ReadIndex(&depth, "catch depth"));
        CALLBACK(OnRethrowExpr, depth);
        CALLBACK(OnOpcodeIndex, depth);
        break;
      }

      case Opcode::Throw: {
        Index index;
        CHECK_RESULT(ReadIndex(&index, "tag index"));
        CALLBACK(OnThrowExpr, index);
        CALLBACK(OnOpcodeIndex, index);
        break;
      }

      case Opcode::I32Extend8S:
      case Opcode::I32Extend16S:
      case Opcode::I64Extend8S:
      case Opcode::I64Extend16S:
      case Opcode::I64Extend32S:
        CALLBACK(OnUnaryExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::I32TruncSatF32S:
      case Opcode::I32TruncSatF32U:
      case Opcode::I32TruncSatF64S:
      case Opcode::I32TruncSatF64U:
      case Opcode::I64TruncSatF32S:
      case Opcode::I64TruncSatF32U:
      case Opcode::I64TruncSatF64S:
      case Opcode::I64TruncSatF64U:
        CALLBACK(OnConvertExpr, opcode);
        CALLBACK0(OnOpcodeBare);
        break;

      case Opcode::MemoryAtomicNotify: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "notify alignment""notify memidx",
                                     "notify offset"));
        CALLBACK(OnAtomicNotifyExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::MemoryAtomicWait32:
      case Opcode::MemoryAtomicWait64: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "wait alignment""wait memidx",
                                     "wait offset"));
        CALLBACK(OnAtomicWaitExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::AtomicFence: {
        uint8_t consistency_model;
        CHECK_RESULT(ReadU8(&consistency_model, "consistency model"));
        ERROR_UNLESS(consistency_model == 0,
                     "atomic.fence consistency model must be 0");
        CALLBACK(OnAtomicFenceExpr, consistency_model);
        CALLBACK(OnOpcodeUint32, consistency_model);
        break;
      }

      case Opcode::I32AtomicLoad8U:
      case Opcode::I32AtomicLoad16U:
      case Opcode::I64AtomicLoad8U:
      case Opcode::I64AtomicLoad16U:
      case Opcode::I64AtomicLoad32U:
      case Opcode::I32AtomicLoad:
      case Opcode::I64AtomicLoad: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "load alignment""load memidx",
                                     "load offset"));
        CALLBACK(OnAtomicLoadExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::I32AtomicStore8:
      case Opcode::I32AtomicStore16:
      case Opcode::I64AtomicStore8:
      case Opcode::I64AtomicStore16:
      case Opcode::I64AtomicStore32:
      case Opcode::I32AtomicStore:
      case Opcode::I64AtomicStore: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "store alignment""store memidx",
                                     "store offset"));
        CALLBACK(OnAtomicStoreExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::I32AtomicRmwAdd:
      case Opcode::I64AtomicRmwAdd:
      case Opcode::I32AtomicRmw8AddU:
      case Opcode::I32AtomicRmw16AddU:
      case Opcode::I64AtomicRmw8AddU:
      case Opcode::I64AtomicRmw16AddU:
      case Opcode::I64AtomicRmw32AddU:
      case Opcode::I32AtomicRmwSub:
      case Opcode::I64AtomicRmwSub:
      case Opcode::I32AtomicRmw8SubU:
      case Opcode::I32AtomicRmw16SubU:
      case Opcode::I64AtomicRmw8SubU:
      case Opcode::I64AtomicRmw16SubU:
      case Opcode::I64AtomicRmw32SubU:
      case Opcode::I32AtomicRmwAnd:
      case Opcode::I64AtomicRmwAnd:
      case Opcode::I32AtomicRmw8AndU:
      case Opcode::I32AtomicRmw16AndU:
      case Opcode::I64AtomicRmw8AndU:
      case Opcode::I64AtomicRmw16AndU:
      case Opcode::I64AtomicRmw32AndU:
      case Opcode::I32AtomicRmwOr:
      case Opcode::I64AtomicRmwOr:
      case Opcode::I32AtomicRmw8OrU:
      case Opcode::I32AtomicRmw16OrU:
      case Opcode::I64AtomicRmw8OrU:
      case Opcode::I64AtomicRmw16OrU:
      case Opcode::I64AtomicRmw32OrU:
      case Opcode::I32AtomicRmwXor:
      case Opcode::I64AtomicRmwXor:
      case Opcode::I32AtomicRmw8XorU:
      case Opcode::I32AtomicRmw16XorU:
      case Opcode::I64AtomicRmw8XorU:
      case Opcode::I64AtomicRmw16XorU:
      case Opcode::I64AtomicRmw32XorU:
      case Opcode::I32AtomicRmwXchg:
      case Opcode::I64AtomicRmwXchg:
      case Opcode::I32AtomicRmw8XchgU:
      case Opcode::I32AtomicRmw16XchgU:
      case Opcode::I64AtomicRmw8XchgU:
      case Opcode::I64AtomicRmw16XchgU:
      case Opcode::I64AtomicRmw32XchgU: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "memory alignment""memory memidx",
                                     "memory offset"));
        CALLBACK(OnAtomicRmwExpr, opcode, memidx, alignment_log2, offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::I32AtomicRmwCmpxchg:
      case Opcode::I64AtomicRmwCmpxchg:
      case Opcode::I32AtomicRmw8CmpxchgU:
      case Opcode::I32AtomicRmw16CmpxchgU:
      case Opcode::I64AtomicRmw8CmpxchgU:
      case Opcode::I64AtomicRmw16CmpxchgU:
      case Opcode::I64AtomicRmw32CmpxchgU: {
        Address alignment_log2;
        Index memidx;
        Address offset;
        CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
                                     "memory alignment""memory memidx",
                                     "memory offset"));
        CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, memidx, alignment_log2,
                 offset);
        CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
        break;
      }

      case Opcode::TableInit: {
        Index segment;
        CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
        Index table_index;
        CHECK_RESULT(ReadIndex(&table_index, "reserved table index"));
        CALLBACK(OnTableInitExpr, segment, table_index);
        CALLBACK(OnOpcodeUint32Uint32, segment, table_index);
        break;
      }

      case Opcode::MemoryInit: {
        Index segment;
        ERROR_IF(data_count_ == kInvalidIndex,
                 "memory.init requires data count section");
        CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
        Index memidx = 0;
        if (!options_.features.multi_memory_enabled()) {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
          ERROR_UNLESS(reserved == 0, "reserved value must be 0");
        } else {
          CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx"));
        }
        CALLBACK(OnMemoryInitExpr, segment, memidx);
        CALLBACK(OnOpcodeUint32Uint32, segment, memidx);
        break;
      }

      case Opcode::DataDrop:
        ERROR_IF(data_count_ == kInvalidIndex,
                 "data.drop requires data count section");
        [[fallthrough]];
      case Opcode::ElemDrop: {
        Index segment;
        CHECK_RESULT(ReadIndex(&segment, "segment index"));
        if (opcode == Opcode::DataDrop) {
          CALLBACK(OnDataDropExpr, segment);
        } else {
          CALLBACK(OnElemDropExpr, segment);
        }
        CALLBACK(OnOpcodeUint32, segment);
        break;
      }

      case Opcode::MemoryFill: {
        Index memidx = 0;
        if (!options_.features.multi_memory_enabled()) {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved"));
          ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0");
        } else {
          CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx"));
        }
        CALLBACK(OnMemoryFillExpr, memidx);
        CALLBACK(OnOpcodeUint32, memidx);
        break;
      }

      case Opcode::MemoryCopy: {
        Index destmemidx = 0;
        Index srcmemidx = 0;
        if (!options_.features.multi_memory_enabled()) {
          uint8_t reserved;
          CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
          ERROR_UNLESS(reserved == 0, "reserved value must be 0");
          CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
          ERROR_UNLESS(reserved == 0, "reserved value must be 0");
        } else {
          CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex"));
          CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx"));
        }
        CALLBACK(OnMemoryCopyExpr, destmemidx, srcmemidx);
        CALLBACK(OnOpcodeUint32Uint32, destmemidx, srcmemidx);
        break;
      }

      case Opcode::TableCopy: {
        Index table_dst;
        Index table_src;
        CHECK_RESULT(ReadIndex(&table_dst, "reserved table index"));
        CHECK_RESULT(ReadIndex(&table_src, "table src"));
        CALLBACK(OnTableCopyExpr, table_dst, table_src);
        CALLBACK(OnOpcodeUint32Uint32, table_dst, table_src);
        break;
      }

      case Opcode::TableGet: {
        Index table;
        CHECK_RESULT(ReadIndex(&table, "table index"));
        CALLBACK(OnTableGetExpr, table);
        CALLBACK(OnOpcodeUint32, table);
        break;
      }

      case Opcode::TableSet: {
        Index table;
        CHECK_RESULT(ReadIndex(&table, "table index"));
        CALLBACK(OnTableSetExpr, table);
        CALLBACK(OnOpcodeUint32, table);
        break;
      }

      case Opcode::TableGrow: {
        Index table;
        CHECK_RESULT(ReadIndex(&table, "table index"));
        CALLBACK(OnTableGrowExpr, table);
        CALLBACK(OnOpcodeUint32, table);
        break;
      }

      case Opcode::TableSize: {
        Index table;
        CHECK_RESULT(ReadIndex(&table, "table index"));
        CALLBACK(OnTableSizeExpr, table);
        CALLBACK(OnOpcodeUint32, table);
        break;
      }

      case Opcode::TableFill: {
        Index table;
        CHECK_RESULT(ReadIndex(&table, "table index"));
        CALLBACK(OnTableFillExpr, table);
        CALLBACK(OnOpcodeUint32, table);
        break;
      }

      case Opcode::RefFunc: {
        Index func;
        CHECK_RESULT(ReadIndex(&func, "func index"));
        CALLBACK(OnRefFuncExpr, func);
        CALLBACK(OnOpcodeUint32, func);
        break;
      }

      case Opcode::RefNull: {
        Type type;
        CHECK_RESULT(ReadRefType(&type, "ref.null type"));
        CALLBACK(OnRefNullExpr, type);
        CALLBACK(OnOpcodeType, type);
        break;
      }

      case Opcode::RefIsNull:
        CALLBACK(OnRefIsNullExpr);
        CALLBACK(OnOpcodeBare);
        break;

      case Opcode::CallRef:
        CALLBACK(OnCallRefExpr);
        CALLBACK(OnOpcodeBare);
        break;

      default:
        return ReportUnexpectedOpcode(opcode);
    }
  }

  PrintError("%s must end with END opcode", context);
  return Result::Error;
}

Result BinaryReader::ReadNameSection(Offset section_size) {
  CALLBACK(BeginNamesSection, section_size);
  Index i = 0;
  uint32_t previous_subsection_type = 0;
  while (state_.offset < read_end_) {
    uint32_t name_type;
    Offset subsection_size;
    CHECK_RESULT(ReadU32Leb128(&name_type, "name type"));
    if (i != 0) {
      ERROR_UNLESS(name_type != previous_subsection_type,
                   "duplicate sub-section");
      ERROR_UNLESS(name_type >= previous_subsection_type,
                   "out-of-order sub-section");
    }
    previous_subsection_type = name_type;
    CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
    size_t subsection_end = state_.offset + subsection_size;
    ERROR_UNLESS(subsection_end <= read_end_,
                 "invalid sub-section size: extends past end");
    ReadEndRestoreGuard guard(this);
    read_end_ = subsection_end;

    NameSectionSubsection type = static_cast<NameSectionSubsection>(name_type);
    if (type <= NameSectionSubsection::Last) {
      CALLBACK(OnNameSubsection, i, type, subsection_size);
    }

    switch (type) {
      case NameSectionSubsection::Module:
        CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size);
        if (subsection_size) {
          std::string_view name;
          CHECK_RESULT(ReadStr(&name, "module name"));
          CALLBACK(OnModuleName, name);
        }
        break;
      case NameSectionSubsection::Function:
        CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size);
        if (subsection_size) {
          Index num_names;
          CHECK_RESULT(ReadCount(&num_names, "name count"));
          CALLBACK(OnFunctionNamesCount, num_names);
          Index last_function_index = kInvalidIndex;

          for (Index j = 0; j < num_names; ++j) {
            Index function_index;
            std::string_view function_name;

            CHECK_RESULT(ReadIndex(&function_index, "function index"));
            ERROR_UNLESS(function_index != last_function_index,
                         "duplicate function name: %u", function_index);
            ERROR_UNLESS(last_function_index == kInvalidIndex ||
                             function_index > last_function_index,
                         "function index out of order: %u", function_index);
            last_function_index = function_index;
            ERROR_UNLESS(function_index < NumTotalFuncs(),
                         "invalid function index: %" PRIindex, function_index);
            CHECK_RESULT(ReadStr(&function_name, "function name"));
            CALLBACK(OnFunctionName, function_index, function_name);
          }
        }
        break;
      case NameSectionSubsection::Local:
        CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size);
        if (subsection_size) {
          Index num_funcs;
          CHECK_RESULT(ReadCount(&num_funcs, "function count"));
          CALLBACK(OnLocalNameFunctionCount, num_funcs);
          Index last_function_index = kInvalidIndex;
          for (Index j = 0; j < num_funcs; ++j) {
            Index function_index;
            CHECK_RESULT(ReadIndex(&function_index, "function index"));
            ERROR_UNLESS(function_index < NumTotalFuncs(),
                         "invalid function index: %u", function_index);
            ERROR_UNLESS(last_function_index == kInvalidIndex ||
                             function_index > last_function_index,
                         "locals function index out of order: %u",
                         function_index);
            last_function_index = function_index;
            Index num_locals;
            CHECK_RESULT(ReadCount(&num_locals, "local count"));
            CALLBACK(OnLocalNameLocalCount, function_index, num_locals);
            Index last_local_index = kInvalidIndex;
            for (Index k = 0; k < num_locals; ++k) {
              Index local_index;
              std::string_view local_name;

              CHECK_RESULT(ReadIndex(&local_index, "named index"));
              ERROR_UNLESS(local_index != last_local_index,
                           "duplicate local index: %u", local_index);
              ERROR_UNLESS(last_local_index == kInvalidIndex ||
                               local_index > last_local_index,
                           "local index out of order: %u", local_index);
              last_local_index = local_index;
              CHECK_RESULT(ReadStr(&local_name, "name"));
              CALLBACK(OnLocalName, function_index, local_index, local_name);
            }
          }
        }
        break;
      case NameSectionSubsection::Label:
        // TODO(sbc): Implement label names. These are slightly more complicated
        // since they refer to offsets in the code section / instruction stream.
        state_.offset = subsection_end;
        break;
      case NameSectionSubsection::Type:
      case NameSectionSubsection::Table:
      case NameSectionSubsection::Memory:
      case NameSectionSubsection::Global:
      case NameSectionSubsection::ElemSegment:
      case NameSectionSubsection::DataSegment:
      case NameSectionSubsection::Tag:
        if (subsection_size) {
          Index num_names;
          CHECK_RESULT(ReadCount(&num_names, "name count"));
          CALLBACK(OnNameCount, num_names);
          for (Index j = 0; j < num_names; ++j) {
            Index index;
            std::string_view name;

            CHECK_RESULT(ReadIndex(&index, "index"));
            CHECK_RESULT(ReadStr(&name, "name"));
            CALLBACK(OnNameEntry, type, index, name);
          }
        }
        state_.offset = subsection_end;
        break;
      default:
        // Unknown subsection, skip it.
        state_.offset = subsection_end;
        break;
    }
    ++i;
    ERROR_UNLESS(state_.offset == subsection_end,
                 "unfinished sub-section (expected end: 0x%" PRIzx ")",
                 subsection_end);
  }
  CALLBACK0(EndNamesSection);
  return Result::Ok;
}

Result BinaryReader::ReadRelocSection(Offset section_size) {
  CALLBACK(BeginRelocSection, section_size);
  uint32_t section_index;
  CHECK_RESULT(ReadU32Leb128(§ion_index, "section index"));
  Index num_relocs;
  CHECK_RESULT(ReadCount(&num_relocs, "relocation count"));
  CALLBACK(OnRelocCount, num_relocs, section_index);
  for (Index i = 0; i < num_relocs; ++i) {
    Offset offset;
    Index index;
    uint32_t reloc_type, addend = 0;
    CHECK_RESULT(ReadU32Leb128(&reloc_type, "relocation type"));
    CHECK_RESULT(ReadOffset(&offset, "offset"));
    CHECK_RESULT(ReadIndex(&index, "index"));
    RelocType type = static_cast<RelocType>(reloc_type);
    switch (type) {
      case RelocType::MemoryAddressLEB:
      case RelocType::MemoryAddressLEB64:
      case RelocType::MemoryAddressSLEB:
      case RelocType::MemoryAddressSLEB64:
      case RelocType::MemoryAddressRelSLEB:
      case RelocType::MemoryAddressRelSLEB64:
      case RelocType::MemoryAddressI32:
      case RelocType::MemoryAddressI64:
      case RelocType::MemoryAddressLocRelI32:
      case RelocType::FunctionOffsetI32:
      case RelocType::FunctionOffsetI64:
      case RelocType::SectionOffsetI32:
      case RelocType::MemoryAddressTLSSLEB:
      case RelocType::MemoryAddressTLSSLEB64:
        CHECK_RESULT(ReadS32Leb128(&addend, "addend"));
        break;

      case RelocType::FuncIndexLEB:
      case RelocType::FuncIndexI32:
      case RelocType::TableIndexSLEB:
      case RelocType::TableIndexSLEB64:
      case RelocType::TableIndexI32:
      case RelocType::TableIndexI64:
      case RelocType::TypeIndexLEB:
      case RelocType::GlobalIndexLEB:
      case RelocType::GlobalIndexI32:
      case RelocType::TagIndexLEB:
      case RelocType::TableIndexRelSLEB:
      case RelocType::TableIndexRelSLEB64:
      case RelocType::TableNumberLEB:
        break;

      default:
        PrintError("unknown reloc type: %s", GetRelocTypeName(type));
        return Result::Error;
    }
    CALLBACK(OnReloc, type, offset, index, addend);
  }
  CALLBACK0(EndRelocSection);
  return Result::Ok;
}

Result BinaryReader::ReadDylink0Section(Offset section_size) {
  CALLBACK(BeginDylinkSection, section_size);

  while (state_.offset < read_end_) {
    uint32_t dylink_type;
    Offset subsection_size;
    CHECK_RESULT(ReadU32Leb128(&dylink_type, "type"));
    CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
    size_t subsection_end = state_.offset + subsection_size;
    ERROR_UNLESS(subsection_end <= read_end_,
                 "invalid sub-section size: extends past end");
    ReadEndRestoreGuard guard(this);
    read_end_ = subsection_end;

    uint32_t count;
    switch (static_cast<DylinkEntryType>(dylink_type)) {
      case DylinkEntryType::MemInfo: {
        uint32_t mem_size;
        uint32_t mem_align;
        uint32_t table_size;
        uint32_t table_align;

        CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
        CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
        CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
        CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
        CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
        break;
      }
      case DylinkEntryType::Needed:
        CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
        CALLBACK(OnDylinkNeededCount, count);
        while (count--) {
          std::string_view so_name;
          CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
          CALLBACK(OnDylinkNeeded, so_name);
        }
        break;
      case DylinkEntryType::ImportInfo:
        CHECK_RESULT(ReadU32Leb128(&count, "count"));
        CALLBACK(OnDylinkImportCount, count);
        for (Index i = 0; i < count; ++i) {
          uint32_t flags = 0;
          std::string_view module;
          std::string_view field;
          CHECK_RESULT(ReadStr(&module, "module"));
          CHECK_RESULT(ReadStr(&field, "field"));
          CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
          CALLBACK(OnDylinkImport, module, field, flags);
        }
        break;
      case DylinkEntryType::ExportInfo:
        CHECK_RESULT(ReadU32Leb128(&count, "count"));
        CALLBACK(OnDylinkExportCount, count);
        for (Index i = 0; i < count; ++i) {
          uint32_t flags = 0;
          std::string_view name;
          CHECK_RESULT(ReadStr(&name, "name"));
          CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
          CALLBACK(OnDylinkExport, name, flags);
        }
        break;
      default:
        // Unknown subsection, skip it.
        state_.offset = subsection_end;
        break;
    }
    ERROR_UNLESS(state_.offset == subsection_end,
                 "unfinished sub-section (expected end: 0x%" PRIzx ")",
                 subsection_end);
  }

  CALLBACK0(EndDylinkSection);
  return Result::Ok;
}

Result BinaryReader::ReadDylinkSection(Offset section_size) {
  CALLBACK(BeginDylinkSection, section_size);
  uint32_t mem_size;
  uint32_t mem_align;
  uint32_t table_size;
  uint32_t table_align;

  CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
  CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
  CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
  CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
  CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);

  uint32_t count;
  CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
  CALLBACK(OnDylinkNeededCount, count);
  while (count--) {
    std::string_view so_name;
    CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
    CALLBACK(OnDylinkNeeded, so_name);
  }

  CALLBACK0(EndDylinkSection);
  return Result::Ok;
}

Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) {
  CALLBACK(BeginTargetFeaturesSection, section_size);
  uint32_t count;
  CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
  CALLBACK(OnFeatureCount, count);
  while (count--) {
    uint8_t prefix;
    std::string_view name;
    CHECK_RESULT(ReadU8(&prefix, "prefix"));
    CHECK_RESULT(ReadStr(&name, "feature name"));
    CALLBACK(OnFeature, prefix, name);
  }
  CALLBACK0(EndTargetFeaturesSection);
  return Result::Ok;
}

Result BinaryReader::ReadGenericCustomSection(std::string_view name,
                                              Offset section_size) {
  CALLBACK(BeginGenericCustomSection, section_size);
  const void* data;
  Offset custom_data_size = read_end_ - state_.offset;
  CHECK_RESULT(
      ReadBytesWithSize(&data, custom_data_size, "custom section data"));
  CALLBACK(OnGenericCustomSection, name, data, custom_data_size);
  CALLBACK0(EndGenericCustomSection);
  return Result::Ok;
}

Result BinaryReader::ReadLinkingSection(Offset section_size) {
  CALLBACK(BeginLinkingSection, section_size);
  uint32_t version;
  CHECK_RESULT(ReadU32Leb128(&version, "version"));
  ERROR_UNLESS(version == 2, "invalid linking metadata version: %u", version);
  while (state_.offset < read_end_) {
    uint32_t linking_type;
    Offset subsection_size;
    CHECK_RESULT(ReadU32Leb128(&linking_type, "type"));
    CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
    size_t subsection_end = state_.offset + subsection_size;
    ERROR_UNLESS(subsection_end <= read_end_,
                 "invalid sub-section size: extends past end");
    ReadEndRestoreGuard guard(this);
    read_end_ = subsection_end;

    uint32_t count;
    switch (static_cast<LinkingEntryType>(linking_type)) {
      case LinkingEntryType::SymbolTable:
        CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
        CALLBACK(OnSymbolCount, count);
        for (Index i = 0; i < count; ++i) {
          std::string_view name;
          uint32_t flags = 0;
          uint32_t kind = 0;
          CHECK_RESULT(ReadU32Leb128(&kind, "sym type"));
          CHECK_RESULT(ReadU32Leb128(&flags, "sym flags"));
          SymbolType sym_type = static_cast<SymbolType>(kind);
          switch (sym_type) {
            case SymbolType::Function:
            case SymbolType::Global:
            case SymbolType::Tag:
            case SymbolType::Table: {
              uint32_t index = 0;
              CHECK_RESULT(ReadU32Leb128(&index, "index"));
              if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0 ||
                  (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) != 0)
                CHECK_RESULT(ReadStr(&name, "symbol name"));
              switch (sym_type) {
                case SymbolType::Function:
                  CALLBACK(OnFunctionSymbol, i, flags, name, index);
                  break;
                case SymbolType::Global:
                  CALLBACK(OnGlobalSymbol, i, flags, name, index);
                  break;
                case SymbolType::Tag:
                  CALLBACK(OnTagSymbol, i, flags, name, index);
                  break;
                case SymbolType::Table:
                  CALLBACK(OnTableSymbol, i, flags, name, index);
                  break;
                default:
                  WABT_UNREACHABLE;
              }
              break;
            }
            case SymbolType::Data: {
              uint32_t segment = 0;
              uint32_t offset = 0;
              uint32_t size = 0;
              CHECK_RESULT(ReadStr(&name, "symbol name"));
              if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0) {
                CHECK_RESULT(ReadU32Leb128(&segment, "segment"));
                CHECK_RESULT(ReadU32Leb128(&offset, "offset"));
                CHECK_RESULT(ReadU32Leb128(&size, "size"));
              }
              CALLBACK(OnDataSymbol, i, flags, name, segment, offset, size);
              break;
            }
            case SymbolType::Section: {
              uint32_t index = 0;
              CHECK_RESULT(ReadU32Leb128(&index, "index"));
              CALLBACK(OnSectionSymbol, i, flags, index);
              break;
            }
          }
        }
        break;
      case LinkingEntryType::SegmentInfo:
        CHECK_RESULT(ReadU32Leb128(&count, "info count"));
        CALLBACK(OnSegmentInfoCount, count);
        for (Index i = 0; i < count; i++) {
          std::string_view name;
          Address alignment_log2;
          uint32_t flags;
          CHECK_RESULT(ReadStr(&name, "segment name"));
          CHECK_RESULT(ReadAlignment(&alignment_log2, "segment alignment"));
          CHECK_RESULT(CheckAlignment(&alignment_log2, "segment alignment"));
          CHECK_RESULT(ReadU32Leb128(&flags, "segment flags"));
          CALLBACK(OnSegmentInfo, i, name, alignment_log2, flags);
        }
        break;
      case LinkingEntryType::InitFunctions:
        CHECK_RESULT(ReadU32Leb128(&count, "info count"));
        CALLBACK(OnInitFunctionCount, count);
        while (count--) {
          uint32_t priority;
          uint32_t symbol;
          CHECK_RESULT(ReadU32Leb128(&priority, "priority"));
          CHECK_RESULT(ReadU32Leb128(&symbol, "symbol index"));
          CALLBACK(OnInitFunction, priority, symbol);
        }
        break;
      case LinkingEntryType::ComdatInfo:
        CHECK_RESULT(ReadU32Leb128(&count, "count"));
        CALLBACK(OnComdatCount, count);
        while (count--) {
          uint32_t flags;
          uint32_t entry_count;
          std::string_view name;
          CHECK_RESULT(ReadStr(&name, "comdat name"));
          CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
          CHECK_RESULT(ReadU32Leb128(&entry_count, "entry count"));
          CALLBACK(OnComdatBegin, name, flags, entry_count);
          while (entry_count--) {
            uint32_t kind;
            uint32_t index;
            CHECK_RESULT(ReadU32Leb128(&kind, "kind"));
            CHECK_RESULT(ReadU32Leb128(&index, "index"));
            ComdatType comdat_type = static_cast<ComdatType>(kind);
            CALLBACK(OnComdatEntry, comdat_type, index);
          }
        }
        break;
      default:
        // Unknown subsection, skip it.
        state_.offset = subsection_end;
        break;
    }
    ERROR_UNLESS(state_.offset == subsection_end,
                 "unfinished sub-section (expected end: 0x%" PRIzx ")",
                 subsection_end);
  }
  CALLBACK0(EndLinkingSection);
  return Result::Ok;
}

Result BinaryReader::ReadTagType(Index* out_sig_index) {
  uint8_t attribute;
  CHECK_RESULT(ReadU8(&attribute, "tag attribute"));
  ERROR_UNLESS(attribute == 0, "tag attribute must be 0");
  CHECK_RESULT(ReadIndex(out_sig_index, "tag signature index"));
  return Result::Ok;
}

Result BinaryReader::ReadTagSection(Offset section_size) {
  CALLBACK(BeginTagSection, section_size);
  Index num_tags;
  CHECK_RESULT(ReadCount(&num_tags, "tag count"));
  CALLBACK(OnTagCount, num_tags);

  for (Index i = 0; i < num_tags; ++i) {
    Index tag_index = num_tag_imports_ + i;
    Index sig_index;
    CHECK_RESULT(ReadTagType(&sig_index));
    CALLBACK(OnTagType, tag_index, sig_index);
  }

  CALLBACK(EndTagSection);
  return Result::Ok;
}

Result BinaryReader::ReadCodeMetadataSection(std::string_view name,
                                             Offset section_size) {
  CALLBACK(BeginCodeMetadataSection, name, section_size);

  Index num_functions;
  CHECK_RESULT(ReadCount(&num_functions, "function count"));
  CALLBACK(OnCodeMetadataFuncCount, num_functions);

  Index last_function_index = kInvalidIndex;
  for (Index i = 0; i < num_functions; ++i) {
    Index function_index;
    CHECK_RESULT(ReadCount(&function_index, "function index"));
    ERROR_UNLESS(function_index >= num_func_imports_,
                 "function import can't have metadata (got %" PRIindex ")",
                 function_index);
    ERROR_UNLESS(function_index < NumTotalFuncs(),
                 "invalid function index: %" PRIindex, function_index);
    ERROR_UNLESS(function_index != last_function_index,
                 "duplicate function index: %" PRIindex, function_index);
    ERROR_UNLESS(last_function_index == kInvalidIndex ||
                     function_index > last_function_index,
                 "function index out of order: %" PRIindex, function_index);
    last_function_index = function_index;

    Index num_metadata;
    CHECK_RESULT(ReadCount(&num_metadata, "metadata instances count"));

    CALLBACK(OnCodeMetadataCount, function_index, num_metadata);

    Offset last_code_offset = kInvalidOffset;
    for (Index j = 0; j < num_metadata; ++j) {
      Offset code_offset;
      CHECK_RESULT(ReadOffset(&code_offset, "code offset"));
      ERROR_UNLESS(code_offset != last_code_offset,
                   "duplicate code offset: %" PRIzx, code_offset);
      ERROR_UNLESS(
          last_code_offset == kInvalidOffset || code_offset > last_code_offset,
          "code offset out of order: %" PRIzx, code_offset);
      last_code_offset = code_offset;

      Address data_size;
      const void* data;
      CHECK_RESULT(ReadBytes(&data, &data_size, "instance data"));
      CALLBACK(OnCodeMetadata, code_offset, data, data_size);
    }
  }

  CALLBACK(EndCodeMetadataSection);
  return Result::Ok;
}

Result BinaryReader::ReadCustomSection(Index section_index,
                                       Offset section_size) {
  std::string_view section_name;
  CHECK_RESULT(ReadStr(§ion_name, "section name"));
  CALLBACK(BeginCustomSection, section_index, section_size, section_name);
  ValueRestoreGuard<bool, &BinaryReader::reading_custom_section_> guard(this);
  reading_custom_section_ = true;

  {
    // Backtrack parser when scope ends
    ValueRestoreGuard<BinaryReaderDelegate::State, &BinaryReader::state_> guard(
        this);
    CHECK_RESULT(ReadGenericCustomSection(section_name, section_size));
  }

  if (options_.read_debug_names && section_name == WABT_BINARY_SECTION_NAME) {
    CHECK_RESULT(ReadNameSection(section_size));
    did_read_names_section_ = true;
  } else if (section_name == WABT_BINARY_SECTION_DYLINK0) {
    CHECK_RESULT(ReadDylink0Section(section_size));
  } else if (section_name == WABT_BINARY_SECTION_DYLINK) {
    CHECK_RESULT(ReadDylinkSection(section_size));
  } else if (section_name.rfind(WABT_BINARY_SECTION_RELOC, 0) == 0) {
    // Reloc sections always begin with "reloc."
    CHECK_RESULT(ReadRelocSection(section_size));
  } else if (section_name == WABT_BINARY_SECTION_TARGET_FEATURES) {
    CHECK_RESULT(ReadTargetFeaturesSections(section_size));
  } else if (section_name == WABT_BINARY_SECTION_LINKING) {
    CHECK_RESULT(ReadLinkingSection(section_size));
  } else if (options_.features.code_metadata_enabled() &&
             section_name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0) {
    std::string_view metadata_name = section_name;
    metadata_name.remove_prefix(sizeof(WABT_BINARY_SECTION_CODE_METADATA) - 1);
    CHECK_RESULT(ReadCodeMetadataSection(metadata_name, section_size));
  } else {
    // Skip. This is a generic custom section, and is handled above.
    state_.offset = read_end_;
  }
  CALLBACK0(EndCustomSection);
  return Result::Ok;
}

Result BinaryReader::ReadTypeSection(Offset section_size) {
  CALLBACK(BeginTypeSection, section_size);
  Index num_signatures;
  CHECK_RESULT(ReadCount(&num_signatures, "type count"));
  CALLBACK(OnTypeCount, num_signatures);

  for (Index i = 0; i < num_signatures; ++i) {
    Type form;
    if (options_.features.gc_enabled()) {
      CHECK_RESULT(ReadType(&form, "type form"));
    } else {
      uint8_t type;
      CHECK_RESULT(ReadU8(&type, "type form"));
      ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type);
      form = Type::Func;
    }

    switch (form) {
      case Type::Func: {
        Index num_params;
        CHECK_RESULT(ReadCount(&num_params, "function param count"));

        param_types_.resize(num_params);

        for (Index j = 0; j < num_params; ++j) {
          Type param_type;
          CHECK_RESULT(ReadType(¶m_type, "function param type"));
          ERROR_UNLESS(IsConcreteType(param_type),
                       "expected valid param type (got " PRItypecode ")",
                       WABT_PRINTF_TYPE_CODE(param_type));
          param_types_[j] = param_type;
        }

        Index num_results;
        CHECK_RESULT(ReadCount(&num_results, "function result count"));

        result_types_.resize(num_results);

        for (Index j = 0; j < num_results; ++j) {
          Type result_type;
          CHECK_RESULT(ReadType(&result_type, "function result type"));
          ERROR_UNLESS(IsConcreteType(result_type),
                       "expected valid result type (got " PRItypecode ")",
                       WABT_PRINTF_TYPE_CODE(result_type));
          result_types_[j] = result_type;
        }

        Type* param_types = num_params ? param_types_.data() : nullptr;
        Type* result_types = num_results ? result_types_.data() : nullptr;

        CALLBACK(OnFuncType, i, num_params, param_types, num_results,
                 result_types);
        break;
      }

      case Type::Struct: {
        ERROR_UNLESS(options_.features.gc_enabled(),
                     "invalid type form: struct not allowed");
        Index num_fields;
        CHECK_RESULT(ReadCount(&num_fields, "field count"));

        fields_.resize(num_fields);
        for (Index j = 0; j < num_fields; ++j) {
          CHECK_RESULT(ReadField(&fields_[j]));
        }

        CALLBACK(OnStructType, i, fields_.size(), fields_.data());
        break;
      }

      case Type::Array: {
        ERROR_UNLESS(options_.features.gc_enabled(),
                     "invalid type form: array not allowed");

        TypeMut field;
        CHECK_RESULT(ReadField(&field));
        CALLBACK(OnArrayType, i, field);
        break;
      };

      default:
        PrintError("unexpected type form (got " PRItypecode ")",
                   WABT_PRINTF_TYPE_CODE(form));
        return Result::Error;
    }
  }
  CALLBACK0(EndTypeSection);
  return Result::Ok;
}

Result BinaryReader::ReadImportSection(Offset section_size) {
  CALLBACK(BeginImportSection, section_size);
  Index num_imports;
  CHECK_RESULT(ReadCount(&num_imports, "import count"));
  CALLBACK(OnImportCount, num_imports);
  for (Index i = 0; i < num_imports; ++i) {
    std::string_view module_name;
    CHECK_RESULT(ReadStr(&module_name, "import module name"));
    std::string_view field_name;
    CHECK_RESULT(ReadStr(&field_name, "import field name"));

    uint8_t kind;
    CHECK_RESULT(ReadU8(&kind, "import kind"));
    CALLBACK(OnImport, i, static_cast<ExternalKind>(kind), module_name,
             field_name);
    switch (static_cast<ExternalKind>(kind)) {
      case ExternalKind::Func: {
        Index sig_index;
        CHECK_RESULT(ReadIndex(&sig_index, "import signature index"));
        CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_,
                 sig_index);
        num_func_imports_++;
        break;
      }

      case ExternalKind::Table: {
        Type elem_type;
        Limits elem_limits;
        CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
        CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_,
                 elem_type, &elem_limits);
        num_table_imports_++;
        break;
      }

      case ExternalKind::Memory: {
        Limits page_limits;
        uint32_t page_size;
        CHECK_RESULT(ReadMemory(&page_limits, &page_size));
        CALLBACK(OnImportMemory, i, module_name, field_name,
                 num_memory_imports_, &page_limits, page_size);
        num_memory_imports_++;
        break;
      }

      case ExternalKind::Global: {
        Type type;
        bool mutable_;
        CHECK_RESULT(ReadGlobalHeader(&type, &mutable_));
        CALLBACK(OnImportGlobal, i, module_name, field_name,
                 num_global_imports_, type, mutable_);
        num_global_imports_++;
        break;
      }

      case ExternalKind::Tag: {
        ERROR_UNLESS(options_.features.exceptions_enabled(),
                     "invalid import tag kind: exceptions not allowed");
        Index sig_index;
        CHECK_RESULT(ReadTagType(&sig_index));
        CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_,
                 sig_index);
        num_tag_imports_++;
        break;
      }

      default:
        PrintError("malformed import kind: %d", kind);
        return Result::Error;
    }
  }

  CALLBACK0(EndImportSection);
  return Result::Ok;
}

Result BinaryReader::ReadFunctionSection(Offset section_size) {
  CALLBACK(BeginFunctionSection, section_size);
  CHECK_RESULT(
      ReadCount(&num_function_signatures_, "function signature count"));
  CALLBACK(OnFunctionCount, num_function_signatures_);
  for (Index i = 0; i < num_function_signatures_; ++i) {
    Index func_index = num_func_imports_ + i;
    Index sig_index;
    CHECK_RESULT(ReadIndex(&sig_index, "function signature index"));
    CALLBACK(OnFunction, func_index, sig_index);
  }
  CALLBACK0(EndFunctionSection);
  return Result::Ok;
}

Result BinaryReader::ReadTableSection(Offset section_size) {
  CALLBACK(BeginTableSection, section_size);
  Index num_tables;
  CHECK_RESULT(ReadCount(&num_tables, "table count"));
  CALLBACK(OnTableCount, num_tables);
  for (Index i = 0; i < num_tables; ++i) {
    Index table_index = num_table_imports_ + i;
    Type elem_type;
    Limits elem_limits;
    CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
    CALLBACK(OnTable, table_index, elem_type, &elem_limits);
  }
  CALLBACK0(EndTableSection);
  return Result::Ok;
}

Result BinaryReader::ReadMemorySection(Offset section_size) {
  CALLBACK(BeginMemorySection, section_size);
  Index num_memories;
  CHECK_RESULT(ReadCount(&num_memories, "memory count"));
  CALLBACK(OnMemoryCount, num_memories);
  for (Index i = 0; i < num_memories; ++i) {
    Index memory_index = num_memory_imports_ + i;
    Limits page_limits;
    uint32_t page_size;
    CHECK_RESULT(ReadMemory(&page_limits, &page_size));
    CALLBACK(OnMemory, memory_index, &page_limits, page_size);
  }
  CALLBACK0(EndMemorySection);
  return Result::Ok;
}

Result BinaryReader::ReadGlobalSection(Offset section_size) {
  CALLBACK(BeginGlobalSection, section_size);
  Index num_globals;
  CHECK_RESULT(ReadCount(&num_globals, "global count"));
  CALLBACK(OnGlobalCount, num_globals);
  for (Index i = 0; i < num_globals; ++i) {
    Index global_index = num_global_imports_ + i;
    Type global_type;
    bool mutable_;
    CHECK_RESULT(ReadGlobalHeader(&global_type, &mutable_));
    CALLBACK(BeginGlobal, global_index, global_type, mutable_);
    CALLBACK(BeginGlobalInitExpr, global_index);
    CHECK_RESULT(ReadInitExpr(global_index));
    CALLBACK(EndGlobalInitExpr, global_index);
    CALLBACK(EndGlobal, global_index);
  }
  CALLBACK0(EndGlobalSection);
  return Result::Ok;
}

Result BinaryReader::ReadExportSection(Offset section_size) {
  CALLBACK(BeginExportSection, section_size);
  Index num_exports;
  CHECK_RESULT(ReadCount(&num_exports, "export count"));
  CALLBACK(OnExportCount, num_exports);
  for (Index i = 0; i < num_exports; ++i) {
    std::string_view name;
    CHECK_RESULT(ReadStr(&name, "export item name"));

    ExternalKind kind;
    CHECK_RESULT(ReadExternalKind(&kind, "export kind"));

    Index item_index;
    CHECK_RESULT(ReadIndex(&item_index, "export item index"));
    if (kind == ExternalKind::Tag) {
      ERROR_UNLESS(options_.features.exceptions_enabled(),
                   "invalid export tag kind: exceptions not allowed");
    }

    CALLBACK(OnExport, i, static_cast<ExternalKind>(kind), item_index, name);
  }
  CALLBACK0(EndExportSection);
  return Result::Ok;
}

Result BinaryReader::ReadStartSection(Offset section_size) {
  CALLBACK(BeginStartSection, section_size);
  Index func_index;
  CHECK_RESULT(ReadIndex(&func_index, "start function index"));
  CALLBACK(OnStartFunction, func_index);
  CALLBACK0(EndStartSection);
  return Result::Ok;
}

Result BinaryReader::ReadElemSection(Offset section_size) {
  CALLBACK(BeginElemSection, section_size);
  Index num_elem_segments;
  CHECK_RESULT(ReadCount(&num_elem_segments, "elem segment count"));
  CALLBACK(OnElemSegmentCount, num_elem_segments);
  for (Index i = 0; i < num_elem_segments; ++i) {
    uint32_t flags;
    CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags"));
    ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags);
    Index table_index(0);
    if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
      CHECK_RESULT(ReadIndex(&table_index, "elem segment table index"));
    }
    Type elem_type = Type::FuncRef;

    CALLBACK(BeginElemSegment, i, table_index, flags);

    if (!(flags & SegPassive)) {
      CALLBACK(BeginElemSegmentInitExpr, i);
      CHECK_RESULT(ReadInitExpr(i));
      CALLBACK(EndElemSegmentInitExpr, i);
    }

    // For backwards compat we support not declaring the element kind.
    if (flags & (SegPassive | SegExplicitIndex)) {
      if (flags & SegUseElemExprs) {
        CHECK_RESULT(ReadRefType(&elem_type, "table elem type"));
      } else {
        ExternalKind kind;
        CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
        ERROR_UNLESS(kind == ExternalKind::Func,
                     "segment elem type must be func (%s)",
                     elem_type.GetName().c_str());
        elem_type = Type::FuncRef;
      }
    }

    CALLBACK(OnElemSegmentElemType, i, elem_type);

    Index num_elem_exprs;
    CHECK_RESULT(ReadCount(&num_elem_exprs, "elem count"));

    CALLBACK(OnElemSegmentElemExprCount, i, num_elem_exprs);
    for (Index j = 0; j < num_elem_exprs; ++j) {
      CALLBACK(BeginElemExpr, i, j);
      if (flags & SegUseElemExprs) {
        CHECK_RESULT(ReadInitExpr(j));
      } else {
        Index func_index;
        CHECK_RESULT(ReadIndex(&func_index, "elem expr func index"));
        CALLBACK(OnOpcode, Opcode::RefFunc);
        CALLBACK(OnRefFuncExpr, func_index);
        CALLBACK(OnOpcodeUint32, func_index);
        CALLBACK0(OnEndExpr);
      }
      CALLBACK(EndElemExpr, i, j);
    }
    CALLBACK(EndElemSegment, i);
  }
  CALLBACK0(EndElemSection);
  return Result::Ok;
}

Result BinaryReader::ReadCodeSection(Offset section_size) {
  CALLBACK(BeginCodeSection, section_size);
  CHECK_RESULT(ReadCount(&num_function_bodies_, "function body count"));
  ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
               "function signature count != function body count");
  CALLBACK(OnFunctionBodyCount, num_function_bodies_);
  for (Index i = 0; i < num_function_bodies_; ++i) {
    Index func_index = num_func_imports_ + i;
    Offset func_offset = state_.offset;
    state_.offset = func_offset;
    uint32_t body_size;
    CHECK_RESULT(ReadU32Leb128(&body_size, "function body size"));
    Offset body_start_offset = state_.offset;
    Offset end_offset = body_start_offset + body_size;
    CALLBACK(BeginFunctionBody, func_index, body_size);

    uint64_t total_locals = 0;
    Index num_local_decls;
    CHECK_RESULT(ReadCount(&num_local_decls, "local declaration count"));
    CALLBACK(OnLocalDeclCount, num_local_decls);
    for (Index k = 0; k < num_local_decls; ++k) {
      Index num_local_types;
      CHECK_RESULT(ReadIndex(&num_local_types, "local type count"));
      total_locals += num_local_types;
      ERROR_UNLESS(total_locals <= UINT32_MAX, "local count must be <= 0x%x",
                   UINT32_MAX);
      Type local_type;
      CHECK_RESULT(ReadType(&local_type, "local type"));
      ERROR_UNLESS(IsConcreteType(local_type), "expected valid local type");
      CALLBACK(OnLocalDecl, k, num_local_types, local_type);
    }
    CALLBACK(EndLocalDecls);

    if (options_.skip_function_bodies) {
      state_.offset = end_offset;
    } else {
      CHECK_RESULT(ReadFunctionBody(end_offset));
    }

    CALLBACK(EndFunctionBody, func_index);
  }
  CALLBACK0(EndCodeSection);
  return Result::Ok;
}

Result BinaryReader::ReadDataSection(Offset section_size) {
  CALLBACK(BeginDataSection, section_size);
  CHECK_RESULT(ReadCount(&num_data_segments_, "data segment count"));
  CALLBACK(OnDataSegmentCount, num_data_segments_);
  // If the DataCount section is not present, then data_count_ will be invalid.
  ERROR_UNLESS(
      data_count_ == kInvalidIndex || data_count_ == num_data_segments_,
      "data segment count does not equal count in DataCount section");
  for (Index i = 0; i < num_data_segments_; ++i) {
    uint32_t flags;
    CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags"));
    ERROR_IF(flags != 0 && !options_.features.bulk_memory_enabled(),
             "invalid memory index %d: bulk memory not allowed", flags);
    ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags);
    Index memory_index(0);
    if (flags & SegExplicitIndex) {
      CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index"));
    }
    CALLBACK(BeginDataSegment, i, memory_index, flags);
    if (!(flags & SegPassive)) {
      CALLBACK(BeginDataSegmentInitExpr, i);
      CHECK_RESULT(ReadInitExpr(i));
      CALLBACK(EndDataSegmentInitExpr, i);
    }

    Address data_size;
    const void* data;
    CHECK_RESULT(ReadBytes(&data, &data_size, "data segment data"));
    CALLBACK(OnDataSegmentData, i, data, data_size);
    CALLBACK(EndDataSegment, i);
  }
  CALLBACK0(EndDataSection);
  return Result::Ok;
}

Result BinaryReader::ReadDataCountSection(Offset section_size) {
  CALLBACK(BeginDataCountSection, section_size);
  Index data_count;
  CHECK_RESULT(ReadIndex(&data_count, "data count"));
  CALLBACK(OnDataCount, data_count);
  CALLBACK0(EndDataCountSection);
  data_count_ = data_count;
  return Result::Ok;
}

Result BinaryReader::ReadSections(const ReadSectionsOptions& options) {
  Result result = Result::Ok;
  Index section_index = 0;
  bool seen_section_code[static_cast<int>(BinarySection::Last) + 1] = {false};

  for (; state_.offset < state_.size; ++section_index) {
    uint8_t section_code;
    Offset section_size;
    CHECK_RESULT(ReadU8(§ion_code, "section code"));
    CHECK_RESULT(ReadOffset(§ion_size, "section size"));
    ReadEndRestoreGuard guard(this);
    read_end_ = state_.offset + section_size;
    if (section_code >= kBinarySectionCount) {
      PrintError("invalid section code: %u", section_code);
      if (options.stop_on_first_error) {
        return Result::Error;
      }
      // If we don't have to stop on first error, continue reading
      // sections, because although we could not understand the
      // current section, we can continue and correctly parse
      // subsequent sections, so we can give back as much information
      // as we can understand.
      result = Result::Error;
      state_.offset = read_end_;
      continue;
    }

    BinarySection section = static_cast<BinarySection>(section_code);
    if (section != BinarySection::Custom) {
      if (seen_section_code[section_code]) {
        PrintError("multiple %s sections", GetSectionName(section));
        return Result::Error;
      }
      seen_section_code[section_code] = true;
    }

    ERROR_UNLESS(read_end_ <= state_.size,
                 "invalid section size: extends past end");

    ERROR_UNLESS(
        last_known_section_ == BinarySection::Invalid ||
            section == BinarySection::Custom ||
            GetSectionOrder(section) > GetSectionOrder(last_known_section_),
        "section %s out of order", GetSectionName(section));

    ERROR_UNLESS(!did_read_names_section_ || section == BinarySection::Custom,
                 "%s section can not occur after Name section",
                 GetSectionName(section));

    CALLBACK(BeginSection, section_index, section, section_size);

    bool stop_on_first_error = options_.stop_on_first_error;
    Result section_result = Result::Error;
    switch (section) {
      case BinarySection::Custom:
        section_result = ReadCustomSection(section_index, section_size);
        if (options_.fail_on_custom_section_error) {
          result |= section_result;
        } else {
          stop_on_first_error = false;
        }
        break;
      case BinarySection::Type:
        section_result = ReadTypeSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Import:
        section_result = ReadImportSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Function:
        section_result = ReadFunctionSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Table:
        section_result = ReadTableSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Memory:
        section_result = ReadMemorySection(section_size);
        result |= section_result;
        break;
      case BinarySection::Global:
        section_result = ReadGlobalSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Export:
        section_result = ReadExportSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Start:
        section_result = ReadStartSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Elem:
        section_result = ReadElemSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Code:
        section_result = ReadCodeSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Data:
        section_result = ReadDataSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Tag:
        ERROR_UNLESS(options_.features.exceptions_enabled(),
                     "invalid section code: %u",
                     static_cast<unsigned int>(section));
        section_result = ReadTagSection(section_size);
        result |= section_result;
        break;
      case BinarySection::DataCount:
        ERROR_UNLESS(options_.features.bulk_memory_enabled(),
                     "invalid section code: %u",
                     static_cast<unsigned int>(section));
        section_result = ReadDataCountSection(section_size);
        result |= section_result;
        break;
      case BinarySection::Invalid:
        WABT_UNREACHABLE;
    }

    if (Succeeded(section_result) && state_.offset != read_end_) {
      PrintError("unfinished section (expected end: 0x%" PRIzx ")", read_end_);
      section_result = Result::Error;
      result |= section_result;
    }

    if (Failed(section_result)) {
      if (stop_on_first_error) {
        return Result::Error;
      }

      // If we're continuing after failing to read this section, move the
      // offset to the expected section end. This way we may be able to read
      // further sections.
      state_.offset = read_end_;
    }

    if (section != BinarySection::Custom) {
      last_known_section_ = section;
    }
  }

  return result;
}

Result BinaryReader::ReadModule(const ReadModuleOptions& options) {
  uint32_t magic = 0;
  CHECK_RESULT(ReadU32(&magic, "magic"));
  ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value");
  uint32_t version = 0;
  CHECK_RESULT(ReadU32(&version, "version"));
  ERROR_UNLESS(version == WABT_BINARY_VERSION,
               "bad wasm file version: %#x (expected %#x)", version,
               WABT_BINARY_VERSION);

  CALLBACK(BeginModule, version);
  CHECK_RESULT(ReadSections(ReadSectionsOptions{options.stop_on_first_error}));
  // This is checked in ReadCodeSection, but it must be checked at the end too,
  // in case the code section was omitted.
  ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
               "function signature count != function body count");
  // This is checked in ReadDataSection, but it must be checked at the end too,
  // in case the data section was omitted.
  ERROR_IF(num_data_segments_ == 0 && data_count_ != kInvalidIndex &&
               data_count_ != 0,
           "Data section missing but DataCount non-zero");
  CALLBACK0(EndModule);

  return Result::Ok;
}

}  // end anonymous namespace

Result ReadBinary(const void* data,
                  size_t size,
                  BinaryReaderDelegate* delegate,
                  const ReadBinaryOptions& options) {
  BinaryReader reader(data, size, delegate, options);
  return reader.ReadModule(
      BinaryReader::ReadModuleOptions{options.stop_on_first_error});
}

}  // namespace wabt

Messung V0.5 in Prozent
C=99 H=93 G=95

¤ Dauer der Verarbeitung: 0.44 Sekunden  (vorverarbeitet am  2026-04-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.