blob: 435d4d5db437c7ecd1852d68777962d4d859001d [file] [log] [blame] [edit]
/*
* 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 "binary-reader.h"
#include <assert.h>
#include <inttypes.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include "binary.h"
#include "binary-reader-logging.h"
#include "config.h"
#include "stream.h"
#if HAVE_ALLOCA
#include <alloca.h>
#endif
namespace wabt {
namespace {
#define CALLBACK0(member) \
RAISE_ERROR_UNLESS(WABT_SUCCEEDED(ctx->reader->member()), \
#member " callback failed")
#define CALLBACK(member, ...) \
RAISE_ERROR_UNLESS(WABT_SUCCEEDED(ctx->reader->member(__VA_ARGS__)), \
#member " callback failed")
#define RAISE_ERROR(...) raise_error(ctx, __VA_ARGS__)
#define RAISE_ERROR_UNLESS(cond, ...) \
if (!(cond)) \
RAISE_ERROR(__VA_ARGS__);
struct Context {
size_t read_end = 0; /* Either the section end or data_size. */
BinaryReader* reader = nullptr;
BinaryReader::State state;
jmp_buf error_jmp_buf;
TypeVector param_types;
std::vector<uint32_t> target_depths;
const ReadBinaryOptions* options = nullptr;
BinarySection last_known_section = BinarySection::Invalid;
uint32_t num_signatures = 0;
uint32_t num_imports = 0;
uint32_t num_func_imports = 0;
uint32_t num_table_imports = 0;
uint32_t num_memory_imports = 0;
uint32_t num_global_imports = 0;
uint32_t num_function_signatures = 0;
uint32_t num_tables = 0;
uint32_t num_memories = 0;
uint32_t num_globals = 0;
uint32_t num_exports = 0;
uint32_t num_function_bodies = 0;
};
} // namespace
static void WABT_PRINTF_FORMAT(2, 3)
raise_error(Context* ctx, const char* format, ...) {
WABT_SNPRINTF_ALLOCA(buffer, length, format);
bool handled = ctx->reader->OnError(buffer);
if (!handled) {
/* Not great to just print, but we don't want to eat the error either. */
fprintf(stderr, "*ERROR*: @0x%08zx: %s\n", ctx->state.offset, buffer);
}
longjmp(ctx->error_jmp_buf, 1);
}
#define IN_SIZE(type) \
if (ctx->state.offset + sizeof(type) > ctx->read_end) { \
RAISE_ERROR("unable to read " #type ": %s", desc); \
} \
memcpy(out_value, ctx->state.data + ctx->state.offset, sizeof(type)); \
ctx->state.offset += sizeof(type)
static void in_u8(Context* ctx, uint8_t* out_value, const char* desc) {
IN_SIZE(uint8_t);
}
static void in_u32(Context* ctx, uint32_t* out_value, const char* desc) {
IN_SIZE(uint32_t);
}
static void in_f32(Context* ctx, uint32_t* out_value, const char* desc) {
IN_SIZE(float);
}
static void in_f64(Context* ctx, uint64_t* out_value, const char* desc) {
IN_SIZE(double);
}
#undef IN_SIZE
#define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift))
#define LEB128_1(type) (BYTE_AT(type, 0, 0))
#define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type))
#define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type))
#define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type))
#define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type))
#define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type))
#define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type))
#define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type))
#define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type))
#define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type))
#define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit))
#define SIGN_EXTEND(type, value, sign_bit) \
(static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \
SHIFT_AMOUNT(type, sign_bit))
size_t read_u32_leb128(const uint8_t* p,
const uint8_t* end,
uint32_t* out_value) {
if (p < end && (p[0] & 0x80) == 0) {
*out_value = LEB128_1(uint32_t);
return 1;
} else if (p + 1 < end && (p[1] & 0x80) == 0) {
*out_value = LEB128_2(uint32_t);
return 2;
} else if (p + 2 < end && (p[2] & 0x80) == 0) {
*out_value = LEB128_3(uint32_t);
return 3;
} else if (p + 3 < end && (p[3] & 0x80) == 0) {
*out_value = LEB128_4(uint32_t);
return 4;
} else if (p + 4 < end && (p[4] & 0x80) == 0) {
/* the top bits set represent values > 32 bits */
if (p[4] & 0xf0)
return 0;
*out_value = LEB128_5(uint32_t);
return 5;
} else {
/* past the end */
*out_value = 0;
return 0;
}
}
static void in_u32_leb128(Context* ctx, uint32_t* out_value, const char* desc) {
const uint8_t* p = ctx->state.data + ctx->state.offset;
const uint8_t* end = ctx->state.data + ctx->read_end;
size_t bytes_read = read_u32_leb128(p, end, out_value);
if (!bytes_read)
RAISE_ERROR("unable to read u32 leb128: %s", desc);
ctx->state.offset += bytes_read;
}
size_t read_i32_leb128(const uint8_t* p,
const uint8_t* end,
uint32_t* out_value) {
if (p < end && (p[0] & 0x80) == 0) {
uint32_t result = LEB128_1(uint32_t);
*out_value = SIGN_EXTEND(int32_t, result, 6);
return 1;
} else if (p + 1 < end && (p[1] & 0x80) == 0) {
uint32_t result = LEB128_2(uint32_t);
*out_value = SIGN_EXTEND(int32_t, result, 13);
return 2;
} else if (p + 2 < end && (p[2] & 0x80) == 0) {
uint32_t result = LEB128_3(uint32_t);
*out_value = SIGN_EXTEND(int32_t, result, 20);
return 3;
} else if (p + 3 < end && (p[3] & 0x80) == 0) {
uint32_t result = LEB128_4(uint32_t);
*out_value = SIGN_EXTEND(int32_t, result, 27);
return 4;
} else if (p + 4 < end && (p[4] & 0x80) == 0) {
/* the top bits should be a sign-extension of the sign bit */
bool sign_bit_set = (p[4] & 0x8);
int top_bits = p[4] & 0xf0;
if ((sign_bit_set && top_bits != 0x70) ||
(!sign_bit_set && top_bits != 0)) {
return 0;
}
uint32_t result = LEB128_5(uint32_t);
*out_value = result;
return 5;
} else {
/* past the end */
return 0;
}
}
static void in_i32_leb128(Context* ctx, uint32_t* out_value, const char* desc) {
const uint8_t* p = ctx->state.data + ctx->state.offset;
const uint8_t* end = ctx->state.data + ctx->read_end;
size_t bytes_read = read_i32_leb128(p, end, out_value);
if (!bytes_read)
RAISE_ERROR("unable to read i32 leb128: %s", desc);
ctx->state.offset += bytes_read;
}
static void in_i64_leb128(Context* ctx, uint64_t* out_value, const char* desc) {
const uint8_t* p = ctx->state.data + ctx->state.offset;
const uint8_t* end = ctx->state.data + ctx->read_end;
if (p < end && (p[0] & 0x80) == 0) {
uint64_t result = LEB128_1(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 6);
ctx->state.offset += 1;
} else if (p + 1 < end && (p[1] & 0x80) == 0) {
uint64_t result = LEB128_2(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 13);
ctx->state.offset += 2;
} else if (p + 2 < end && (p[2] & 0x80) == 0) {
uint64_t result = LEB128_3(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 20);
ctx->state.offset += 3;
} else if (p + 3 < end && (p[3] & 0x80) == 0) {
uint64_t result = LEB128_4(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 27);
ctx->state.offset += 4;
} else if (p + 4 < end && (p[4] & 0x80) == 0) {
uint64_t result = LEB128_5(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 34);
ctx->state.offset += 5;
} else if (p + 5 < end && (p[5] & 0x80) == 0) {
uint64_t result = LEB128_6(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 41);
ctx->state.offset += 6;
} else if (p + 6 < end && (p[6] & 0x80) == 0) {
uint64_t result = LEB128_7(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 48);
ctx->state.offset += 7;
} else if (p + 7 < end && (p[7] & 0x80) == 0) {
uint64_t result = LEB128_8(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 55);
ctx->state.offset += 8;
} else if (p + 8 < end && (p[8] & 0x80) == 0) {
uint64_t result = LEB128_9(uint64_t);
*out_value = SIGN_EXTEND(int64_t, result, 62);
ctx->state.offset += 9;
} else if (p + 9 < end && (p[9] & 0x80) == 0) {
/* the top bits should be a sign-extension of the sign bit */
bool sign_bit_set = (p[9] & 0x1);
int top_bits = p[9] & 0xfe;
if ((sign_bit_set && top_bits != 0x7e) ||
(!sign_bit_set && top_bits != 0)) {
RAISE_ERROR("invalid i64 leb128: %s", desc);
}
uint64_t result = LEB128_10(uint64_t);
*out_value = result;
ctx->state.offset += 10;
} else {
/* past the end */
RAISE_ERROR("unable to read i64 leb128: %s", desc);
}
}
#undef BYTE_AT
#undef LEB128_1
#undef LEB128_2
#undef LEB128_3
#undef LEB128_4
#undef LEB128_5
#undef LEB128_6
#undef LEB128_7
#undef LEB128_8
#undef LEB128_9
#undef LEB128_10
#undef SHIFT_AMOUNT
#undef SIGN_EXTEND
static void in_type(Context* ctx, Type* out_value, const char* desc) {
uint32_t type = 0;
in_i32_leb128(ctx, &type, desc);
/* Must be in the vs7 range: [-128, 127). */
if (static_cast<int32_t>(type) < -128 || static_cast<int32_t>(type) > 127)
RAISE_ERROR("invalid type: %d", type);
*out_value = static_cast<Type>(type);
}
static void in_str(Context* ctx, StringSlice* out_str, const char* desc) {
uint32_t str_len = 0;
in_u32_leb128(ctx, &str_len, "string length");
if (ctx->state.offset + str_len > ctx->read_end)
RAISE_ERROR("unable to read string: %s", desc);
out_str->start =
reinterpret_cast<const char*>(ctx->state.data) + ctx->state.offset;
out_str->length = str_len;
ctx->state.offset += str_len;
}
static void in_bytes(Context* ctx,
const void** out_data,
uint32_t* out_data_size,
const char* desc) {
uint32_t data_size = 0;
in_u32_leb128(ctx, &data_size, "data size");
if (ctx->state.offset + data_size > ctx->read_end)
RAISE_ERROR("unable to read data: %s", desc);
*out_data =
static_cast<const uint8_t*>(ctx->state.data) + ctx->state.offset;
*out_data_size = data_size;
ctx->state.offset += data_size;
}
static bool is_valid_external_kind(uint8_t kind) {
return kind < kExternalKindCount;
}
static bool is_concrete_type(Type type) {
switch (type) {
case Type::I32:
case Type::I64:
case Type::F32:
case Type::F64:
return true;
default:
return false;
}
}
static bool is_inline_sig_type(Type type) {
return is_concrete_type(type) || type == Type::Void;
}
static uint32_t num_total_funcs(Context* ctx) {
return ctx->num_func_imports + ctx->num_function_signatures;
}
static uint32_t num_total_tables(Context* ctx) {
return ctx->num_table_imports + ctx->num_tables;
}
static uint32_t num_total_memories(Context* ctx) {
return ctx->num_memory_imports + ctx->num_memories;
}
static uint32_t num_total_globals(Context* ctx) {
return ctx->num_global_imports + ctx->num_globals;
}
static void read_init_expr(Context* ctx, uint32_t index) {
uint8_t opcode;
in_u8(ctx, &opcode, "opcode");
switch (static_cast<Opcode>(opcode)) {
case Opcode::I32Const: {
uint32_t value = 0;
in_i32_leb128(ctx, &value, "init_expr i32.const value");
CALLBACK(OnInitExprI32ConstExpr, index, value);
break;
}
case Opcode::I64Const: {
uint64_t value = 0;
in_i64_leb128(ctx, &value, "init_expr i64.const value");
CALLBACK(OnInitExprI64ConstExpr, index, value);
break;
}
case Opcode::F32Const: {
uint32_t value_bits = 0;
in_f32(ctx, &value_bits, "init_expr f32.const value");
CALLBACK(OnInitExprF32ConstExpr, index, value_bits);
break;
}
case Opcode::F64Const: {
uint64_t value_bits = 0;
in_f64(ctx, &value_bits, "init_expr f64.const value");
CALLBACK(OnInitExprF64ConstExpr, index, value_bits);
break;
}
case Opcode::GetGlobal: {
uint32_t global_index;
in_u32_leb128(ctx, &global_index, "init_expr get_global index");
CALLBACK(OnInitExprGetGlobalExpr, index, global_index);
break;
}
case Opcode::End:
return;
default:
RAISE_ERROR("unexpected opcode in initializer expression: %d (0x%x)",
opcode, opcode);
break;
}
in_u8(ctx, &opcode, "opcode");
RAISE_ERROR_UNLESS(static_cast<Opcode>(opcode) == Opcode::End,
"expected END opcode after initializer expression");
}
static void read_table(Context* ctx,
Type* out_elem_type,
Limits* out_elem_limits) {
in_type(ctx, out_elem_type, "table elem type");
RAISE_ERROR_UNLESS(*out_elem_type == Type::Anyfunc,
"table elem type must by anyfunc");
uint32_t flags;
uint32_t initial;
uint32_t max = 0;
in_u32_leb128(ctx, &flags, "table flags");
in_u32_leb128(ctx, &initial, "table initial elem count");
bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
if (has_max) {
in_u32_leb128(ctx, &max, "table max elem count");
RAISE_ERROR_UNLESS(initial <= max,
"table initial elem count must be <= max elem count");
}
out_elem_limits->has_max = has_max;
out_elem_limits->initial = initial;
out_elem_limits->max = max;
}
static void read_memory(Context* ctx, Limits* out_page_limits) {
uint32_t flags;
uint32_t initial;
uint32_t max = 0;
in_u32_leb128(ctx, &flags, "memory flags");
in_u32_leb128(ctx, &initial, "memory initial page count");
bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
RAISE_ERROR_UNLESS(initial <= WABT_MAX_PAGES, "invalid memory initial size");
if (has_max) {
in_u32_leb128(ctx, &max, "memory max page count");
RAISE_ERROR_UNLESS(max <= WABT_MAX_PAGES, "invalid memory max size");
RAISE_ERROR_UNLESS(initial <= max,
"memory initial size must be <= max size");
}
out_page_limits->has_max = has_max;
out_page_limits->initial = initial;
out_page_limits->max = max;
}
static void read_global_header(Context* ctx,
Type* out_type,
bool* out_mutable) {
Type global_type;
uint8_t mutable_;
in_type(ctx, &global_type, "global type");
RAISE_ERROR_UNLESS(is_concrete_type(global_type),
"invalid global type: %#x", static_cast<int>(global_type));
in_u8(ctx, &mutable_, "global mutability");
RAISE_ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1");
*out_type = global_type;
*out_mutable = mutable_;
}
static void read_function_body(Context* ctx, uint32_t end_offset) {
bool seen_end_opcode = false;
while (ctx->state.offset < end_offset) {
uint8_t opcode_u8;
in_u8(ctx, &opcode_u8, "opcode");
Opcode opcode = static_cast<Opcode>(opcode_u8);
CALLBACK(OnOpcode, opcode);
switch (opcode) {
case Opcode::Unreachable:
CALLBACK0(OnUnreachableExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Block: {
Type sig_type;
in_type(ctx, &sig_type, "block signature type");
RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type),
"expected valid block signature type");
uint32_t num_types = sig_type == Type::Void ? 0 : 1;
CALLBACK(OnBlockExpr, num_types, &sig_type);
CALLBACK(OnOpcodeBlockSig, num_types, &sig_type);
break;
}
case Opcode::Loop: {
Type sig_type;
in_type(ctx, &sig_type, "loop signature type");
RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type),
"expected valid block signature type");
uint32_t num_types = sig_type == Type::Void ? 0 : 1;
CALLBACK(OnLoopExpr, num_types, &sig_type);
CALLBACK(OnOpcodeBlockSig, num_types, &sig_type);
break;
}
case Opcode::If: {
Type sig_type;
in_type(ctx, &sig_type, "if signature type");
RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type),
"expected valid block signature type");
uint32_t num_types = sig_type == Type::Void ? 0 : 1;
CALLBACK(OnIfExpr, num_types, &sig_type);
CALLBACK(OnOpcodeBlockSig, num_types, &sig_type);
break;
}
case Opcode::Else:
CALLBACK0(OnElseExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Select:
CALLBACK0(OnSelectExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Br: {
uint32_t depth;
in_u32_leb128(ctx, &depth, "br depth");
CALLBACK(OnBrExpr, depth);
CALLBACK(OnOpcodeUint32, depth);
break;
}
case Opcode::BrIf: {
uint32_t depth;
in_u32_leb128(ctx, &depth, "br_if depth");
CALLBACK(OnBrIfExpr, depth);
CALLBACK(OnOpcodeUint32, depth);
break;
}
case Opcode::BrTable: {
uint32_t num_targets;
in_u32_leb128(ctx, &num_targets, "br_table target count");
ctx->target_depths.resize(num_targets);
for (uint32_t i = 0; i < num_targets; ++i) {
uint32_t target_depth;
in_u32_leb128(ctx, &target_depth, "br_table target depth");
ctx->target_depths[i] = target_depth;
}
uint32_t default_target_depth;
in_u32_leb128(ctx, &default_target_depth,
"br_table default target depth");
uint32_t* target_depths =
num_targets ? ctx->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:
if (ctx->state.offset == end_offset) {
seen_end_opcode = true;
CALLBACK0(OnEndFunc);
} else {
CALLBACK0(OnEndExpr);
}
break;
case Opcode::I32Const: {
uint32_t value = 0;
in_i32_leb128(ctx, &value, "i32.const value");
CALLBACK(OnI32ConstExpr, value);
CALLBACK(OnOpcodeUint32, value);
break;
}
case Opcode::I64Const: {
uint64_t value = 0;
in_i64_leb128(ctx, &value, "i64.const value");
CALLBACK(OnI64ConstExpr, value);
CALLBACK(OnOpcodeUint64, value);
break;
}
case Opcode::F32Const: {
uint32_t value_bits = 0;
in_f32(ctx, &value_bits, "f32.const value");
CALLBACK(OnF32ConstExpr, value_bits);
CALLBACK(OnOpcodeF32, value_bits);
break;
}
case Opcode::F64Const: {
uint64_t value_bits = 0;
in_f64(ctx, &value_bits, "f64.const value");
CALLBACK(OnF64ConstExpr, value_bits);
CALLBACK(OnOpcodeF64, value_bits);
break;
}
case Opcode::GetGlobal: {
uint32_t global_index;
in_u32_leb128(ctx, &global_index, "get_global global index");
CALLBACK(OnGetGlobalExpr, global_index);
CALLBACK(OnOpcodeUint32, global_index);
break;
}
case Opcode::GetLocal: {
uint32_t local_index;
in_u32_leb128(ctx, &local_index, "get_local local index");
CALLBACK(OnGetLocalExpr, local_index);
CALLBACK(OnOpcodeUint32, local_index);
break;
}
case Opcode::SetGlobal: {
uint32_t global_index;
in_u32_leb128(ctx, &global_index, "set_global global index");
CALLBACK(OnSetGlobalExpr, global_index);
CALLBACK(OnOpcodeUint32, global_index);
break;
}
case Opcode::SetLocal: {
uint32_t local_index;
in_u32_leb128(ctx, &local_index, "set_local local index");
CALLBACK(OnSetLocalExpr, local_index);
CALLBACK(OnOpcodeUint32, local_index);
break;
}
case Opcode::Call: {
uint32_t func_index;
in_u32_leb128(ctx, &func_index, "call function index");
RAISE_ERROR_UNLESS(func_index < num_total_funcs(ctx),
"invalid call function index");
CALLBACK(OnCallExpr, func_index);
CALLBACK(OnOpcodeUint32, func_index);
break;
}
case Opcode::CallIndirect: {
uint32_t sig_index;
in_u32_leb128(ctx, &sig_index, "call_indirect signature index");
RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures,
"invalid call_indirect signature index");
uint32_t reserved;
in_u32_leb128(ctx, &reserved, "call_indirect reserved");
RAISE_ERROR_UNLESS(reserved == 0,
"call_indirect reserved value must be 0");
CALLBACK(OnCallIndirectExpr, sig_index);
CALLBACK(OnOpcodeUint32Uint32, sig_index, reserved);
break;
}
case Opcode::TeeLocal: {
uint32_t local_index;
in_u32_leb128(ctx, &local_index, "tee_local local index");
CALLBACK(OnTeeLocalExpr, local_index);
CALLBACK(OnOpcodeUint32, 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: {
uint32_t alignment_log2;
in_u32_leb128(ctx, &alignment_log2, "load alignment");
uint32_t offset;
in_u32_leb128(ctx, &offset, "load offset");
CALLBACK(OnLoadExpr, opcode, alignment_log2, offset);
CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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: {
uint32_t alignment_log2;
in_u32_leb128(ctx, &alignment_log2, "store alignment");
uint32_t offset;
in_u32_leb128(ctx, &offset, "store offset");
CALLBACK(OnStoreExpr, opcode, alignment_log2, offset);
CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
break;
}
case Opcode::CurrentMemory: {
uint32_t reserved;
in_u32_leb128(ctx, &reserved, "current_memory reserved");
RAISE_ERROR_UNLESS(reserved == 0,
"current_memory reserved value must be 0");
CALLBACK0(OnCurrentMemoryExpr);
CALLBACK(OnOpcodeUint32, reserved);
break;
}
case Opcode::GrowMemory: {
uint32_t reserved;
in_u32_leb128(ctx, &reserved, "grow_memory reserved");
RAISE_ERROR_UNLESS(reserved == 0,
"grow_memory reserved value must be 0");
CALLBACK0(OnGrowMemoryExpr);
CALLBACK(OnOpcodeUint32, reserved);
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:
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:
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:
CALLBACK(OnUnaryExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::I32TruncSF32:
case Opcode::I32TruncSF64:
case Opcode::I32TruncUF32:
case Opcode::I32TruncUF64:
case Opcode::I32WrapI64:
case Opcode::I64TruncSF32:
case Opcode::I64TruncSF64:
case Opcode::I64TruncUF32:
case Opcode::I64TruncUF64:
case Opcode::I64ExtendSI32:
case Opcode::I64ExtendUI32:
case Opcode::F32ConvertSI32:
case Opcode::F32ConvertUI32:
case Opcode::F32ConvertSI64:
case Opcode::F32ConvertUI64:
case Opcode::F32DemoteF64:
case Opcode::F32ReinterpretI32:
case Opcode::F64ConvertSI32:
case Opcode::F64ConvertUI32:
case Opcode::F64ConvertSI64:
case Opcode::F64ConvertUI64:
case Opcode::F64PromoteF32:
case Opcode::F64ReinterpretI64:
case Opcode::I32ReinterpretF32:
case Opcode::I64ReinterpretF64:
case Opcode::I32Eqz:
case Opcode::I64Eqz:
CALLBACK(OnConvertExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
default:
RAISE_ERROR("unexpected opcode: %d (0x%x)", static_cast<int>(opcode),
static_cast<unsigned>(opcode));
}
}
RAISE_ERROR_UNLESS(ctx->state.offset == end_offset,
"function body longer than given size");
RAISE_ERROR_UNLESS(seen_end_opcode, "function body must end with END opcode");
}
static void read_names_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginNamesSection, section_size);
uint32_t i = 0;
size_t previous_read_end = ctx->read_end;
uint32_t previous_subsection_type = 0;
while (ctx->state.offset < ctx->read_end) {
uint32_t name_type;
uint32_t subsection_size;
in_u32_leb128(ctx, &name_type, "name type");
if (i != 0) {
if (name_type == previous_subsection_type)
RAISE_ERROR("duplicate sub-section");
if (name_type < previous_subsection_type)
RAISE_ERROR("out-of-order sub-section");
}
previous_subsection_type = name_type;
in_u32_leb128(ctx, &subsection_size, "subsection size");
size_t subsection_end = ctx->state.offset + subsection_size;
if (subsection_end > ctx->read_end)
RAISE_ERROR("invalid sub-section size: extends past end");
ctx->read_end = subsection_end;
switch (static_cast<NameSectionSubsection>(name_type)) {
case NameSectionSubsection::Function:
CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size);
if (subsection_size) {
uint32_t num_names;
in_u32_leb128(ctx, &num_names, "name count");
CALLBACK(OnFunctionNamesCount, num_names);
for (uint32_t j = 0; j < num_names; ++j) {
uint32_t function_index;
StringSlice function_name;
in_u32_leb128(ctx, &function_index, "function index");
RAISE_ERROR_UNLESS(function_index < num_total_funcs(ctx),
"invalid function index: %u", function_index);
in_str(ctx, &function_name, "function name");
CALLBACK(OnFunctionName, function_index, function_name);
}
}
break;
case NameSectionSubsection::Local:
CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size);
if (subsection_size) {
uint32_t num_funcs;
in_u32_leb128(ctx, &num_funcs, "function count");
CALLBACK(OnLocalNameFunctionCount, num_funcs);
for (uint32_t j = 0; j < num_funcs; ++j) {
uint32_t function_index;
in_u32_leb128(ctx, &function_index, "function index");
uint32_t num_locals;
in_u32_leb128(ctx, &num_locals, "local count");
CALLBACK(OnLocalNameLocalCount, function_index, num_locals);
for (uint32_t k = 0; k < num_locals; ++k) {
uint32_t local_index;
StringSlice local_name;
in_u32_leb128(ctx, &local_index, "named index");
in_str(ctx, &local_name, "name");
CALLBACK(OnLocalName, function_index, local_index, local_name);
}
}
}
break;
default:
/* unknown subsection, skip it */
ctx->state.offset = subsection_end;
break;
}
++i;
if (ctx->state.offset != subsection_end) {
RAISE_ERROR("unfinished sub-section (expected end: 0x%" PRIzx ")",
subsection_end);
}
ctx->read_end = previous_read_end;
}
CALLBACK0(EndNamesSection);
}
static void read_reloc_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginRelocSection, section_size);
uint32_t num_relocs, section;
in_u32_leb128(ctx, &section, "section");
StringSlice section_name;
WABT_ZERO_MEMORY(section_name);
if (static_cast<BinarySection>(section) == BinarySection::Custom)
in_str(ctx, &section_name, "section name");
in_u32_leb128(ctx, &num_relocs, "relocation count");
CALLBACK(OnRelocCount, num_relocs, static_cast<BinarySection>(section),
section_name);
for (uint32_t i = 0; i < num_relocs; ++i) {
uint32_t reloc_type, offset, index, addend = 0;
in_u32_leb128(ctx, &reloc_type, "relocation type");
in_u32_leb128(ctx, &offset, "offset");
in_u32_leb128(ctx, &index, "index");
RelocType type = static_cast<RelocType>(reloc_type);
switch (type) {
case RelocType::MemoryAddressLEB:
case RelocType::MemoryAddressSLEB:
case RelocType::MemoryAddressI32:
in_i32_leb128(ctx, &addend, "addend");
break;
default:
break;
}
CALLBACK(OnReloc, type, offset, index, addend);
}
CALLBACK0(EndRelocSection);
}
static void read_custom_section(Context* ctx, uint32_t section_size) {
StringSlice section_name;
in_str(ctx, &section_name, "section name");
CALLBACK(BeginCustomSection, section_size, section_name);
bool name_section_ok = ctx->last_known_section >= BinarySection::Import;
if (ctx->options->read_debug_names && name_section_ok &&
strncmp(section_name.start, WABT_BINARY_SECTION_NAME,
section_name.length) == 0) {
read_names_section(ctx, section_size);
} else if (strncmp(section_name.start, WABT_BINARY_SECTION_RELOC,
strlen(WABT_BINARY_SECTION_RELOC)) == 0) {
read_reloc_section(ctx, section_size);
} else {
/* This is an unknown custom section, skip it. */
ctx->state.offset = ctx->read_end;
}
CALLBACK0(EndCustomSection);
}
static void read_type_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginTypeSection, section_size);
in_u32_leb128(ctx, &ctx->num_signatures, "type count");
CALLBACK(OnTypeCount, ctx->num_signatures);
for (uint32_t i = 0; i < ctx->num_signatures; ++i) {
Type form;
in_type(ctx, &form, "type form");
RAISE_ERROR_UNLESS(form == Type::Func, "unexpected type form: %d",
static_cast<int>(form));
uint32_t num_params;
in_u32_leb128(ctx, &num_params, "function param count");
ctx->param_types.resize(num_params);
for (uint32_t j = 0; j < num_params; ++j) {
Type param_type;
in_type(ctx, &param_type, "function param type");
RAISE_ERROR_UNLESS(is_concrete_type(param_type),
"expected valid param type (got %d)",
static_cast<int>(param_type));
ctx->param_types[j] = param_type;
}
uint32_t num_results;
in_u32_leb128(ctx, &num_results, "function result count");
RAISE_ERROR_UNLESS(num_results <= 1, "result count must be 0 or 1");
Type result_type = Type::Void;
if (num_results) {
in_type(ctx, &result_type, "function result type");
RAISE_ERROR_UNLESS(is_concrete_type(result_type),
"expected valid result type: %d",
static_cast<int>(result_type));
}
Type* param_types = num_params ? ctx->param_types.data() : nullptr;
CALLBACK(OnType, i, num_params, param_types, num_results, &result_type);
}
CALLBACK0(EndTypeSection);
}
static void read_import_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginImportSection, section_size);
in_u32_leb128(ctx, &ctx->num_imports, "import count");
CALLBACK(OnImportCount, ctx->num_imports);
for (uint32_t i = 0; i < ctx->num_imports; ++i) {
StringSlice module_name;
in_str(ctx, &module_name, "import module name");
StringSlice field_name;
in_str(ctx, &field_name, "import field name");
uint32_t kind;
in_u32_leb128(ctx, &kind, "import kind");
switch (static_cast<ExternalKind>(kind)) {
case ExternalKind::Func: {
uint32_t sig_index;
in_u32_leb128(ctx, &sig_index, "import signature index");
RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures,
"invalid import signature index");
CALLBACK(OnImport, i, module_name, field_name);
CALLBACK(OnImportFunc, i, module_name, field_name,
ctx->num_func_imports, sig_index);
ctx->num_func_imports++;
break;
}
case ExternalKind::Table: {
Type elem_type;
Limits elem_limits;
read_table(ctx, &elem_type, &elem_limits);
CALLBACK(OnImport, i, module_name, field_name);
CALLBACK(OnImportTable, i, module_name, field_name,
ctx->num_table_imports, elem_type, &elem_limits);
ctx->num_table_imports++;
break;
}
case ExternalKind::Memory: {
Limits page_limits;
read_memory(ctx, &page_limits);
CALLBACK(OnImport, i, module_name, field_name);
CALLBACK(OnImportMemory, i, module_name, field_name,
ctx->num_memory_imports, &page_limits);
ctx->num_memory_imports++;
break;
}
case ExternalKind::Global: {
Type type;
bool mutable_;
read_global_header(ctx, &type, &mutable_);
CALLBACK(OnImport, i, module_name, field_name);
CALLBACK(OnImportGlobal, i, module_name, field_name,
ctx->num_global_imports, type, mutable_);
ctx->num_global_imports++;
break;
}
default:
RAISE_ERROR("invalid import kind: %d", kind);
}
}
CALLBACK0(EndImportSection);
}
static void read_function_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginFunctionSection, section_size);
in_u32_leb128(ctx, &ctx->num_function_signatures, "function signature count");
CALLBACK(OnFunctionCount, ctx->num_function_signatures);
for (uint32_t i = 0; i < ctx->num_function_signatures; ++i) {
uint32_t func_index = ctx->num_func_imports + i;
uint32_t sig_index;
in_u32_leb128(ctx, &sig_index, "function signature index");
RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures,
"invalid function signature index: %d", sig_index);
CALLBACK(OnFunction, func_index, sig_index);
}
CALLBACK0(EndFunctionSection);
}
static void read_table_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginTableSection, section_size);
in_u32_leb128(ctx, &ctx->num_tables, "table count");
RAISE_ERROR_UNLESS(ctx->num_tables <= 1, "table count (%d) must be 0 or 1",
ctx->num_tables);
CALLBACK(OnTableCount, ctx->num_tables);
for (uint32_t i = 0; i < ctx->num_tables; ++i) {
uint32_t table_index = ctx->num_table_imports + i;
Type elem_type;
Limits elem_limits;
read_table(ctx, &elem_type, &elem_limits);
CALLBACK(OnTable, table_index, elem_type, &elem_limits);
}
CALLBACK0(EndTableSection);
}
static void read_memory_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginMemorySection, section_size);
in_u32_leb128(ctx, &ctx->num_memories, "memory count");
RAISE_ERROR_UNLESS(ctx->num_memories <= 1, "memory count must be 0 or 1");
CALLBACK(OnMemoryCount, ctx->num_memories);
for (uint32_t i = 0; i < ctx->num_memories; ++i) {
uint32_t memory_index = ctx->num_memory_imports + i;
Limits page_limits;
read_memory(ctx, &page_limits);
CALLBACK(OnMemory, memory_index, &page_limits);
}
CALLBACK0(EndMemorySection);
}
static void read_global_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginGlobalSection, section_size);
in_u32_leb128(ctx, &ctx->num_globals, "global count");
CALLBACK(OnGlobalCount, ctx->num_globals);
for (uint32_t i = 0; i < ctx->num_globals; ++i) {
uint32_t global_index = ctx->num_global_imports + i;
Type global_type;
bool mutable_;
read_global_header(ctx, &global_type, &mutable_);
CALLBACK(BeginGlobal, global_index, global_type, mutable_);
CALLBACK(BeginGlobalInitExpr, global_index);
read_init_expr(ctx, global_index);
CALLBACK(EndGlobalInitExpr, global_index);
CALLBACK(EndGlobal, global_index);
}
CALLBACK0(EndGlobalSection);
}
static void read_export_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginExportSection, section_size);
in_u32_leb128(ctx, &ctx->num_exports, "export count");
CALLBACK(OnExportCount, ctx->num_exports);
for (uint32_t i = 0; i < ctx->num_exports; ++i) {
StringSlice name;
in_str(ctx, &name, "export item name");
uint8_t external_kind;
in_u8(ctx, &external_kind, "export external kind");
RAISE_ERROR_UNLESS(is_valid_external_kind(external_kind),
"invalid export external kind");
uint32_t item_index;
in_u32_leb128(ctx, &item_index, "export item index");
switch (static_cast<ExternalKind>(external_kind)) {
case ExternalKind::Func:
RAISE_ERROR_UNLESS(item_index < num_total_funcs(ctx),
"invalid export func index: %d", item_index);
break;
case ExternalKind::Table:
RAISE_ERROR_UNLESS(item_index < num_total_tables(ctx),
"invalid export table index");
break;
case ExternalKind::Memory:
RAISE_ERROR_UNLESS(item_index < num_total_memories(ctx),
"invalid export memory index");
break;
case ExternalKind::Global:
RAISE_ERROR_UNLESS(item_index < num_total_globals(ctx),
"invalid export global index");
break;
}
CALLBACK(OnExport, i, static_cast<ExternalKind>(external_kind), item_index,
name);
}
CALLBACK0(EndExportSection);
}
static void read_start_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginStartSection, section_size);
uint32_t func_index;
in_u32_leb128(ctx, &func_index, "start function index");
RAISE_ERROR_UNLESS(func_index < num_total_funcs(ctx),
"invalid start function index");
CALLBACK(OnStartFunction, func_index);
CALLBACK0(EndStartSection);
}
static void read_elem_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginElemSection, section_size);
uint32_t num_elem_segments;
in_u32_leb128(ctx, &num_elem_segments, "elem segment count");
CALLBACK(OnElemSegmentCount, num_elem_segments);
RAISE_ERROR_UNLESS(num_elem_segments == 0 || num_total_tables(ctx) > 0,
"elem section without table section");
for (uint32_t i = 0; i < num_elem_segments; ++i) {
uint32_t table_index;
in_u32_leb128(ctx, &table_index, "elem segment table index");
CALLBACK(BeginElemSegment, i, table_index);
CALLBACK(BeginElemSegmentInitExpr, i);
read_init_expr(ctx, i);
CALLBACK(EndElemSegmentInitExpr, i);
uint32_t num_function_indexes;
in_u32_leb128(ctx, &num_function_indexes,
"elem segment function index count");
CALLBACK(OnElemSegmentFunctionIndexCount, i, num_function_indexes);
for (uint32_t j = 0; j < num_function_indexes; ++j) {
uint32_t func_index;
in_u32_leb128(ctx, &func_index, "elem segment function index");
CALLBACK(OnElemSegmentFunctionIndex, i, func_index);
}
CALLBACK(EndElemSegment, i);
}
CALLBACK0(EndElemSection);
}
static void read_code_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginCodeSection, section_size);
in_u32_leb128(ctx, &ctx->num_function_bodies, "function body count");
RAISE_ERROR_UNLESS(ctx->num_function_signatures == ctx->num_function_bodies,
"function signature count != function body count");
CALLBACK(OnFunctionBodyCount, ctx->num_function_bodies);
for (uint32_t i = 0; i < ctx->num_function_bodies; ++i) {
uint32_t func_index = ctx->num_func_imports + i;
uint32_t func_offset = ctx->state.offset;
ctx->state.offset = func_offset;
CALLBACK(BeginFunctionBody, func_index);
uint32_t body_size;
in_u32_leb128(ctx, &body_size, "function body size");
uint32_t body_start_offset = ctx->state.offset;
uint32_t end_offset = body_start_offset + body_size;
uint32_t num_local_decls;
in_u32_leb128(ctx, &num_local_decls, "local declaration count");
CALLBACK(OnLocalDeclCount, num_local_decls);
for (uint32_t k = 0; k < num_local_decls; ++k) {
uint32_t num_local_types;
in_u32_leb128(ctx, &num_local_types, "local type count");
Type local_type;
in_type(ctx, &local_type, "local type");
RAISE_ERROR_UNLESS(is_concrete_type(local_type),
"expected valid local type");
CALLBACK(OnLocalDecl, k, num_local_types, local_type);
}
read_function_body(ctx, end_offset);
CALLBACK(EndFunctionBody, func_index);
}
CALLBACK0(EndCodeSection);
}
static void read_data_section(Context* ctx, uint32_t section_size) {
CALLBACK(BeginDataSection, section_size);
uint32_t num_data_segments;
in_u32_leb128(ctx, &num_data_segments, "data segment count");
CALLBACK(OnDataSegmentCount, num_data_segments);
RAISE_ERROR_UNLESS(num_data_segments == 0 || num_total_memories(ctx) > 0,
"data section without memory section");
for (uint32_t i = 0; i < num_data_segments; ++i) {
uint32_t memory_index;
in_u32_leb128(ctx, &memory_index, "data segment memory index");
CALLBACK(BeginDataSegment, i, memory_index);
CALLBACK(BeginDataSegmentInitExpr, i);
read_init_expr(ctx, i);
CALLBACK(EndDataSegmentInitExpr, i);
uint32_t data_size;
const void* data;
in_bytes(ctx, &data, &data_size, "data segment data");
CALLBACK(OnDataSegmentData, i, data, data_size);
CALLBACK(EndDataSegment, i);
}
CALLBACK0(EndDataSection);
}
static void read_sections(Context* ctx) {
while (ctx->state.offset < ctx->state.size) {
uint32_t section_code;
uint32_t section_size;
/* Temporarily reset read_end to the full data size so the next section
* can be read. */
ctx->read_end = ctx->state.size;
in_u32_leb128(ctx, &section_code, "section code");
in_u32_leb128(ctx, &section_size, "section size");
ctx->read_end = ctx->state.offset + section_size;
if (section_code >= kBinarySectionCount) {
RAISE_ERROR("invalid section code: %u; max is %u", section_code,
kBinarySectionCount - 1);
}
BinarySection section = static_cast<BinarySection>(section_code);
if (ctx->read_end > ctx->state.size)
RAISE_ERROR("invalid section size: extends past end");
if (ctx->last_known_section != BinarySection::Invalid &&
section != BinarySection::Custom &&
section <= ctx->last_known_section) {
RAISE_ERROR("section %s out of order", get_section_name(section));
}
CALLBACK(BeginSection, section, section_size);
#define V(Name, name, code) \
case BinarySection::Name: \
read_##name##_section(ctx, section_size); \
break;
switch (section) {
WABT_FOREACH_BINARY_SECTION(V)
default:
assert(0);
break;
}
#undef V
if (ctx->state.offset != ctx->read_end) {
RAISE_ERROR("unfinished section (expected end: 0x%" PRIzx ")",
ctx->read_end);
}
if (section != BinarySection::Custom)
ctx->last_known_section = section;
}
}
Result read_binary(const void* data,
size_t size,
BinaryReader* reader,
const ReadBinaryOptions* options) {
BinaryReaderLogging logging_reader(options->log_stream, reader);
Context context;
/* all the macros assume a Context* named ctx */
Context* ctx = &context;
ctx->state.data = static_cast<const uint8_t*>(data);
ctx->state.size = ctx->read_end = size;
ctx->state.offset = 0;
ctx->reader = options->log_stream ? &logging_reader : reader;
ctx->options = options;
ctx->last_known_section = BinarySection::Invalid;
if (setjmp(ctx->error_jmp_buf) == 1) {
return Result::Error;
}
ctx->reader->OnSetState(&ctx->state);
uint32_t magic;
in_u32(ctx, &magic, "magic");
RAISE_ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value");
uint32_t version;
in_u32(ctx, &version, "version");
RAISE_ERROR_UNLESS(version == WABT_BINARY_VERSION,
"bad wasm file version: %#x (expected %#x)", version,
WABT_BINARY_VERSION);
CALLBACK(BeginModule, version);
read_sections(ctx);
CALLBACK0(EndModule);
return Result::Ok;
}
} // namespace wabt