| /* |
| * 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 <map> |
| #include <set> |
| |
| #include "src/binary-reader-nop.h" |
| #include "src/feature.h" |
| #include "src/interp/interp.h" |
| #include "src/shared-validator.h" |
| #include "src/stream.h" |
| |
| namespace wabt { |
| namespace interp { |
| |
| namespace { |
| |
| ValueTypes ToInterp(Index count, Type* types) { |
| return ValueTypes(&types[0], &types[count]); |
| } |
| |
| Mutability ToMutability(bool mut) { |
| return mut ? Mutability::Var : Mutability::Const; |
| } |
| |
| SegmentMode ToSegmentMode(uint8_t flags) { |
| if ((flags & SegDeclared) == SegDeclared) { |
| return SegmentMode::Declared; |
| } else if ((flags & SegPassive) == SegPassive) { |
| return SegmentMode::Passive; |
| } else { |
| return SegmentMode::Active; |
| } |
| } |
| |
| // This is only used to distinguish try blocks and all other blocks, |
| // so there are only two kinds. |
| enum class LabelKind { Block, Try }; |
| |
| struct Label { |
| LabelKind kind; |
| Istream::Offset offset; |
| Istream::Offset fixup_offset; |
| // Only needs to be set for try blocks. |
| u32 handler_desc_index; |
| }; |
| |
| struct FixupMap { |
| using Offset = Istream::Offset; |
| using Fixups = std::vector<Offset>; |
| |
| void Clear(); |
| void Append(Index, Offset); |
| void Resolve(Istream&, Index); |
| |
| std::map<Index, Fixups> map; |
| }; |
| |
| class BinaryReaderInterp : public BinaryReaderNop { |
| public: |
| BinaryReaderInterp(ModuleDesc* module, |
| Errors* errors, |
| const Features& features); |
| |
| ValueType GetType(InitExpr); |
| |
| // Implement BinaryReader. |
| bool OnError(const Error&) override; |
| |
| Result EndModule() override; |
| |
| Result OnTypeCount(Index count) override; |
| Result OnFuncType(Index index, |
| Index param_count, |
| Type* param_types, |
| Index result_count, |
| Type* result_types) override; |
| |
| Result OnImportFunc(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index func_index, |
| Index sig_index) override; |
| Result OnImportTable(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index table_index, |
| Type elem_type, |
| const Limits* elem_limits) override; |
| Result OnImportMemory(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index memory_index, |
| const Limits* page_limits) override; |
| Result OnImportGlobal(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index global_index, |
| Type type, |
| bool mutable_) override; |
| Result OnImportTag(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index tag_index, |
| Index sig_index) override; |
| |
| Result OnFunctionCount(Index count) override; |
| Result OnFunction(Index index, Index sig_index) override; |
| |
| Result OnTableCount(Index count) override; |
| Result OnTable(Index index, |
| Type elem_type, |
| const Limits* elem_limits) override; |
| |
| Result OnMemoryCount(Index count) override; |
| Result OnMemory(Index index, const Limits* limits) override; |
| |
| Result OnGlobalCount(Index count) override; |
| Result BeginGlobal(Index index, Type type, bool mutable_) override; |
| Result BeginGlobalInitExpr(Index index) override; |
| Result EndGlobalInitExpr(Index index) override; |
| |
| Result OnTagCount(Index count) override; |
| Result OnTagType(Index index, Index sig_index) override; |
| |
| Result OnExport(Index index, |
| ExternalKind kind, |
| Index item_index, |
| string_view name) override; |
| |
| Result OnStartFunction(Index func_index) override; |
| |
| Result BeginFunctionBody(Index index, Offset size) override; |
| Result OnLocalDeclCount(Index count) override; |
| Result OnLocalDecl(Index decl_index, Index count, Type type) override; |
| |
| Result OnOpcode(Opcode Opcode) override; |
| Result OnAtomicLoadExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnAtomicStoreExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnAtomicRmwExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnAtomicRmwCmpxchgExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnAtomicWaitExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnAtomicFenceExpr(uint32_t consistency_model) override; |
| Result OnAtomicNotifyExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnBinaryExpr(Opcode opcode) override; |
| Result OnBlockExpr(Type sig_type) override; |
| Result OnBrExpr(Index depth) override; |
| Result OnBrIfExpr(Index depth) override; |
| Result OnBrTableExpr(Index num_targets, |
| Index* target_depths, |
| Index default_target_depth) override; |
| Result OnCallExpr(Index func_index) override; |
| Result OnCallIndirectExpr(Index sig_index, Index table_index) override; |
| Result OnCatchExpr(Index tag_index) override; |
| Result OnCatchAllExpr() override; |
| Result OnDelegateExpr(Index depth) override; |
| Result OnReturnCallExpr(Index func_index) override; |
| Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override; |
| Result OnCompareExpr(Opcode opcode) override; |
| Result OnConvertExpr(Opcode opcode) override; |
| Result OnDropExpr() override; |
| Result OnElseExpr() override; |
| Result OnEndExpr() override; |
| Result OnF32ConstExpr(uint32_t value_bits) override; |
| Result OnF64ConstExpr(uint64_t value_bits) override; |
| Result OnV128ConstExpr(v128 value_bits) override; |
| Result OnGlobalGetExpr(Index global_index) override; |
| Result OnGlobalSetExpr(Index global_index) override; |
| Result OnI32ConstExpr(uint32_t value) override; |
| Result OnI64ConstExpr(uint64_t value) override; |
| Result OnIfExpr(Type sig_type) override; |
| Result OnLoadExpr(Opcode opcode, |
| Index memidx, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnLocalGetExpr(Index local_index) override; |
| Result OnLocalSetExpr(Index local_index) override; |
| Result OnLocalTeeExpr(Index local_index) override; |
| Result OnLoopExpr(Type sig_type) override; |
| Result OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) override; |
| Result OnDataDropExpr(Index segment_index) override; |
| Result OnMemoryGrowExpr(Index memidx) override; |
| Result OnMemoryFillExpr(Index memidx) override; |
| Result OnMemoryInitExpr(Index segment_index, Index memidx) override; |
| Result OnMemorySizeExpr(Index memidx) override; |
| Result OnRefFuncExpr(Index func_index) override; |
| Result OnRefNullExpr(Type type) override; |
| Result OnRefIsNullExpr() override; |
| Result OnNopExpr() override; |
| Result OnRethrowExpr(Index depth) override; |
| Result OnReturnExpr() override; |
| Result OnSelectExpr(Index result_count, Type* result_types) override; |
| Result OnStoreExpr(Opcode opcode, |
| Index memidx, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnUnaryExpr(Opcode opcode) override; |
| Result OnTableCopyExpr(Index dst_index, Index src_index) override; |
| Result OnTableGetExpr(Index table_index) override; |
| Result OnTableSetExpr(Index table_index) override; |
| Result OnTableGrowExpr(Index table_index) override; |
| Result OnTableSizeExpr(Index table_index) override; |
| Result OnTableFillExpr(Index table_index) override; |
| Result OnElemDropExpr(Index segment_index) override; |
| Result OnTableInitExpr(Index segment_index, Index table_index) override; |
| Result OnTernaryExpr(Opcode opcode) override; |
| Result OnThrowExpr(Index tag_index) override; |
| Result OnTryExpr(Type sig_type) override; |
| Result OnUnreachableExpr() override; |
| Result EndFunctionBody(Index index) override; |
| Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; |
| Result OnSimdLoadLaneExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset, |
| uint64_t value) override; |
| Result OnSimdStoreLaneExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset, |
| uint64_t value) override; |
| Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; |
| Result OnLoadSplatExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| Result OnLoadZeroExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset) override; |
| |
| Result OnElemSegmentCount(Index count) override; |
| Result BeginElemSegment(Index index, |
| Index table_index, |
| uint8_t flags) override; |
| Result BeginElemSegmentInitExpr(Index index) override; |
| Result EndElemSegmentInitExpr(Index index) override; |
| Result OnElemSegmentElemType(Index index, Type elem_type) override; |
| Result OnElemSegmentElemExprCount(Index index, Index count) override; |
| Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override; |
| Result OnElemSegmentElemExpr_RefFunc(Index segment_index, |
| Index func_index) override; |
| |
| Result OnDataCount(Index count) override; |
| Result BeginDataSegmentInitExpr(Index index) override; |
| Result EndDataSegmentInitExpr(Index index) override; |
| Result BeginDataSegment(Index index, |
| Index memory_index, |
| uint8_t flags) override; |
| Result OnDataSegmentData(Index index, |
| const void* data, |
| Address size) override; |
| |
| private: |
| Label* GetLabel(Index depth); |
| Label* GetNearestTryLabel(Index depth); |
| Label* TopLabel(); |
| void PushLabel(LabelKind label = LabelKind::Block, |
| Istream::Offset offset = Istream::kInvalidOffset, |
| Istream::Offset fixup_offset = Istream::kInvalidOffset, |
| u32 handler_desc_index = kInvalidIndex); |
| void PopLabel(); |
| |
| void PrintError(const char* format, ...); |
| |
| Result GetDropCount(Index keep_count, |
| size_t type_stack_limit, |
| Index* out_drop_count); |
| Result GetBrDropKeepCount(Index depth, |
| Index* out_drop_count, |
| Index* out_keep_count); |
| Result GetReturnDropKeepCount(Index* out_drop_count, Index* out_keep_count); |
| Result GetReturnCallDropKeepCount(const FuncType&, |
| Index keep_extra, |
| Index* out_drop_count, |
| Index* out_keep_count); |
| Result BeginInitExpr(); |
| Result EndInitExpr(); |
| Result CheckEmptyInitExpr(); |
| Result CheckNotInitExpr(); |
| |
| void EmitBr(Index depth, |
| Index drop_count, |
| Index keep_count, |
| Index catch_drop_count); |
| void FixupTopLabel(); |
| u32 GetFuncOffset(Index func_index); |
| |
| Index TranslateLocalIndex(Index local_index); |
| |
| Index num_func_imports() const; |
| |
| Errors* errors_ = nullptr; |
| ModuleDesc& module_; |
| Istream& istream_; |
| |
| SharedValidator validator_; |
| |
| FuncDesc* func_; |
| std::vector<Label> label_stack_; |
| FixupMap depth_fixups_; |
| FixupMap func_fixups_; |
| |
| bool reading_init_expr_ = false; |
| InitExpr init_expr_; |
| u32 local_decl_count_; |
| u32 local_count_; |
| |
| std::vector<FuncType> func_types_; // Includes imported and defined. |
| std::vector<TableType> table_types_; // Includes imported and defined. |
| std::vector<MemoryType> memory_types_; // Includes imported and defined. |
| std::vector<GlobalType> global_types_; // Includes imported and defined. |
| std::vector<TagType> tag_types_; // Includes imported and defined. |
| |
| static const Index kMemoryIndex0 = 0; |
| |
| // TODO: Use this in all locations below, for now. In the future we'll want |
| // to use the real locations. |
| static const Location loc; |
| }; |
| |
| // static |
| const Location BinaryReaderInterp::loc{kInvalidOffset}; |
| |
| void FixupMap::Clear() { |
| map.clear(); |
| } |
| |
| void FixupMap::Append(Index index, Offset offset) { |
| map[index].push_back(offset); |
| } |
| |
| void FixupMap::Resolve(Istream& istream, Index index) { |
| auto iter = map.find(index); |
| if (iter == map.end()) { |
| return; |
| } |
| for (Offset offset : iter->second) { |
| istream.ResolveFixupU32(offset); |
| } |
| map.erase(iter); |
| } |
| |
| BinaryReaderInterp::BinaryReaderInterp(ModuleDesc* module, |
| Errors* errors, |
| const Features& features) |
| : errors_(errors), |
| module_(*module), |
| istream_(module->istream), |
| validator_(errors, ValidateOptions(features)) {} |
| |
| Label* BinaryReaderInterp::GetLabel(Index depth) { |
| assert(depth < label_stack_.size()); |
| return &label_stack_[label_stack_.size() - depth - 1]; |
| } |
| |
| Label* BinaryReaderInterp::GetNearestTryLabel(Index depth) { |
| for (size_t i = depth; i < label_stack_.size(); i++) { |
| Label* label = &label_stack_[label_stack_.size() - i - 1]; |
| if (label->kind == LabelKind::Try) { |
| return label; |
| } |
| } |
| return nullptr; |
| } |
| |
| 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); |
| } |
| |
| Result BinaryReaderInterp::GetDropCount(Index keep_count, |
| size_t type_stack_limit, |
| Index* out_drop_count) { |
| assert(validator_.type_stack_size() >= type_stack_limit); |
| Index type_stack_count = validator_.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 Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::GetBrDropKeepCount(Index depth, |
| Index* out_drop_count, |
| Index* out_keep_count) { |
| SharedValidator::Label* label; |
| CHECK_RESULT(validator_.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 Result::Ok; |
| } |
| |
| 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 += validator_.GetLocalCount(); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::GetReturnCallDropKeepCount(const FuncType& func_type, |
| Index keep_extra, |
| Index* out_drop_count, |
| Index* out_keep_count) { |
| Index keep_count = static_cast<Index>(func_type.params.size()) + keep_extra; |
| CHECK_RESULT(GetDropCount(keep_count, 0, out_drop_count)); |
| *out_drop_count += validator_.GetLocalCount(); |
| *out_keep_count = keep_count; |
| return Result::Ok; |
| } |
| |
| void BinaryReaderInterp::EmitBr(Index depth, |
| Index drop_count, |
| Index keep_count, |
| Index catch_drop_count) { |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.EmitCatchDrop(catch_drop_count); |
| Istream::Offset offset = GetLabel(depth)->offset; |
| istream_.Emit(Opcode::Br); |
| if (offset == Istream::kInvalidOffset) { |
| // depth_fixups_ stores the depth counting up from zero, where zero is the |
| // top-level function scope. |
| depth_fixups_.Append(label_stack_.size() - 1 - depth, istream_.end()); |
| } |
| istream_.Emit(offset); |
| } |
| |
| void BinaryReaderInterp::FixupTopLabel() { |
| depth_fixups_.Resolve(istream_, label_stack_.size() - 1); |
| } |
| |
| u32 BinaryReaderInterp::GetFuncOffset(Index func_index) { |
| assert(func_index >= num_func_imports()); |
| FuncDesc& func = module_.funcs[func_index - num_func_imports()]; |
| if (func.code_offset == Istream::kInvalidOffset) { |
| func_fixups_.Append(func_index, istream_.end()); |
| } |
| return func.code_offset; |
| } |
| |
| bool BinaryReaderInterp::OnError(const Error& error) { |
| errors_->push_back(error); |
| return true; |
| } |
| |
| Result BinaryReaderInterp::EndModule() { |
| CHECK_RESULT(validator_.EndModule()); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTypeCount(Index count) { |
| module_.func_types.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnFuncType(Index index, |
| Index param_count, |
| Type* param_types, |
| Index result_count, |
| Type* result_types) { |
| CHECK_RESULT(validator_.OnFuncType(loc, param_count, param_types, |
| result_count, result_types, index)); |
| module_.func_types.push_back(FuncType(ToInterp(param_count, param_types), |
| ToInterp(result_count, result_types))); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnImportFunc(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index func_index, |
| Index sig_index) { |
| CHECK_RESULT(validator_.OnFunction(loc, Var(sig_index))); |
| FuncType& func_type = module_.func_types[sig_index]; |
| module_.imports.push_back(ImportDesc{ImportType( |
| module_name.to_string(), field_name.to_string(), func_type.Clone())}); |
| func_types_.push_back(func_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnImportTable(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index table_index, |
| Type elem_type, |
| const Limits* elem_limits) { |
| CHECK_RESULT(validator_.OnTable(loc, elem_type, *elem_limits)); |
| TableType table_type{elem_type, *elem_limits}; |
| module_.imports.push_back(ImportDesc{ImportType( |
| module_name.to_string(), field_name.to_string(), table_type.Clone())}); |
| table_types_.push_back(table_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnImportMemory(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index memory_index, |
| const Limits* page_limits) { |
| CHECK_RESULT(validator_.OnMemory(loc, *page_limits)); |
| MemoryType memory_type{*page_limits}; |
| module_.imports.push_back(ImportDesc{ImportType( |
| module_name.to_string(), field_name.to_string(), memory_type.Clone())}); |
| memory_types_.push_back(memory_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnImportGlobal(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index global_index, |
| Type type, |
| bool mutable_) { |
| CHECK_RESULT(validator_.OnGlobalImport(loc, type, mutable_)); |
| GlobalType global_type{type, ToMutability(mutable_)}; |
| module_.imports.push_back(ImportDesc{ImportType( |
| module_name.to_string(), field_name.to_string(), global_type.Clone())}); |
| global_types_.push_back(global_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnImportTag(Index import_index, |
| string_view module_name, |
| string_view field_name, |
| Index tag_index, |
| Index sig_index) { |
| CHECK_RESULT(validator_.OnTag(loc, Var(sig_index))); |
| FuncType& func_type = module_.func_types[sig_index]; |
| TagType tag_type{TagAttr::Exception, func_type.params}; |
| module_.imports.push_back(ImportDesc{ImportType( |
| module_name.to_string(), field_name.to_string(), tag_type.Clone())}); |
| tag_types_.push_back(tag_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnFunctionCount(Index count) { |
| module_.funcs.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnFunction(Index index, Index sig_index) { |
| CHECK_RESULT(validator_.OnFunction(loc, Var(sig_index))); |
| FuncType& func_type = module_.func_types[sig_index]; |
| module_.funcs.push_back(FuncDesc{func_type, {}, 0, {}}); |
| func_types_.push_back(func_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableCount(Index count) { |
| module_.tables.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTable(Index index, |
| Type elem_type, |
| const Limits* elem_limits) { |
| CHECK_RESULT(validator_.OnTable(loc, elem_type, *elem_limits)); |
| TableType table_type{elem_type, *elem_limits}; |
| module_.tables.push_back(TableDesc{table_type}); |
| table_types_.push_back(table_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemoryCount(Index count) { |
| module_.memories.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemory(Index index, const Limits* limits) { |
| CHECK_RESULT(validator_.OnMemory(loc, *limits)); |
| MemoryType memory_type{*limits}; |
| module_.memories.push_back(MemoryDesc{memory_type}); |
| memory_types_.push_back(memory_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnGlobalCount(Index count) { |
| module_.globals.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginGlobal(Index index, Type type, bool mutable_) { |
| CHECK_RESULT(validator_.OnGlobal(loc, type, mutable_)); |
| GlobalType global_type{type, ToMutability(mutable_)}; |
| module_.globals.push_back(GlobalDesc{global_type, InitExpr{}}); |
| global_types_.push_back(global_type); |
| init_expr_.kind = InitExprKind::None; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginGlobalInitExpr(Index index) { |
| return BeginInitExpr(); |
| } |
| |
| Result BinaryReaderInterp::EndInitExpr() { |
| assert(reading_init_expr_); |
| reading_init_expr_ = false; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginInitExpr() { |
| assert(!reading_init_expr_); |
| reading_init_expr_ = true; |
| init_expr_.kind = InitExprKind::None; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::CheckEmptyInitExpr() { |
| assert(reading_init_expr_); |
| if (init_expr_.kind != InitExprKind::None) { |
| PrintError("expected END opcode after initializer expression"); |
| return Result::Error; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::CheckNotInitExpr() { |
| if (reading_init_expr_) { |
| PrintError("Unepxected opcode in init expr"); |
| return Result::Error; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::EndGlobalInitExpr(Index index) { |
| CHECK_RESULT(EndInitExpr()); |
| switch (init_expr_.kind) { |
| case InitExprKind::I32: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::I32)); |
| break; |
| |
| case InitExprKind::I64: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::I64)); |
| break; |
| |
| case InitExprKind::F32: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::F32)); |
| break; |
| |
| case InitExprKind::F64: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::F64)); |
| break; |
| |
| case InitExprKind::V128: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Const(loc, ValueType::V128)); |
| break; |
| |
| case InitExprKind::GlobalGet: |
| CHECK_RESULT( |
| validator_.OnGlobalInitExpr_GlobalGet(loc, Var(init_expr_.index_))); |
| break; |
| |
| case InitExprKind::RefNull: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_RefNull(loc, init_expr_.type_)); |
| break; |
| |
| case InitExprKind::RefFunc: |
| CHECK_RESULT( |
| validator_.OnGlobalInitExpr_RefFunc(loc, Var(init_expr_.index_))); |
| break; |
| |
| default: |
| CHECK_RESULT(validator_.OnGlobalInitExpr_Other(loc)); |
| break; |
| } |
| |
| GlobalDesc& global = module_.globals.back(); |
| global.init = init_expr_; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTagCount(Index count) { |
| module_.tags.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTagType(Index index, Index sig_index) { |
| CHECK_RESULT(validator_.OnTag(loc, Var(sig_index))); |
| FuncType& func_type = module_.func_types[sig_index]; |
| TagType tag_type{TagAttr::Exception, func_type.params}; |
| module_.tags.push_back(TagDesc{tag_type}); |
| tag_types_.push_back(tag_type); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnExport(Index index, |
| ExternalKind kind, |
| Index item_index, |
| string_view name) { |
| CHECK_RESULT(validator_.OnExport(loc, kind, Var(item_index), name)); |
| |
| std::unique_ptr<ExternType> type; |
| switch (kind) { |
| case ExternalKind::Func: type = func_types_[item_index].Clone(); break; |
| case ExternalKind::Table: type = table_types_[item_index].Clone(); break; |
| case ExternalKind::Memory: type = memory_types_[item_index].Clone(); break; |
| case ExternalKind::Global: type = global_types_[item_index].Clone(); break; |
| case ExternalKind::Tag: type = tag_types_[item_index].Clone(); break; |
| } |
| module_.exports.push_back( |
| ExportDesc{ExportType(name.to_string(), std::move(type)), item_index}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnStartFunction(Index func_index) { |
| CHECK_RESULT(validator_.OnStart(loc, Var(func_index))); |
| module_.starts.push_back(StartDesc{func_index}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemSegmentCount(Index count) { |
| module_.elems.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginElemSegment(Index index, |
| Index table_index, |
| uint8_t flags) { |
| auto mode = ToSegmentMode(flags); |
| CHECK_RESULT(validator_.OnElemSegment(loc, Var(table_index), mode)); |
| |
| ElemDesc desc; |
| desc.type = ValueType::Void; // Initialized later in OnElemSegmentElemType. |
| desc.mode = mode; |
| desc.table_index = table_index; |
| module_.elems.push_back(desc); |
| init_expr_.kind = InitExprKind::None; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginElemSegmentInitExpr(Index index) { |
| return BeginInitExpr(); |
| } |
| |
| Result BinaryReaderInterp::EndElemSegmentInitExpr(Index index) { |
| CHECK_RESULT(EndInitExpr()); |
| switch (init_expr_.kind) { |
| case InitExprKind::I32: |
| CHECK_RESULT(validator_.OnElemSegmentInitExpr_Const(loc, ValueType::I32)); |
| break; |
| |
| case InitExprKind::GlobalGet: |
| CHECK_RESULT(validator_.OnElemSegmentInitExpr_GlobalGet( |
| loc, Var(init_expr_.index_))); |
| break; |
| |
| default: |
| CHECK_RESULT(validator_.OnElemSegmentInitExpr_Other(loc)); |
| break; |
| } |
| |
| ElemDesc& elem = module_.elems.back(); |
| elem.offset = init_expr_; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemSegmentElemType(Index index, Type elem_type) { |
| validator_.OnElemSegmentElemType(elem_type); |
| ElemDesc& elem = module_.elems.back(); |
| elem.type = elem_type; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemSegmentElemExprCount(Index index, |
| Index count) { |
| ElemDesc& elem = module_.elems.back(); |
| elem.elements.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemSegmentElemExpr_RefNull(Index segment_index, |
| Type type) { |
| CHECK_RESULT(validator_.OnElemSegmentElemExpr_RefNull(loc, type)); |
| ElemDesc& elem = module_.elems.back(); |
| elem.elements.push_back(ElemExpr{ElemKind::RefNull, 0}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemSegmentElemExpr_RefFunc(Index segment_index, |
| Index func_index) { |
| CHECK_RESULT(validator_.OnElemSegmentElemExpr_RefFunc(loc, Var(func_index))); |
| ElemDesc& elem = module_.elems.back(); |
| elem.elements.push_back(ElemExpr{ElemKind::RefFunc, func_index}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnDataCount(Index count) { |
| validator_.OnDataCount(count); |
| module_.datas.reserve(count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginDataSegmentInitExpr(Index index) { |
| return BeginInitExpr(); |
| } |
| |
| Result BinaryReaderInterp::EndDataSegmentInitExpr(Index index) { |
| CHECK_RESULT(EndInitExpr()); |
| switch (init_expr_.kind) { |
| case InitExprKind::I32: |
| CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I32)); |
| break; |
| |
| case InitExprKind::I64: |
| CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I64)); |
| break; |
| |
| case InitExprKind::GlobalGet: |
| CHECK_RESULT(validator_.OnDataSegmentInitExpr_GlobalGet( |
| loc, Var(init_expr_.index_))); |
| break; |
| |
| default: |
| CHECK_RESULT(validator_.OnDataSegmentInitExpr_Other(loc)); |
| break; |
| } |
| |
| DataDesc& data = module_.datas.back(); |
| data.offset = init_expr_; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::BeginDataSegment(Index index, |
| Index memory_index, |
| uint8_t flags) { |
| auto mode = ToSegmentMode(flags); |
| CHECK_RESULT(validator_.OnDataSegment(loc, Var(memory_index), mode)); |
| |
| DataDesc desc; |
| desc.mode = mode; |
| desc.memory_index = memory_index; |
| module_.datas.push_back(desc); |
| init_expr_.kind = InitExprKind::None; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnDataSegmentData(Index index, |
| const void* src_data, |
| Address size) { |
| DataDesc& dst_data = module_.datas.back(); |
| if (size > 0) { |
| dst_data.data.resize(size); |
| memcpy(dst_data.data.data(), src_data, size); |
| } |
| return Result::Ok; |
| } |
| |
| void BinaryReaderInterp::PushLabel(LabelKind kind, |
| Istream::Offset offset, |
| Istream::Offset fixup_offset, |
| u32 handler_desc_index) { |
| label_stack_.push_back(Label{kind, offset, fixup_offset, handler_desc_index}); |
| } |
| |
| void BinaryReaderInterp::PopLabel() { |
| label_stack_.pop_back(); |
| } |
| |
| Result BinaryReaderInterp::BeginFunctionBody(Index index, Offset size) { |
| Index defined_index = index - num_func_imports(); |
| func_ = &module_.funcs[defined_index]; |
| func_->code_offset = istream_.end(); |
| |
| depth_fixups_.Clear(); |
| label_stack_.clear(); |
| |
| func_fixups_.Resolve(istream_, defined_index); |
| |
| CHECK_RESULT(validator_.BeginFunctionBody(loc, index)); |
| |
| // Push implicit func label (equivalent to return). |
| // With exception handling it acts as a catch-less try block, which is |
| // needed to support delegating to the caller of a function using the |
| // try-delegate instruction. |
| PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset, |
| func_->handlers.size()); |
| func_->handlers.push_back(HandlerDesc{HandlerKind::Catch, |
| istream_.end(), |
| Istream::kInvalidOffset, |
| {}, |
| {Istream::kInvalidOffset}, |
| static_cast<u32>(func_->locals.size()), |
| 0}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::EndFunctionBody(Index index) { |
| FixupTopLabel(); |
| Index drop_count, keep_count; |
| CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count)); |
| CHECK_RESULT(validator_.EndFunctionBody(loc)); |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.Emit(Opcode::Return); |
| PopLabel(); |
| func_ = nullptr; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLocalDeclCount(Index count) { |
| local_decl_count_ = count; |
| local_count_ = 0; |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLocalDecl(Index decl_index, |
| Index count, |
| Type type) { |
| CHECK_RESULT(validator_.OnLocalDecl(loc, count, type)); |
| |
| local_count_ += count; |
| func_->locals.push_back(LocalDesc{type, count, local_count_}); |
| |
| if (decl_index == local_decl_count_ - 1) { |
| istream_.Emit(Opcode::InterpAlloca, local_count_); |
| } |
| return Result::Ok; |
| } |
| |
| Index BinaryReaderInterp::num_func_imports() const { |
| return func_types_.size() - module_.funcs.size(); |
| } |
| |
| Result BinaryReaderInterp::OnOpcode(Opcode opcode) { |
| if ((func_ == nullptr || label_stack_.empty()) && !reading_init_expr_) { |
| PrintError("Unexpected instruction after end of function"); |
| return Result::Error; |
| } |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnUnaryExpr(Opcode opcode) { |
| CHECK_RESULT(CheckNotInitExpr()); |
| CHECK_RESULT(validator_.OnUnary(loc, opcode)); |
| istream_.Emit(opcode); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTernaryExpr(Opcode opcode) { |
| CHECK_RESULT(CheckNotInitExpr()); |
| CHECK_RESULT(validator_.OnTernary(loc, opcode)); |
| istream_.Emit(opcode); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { |
| CHECK_RESULT(validator_.OnSimdLaneOp(loc, opcode, value)); |
| istream_.Emit(opcode, static_cast<u8>(value)); |
| return Result::Ok; |
| } |
| |
| uint32_t GetAlignment(Address alignment_log2) { |
| return alignment_log2 < 32 ? 1 << alignment_log2 : ~0u; |
| } |
| |
| Result BinaryReaderInterp::OnSimdLoadLaneExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset, |
| uint64_t value) { |
| CHECK_RESULT(validator_.OnSimdLoadLane(loc, opcode, GetAlignment(alignment_log2), value)); |
| istream_.Emit(opcode, kMemoryIndex0, offset, static_cast<u8>(value)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnSimdStoreLaneExpr(Opcode opcode, |
| Address alignment_log2, |
| Address offset, |
| uint64_t value) { |
| CHECK_RESULT(validator_.OnSimdStoreLane(loc, opcode, |
| GetAlignment(alignment_log2), value)); |
| istream_.Emit(opcode, kMemoryIndex0, offset, static_cast<u8>(value)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { |
| CHECK_RESULT(validator_.OnSimdShuffleOp(loc, opcode, value)); |
| istream_.Emit(opcode, value); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLoadSplatExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnLoadSplat(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLoadZeroExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnLoadZero(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicLoadExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnAtomicLoad(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicStoreExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnAtomicStore(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicRmwExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnAtomicRmw(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicRmwCmpxchgExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT( |
| validator_.OnAtomicRmwCmpxchg(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnBinaryExpr(Opcode opcode) { |
| CHECK_RESULT(validator_.OnBinary(loc, opcode)); |
| istream_.Emit(opcode); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnBlockExpr(Type sig_type) { |
| CHECK_RESULT(validator_.OnBlock(loc, sig_type)); |
| PushLabel(); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLoopExpr(Type sig_type) { |
| CHECK_RESULT(validator_.OnLoop(loc, sig_type)); |
| PushLabel(LabelKind::Block, istream_.end()); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnIfExpr(Type sig_type) { |
| CHECK_RESULT(validator_.OnIf(loc, sig_type)); |
| istream_.Emit(Opcode::InterpBrUnless); |
| auto fixup = istream_.EmitFixupU32(); |
| PushLabel(LabelKind::Block, Istream::kInvalidOffset, fixup); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElseExpr() { |
| CHECK_RESULT(validator_.OnElse(loc)); |
| Label* label = TopLabel(); |
| Istream::Offset fixup_cond_offset = label->fixup_offset; |
| istream_.Emit(Opcode::Br); |
| label->fixup_offset = istream_.EmitFixupU32(); |
| istream_.ResolveFixupU32(fixup_cond_offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnEndExpr() { |
| if (reading_init_expr_) { |
| //CHECK_RESULT(validator_.OnEnd(loc)); |
| return Result::Ok; |
| } |
| if (label_stack_.size() == 1) { |
| return Result::Ok; |
| } |
| SharedValidator::Label* label; |
| CHECK_RESULT(validator_.GetLabel(0, &label)); |
| LabelType label_type = label->label_type; |
| CHECK_RESULT(validator_.OnEnd(loc)); |
| if (label_type == LabelType::If || label_type == LabelType::Else) { |
| istream_.ResolveFixupU32(TopLabel()->fixup_offset); |
| } else if (label_type == LabelType::Try) { |
| // Catch-less try blocks need to fill in the handler description |
| // so that it can trigger an exception rethrow when it's reached. |
| Label* local_label = TopLabel(); |
| HandlerDesc& desc = func_->handlers[local_label->handler_desc_index]; |
| desc.try_end_offset = istream_.end(); |
| assert(desc.catches.size() == 0); |
| } else if (label_type == LabelType::Catch) { |
| istream_.EmitCatchDrop(1); |
| } |
| FixupTopLabel(); |
| PopLabel(); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnBrExpr(Index depth) { |
| Index drop_count, keep_count, catch_drop_count; |
| CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); |
| CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count)); |
| CHECK_RESULT(validator_.OnBr(loc, Var(depth))); |
| EmitBr(depth, drop_count, keep_count, catch_drop_count); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnBrIfExpr(Index depth) { |
| Index drop_count, keep_count, catch_drop_count; |
| CHECK_RESULT(validator_.OnBrIf(loc, Var(depth))); |
| CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); |
| CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count)); |
| // Flip the br_if so if <cond> is true it can drop values from the stack. |
| istream_.Emit(Opcode::InterpBrUnless); |
| auto fixup = istream_.EmitFixupU32(); |
| EmitBr(depth, drop_count, keep_count, catch_drop_count); |
| istream_.ResolveFixupU32(fixup); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnBrTableExpr(Index num_targets, |
| Index* target_depths, |
| Index default_target_depth) { |
| CHECK_RESULT(validator_.BeginBrTable(loc)); |
| Index drop_count, keep_count, catch_drop_count; |
| istream_.Emit(Opcode::BrTable, num_targets); |
| |
| for (Index i = 0; i < num_targets; ++i) { |
| Index depth = target_depths[i]; |
| CHECK_RESULT(validator_.OnBrTableTarget(loc, Var(depth))); |
| CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); |
| CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count)); |
| // Emit DropKeep directly (instead of using EmitDropKeep) so the |
| // instruction has a fixed size. Same for CatchDrop as well. |
| istream_.Emit(Opcode::InterpDropKeep, drop_count, keep_count); |
| istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count); |
| EmitBr(depth, 0, 0, 0); |
| } |
| CHECK_RESULT(validator_.OnBrTableTarget(loc, Var(default_target_depth))); |
| CHECK_RESULT( |
| GetBrDropKeepCount(default_target_depth, &drop_count, &keep_count)); |
| CHECK_RESULT( |
| validator_.GetCatchCount(default_target_depth, &catch_drop_count)); |
| // The default case doesn't need a fixed size, since it is never jumped over. |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count); |
| EmitBr(default_target_depth, 0, 0, 0); |
| |
| CHECK_RESULT(validator_.EndBrTable(loc)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnCallExpr(Index func_index) { |
| CHECK_RESULT(validator_.OnCall(loc, Var(func_index))); |
| |
| if (func_index >= num_func_imports()) { |
| istream_.Emit(Opcode::Call, func_index); |
| } else { |
| istream_.Emit(Opcode::InterpCallImport, func_index); |
| } |
| |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnCallIndirectExpr(Index sig_index, |
| Index table_index) { |
| CHECK_RESULT( |
| validator_.OnCallIndirect(loc, Var(sig_index), Var(table_index))); |
| istream_.Emit(Opcode::CallIndirect, table_index, sig_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnReturnCallExpr(Index func_index) { |
| FuncType& func_type = func_types_[func_index]; |
| |
| Index drop_count, keep_count, catch_drop_count; |
| CHECK_RESULT( |
| GetReturnCallDropKeepCount(func_type, 0, &drop_count, &keep_count)); |
| CHECK_RESULT( |
| validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count)); |
| // The validator must be run after we get the drop/keep counts, since it |
| // will change the type stack. |
| CHECK_RESULT(validator_.OnReturnCall(loc, Var(func_index))); |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.EmitCatchDrop(catch_drop_count); |
| |
| if (func_index >= num_func_imports()) { |
| istream_.Emit(Opcode::InterpAdjustFrameForReturnCall, func_index); |
| istream_.Emit(Opcode::Br, GetFuncOffset(func_index)); |
| } else { |
| istream_.Emit(Opcode::InterpCallImport, func_index); |
| istream_.Emit(Opcode::Return); |
| } |
| |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnReturnCallIndirectExpr(Index sig_index, |
| Index table_index) { |
| FuncType& func_type = module_.func_types[sig_index]; |
| |
| Index drop_count, keep_count, catch_drop_count; |
| // +1 to include the index of the function. |
| CHECK_RESULT( |
| GetReturnCallDropKeepCount(func_type, +1, &drop_count, &keep_count)); |
| CHECK_RESULT( |
| validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count)); |
| // The validator must be run after we get the drop/keep counts, since it |
| // changes the type stack. |
| CHECK_RESULT( |
| validator_.OnReturnCallIndirect(loc, Var(sig_index), Var(table_index))); |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.EmitCatchDrop(catch_drop_count); |
| istream_.Emit(Opcode::ReturnCallIndirect, table_index, sig_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnCompareExpr(Opcode opcode) { |
| CHECK_RESULT(validator_.OnCompare(loc, opcode)); |
| istream_.Emit(opcode); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnConvertExpr(Opcode opcode) { |
| CHECK_RESULT(validator_.OnConvert(loc, opcode)); |
| istream_.Emit(opcode); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnDropExpr() { |
| CHECK_RESULT(validator_.OnDrop(loc)); |
| istream_.Emit(Opcode::Drop); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnI32ConstExpr(uint32_t value) { |
| CHECK_RESULT(validator_.OnConst(loc, Type::I32)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::I32; |
| init_expr_.i32_ = value; |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::I32Const, value); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnI64ConstExpr(uint64_t value) { |
| CHECK_RESULT(validator_.OnConst(loc, Type::I64)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::I64; |
| init_expr_.i64_ = value; |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::I64Const, value); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnF32ConstExpr(uint32_t value_bits) { |
| CHECK_RESULT(validator_.OnConst(loc, Type::F32)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::F32; |
| init_expr_.f32_ = Bitcast<f32>(value_bits); |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::F32Const, value_bits); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnF64ConstExpr(uint64_t value_bits) { |
| CHECK_RESULT(validator_.OnConst(loc, Type::F64)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::F64; |
| init_expr_.f64_ = Bitcast<f64>(value_bits); |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::F64Const, value_bits); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnV128ConstExpr(v128 value_bits) { |
| CHECK_RESULT(validator_.OnConst(loc, Type::V128)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::V128; |
| init_expr_.v128_ = Bitcast<v128>(value_bits); |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::V128Const, value_bits); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnGlobalGetExpr(Index global_index) { |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::GlobalGet; |
| init_expr_.index_ = global_index; |
| return Result::Ok; |
| } |
| CHECK_RESULT(validator_.OnGlobalGet(loc, Var(global_index))); |
| istream_.Emit(Opcode::GlobalGet, global_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnGlobalSetExpr(Index global_index) { |
| CHECK_RESULT(CheckNotInitExpr()); |
| CHECK_RESULT(validator_.OnGlobalSet(loc, Var(global_index))); |
| istream_.Emit(Opcode::GlobalSet, global_index); |
| return Result::Ok; |
| } |
| |
| Index BinaryReaderInterp::TranslateLocalIndex(Index local_index) { |
| return validator_.type_stack_size() + validator_.GetLocalCount() - |
| local_index; |
| } |
| |
| Result BinaryReaderInterp::OnLocalGetExpr(Index local_index) { |
| // Get the translated index before calling validator_.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(validator_.OnLocalGet(loc, Var(local_index))); |
| istream_.Emit(Opcode::LocalGet, translated_local_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLocalSetExpr(Index local_index) { |
| // See comment in OnLocalGetExpr above. |
| Index translated_local_index = TranslateLocalIndex(local_index); |
| CHECK_RESULT(validator_.OnLocalSet(loc, Var(local_index))); |
| istream_.Emit(Opcode::LocalSet, translated_local_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLocalTeeExpr(Index local_index) { |
| CHECK_RESULT(validator_.OnLocalTee(loc, Var(local_index))); |
| istream_.Emit(Opcode::LocalTee, TranslateLocalIndex(local_index)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnLoadExpr(Opcode opcode, |
| Index memidx, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT( |
| validator_.OnLoad(loc, opcode, Var(memidx), GetAlignment(align_log2))); |
| istream_.Emit(opcode, memidx, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnStoreExpr(Opcode opcode, |
| Index memidx, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT( |
| validator_.OnStore(loc, opcode, Var(memidx), GetAlignment(align_log2))); |
| istream_.Emit(opcode, memidx, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemoryGrowExpr(Index memidx) { |
| CHECK_RESULT(validator_.OnMemoryGrow(loc, Var(memidx))); |
| istream_.Emit(Opcode::MemoryGrow, memidx); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemorySizeExpr(Index memidx) { |
| CHECK_RESULT(validator_.OnMemorySize(loc, Var(memidx))); |
| istream_.Emit(Opcode::MemorySize, memidx); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableGrowExpr(Index table_index) { |
| CHECK_RESULT(validator_.OnTableGrow(loc, Var(table_index))); |
| istream_.Emit(Opcode::TableGrow, table_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableSizeExpr(Index table_index) { |
| CHECK_RESULT(validator_.OnTableSize(loc, Var(table_index))); |
| istream_.Emit(Opcode::TableSize, table_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableFillExpr(Index table_index) { |
| CHECK_RESULT(validator_.OnTableFill(loc, Var(table_index))); |
| istream_.Emit(Opcode::TableFill, table_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnRefFuncExpr(Index func_index) { |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::RefFunc; |
| init_expr_.index_ = func_index; |
| return Result::Ok; |
| } |
| CHECK_RESULT(validator_.OnRefFunc(loc, Var(func_index))); |
| istream_.Emit(Opcode::RefFunc, func_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnRefNullExpr(Type type) { |
| CHECK_RESULT(validator_.OnRefNull(loc, type)); |
| if (reading_init_expr_) { |
| CHECK_RESULT(CheckEmptyInitExpr()); |
| init_expr_.kind = InitExprKind::RefNull; |
| init_expr_.type_ = type; |
| return Result::Ok; |
| } |
| istream_.Emit(Opcode::RefNull); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnRefIsNullExpr() { |
| CHECK_RESULT(validator_.OnRefIsNull(loc)); |
| istream_.Emit(Opcode::RefIsNull); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnNopExpr() { |
| CHECK_RESULT(CheckNotInitExpr()); |
| CHECK_RESULT(validator_.OnNop(loc)); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnReturnExpr() { |
| Index drop_count, keep_count, catch_drop_count; |
| CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count)); |
| CHECK_RESULT( |
| validator_.GetCatchCount(label_stack_.size() - 1, &catch_drop_count)); |
| CHECK_RESULT(validator_.OnReturn(loc)); |
| istream_.EmitDropKeep(drop_count, keep_count); |
| istream_.EmitCatchDrop(catch_drop_count); |
| istream_.Emit(Opcode::Return); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnSelectExpr(Index result_count, |
| Type* result_types) { |
| CHECK_RESULT(validator_.OnSelect(loc, result_count, result_types)); |
| istream_.Emit(Opcode::Select); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnUnreachableExpr() { |
| CHECK_RESULT(validator_.OnUnreachable(loc)); |
| istream_.Emit(Opcode::Unreachable); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicWaitExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT(validator_.OnAtomicWait(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicFenceExpr(uint32_t consistency_model) { |
| CHECK_RESULT(validator_.OnAtomicFence(loc, consistency_model)); |
| istream_.Emit(Opcode::AtomicFence, consistency_model); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnAtomicNotifyExpr(Opcode opcode, |
| Address align_log2, |
| Address offset) { |
| CHECK_RESULT( |
| validator_.OnAtomicNotify(loc, opcode, GetAlignment(align_log2))); |
| istream_.Emit(opcode, kMemoryIndex0, offset); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) { |
| CHECK_RESULT(validator_.OnMemoryCopy(loc, Var(srcmemidx), Var(destmemidx))); |
| istream_.Emit(Opcode::MemoryCopy, srcmemidx, destmemidx); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnDataDropExpr(Index segment_index) { |
| CHECK_RESULT(validator_.OnDataDrop(loc, Var(segment_index))); |
| istream_.Emit(Opcode::DataDrop, segment_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemoryFillExpr(Index memidx) { |
| CHECK_RESULT(validator_.OnMemoryFill(loc, Var(memidx))); |
| istream_.Emit(Opcode::MemoryFill, memidx); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnMemoryInitExpr(Index segment_index, Index memidx) { |
| CHECK_RESULT(validator_.OnMemoryInit(loc, Var(segment_index), Var(memidx))); |
| istream_.Emit(Opcode::MemoryInit, memidx, segment_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableGetExpr(Index table_index) { |
| CHECK_RESULT(validator_.OnTableGet(loc, Var(table_index))); |
| istream_.Emit(Opcode::TableGet, table_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableSetExpr(Index table_index) { |
| CHECK_RESULT(validator_.OnTableSet(loc, Var(table_index))); |
| istream_.Emit(Opcode::TableSet, table_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableCopyExpr(Index dst_index, Index src_index) { |
| CHECK_RESULT(validator_.OnTableCopy(loc, Var(dst_index), Var(src_index))); |
| istream_.Emit(Opcode::TableCopy, dst_index, src_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnElemDropExpr(Index segment_index) { |
| CHECK_RESULT(validator_.OnElemDrop(loc, Var(segment_index))); |
| istream_.Emit(Opcode::ElemDrop, segment_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTableInitExpr(Index segment_index, |
| Index table_index) { |
| CHECK_RESULT( |
| validator_.OnTableInit(loc, Var(segment_index), Var(table_index))); |
| istream_.Emit(Opcode::TableInit, table_index, segment_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnThrowExpr(Index tag_index) { |
| CHECK_RESULT(validator_.OnThrow(loc, Var(tag_index))); |
| istream_.Emit(Opcode::Throw, tag_index); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnRethrowExpr(Index depth) { |
| Index catch_depth; |
| CHECK_RESULT(validator_.OnRethrow(loc, Var(depth))); |
| CHECK_RESULT(validator_.GetCatchCount(depth, &catch_depth)); |
| // The rethrow opcode takes an index into the exception stack rather than |
| // the number of catch nestings, so we subtract one here. |
| istream_.Emit(Opcode::Rethrow, catch_depth - 1); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnTryExpr(Type sig_type) { |
| u32 exn_stack_height; |
| CHECK_RESULT( |
| validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height)); |
| u32 value_stack_height = validator_.type_stack_size(); |
| CHECK_RESULT(validator_.OnTry(loc, sig_type)); |
| // Push a label that tracks mapping of exn -> catch |
| PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset, |
| func_->handlers.size()); |
| func_->handlers.push_back(HandlerDesc{HandlerKind::Catch, |
| istream_.end(), |
| Istream::kInvalidOffset, |
| {}, |
| {Istream::kInvalidOffset}, |
| value_stack_height, |
| exn_stack_height}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnCatchExpr(Index tag_index) { |
| CHECK_RESULT(validator_.OnCatch(loc, Var(tag_index), false)); |
| Label* label = TopLabel(); |
| HandlerDesc& desc = func_->handlers[label->handler_desc_index]; |
| desc.kind = HandlerKind::Catch; |
| // Drop the previous block's exception if it was a catch. |
| if (label->kind == LabelKind::Block) { |
| istream_.EmitCatchDrop(1); |
| } |
| // Jump to the end of the block at the end of the previous try or catch. |
| Istream::Offset offset = label->offset; |
| istream_.Emit(Opcode::Br); |
| assert(offset == Istream::kInvalidOffset); |
| depth_fixups_.Append(label_stack_.size() - 1, istream_.end()); |
| istream_.Emit(offset); |
| // The offset is only set after the first catch block, as the offset range |
| // should only cover the try block itself. |
| if (desc.try_end_offset == Istream::kInvalidOffset) { |
| desc.try_end_offset = istream_.end(); |
| } |
| // The label kind is switched to Block from Try in order to distinguish |
| // catch blocks from try blocks. This is used to ensure that a try-delegate |
| // inside this catch will not delegate to the catch, and instead find outer |
| // try blocks to use as a delegate target. |
| label->kind = LabelKind::Block; |
| desc.catches.push_back(CatchDesc{tag_index, istream_.end()}); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnCatchAllExpr() { |
| CHECK_RESULT(validator_.OnCatch(loc, Var(), true)); |
| Label* label = TopLabel(); |
| HandlerDesc& desc = func_->handlers[label->handler_desc_index]; |
| desc.kind = HandlerKind::Catch; |
| if (label->kind == LabelKind::Block) { |
| istream_.EmitCatchDrop(1); |
| } |
| Istream::Offset offset = label->offset; |
| istream_.Emit(Opcode::Br); |
| assert(offset == Istream::kInvalidOffset); |
| depth_fixups_.Append(label_stack_.size() - 1, istream_.end()); |
| istream_.Emit(offset); |
| if (desc.try_end_offset == Istream::kInvalidOffset) { |
| desc.try_end_offset = istream_.end(); |
| } |
| label->kind = LabelKind::Block; |
| desc.catch_all_offset = istream_.end(); |
| return Result::Ok; |
| } |
| |
| Result BinaryReaderInterp::OnDelegateExpr(Index depth) { |
| CHECK_RESULT(validator_.OnDelegate(loc, Var(depth))); |
| Label* label = TopLabel(); |
| assert(label->kind == LabelKind::Try); |
| HandlerDesc& desc = func_->handlers[label->handler_desc_index]; |
| desc.kind = HandlerKind::Delegate; |
| Istream::Offset offset = label->offset; |
| istream_.Emit(Opcode::Br); |
| assert(offset == Istream::kInvalidOffset); |
| depth_fixups_.Append(label_stack_.size() - 1, istream_.end()); |
| istream_.Emit(offset); |
| desc.try_end_offset = istream_.end(); |
| Label* target_label = GetNearestTryLabel(depth + 1); |
| assert(target_label); |
| desc.delegate_handler_index = target_label->handler_desc_index; |
| FixupTopLabel(); |
| PopLabel(); |
| return Result::Ok; |
| } |
| |
| } // namespace |
| |
| Result ReadBinaryInterp(const void* data, |
| size_t size, |
| const ReadBinaryOptions& options, |
| Errors* errors, |
| ModuleDesc* out_module) { |
| BinaryReaderInterp reader(out_module, errors, options.features); |
| return ReadBinary(data, size, &reader, options); |
| } |
| |
| } // namespace interp |
| } // namespace wabt |