/*
* 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;
--> --------------------
--> maximum size reached
--> --------------------