blob: ce257113e9a3ddeff7863c70832c406f2f309db1 [file] [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 "src/interp/binary-reader-interp.h"
#include <cassert>
#include <cinttypes>
#include <cstdarg>
#include <cstdio>
#include <vector>
#include "src/binary-reader-nop.h"
#include "src/cast.h"
#include "src/feature.h"
#include "src/interp/interp.h"
#include "src/interp/interp-internal.h"
#include "src/stream.h"
#include "src/type-checker.h"
namespace wabt {
using namespace interp;
namespace {
typedef std::vector<Index> IndexVector;
typedef std::vector<IstreamOffset> IstreamOffsetVector;
typedef std::vector<IstreamOffsetVector> IstreamOffsetVectorVector;
struct Label {
Label(IstreamOffset offset, IstreamOffset fixup_offset);
IstreamOffset offset;
IstreamOffset fixup_offset;
};
Label::Label(IstreamOffset offset, IstreamOffset fixup_offset)
: offset(offset), fixup_offset(fixup_offset) {}
struct ElemSegmentInfo {
ElemSegmentInfo(Table* table, Index dst) : table(table), dst(dst) {}
Table* table;
Index dst;
std::vector<Ref> src;
};
struct DataSegmentInfo {
DataSegmentInfo(Memory* memory,
Address dst,
const void* src,
IstreamOffset size)
: memory(memory), dst(dst), src(src), size(size) {}
Memory* memory;
Address dst;
const void* src; // Not owned.
IstreamOffset size;
};
class BinaryReaderInterp : public BinaryReaderNop {
public:
BinaryReaderInterp(Environment* env,
DefinedModule* module,
std::unique_ptr<OutputBuffer> istream,
Errors* errors,
const Features& features);
wabt::Result ReadBinary(DefinedModule* out_module);
std::unique_ptr<OutputBuffer> ReleaseOutputBuffer();
wabt::Result InitializeSegments();
// Implement BinaryReader.
bool OnError(const Error&) override;
wabt::Result OnTypeCount(Index count) override;
wabt::Result OnType(Index index,
Index param_count,
Type* param_types,
Index result_count,
Type* result_types) override;
wabt::Result OnImportFunc(Index import_index,
string_view module_name,
string_view field_name,
Index func_index,
Index sig_index) override;
wabt::Result OnImportTable(Index import_index,
string_view module_name,
string_view field_name,
Index table_index,
Type elem_type,
const Limits* elem_limits) override;
wabt::Result OnImportMemory(Index import_index,
string_view module_name,
string_view field_name,
Index memory_index,
const Limits* page_limits) override;
wabt::Result OnImportGlobal(Index import_index,
string_view module_name,
string_view field_name,
Index global_index,
Type type,
bool mutable_) override;
wabt::Result OnFunctionCount(Index count) override;
wabt::Result OnFunction(Index index, Index sig_index) override;
wabt::Result OnTableCount(Index count) override;
wabt::Result OnTable(Index index,
Type elem_type,
const Limits* elem_limits) override;
wabt::Result OnMemory(Index index, const Limits* limits) override;
wabt::Result OnGlobalCount(Index count) override;
wabt::Result BeginGlobal(Index index, Type type, bool mutable_) override;
wabt::Result EndGlobalInitExpr(Index index) override;
wabt::Result OnExport(Index index,
ExternalKind kind,
Index item_index,
string_view name) override;
wabt::Result OnStartFunction(Index func_index) override;
wabt::Result BeginFunctionBody(Index index, Offset size) override;
wabt::Result OnLocalDeclCount(Index count) override;
wabt::Result OnLocalDecl(Index decl_index, Index count, Type type) override;
wabt::Result OnOpcode(Opcode Opcode) override;
wabt::Result OnAtomicLoadExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnAtomicStoreExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnAtomicRmwExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnAtomicWaitExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnAtomicNotifyExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnBinaryExpr(wabt::Opcode opcode) override;
wabt::Result OnBlockExpr(Type sig_type) override;
wabt::Result OnBrExpr(Index depth) override;
wabt::Result OnBrIfExpr(Index depth) override;
wabt::Result OnBrTableExpr(Index num_targets,
Index* target_depths,
Index default_target_depth) override;
wabt::Result OnCallExpr(Index func_index) override;
wabt::Result OnCallIndirectExpr(Index sig_index, Index table_index) override;
wabt::Result OnReturnCallExpr(Index func_index) override;
wabt::Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override;
wabt::Result OnCompareExpr(wabt::Opcode opcode) override;
wabt::Result OnConvertExpr(wabt::Opcode opcode) override;
wabt::Result OnDropExpr() override;
wabt::Result OnElseExpr() override;
wabt::Result OnEndExpr() override;
wabt::Result OnF32ConstExpr(uint32_t value_bits) override;
wabt::Result OnF64ConstExpr(uint64_t value_bits) override;
wabt::Result OnV128ConstExpr(v128 value_bits) override;
wabt::Result OnGlobalGetExpr(Index global_index) override;
wabt::Result OnGlobalSetExpr(Index global_index) override;
wabt::Result OnI32ConstExpr(uint32_t value) override;
wabt::Result OnI64ConstExpr(uint64_t value) override;
wabt::Result OnIfExpr(Type sig_type) override;
wabt::Result OnLoadExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnLocalGetExpr(Index local_index) override;
wabt::Result OnLocalSetExpr(Index local_index) override;
wabt::Result OnLocalTeeExpr(Index local_index) override;
wabt::Result OnLoopExpr(Type sig_type) override;
wabt::Result OnMemoryCopyExpr() override;
wabt::Result OnDataDropExpr(Index segment_index) override;
wabt::Result OnMemoryGrowExpr() override;
wabt::Result OnMemoryFillExpr() override;
wabt::Result OnMemoryInitExpr(Index segment_index) override;
wabt::Result OnMemorySizeExpr() override;
wabt::Result OnRefFuncExpr(Index func_index) override;
wabt::Result OnRefNullExpr() override;
wabt::Result OnRefIsNullExpr() override;
wabt::Result OnNopExpr() override;
wabt::Result OnReturnExpr() override;
wabt::Result OnSelectExpr() override;
wabt::Result OnStoreExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnUnaryExpr(wabt::Opcode opcode) override;
wabt::Result OnTableCopyExpr(Index dst_index, Index src_index) override;
wabt::Result OnTableGetExpr(Index table_index) override;
wabt::Result OnTableSetExpr(Index table_index) override;
wabt::Result OnElemDropExpr(Index segment_index) override;
wabt::Result OnTableInitExpr(Index segment_index, Index table_index) override;
wabt::Result OnTernaryExpr(wabt::Opcode opcode) override;
wabt::Result OnUnreachableExpr() override;
wabt::Result EndFunctionBody(Index index) override;
wabt::Result OnSimdLaneOpExpr(wabt::Opcode opcode, uint64_t value) override;
wabt::Result OnSimdShuffleOpExpr(wabt::Opcode opcode, v128 value) override;
wabt::Result OnLoadSplatExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) override;
wabt::Result OnElemSegmentCount(Index count) override;
wabt::Result BeginElemSegment(Index index,
Index table_index,
uint8_t flags,
Type elem_type) override;
wabt::Result BeginElemSegmentInitExpr(Index index) override;
wabt::Result EndElemSegmentInitExpr(Index index) override;
wabt::Result OnElemSegmentElemExprCount(Index index, Index count) override;
wabt::Result OnElemSegmentElemExpr_RefNull(Index segment_index) override;
wabt::Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
Index func_index) override;
wabt::Result OnDataCount(Index count) override;
wabt::Result BeginDataSegment(Index index,
Index memory_index,
uint8_t flags) override;
wabt::Result BeginDataSegmentInitExpr(Index index) override;
wabt::Result OnDataSegmentData(Index index,
const void* data,
Address size) override;
wabt::Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
wabt::Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
wabt::Result OnInitExprV128ConstExpr(Index index, v128 value) override;
wabt::Result OnInitExprGlobalGetExpr(Index index,
Index global_index) override;
wabt::Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
wabt::Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
wabt::Result OnInitExprRefNull(Index index) override;
private:
Label* GetLabel(Index depth);
Label* TopLabel();
void PushLabel(IstreamOffset offset, IstreamOffset fixup_offset);
void PopLabel();
void PrintError(const char* format, ...);
Index TranslateSigIndexToEnv(Index sig_index);
void GetBlockSignature(Type sig_type,
TypeVector* out_param_types,
TypeVector* out_result_types);
FuncSignature* GetSignatureByModuleIndex(Index sig_index);
Index TranslateFuncIndexToEnv(Index func_index);
Table* GetTableByModuleIndex(Index table_index);
Index TranslateTableIndexToEnv(Index table_index);
Index TranslateModuleFuncIndexToDefined(Index func_index);
Func* GetFuncByModuleIndex(Index func_index);
Index TranslateGlobalIndexToEnv(Index global_index);
Global* GetGlobalByModuleIndex(Index global_index);
Type GetGlobalTypeByModuleIndex(Index global_index);
Index TranslateLocalIndex(Index local_index);
Type GetLocalTypeByIndex(Func* func, Index local_index);
Index TranslateDataSegmentIndexToEnv(Index data_segment_index);
Index TranslateElemSegmentIndexToEnv(Index elem_segment_index);
IstreamOffset GetIstreamOffset();
wabt::Result EmitDataAt(IstreamOffset offset,
const void* data,
IstreamOffset size);
wabt::Result EmitData(const void* data, IstreamOffset size);
wabt::Result EmitOpcode(Opcode opcode);
wabt::Result EmitI8(uint8_t value);
wabt::Result EmitI32(uint32_t value);
wabt::Result EmitI64(uint64_t value);
wabt::Result EmitV128(v128 value);
wabt::Result EmitI32At(IstreamOffset offset, uint32_t value);
wabt::Result EmitDropKeep(uint32_t drop, uint32_t keep);
wabt::Result AppendFixup(IstreamOffsetVectorVector* fixups_vector,
Index index);
wabt::Result EmitBrOffset(Index depth, IstreamOffset offset);
wabt::Result GetDropCount(Index keep_count,
size_t type_stack_limit,
Index* out_drop_count);
wabt::Result GetBrDropKeepCount(Index depth,
Index* out_drop_count,
Index* out_keep_count);
wabt::Result GetReturnDropKeepCount(Index* out_drop_count,
Index* out_keep_count);
wabt::Result GetReturnCallDropKeepCount(FuncSignature* sig,
Index keep_extra,
Index* out_drop_count,
Index* out_keep_count);
wabt::Result EmitBr(Index depth, Index drop_count, Index keep_count);
wabt::Result EmitBrTableOffset(Index depth);
wabt::Result FixupTopLabel();
wabt::Result EmitFuncOffset(DefinedFunc* func, Index func_index);
wabt::Result CheckLocal(Index local_index);
wabt::Result CheckGlobal(Index global_index);
wabt::Result CheckDataSegment(Index data_segment_index);
wabt::Result CheckElemSegment(Index elem_segment_index);
wabt::Result CheckImportKind(string_view module,
string_view field,
ExternalKind kind,
ExternalKind actual_kind);
wabt::Result CheckImportLimits(const Limits* declared_limits,
const Limits* actual_limits);
wabt::Result CheckHasMemory(wabt::Opcode opcode);
wabt::Result CheckHasTable(wabt::Opcode opcode);
wabt::Result CheckAlign(uint32_t alignment_log2, Address natural_alignment);
wabt::Result CheckAtomicAlign(uint32_t alignment_log2,
Address natural_alignment);
wabt::Result CheckInFunction();
wabt::Result AppendExport(Module* module,
ExternalKind kind,
Index item_index,
string_view name);
wabt::Result FindRegisteredModule(string_view module_name,
Module** out_module);
wabt::Result GetModuleExport(Module* module,
string_view field_name,
Export** out_export);
Features features_;
Errors* errors_ = nullptr;
Environment* env_ = nullptr;
DefinedModule* module_ = nullptr;
DefinedFunc* current_func_ = nullptr;
TypeChecker typechecker_;
std::vector<Label> label_stack_;
IstreamOffsetVectorVector func_fixups_;
IstreamOffsetVectorVector depth_fixups_;
MemoryStream istream_;
IstreamOffset istream_offset_ = 0;
/* mappings from module index space to env index space; this won't just be a
* translation, because imported values will be resolved as well */
IndexVector sig_index_mapping_;
IndexVector func_index_mapping_;
IndexVector table_index_mapping_;
IndexVector global_index_mapping_;
IndexVector data_segment_index_mapping_;
IndexVector elem_segment_index_mapping_;
Index num_func_imports_ = 0;
Index num_global_imports_ = 0;
// Changes to linear memory and tables should not apply if a validation error
// occurs; these vectors cache the changes that must be applied after we know
// that there are no validation errors.
//
// Note that this behavior changed after the bulk memory proposal; in that
// case each segment is initialized as it is encountered. If one fails, then
// no further segments are processed.
std::vector<ElemSegmentInfo> elem_segment_infos_;
std::vector<DataSegmentInfo> data_segment_infos_;
// Values cached so they can be shared between callbacks.
TypedValue init_expr_value_;
IstreamOffset table_offset_ = 0;
uint8_t segment_flags_ = 0;
Index segment_table_index_ = kInvalidIndex;
ElemSegment* elem_segment_ = nullptr;
ElemSegmentInfo* elem_segment_info_ = nullptr;
bool has_table = false;
};
BinaryReaderInterp::BinaryReaderInterp(Environment* env,
DefinedModule* module,
std::unique_ptr<OutputBuffer> istream,
Errors* errors,
const Features& features)
: features_(features),
errors_(errors),
env_(env),
module_(module),
istream_(std::move(istream)),
istream_offset_(istream_.output_buffer().size()) {
typechecker_.set_error_callback(
[this](const char* msg) { PrintError("%s", msg); });
}
std::unique_ptr<OutputBuffer> BinaryReaderInterp::ReleaseOutputBuffer() {
return istream_.ReleaseOutputBuffer();
}
Label* BinaryReaderInterp::GetLabel(Index depth) {
assert(depth < label_stack_.size());
return &label_stack_[label_stack_.size() - depth - 1];
}
Label* BinaryReaderInterp::TopLabel() {
return GetLabel(0);
}
void WABT_PRINTF_FORMAT(2, 3) BinaryReaderInterp::PrintError(const char* format,
...) {
WABT_SNPRINTF_ALLOCA(buffer, length, format);
errors_->emplace_back(ErrorLevel::Error, Location(kInvalidOffset), buffer);
}
Index BinaryReaderInterp::TranslateSigIndexToEnv(Index sig_index) {
assert(sig_index < sig_index_mapping_.size());
return sig_index_mapping_[sig_index];
}
void BinaryReaderInterp::GetBlockSignature(Type sig_type,
TypeVector* out_param_types,
TypeVector* out_result_types) {
if (IsTypeIndex(sig_type)) {
FuncSignature* func_sig = GetSignatureByModuleIndex(GetTypeIndex(sig_type));
*out_param_types = func_sig->param_types;
*out_result_types = func_sig->result_types;
} else {
out_param_types->clear();
*out_result_types = GetInlineTypeVector(sig_type);
}
}
FuncSignature* BinaryReaderInterp::GetSignatureByModuleIndex(Index sig_index) {
return env_->GetFuncSignature(TranslateSigIndexToEnv(sig_index));
}
Index BinaryReaderInterp::TranslateFuncIndexToEnv(Index func_index) {
assert(func_index < func_index_mapping_.size());
return func_index_mapping_[func_index];
}
Index BinaryReaderInterp::TranslateTableIndexToEnv(Index table_index) {
assert(table_index < table_index_mapping_.size());
return table_index_mapping_[table_index];
}
Index BinaryReaderInterp::TranslateModuleFuncIndexToDefined(Index func_index) {
assert(func_index >= num_func_imports_);
return func_index - num_func_imports_;
}
Func* BinaryReaderInterp::GetFuncByModuleIndex(Index func_index) {
return env_->GetFunc(TranslateFuncIndexToEnv(func_index));
}
Table* BinaryReaderInterp::GetTableByModuleIndex(Index table_index) {
return env_->GetTable(TranslateTableIndexToEnv(table_index));
}
Index BinaryReaderInterp::TranslateGlobalIndexToEnv(Index global_index) {
return global_index_mapping_[global_index];
}
Global* BinaryReaderInterp::GetGlobalByModuleIndex(Index global_index) {
return env_->GetGlobal(TranslateGlobalIndexToEnv(global_index));
}
Type BinaryReaderInterp::GetGlobalTypeByModuleIndex(Index global_index) {
return GetGlobalByModuleIndex(global_index)->typed_value.type;
}
Type BinaryReaderInterp::GetLocalTypeByIndex(Func* func, Index local_index) {
assert(!func->is_host);
return cast<DefinedFunc>(func)->param_and_local_types[local_index];
}
Index BinaryReaderInterp::TranslateDataSegmentIndexToEnv(Index index) {
return data_segment_index_mapping_[index];
}
Index BinaryReaderInterp::TranslateElemSegmentIndexToEnv(Index index) {
return elem_segment_index_mapping_[index];
}
IstreamOffset BinaryReaderInterp::GetIstreamOffset() {
return istream_offset_;
}
wabt::Result BinaryReaderInterp::EmitDataAt(IstreamOffset offset,
const void* data,
IstreamOffset size) {
istream_.WriteDataAt(offset, data, size);
return istream_.result();
}
wabt::Result BinaryReaderInterp::EmitData(const void* data,
IstreamOffset size) {
CHECK_RESULT(EmitDataAt(istream_offset_, data, size));
istream_offset_ += size;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EmitOpcode(Opcode opcode) {
return EmitI32(static_cast<uint32_t>(opcode));
}
wabt::Result BinaryReaderInterp::EmitI8(uint8_t value) {
return EmitData(&value, sizeof(value));
}
wabt::Result BinaryReaderInterp::EmitI32(uint32_t value) {
return EmitData(&value, sizeof(value));
}
wabt::Result BinaryReaderInterp::EmitI64(uint64_t value) {
return EmitData(&value, sizeof(value));
}
wabt::Result BinaryReaderInterp::EmitV128(v128 value) {
return EmitData(&value, sizeof(value));
}
wabt::Result BinaryReaderInterp::EmitI32At(IstreamOffset offset,
uint32_t value) {
return EmitDataAt(offset, &value, sizeof(value));
}
wabt::Result BinaryReaderInterp::EmitDropKeep(uint32_t drop, uint32_t keep) {
assert(drop != UINT32_MAX);
assert(keep != UINT32_MAX);
if (drop > 0) {
if (drop == 1 && keep == 0) {
CHECK_RESULT(EmitOpcode(Opcode::Drop));
} else {
CHECK_RESULT(EmitOpcode(Opcode::InterpDropKeep));
CHECK_RESULT(EmitI32(drop));
CHECK_RESULT(EmitI32(keep));
}
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::AppendFixup(
IstreamOffsetVectorVector* fixups_vector,
Index index) {
if (index >= fixups_vector->size()) {
fixups_vector->resize(index + 1);
}
(*fixups_vector)[index].push_back(GetIstreamOffset());
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EmitBrOffset(Index depth,
IstreamOffset offset) {
if (offset == kInvalidIstreamOffset) {
/* depth_fixups_ stores the depth counting up from zero, where zero is the
* top-level function scope. */
depth = label_stack_.size() - 1 - depth;
CHECK_RESULT(AppendFixup(&depth_fixups_, depth));
}
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::GetDropCount(Index keep_count,
size_t type_stack_limit,
Index* out_drop_count) {
assert(typechecker_.type_stack_size() >= type_stack_limit);
Index type_stack_count = typechecker_.type_stack_size() - type_stack_limit;
// The keep_count may be larger than the type_stack_count if the typechecker
// is currently unreachable. In that case, it doesn't matter what value we
// drop, but 0 is a reasonable choice.
*out_drop_count =
type_stack_count >= keep_count ? type_stack_count - keep_count : 0;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::GetBrDropKeepCount(Index depth,
Index* out_drop_count,
Index* out_keep_count) {
TypeChecker::Label* label;
CHECK_RESULT(typechecker_.GetLabel(depth, &label));
Index keep_count = label->br_types().size();
CHECK_RESULT(
GetDropCount(keep_count, label->type_stack_limit, out_drop_count));
*out_keep_count = keep_count;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::GetReturnDropKeepCount(Index* out_drop_count,
Index* out_keep_count) {
CHECK_RESULT(GetBrDropKeepCount(label_stack_.size() - 1, out_drop_count,
out_keep_count));
*out_drop_count += current_func_->param_and_local_types.size();
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::GetReturnCallDropKeepCount(
FuncSignature* sig,
Index keep_extra,
Index* out_drop_count,
Index* out_keep_count) {
Index keep_count = static_cast<Index>(sig->param_types.size()) + keep_extra;
CHECK_RESULT(GetDropCount(keep_count, 0, out_drop_count));
*out_drop_count += current_func_->param_and_local_types.size();
*out_keep_count = keep_count;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EmitBr(Index depth,
Index drop_count,
Index keep_count) {
CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
CHECK_RESULT(EmitOpcode(Opcode::Br));
CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EmitBrTableOffset(Index depth) {
Index drop_count, keep_count;
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset));
CHECK_RESULT(EmitI32(drop_count));
CHECK_RESULT(EmitI32(keep_count));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::FixupTopLabel() {
IstreamOffset offset = GetIstreamOffset();
Index top = label_stack_.size() - 1;
if (top >= depth_fixups_.size()) {
/* nothing to fixup */
return wabt::Result::Ok;
}
IstreamOffsetVector& fixups = depth_fixups_[top];
for (IstreamOffset fixup : fixups)
CHECK_RESULT(EmitI32At(fixup, offset));
fixups.clear();
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EmitFuncOffset(DefinedFunc* func,
Index func_index) {
if (func->offset == kInvalidIstreamOffset) {
Index defined_index = TranslateModuleFuncIndexToDefined(func_index);
CHECK_RESULT(AppendFixup(&func_fixups_, defined_index));
}
CHECK_RESULT(EmitI32(func->offset));
return wabt::Result::Ok;
}
bool BinaryReaderInterp::OnError(const Error& error) {
errors_->push_back(error);
return true;
}
wabt::Result BinaryReaderInterp::OnTypeCount(Index count) {
Index sig_count = env_->GetFuncSignatureCount();
sig_index_mapping_.resize(count);
for (Index i = 0; i < count; ++i)
sig_index_mapping_[i] = sig_count + i;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnType(Index index,
Index param_count,
Type* param_types,
Index result_count,
Type* result_types) {
assert(TranslateSigIndexToEnv(index) == env_->GetFuncSignatureCount());
env_->EmplaceBackFuncSignature(param_count, param_types, result_count,
result_types);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckLocal(Index local_index) {
Index max_local_index = current_func_->param_and_local_types.size();
if (local_index >= max_local_index) {
PrintError("invalid local_index: %" PRIindex " (max %" PRIindex ")",
local_index, max_local_index);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckGlobal(Index global_index) {
Index max_global_index = global_index_mapping_.size();
if (global_index >= max_global_index) {
PrintError("invalid global_index: %" PRIindex " (max %" PRIindex ")",
global_index, max_global_index);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckDataSegment(Index data_segment_index) {
Index max_data_segment_index = data_segment_index_mapping_.size();
if (data_segment_index >= max_data_segment_index) {
PrintError("invalid data_segment_index: %" PRIindex " (max %" PRIindex ")",
data_segment_index, max_data_segment_index);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckElemSegment(Index elem_segment_index) {
Index max_elem_segment_index = elem_segment_index_mapping_.size();
if (elem_segment_index >= max_elem_segment_index) {
PrintError("invalid elem_segment_index: %" PRIindex " (max %" PRIindex ")",
elem_segment_index, max_elem_segment_index);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckImportKind(string_view module_name,
string_view field_name,
ExternalKind kind,
ExternalKind actual_kind) {
if (kind != actual_kind) {
PrintError("expected import \"" PRIstringview "." PRIstringview
"\" to have kind %s, not %s",
WABT_PRINTF_STRING_VIEW_ARG(module_name),
WABT_PRINTF_STRING_VIEW_ARG(field_name),
GetKindName(kind), GetKindName(actual_kind));
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckImportLimits(
const Limits* declared_limits,
const Limits* actual_limits) {
if (actual_limits->initial < declared_limits->initial) {
PrintError("actual size (%" PRIu64 ") smaller than declared (%" PRIu64 ")",
actual_limits->initial, declared_limits->initial);
return wabt::Result::Error;
}
if (declared_limits->has_max) {
if (!actual_limits->has_max) {
PrintError("max size (unspecified) larger than declared (%" PRIu64 ")",
declared_limits->max);
return wabt::Result::Error;
} else if (actual_limits->max > declared_limits->max) {
PrintError("max size (%" PRIu64 ") larger than declared (%" PRIu64 ")",
actual_limits->max, declared_limits->max);
return wabt::Result::Error;
}
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::AppendExport(Module* module,
ExternalKind kind,
Index item_index,
string_view name) {
// Host modules are allowed to have duplicated exports; e.g. "spectest.print"
if (isa<DefinedModule>(module) &&
module->export_bindings.FindIndex(name) != kInvalidIndex) {
PrintError("duplicate export \"" PRIstringview "\"",
WABT_PRINTF_STRING_VIEW_ARG(name));
return wabt::Result::Error;
}
module->AppendExport(kind, item_index, name);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::FindRegisteredModule(string_view module_name,
Module** out_module) {
Module* module = env_->FindRegisteredModule(module_name);
if (!module) {
PrintError("unknown import module \"" PRIstringview "\"",
WABT_PRINTF_STRING_VIEW_ARG(module_name));
return wabt::Result::Error;
}
*out_module = module;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::GetModuleExport(Module* module,
string_view field_name,
Export** out_export) {
Export* export_ = module->GetExport(field_name);
if (!export_) {
PrintError("unknown module field \"" PRIstringview "\"",
WABT_PRINTF_STRING_VIEW_ARG(field_name));
return wabt::Result::Error;
}
*out_export = export_;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnImportFunc(Index import_index,
string_view module_name,
string_view field_name,
Index func_index,
Index sig_index) {
Index env_sig_index = TranslateSigIndexToEnv(sig_index);
Module* import_module;
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
Export* export_ =
import_module->GetFuncExport(env_, field_name, env_sig_index);
if (!export_) {
// If GetFuncExport fails then GetModuleExport will fail too. But it's
// useful to call here to share the same error handling code as other
// imports.
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
}
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Func,
export_->kind));
Func* func = env_->GetFunc(export_->index);
if (!env_->FuncSignaturesAreEqual(env_sig_index, func->sig_index)) {
PrintError("import signature mismatch");
return wabt::Result::Error;
}
func_index_mapping_.push_back(export_->index);
num_func_imports_++;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnImportTable(Index import_index,
string_view module_name,
string_view field_name,
Index table_index,
Type elem_type,
const Limits* elem_limits) {
has_table = true;
Module* import_module;
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
Export* export_;
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Table, export_->kind));
Table* table = env_->GetTable(export_->index);
CHECK_RESULT(CheckImportLimits(elem_limits, &table->limits));
table_index_mapping_.push_back(export_->index);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnImportMemory(Index import_index,
string_view module_name,
string_view field_name,
Index memory_index,
const Limits* page_limits) {
if (module_->memory_index != kInvalidIndex) {
PrintError("only one memory allowed");
return wabt::Result::Error;
}
Module* import_module;
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
Export* export_;
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Memory, export_->kind));
Memory* memory = env_->GetMemory(export_->index);
CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits));
module_->memory_index = export_->index;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnImportGlobal(Index import_index,
string_view module_name,
string_view field_name,
Index global_index,
Type type,
bool mutable_) {
Module* import_module;
CHECK_RESULT(FindRegisteredModule(module_name, &import_module));
Export* export_;
CHECK_RESULT(GetModuleExport(import_module, field_name, &export_));
CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Global, export_->kind));
Global* exported_global = env_->GetGlobal(export_->index);
if (Failed(typechecker_.CheckType(type, exported_global->typed_value.type))) {
PrintError("type mismatch in imported global, expected %s but got %s.",
GetTypeName(exported_global->typed_value.type),
GetTypeName(type));
return wabt::Result::Error;
}
if (exported_global->mutable_ != mutable_) {
const char* kMutableNames[] = {"immutable", "mutable"};
PrintError(
"mutability mismatch in imported global, expected %s but got %s.",
kMutableNames[exported_global->mutable_], kMutableNames[mutable_]);
return wabt::Result::Error;
}
global_index_mapping_.push_back(export_->index);
num_global_imports_++;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnFunctionCount(Index count) {
for (Index i = 0; i < count; ++i)
func_index_mapping_.push_back(env_->GetFuncCount() + i);
func_fixups_.resize(count);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnFunction(Index index, Index sig_index) {
env_->EmplaceBackFunc(new DefinedFunc(TranslateSigIndexToEnv(sig_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTableCount(Index count) {
for (Index i = 0; i < count; ++i)
table_index_mapping_.push_back(env_->GetTableCount() + i);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTable(Index index,
Type elem_type,
const Limits* elem_limits) {
has_table = true;
env_->EmplaceBackTable(elem_type, *elem_limits);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemory(Index index,
const Limits* page_limits) {
if (module_->memory_index != kInvalidIndex) {
PrintError("only one memory allowed");
return wabt::Result::Error;
}
env_->EmplaceBackMemory(*page_limits);
module_->memory_index = env_->GetMemoryCount() - 1;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnGlobalCount(Index count) {
for (Index i = 0; i < count; ++i)
global_index_mapping_.push_back(env_->GetGlobalCount() + i);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::BeginGlobal(Index index,
Type type,
bool mutable_) {
assert(TranslateGlobalIndexToEnv(index) == env_->GetGlobalCount());
env_->EmplaceBackGlobal(type, mutable_);
init_expr_value_.type = Type::Void;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EndGlobalInitExpr(Index index) {
Global* global = GetGlobalByModuleIndex(index);
if (Failed(typechecker_.CheckType(init_expr_value_.type, global->type))) {
PrintError("type mismatch in global, expected %s but got %s.",
GetTypeName(global->type), GetTypeName(init_expr_value_.type));
return wabt::Result::Error;
}
global->typed_value = init_expr_value_;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprF32ConstExpr(Index index,
uint32_t value_bits) {
init_expr_value_.type = Type::F32;
init_expr_value_.value.f32_bits = value_bits;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprF64ConstExpr(Index index,
uint64_t value_bits) {
init_expr_value_.type = Type::F64;
init_expr_value_.value.f64_bits = value_bits;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprV128ConstExpr(Index index,
v128 value_bits) {
init_expr_value_.type = Type::V128;
init_expr_value_.value.vec128 = value_bits;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprGlobalGetExpr(Index index,
Index global_index) {
if (global_index >= num_global_imports_) {
PrintError("initializer expression can only reference an imported global");
return wabt::Result::Error;
}
Global* ref_global = GetGlobalByModuleIndex(global_index);
if (ref_global->mutable_) {
PrintError("initializer expression cannot reference a mutable global");
return wabt::Result::Error;
}
init_expr_value_ = ref_global->typed_value;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprI32ConstExpr(Index index,
uint32_t value) {
init_expr_value_.type = Type::I32;
init_expr_value_.value.i32 = value;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprI64ConstExpr(Index index,
uint64_t value) {
init_expr_value_.type = Type::I64;
init_expr_value_.value.i64 = value;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnInitExprRefNull(Index index) {
init_expr_value_.type = Type::Nullref;
init_expr_value_.set_ref({RefType::Null, kInvalidIndex});
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnExport(Index index,
ExternalKind kind,
Index item_index,
string_view name) {
switch (kind) {
case ExternalKind::Func:
item_index = TranslateFuncIndexToEnv(item_index);
break;
case ExternalKind::Table:
item_index = TranslateTableIndexToEnv(item_index);
break;
case ExternalKind::Memory:
item_index = module_->memory_index;
break;
case ExternalKind::Global: {
item_index = TranslateGlobalIndexToEnv(item_index);
Global* global = env_->GetGlobal(item_index);
if (global->mutable_ && !features_.mutable_globals_enabled()) {
PrintError("mutable globals cannot be exported");
return wabt::Result::Error;
}
break;
}
case ExternalKind::Event:
// TODO(karlschimpf) Define
WABT_FATAL("BinaryReaderInterp::OnExport(event) not implemented");
break;
}
return AppendExport(module_, kind, item_index, name);
}
wabt::Result BinaryReaderInterp::OnStartFunction(Index func_index) {
Index start_func_index = TranslateFuncIndexToEnv(func_index);
Func* start_func = env_->GetFunc(start_func_index);
FuncSignature* sig = env_->GetFuncSignature(start_func->sig_index);
if (sig->param_types.size() != 0) {
PrintError("start function must be nullary");
return wabt::Result::Error;
}
if (sig->result_types.size() != 0) {
PrintError("start function must not return anything");
return wabt::Result::Error;
}
module_->start_func_index = start_func_index;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElemSegmentCount(Index count) {
for (Index i = 0; i < count; ++i) {
elem_segment_index_mapping_.push_back(env_->GetElemSegmentCount() + i);
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::BeginElemSegment(Index index,
Index table_index,
uint8_t flags,
Type elem_type) {
segment_flags_ = flags;
segment_table_index_ = table_index;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::BeginElemSegmentInitExpr(Index index) {
init_expr_value_.type = Type::Void;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EndElemSegmentInitExpr(Index index) {
assert(!(segment_flags_ & SegPassive));
if (init_expr_value_.type != Type::I32) {
PrintError(
"type mismatch in elem segment initializer expression, expected i32 "
"but got %s",
GetTypeName(init_expr_value_.type));
return wabt::Result::Error;
}
table_offset_ = init_expr_value_.value.i32;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElemSegmentElemExprCount(Index index,
Index count) {
elem_segment_ = env_->EmplaceBackElemSegment();
if (segment_flags_ & SegPassive) {
elem_segment_info_ = nullptr;
} else {
// An active segment still is present in the segment index space, but
// cannot be used with `table.init` (it's as if it has already been
// dropped).
elem_segment_->dropped = true;
assert(segment_table_index_ != kInvalidIndex);
Table* table = GetTableByModuleIndex(segment_table_index_);
elem_segment_infos_.emplace_back(table, table_offset_);
elem_segment_info_ = &elem_segment_infos_.back();
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElemSegmentElemExpr_RefNull(
Index segment_index) {
assert(segment_flags_ & SegPassive);
elem_segment_->elems.push_back({RefType::Null, kInvalidIndex});
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElemSegmentElemExpr_RefFunc(
Index index,
Index func_index) {
Index max_func_index = func_index_mapping_.size();
if (func_index >= max_func_index) {
PrintError("invalid func_index: %" PRIindex " (max %" PRIindex ")",
func_index, max_func_index);
return wabt::Result::Error;
}
func_index = TranslateFuncIndexToEnv(func_index);
if (segment_flags_ & SegPassive) {
elem_segment_->elems.push_back({RefType::Func, func_index});
} else {
elem_segment_info_->src.push_back({RefType::Func, func_index});
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnDataCount(Index count) {
for (Index i = 0; i < count; ++i) {
data_segment_index_mapping_.push_back(env_->GetDataSegmentCount() + i);
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::BeginDataSegment(Index index,
Index memory_index,
uint8_t flags) {
segment_flags_ = flags;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::BeginDataSegmentInitExpr(Index index) {
init_expr_value_.type = Type::Void;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnDataSegmentData(Index index,
const void* src_data,
Address size) {
DataSegment* segment = env_->EmplaceBackDataSegment();
if (segment_flags_ & SegPassive) {
segment->data.resize(size);
if (size > 0) {
memcpy(segment->data.data(), src_data, size);
}
} else {
if (init_expr_value_.type != Type::I32) {
PrintError(
"type mismatch in data segment initializer expression, expected i32 "
"but got %s",
GetTypeName(init_expr_value_.type));
return wabt::Result::Error;
}
// An active segment still is present in the segment index space, but
// cannot be used with `memory.init` (it's as if it has already been
// dropped).
segment->dropped = true;
assert(module_->memory_index != kInvalidIndex);
Memory* memory = env_->GetMemory(module_->memory_index);
Address address = init_expr_value_.value.i32;
data_segment_infos_.emplace_back(memory, address, src_data, size);
}
return wabt::Result::Ok;
}
void BinaryReaderInterp::PushLabel(IstreamOffset offset,
IstreamOffset fixup_offset) {
label_stack_.emplace_back(offset, fixup_offset);
}
void BinaryReaderInterp::PopLabel() {
label_stack_.pop_back();
/* reduce the depth_fixups_ stack as well, but it may be smaller than
* label_stack_ so only do it conditionally. */
if (depth_fixups_.size() > label_stack_.size()) {
depth_fixups_.erase(depth_fixups_.begin() + label_stack_.size(),
depth_fixups_.end());
}
}
wabt::Result BinaryReaderInterp::BeginFunctionBody(Index index, Offset size) {
auto* func = cast<DefinedFunc>(GetFuncByModuleIndex(index));
FuncSignature* sig = env_->GetFuncSignature(func->sig_index);
func->offset = GetIstreamOffset();
func->local_decl_count = 0;
func->local_count = 0;
current_func_ = func;
depth_fixups_.clear();
label_stack_.clear();
/* fixup function references */
Index defined_index = TranslateModuleFuncIndexToDefined(index);
IstreamOffsetVector& fixups = func_fixups_[defined_index];
for (IstreamOffset fixup : fixups)
CHECK_RESULT(EmitI32At(fixup, func->offset));
/* append param types */
for (Type param_type : sig->param_types)
func->param_and_local_types.push_back(param_type);
CHECK_RESULT(typechecker_.BeginFunction(sig->result_types));
/* push implicit func label (equivalent to return) */
PushLabel(kInvalidIstreamOffset, kInvalidIstreamOffset);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::EndFunctionBody(Index index) {
FixupTopLabel();
Index drop_count, keep_count;
CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
CHECK_RESULT(typechecker_.EndFunction());
CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
CHECK_RESULT(EmitOpcode(Opcode::Return));
PopLabel();
current_func_ = nullptr;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLocalDeclCount(Index count) {
current_func_->local_decl_count = count;
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLocalDecl(Index decl_index,
Index count,
Type type) {
current_func_->local_count += count;
for (Index i = 0; i < count; ++i)
current_func_->param_and_local_types.push_back(type);
if (decl_index == current_func_->local_decl_count - 1) {
/* last local declaration, allocate space for all locals. */
CHECK_RESULT(EmitOpcode(Opcode::InterpAlloca));
CHECK_RESULT(EmitI32(current_func_->local_count));
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckHasMemory(wabt::Opcode opcode) {
if (module_->memory_index == kInvalidIndex) {
PrintError("%s requires an imported or defined memory.", opcode.GetName());
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckHasTable(wabt::Opcode opcode) {
if (!has_table) {
PrintError("%s requires an imported or defined table.", opcode.GetName());
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckAlign(uint32_t alignment_log2,
Address natural_alignment) {
if (alignment_log2 >= 32 || (1U << alignment_log2) > natural_alignment) {
PrintError("alignment must not be larger than natural alignment (%u)",
natural_alignment);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::CheckAtomicAlign(uint32_t alignment_log2,
Address natural_alignment) {
if (alignment_log2 >= 32 || (1U << alignment_log2) != natural_alignment) {
PrintError("alignment must be equal to natural alignment (%u)",
natural_alignment);
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnOpcode(Opcode opcode) {
if (current_func_ == nullptr || label_stack_.size() == 0) {
PrintError("Unexpected instruction after end of function");
return wabt::Result::Error;
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnUnaryExpr(wabt::Opcode opcode) {
CHECK_RESULT(typechecker_.OnUnary(opcode));
CHECK_RESULT(EmitOpcode(opcode));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTernaryExpr(wabt::Opcode opcode) {
CHECK_RESULT(typechecker_.OnTernary(opcode));
CHECK_RESULT(EmitOpcode(opcode));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnSimdLaneOpExpr(wabt::Opcode opcode,
uint64_t value) {
CHECK_RESULT(typechecker_.OnSimdLaneOp(opcode, value));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI8(static_cast<uint8_t>(value)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnSimdShuffleOpExpr(wabt::Opcode opcode,
v128 value) {
CHECK_RESULT(typechecker_.OnSimdShuffleOp(opcode, value));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitV128(value));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLoadSplatExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnLoad(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicLoadExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicLoad(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicStoreExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicStore(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicRmwExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicRmw(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicRmwCmpxchgExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicRmwCmpxchg(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnBinaryExpr(wabt::Opcode opcode) {
CHECK_RESULT(typechecker_.OnBinary(opcode));
CHECK_RESULT(EmitOpcode(opcode));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnBlockExpr(Type sig_type) {
TypeVector param_types, result_types;
GetBlockSignature(sig_type, &param_types, &result_types);
CHECK_RESULT(typechecker_.OnBlock(param_types, result_types));
PushLabel(kInvalidIstreamOffset, kInvalidIstreamOffset);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLoopExpr(Type sig_type) {
TypeVector param_types, result_types;
GetBlockSignature(sig_type, &param_types, &result_types);
CHECK_RESULT(typechecker_.OnLoop(param_types, result_types));
PushLabel(GetIstreamOffset(), kInvalidIstreamOffset);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnIfExpr(Type sig_type) {
TypeVector param_types, result_types;
GetBlockSignature(sig_type, &param_types, &result_types);
CHECK_RESULT(typechecker_.OnIf(param_types, result_types));
CHECK_RESULT(EmitOpcode(Opcode::InterpBrUnless));
IstreamOffset fixup_offset = GetIstreamOffset();
CHECK_RESULT(EmitI32(kInvalidIstreamOffset));
PushLabel(kInvalidIstreamOffset, fixup_offset);
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElseExpr() {
CHECK_RESULT(typechecker_.OnElse());
Label* label = TopLabel();
IstreamOffset fixup_cond_offset = label->fixup_offset;
CHECK_RESULT(EmitOpcode(Opcode::Br));
label->fixup_offset = GetIstreamOffset();
CHECK_RESULT(EmitI32(kInvalidIstreamOffset));
CHECK_RESULT(EmitI32At(fixup_cond_offset, GetIstreamOffset()));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnEndExpr() {
TypeChecker::Label* label;
CHECK_RESULT(typechecker_.GetLabel(0, &label));
LabelType label_type = label->label_type;
CHECK_RESULT(typechecker_.OnEnd());
if (label_type == LabelType::If || label_type == LabelType::Else) {
CHECK_RESULT(EmitI32At(TopLabel()->fixup_offset, GetIstreamOffset()));
}
FixupTopLabel();
PopLabel();
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnBrExpr(Index depth) {
Index drop_count, keep_count;
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
CHECK_RESULT(typechecker_.OnBr(depth));
CHECK_RESULT(EmitBr(depth, drop_count, keep_count));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnBrIfExpr(Index depth) {
Index drop_count, keep_count;
CHECK_RESULT(typechecker_.OnBrIf(depth));
CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
/* flip the br_if so if <cond> is true it can drop values from the stack */
CHECK_RESULT(EmitOpcode(Opcode::InterpBrUnless));
IstreamOffset fixup_br_offset = GetIstreamOffset();
CHECK_RESULT(EmitI32(kInvalidIstreamOffset));
CHECK_RESULT(EmitBr(depth, drop_count, keep_count));
CHECK_RESULT(EmitI32At(fixup_br_offset, GetIstreamOffset()));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnBrTableExpr(Index num_targets,
Index* target_depths,
Index default_target_depth) {
CHECK_RESULT(typechecker_.BeginBrTable());
CHECK_RESULT(EmitOpcode(Opcode::BrTable));
CHECK_RESULT(EmitI32(num_targets));
IstreamOffset fixup_table_offset = GetIstreamOffset();
CHECK_RESULT(EmitI32(kInvalidIstreamOffset));
/* not necessary for the interp, but it makes it easier to disassemble.
* This opcode specifies how many bytes of data follow. */
CHECK_RESULT(EmitOpcode(Opcode::InterpData));
CHECK_RESULT(EmitI32((num_targets + 1) * WABT_TABLE_ENTRY_SIZE));
CHECK_RESULT(EmitI32At(fixup_table_offset, GetIstreamOffset()));
for (Index i = 0; i <= num_targets; ++i) {
Index depth = i != num_targets ? target_depths[i] : default_target_depth;
CHECK_RESULT(typechecker_.OnBrTableTarget(depth));
CHECK_RESULT(EmitBrTableOffset(depth));
}
CHECK_RESULT(typechecker_.EndBrTable());
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnCallExpr(Index func_index) {
Func* func = GetFuncByModuleIndex(func_index);
FuncSignature* sig = env_->GetFuncSignature(func->sig_index);
CHECK_RESULT(typechecker_.OnCall(sig->param_types, sig->result_types));
if (func->is_host) {
CHECK_RESULT(EmitOpcode(Opcode::InterpCallHost));
CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index)));
} else {
CHECK_RESULT(EmitOpcode(Opcode::Call));
CHECK_RESULT(EmitFuncOffset(cast<DefinedFunc>(func), func_index));
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnCallIndirectExpr(Index sig_index,
Index table_index) {
if (!has_table) {
PrintError("found call_indirect operator, but no table");
return wabt::Result::Error;
}
FuncSignature* sig = GetSignatureByModuleIndex(sig_index);
CHECK_RESULT(
typechecker_.OnCallIndirect(sig->param_types, sig->result_types));
CHECK_RESULT(EmitOpcode(Opcode::CallIndirect));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index)));
CHECK_RESULT(EmitI32(TranslateSigIndexToEnv(sig_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnReturnCallExpr(Index func_index) {
Func* func = GetFuncByModuleIndex(func_index);
FuncSignature* sig = env_->GetFuncSignature(func->sig_index);
Index drop_count, keep_count;
CHECK_RESULT(GetReturnCallDropKeepCount(sig, 0, &drop_count, &keep_count));
// The typechecker must be run after we get the drop/keep counts, since it
// will change the type stack.
CHECK_RESULT(typechecker_.OnReturnCall(sig->param_types, sig->result_types));
CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
if (func->is_host) {
CHECK_RESULT(EmitOpcode(Opcode::InterpCallHost));
CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index)));
CHECK_RESULT(EmitOpcode(Opcode::Return));
} else {
CHECK_RESULT(EmitOpcode(Opcode::ReturnCall));
CHECK_RESULT(EmitFuncOffset(cast<DefinedFunc>(func), func_index));
}
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnReturnCallIndirectExpr(Index sig_index,
Index table_index) {
if (!has_table) {
PrintError("found return_call_indirect operator, but no table");
return wabt::Result::Error;
}
FuncSignature* sig = GetSignatureByModuleIndex(sig_index);
Index drop_count, keep_count;
// +1 to include the index of the function.
CHECK_RESULT(GetReturnCallDropKeepCount(sig, +1, &drop_count, &keep_count));
// The typechecker must be run after we get the drop/keep counts, since it
// changes the type stack.
CHECK_RESULT(
typechecker_.OnReturnCallIndirect(sig->param_types, sig->result_types));
CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
CHECK_RESULT(EmitOpcode(Opcode::ReturnCallIndirect));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index)));
CHECK_RESULT(EmitI32(TranslateSigIndexToEnv(sig_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnCompareExpr(wabt::Opcode opcode) {
return OnBinaryExpr(opcode);
}
wabt::Result BinaryReaderInterp::OnConvertExpr(wabt::Opcode opcode) {
return OnUnaryExpr(opcode);
}
wabt::Result BinaryReaderInterp::OnDropExpr() {
CHECK_RESULT(typechecker_.OnDrop());
CHECK_RESULT(EmitOpcode(Opcode::Drop));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnI32ConstExpr(uint32_t value) {
CHECK_RESULT(typechecker_.OnConst(Type::I32));
CHECK_RESULT(EmitOpcode(Opcode::I32Const));
CHECK_RESULT(EmitI32(value));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnI64ConstExpr(uint64_t value) {
CHECK_RESULT(typechecker_.OnConst(Type::I64));
CHECK_RESULT(EmitOpcode(Opcode::I64Const));
CHECK_RESULT(EmitI64(value));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnF32ConstExpr(uint32_t value_bits) {
CHECK_RESULT(typechecker_.OnConst(Type::F32));
CHECK_RESULT(EmitOpcode(Opcode::F32Const));
CHECK_RESULT(EmitI32(value_bits));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnF64ConstExpr(uint64_t value_bits) {
CHECK_RESULT(typechecker_.OnConst(Type::F64));
CHECK_RESULT(EmitOpcode(Opcode::F64Const));
CHECK_RESULT(EmitI64(value_bits));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnV128ConstExpr(v128 value_bits) {
CHECK_RESULT(typechecker_.OnConst(Type::V128));
CHECK_RESULT(EmitOpcode(Opcode::V128Const));
CHECK_RESULT(EmitV128(value_bits));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnGlobalGetExpr(Index global_index) {
CHECK_RESULT(CheckGlobal(global_index));
Type type = GetGlobalTypeByModuleIndex(global_index);
CHECK_RESULT(typechecker_.OnGlobalGet(type));
CHECK_RESULT(EmitOpcode(Opcode::GlobalGet));
CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnGlobalSetExpr(Index global_index) {
CHECK_RESULT(CheckGlobal(global_index));
Global* global = GetGlobalByModuleIndex(global_index);
if (!global->mutable_) {
PrintError("can't global.set on immutable global at index %" PRIindex ".",
global_index);
return wabt::Result::Error;
}
CHECK_RESULT(typechecker_.OnGlobalSet(global->type));
CHECK_RESULT(EmitOpcode(Opcode::GlobalSet));
CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index)));
return wabt::Result::Ok;
}
Index BinaryReaderInterp::TranslateLocalIndex(Index local_index) {
return typechecker_.type_stack_size() +
current_func_->param_and_local_types.size() - local_index;
}
wabt::Result BinaryReaderInterp::OnLocalGetExpr(Index local_index) {
CHECK_RESULT(CheckLocal(local_index));
Type type = GetLocalTypeByIndex(current_func_, local_index);
// Get the translated index before calling typechecker_.OnLocalGet because it
// will update the type stack size. We need the index to be relative to the
// old stack size.
Index translated_local_index = TranslateLocalIndex(local_index);
CHECK_RESULT(typechecker_.OnLocalGet(type));
CHECK_RESULT(EmitOpcode(Opcode::LocalGet));
CHECK_RESULT(EmitI32(translated_local_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLocalSetExpr(Index local_index) {
CHECK_RESULT(CheckLocal(local_index));
Type type = GetLocalTypeByIndex(current_func_, local_index);
CHECK_RESULT(typechecker_.OnLocalSet(type));
CHECK_RESULT(EmitOpcode(Opcode::LocalSet));
CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLocalTeeExpr(Index local_index) {
CHECK_RESULT(CheckLocal(local_index));
Type type = GetLocalTypeByIndex(current_func_, local_index);
CHECK_RESULT(typechecker_.OnLocalTee(type));
CHECK_RESULT(EmitOpcode(Opcode::LocalTee));
CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnLoadExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnLoad(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnStoreExpr(wabt::Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnStore(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemoryGrowExpr() {
CHECK_RESULT(CheckHasMemory(wabt::Opcode::MemoryGrow));
CHECK_RESULT(typechecker_.OnMemoryGrow());
CHECK_RESULT(EmitOpcode(Opcode::MemoryGrow));
CHECK_RESULT(EmitI32(module_->memory_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemorySizeExpr() {
CHECK_RESULT(CheckHasMemory(wabt::Opcode::MemorySize));
CHECK_RESULT(typechecker_.OnMemorySize());
CHECK_RESULT(EmitOpcode(Opcode::MemorySize));
CHECK_RESULT(EmitI32(module_->memory_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnRefFuncExpr(Index func_index) {
CHECK_RESULT(typechecker_.OnRefFuncExpr(func_index));
CHECK_RESULT(EmitOpcode(Opcode::RefFunc));
CHECK_RESULT(EmitI32(func_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnRefNullExpr() {
CHECK_RESULT(typechecker_.OnRefNullExpr());
CHECK_RESULT(EmitOpcode(Opcode::RefNull));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnRefIsNullExpr() {
CHECK_RESULT(typechecker_.OnRefIsNullExpr());
CHECK_RESULT(EmitOpcode(Opcode::RefIsNull));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnNopExpr() {
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnReturnExpr() {
Index drop_count, keep_count;
CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count));
CHECK_RESULT(typechecker_.OnReturn());
CHECK_RESULT(EmitDropKeep(drop_count, keep_count));
CHECK_RESULT(EmitOpcode(Opcode::Return));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnSelectExpr() {
CHECK_RESULT(typechecker_.OnSelect());
CHECK_RESULT(EmitOpcode(Opcode::Select));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnUnreachableExpr() {
CHECK_RESULT(typechecker_.OnUnreachable());
CHECK_RESULT(EmitOpcode(Opcode::Unreachable));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicWaitExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicWait(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnAtomicNotifyExpr(Opcode opcode,
uint32_t alignment_log2,
Address offset) {
CHECK_RESULT(CheckHasMemory(opcode));
CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize()));
CHECK_RESULT(typechecker_.OnAtomicNotify(opcode));
CHECK_RESULT(EmitOpcode(opcode));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(offset));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemoryCopyExpr() {
CHECK_RESULT(CheckHasMemory(Opcode::MemoryCopy));
CHECK_RESULT(typechecker_.OnMemoryCopy());
CHECK_RESULT(EmitOpcode(Opcode::MemoryCopy));
CHECK_RESULT(EmitI32(module_->memory_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnDataDropExpr(Index segment_index) {
CHECK_RESULT(CheckDataSegment(segment_index));
CHECK_RESULT(typechecker_.OnDataDrop(segment_index));
CHECK_RESULT(EmitOpcode(Opcode::DataDrop));
CHECK_RESULT(EmitI32(TranslateDataSegmentIndexToEnv(segment_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemoryFillExpr() {
CHECK_RESULT(CheckHasMemory(Opcode::MemoryFill));
CHECK_RESULT(typechecker_.OnMemoryFill());
CHECK_RESULT(EmitOpcode(Opcode::MemoryFill));
CHECK_RESULT(EmitI32(module_->memory_index));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnMemoryInitExpr(Index segment_index) {
CHECK_RESULT(CheckHasMemory(Opcode::MemoryInit));
CHECK_RESULT(CheckDataSegment(segment_index));
CHECK_RESULT(typechecker_.OnMemoryInit(segment_index));
CHECK_RESULT(EmitOpcode(Opcode::MemoryInit));
CHECK_RESULT(EmitI32(module_->memory_index));
CHECK_RESULT(EmitI32(TranslateDataSegmentIndexToEnv(segment_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTableGetExpr(Index table_index) {
const Table* table = GetTableByModuleIndex(table_index);
CHECK_RESULT(typechecker_.OnTableGet(table->elem_type));
CHECK_RESULT(EmitOpcode(Opcode::TableGet));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTableSetExpr(Index table_index) {
const Table* table = GetTableByModuleIndex(table_index);
CHECK_RESULT(typechecker_.OnTableSet(table->elem_type));
CHECK_RESULT(EmitOpcode(Opcode::TableSet));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTableCopyExpr(Index dst_index,
Index src_index) {
CHECK_RESULT(CheckHasTable(Opcode::TableCopy));
CHECK_RESULT(typechecker_.OnTableCopy());
CHECK_RESULT(EmitOpcode(Opcode::TableCopy));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(dst_index)));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(src_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnElemDropExpr(Index segment_index) {
CHECK_RESULT(CheckElemSegment(segment_index));
CHECK_RESULT(typechecker_.OnElemDrop(segment_index));
CHECK_RESULT(EmitOpcode(Opcode::ElemDrop));
CHECK_RESULT(EmitI32(TranslateElemSegmentIndexToEnv(segment_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::OnTableInitExpr(Index segment_index,
Index table_index) {
CHECK_RESULT(CheckHasTable(Opcode::TableInit));
CHECK_RESULT(CheckElemSegment(segment_index));
CHECK_RESULT(typechecker_.OnTableInit(table_index, segment_index));
CHECK_RESULT(EmitOpcode(Opcode::TableInit));
CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index)));
CHECK_RESULT(EmitI32(TranslateElemSegmentIndexToEnv(segment_index)));
return wabt::Result::Ok;
}
wabt::Result BinaryReaderInterp::InitializeSegments() {
// The MVP requires that all segments are bounds-checked before being copied
// into the table or memory. The bulk memory proposal changes this behavior;
// instead, each segment is copied in order. If any segment fails, then no
// further segments are copied. Any data that was written persists.
enum Pass { Check = 0, Init = 1 };
int pass = features_.bulk_memory_enabled() ? Init : Check;
for (; pass <= Init; ++pass) {
for (const ElemSegmentInfo& info : elem_segment_infos_) {
uint32_t table_size = info.table->size();
uint32_t segment_size = info.src.size();
uint32_t copy_size = segment_size;
bool ok = ClampToBounds(info.dst, &copy_size, table_size);
if (pass == Init && copy_size > 0) {
std::copy(info.src.begin(), info.src.begin() + copy_size,
info.table->entries.begin() + info.dst);
}
if (!ok) {
PrintError("elem segment is out of bounds: [%u, %" PRIu64
") >= max value %u",
info.dst, static_cast<uint64_t>(info.dst) + segment_size,
table_size);
return wabt::Result::Error;
}
}
for (const DataSegmentInfo& info : data_segment_infos_) {
uint32_t memory_size = info.memory->data.size();
uint32_t segment_size = info.size;
uint32_t copy_size = segment_size;
bool ok = ClampToBounds(info.dst, &copy_size, memory_size);
if (pass == Init && copy_size > 0) {
const char* src_data = static_cast<const char*>(info.src);
std::copy(src_data, src_data + copy_size,
info.memory->data.begin() + info.dst);
}
if (!ok) {
PrintError("data segment is out of bounds: [%u, %" PRIu64
") >= max value %u",
info.dst, static_cast<uint64_t>(info.dst) + segment_size,
memory_size);
return wabt::Result::Error;
}
}
}
return wabt::Result::Ok;
}
} // end anonymous namespace
wabt::Result ReadBinaryInterp(Environment* env,
const void* data,
size_t size,
const ReadBinaryOptions& options,
Errors* errors,
DefinedModule** out_module) {
// Need to mark before taking ownership of env->istream.
Environment::MarkPoint mark = env->Mark();
std::unique_ptr<OutputBuffer> istream = env->ReleaseIstream();
IstreamOffset istream_offset = istream->size();
DefinedModule* module = new DefinedModule(env);
BinaryReaderInterp reader(env, module, std::move(istream), errors,
options.features);
env->EmplaceBackModule(module);
wabt::Result result = ReadBinary(data, size, &reader, options);
env->SetIstream(reader.ReleaseOutputBuffer());
if (Succeeded(result)) {
module->istream_start = istream_offset;
module->istream_end = env->istream().size();
result = reader.InitializeSegments();
if (Succeeded(result)) {
*out_module = module;
} else {
// We failed to initialize data and element segments, but we can't reset
// to the mark point. An element segment may have initialized an imported
// table with a function from this module, which is still callable.
*out_module = nullptr;
}
} else {
env->ResetToMarkPoint(mark);
*out_module = nullptr;
}
return result;
}
} // namespace wabt