From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/wasm2c/src/apply-names.cc | 581 +++ third_party/wasm2c/src/binary-reader-ir.cc | 1775 +++++++ third_party/wasm2c/src/binary-reader-logging.cc | 971 ++++ third_party/wasm2c/src/binary-reader-objdump.cc | 2408 +++++++++ third_party/wasm2c/src/binary-reader-opcnt.cc | 314 ++ third_party/wasm2c/src/binary-reader.cc | 3027 +++++++++++ third_party/wasm2c/src/binary-writer-spec.cc | 672 +++ third_party/wasm2c/src/binary-writer.cc | 1798 +++++++ third_party/wasm2c/src/binary.cc | 68 + third_party/wasm2c/src/binding-hash.cc | 91 + third_party/wasm2c/src/c-writer.cc | 5295 ++++++++++++++++++++ third_party/wasm2c/src/color.cc | 78 + third_party/wasm2c/src/common.cc | 148 + third_party/wasm2c/src/config.cc | 162 + third_party/wasm2c/src/config.h.in | 332 ++ third_party/wasm2c/src/decompiler.cc | 857 ++++ third_party/wasm2c/src/emscripten-exports.txt | 72 + third_party/wasm2c/src/emscripten-helpers.cc | 403 ++ third_party/wasm2c/src/error-formatter.cc | 127 + third_party/wasm2c/src/expr-visitor.cc | 472 ++ third_party/wasm2c/src/feature.cc | 56 + third_party/wasm2c/src/filenames.cc | 57 + third_party/wasm2c/src/generate-names.cc | 433 ++ third_party/wasm2c/src/ir-util.cc | 271 + third_party/wasm2c/src/ir.cc | 701 +++ third_party/wasm2c/src/leb128.cc | 361 ++ third_party/wasm2c/src/lexer-keywords.txt | 609 +++ third_party/wasm2c/src/lexer-source-line-finder.cc | 152 + third_party/wasm2c/src/lexer-source.cc | 67 + third_party/wasm2c/src/literal.cc | 829 +++ third_party/wasm2c/src/opcode-code-table.c | 41 + third_party/wasm2c/src/opcode.cc | 429 ++ third_party/wasm2c/src/option-parser.cc | 351 ++ third_party/wasm2c/src/prebuilt/.clang-format | 2 + third_party/wasm2c/src/prebuilt/lexer-keywords.cc | 1796 +++++++ .../wasm2c/src/prebuilt/wasm2c_header_bottom.cc | 7 + .../wasm2c/src/prebuilt/wasm2c_header_top.cc | 37 + .../prebuilt/wasm2c_simd_source_declarations.cc | 224 + .../src/prebuilt/wasm2c_source_declarations.cc | 1110 ++++ .../wasm2c/src/prebuilt/wasm2c_source_includes.cc | 33 + third_party/wasm2c/src/resolve-names.cc | 649 +++ third_party/wasm2c/src/sha256.cc | 48 + third_party/wasm2c/src/shared-validator.cc | 1203 +++++ third_party/wasm2c/src/stream.cc | 330 ++ third_party/wasm2c/src/test-binary-reader.cc | 75 + third_party/wasm2c/src/test-circular-array.cc | 284 ++ third_party/wasm2c/src/test-filenames.cc | 61 + third_party/wasm2c/src/test-hexfloat.cc | 264 + third_party/wasm2c/src/test-interp.cc | 723 +++ third_party/wasm2c/src/test-intrusive-list.cc | 583 +++ third_party/wasm2c/src/test-literal.cc | 838 ++++ third_party/wasm2c/src/test-option-parser.cc | 180 + third_party/wasm2c/src/test-utf8.cc | 167 + third_party/wasm2c/src/test-wast-parser.cc | 88 + third_party/wasm2c/src/token.cc | 99 + third_party/wasm2c/src/tools/wasm2c.cc | 216 + third_party/wasm2c/src/tracing.cc | 71 + third_party/wasm2c/src/type-checker.cc | 989 ++++ third_party/wasm2c/src/utf8.cc | 106 + third_party/wasm2c/src/validator.cc | 1086 ++++ third_party/wasm2c/src/wabt.post.js | 387 ++ third_party/wasm2c/src/wast-lexer.cc | 626 +++ third_party/wasm2c/src/wast-parser.cc | 3616 +++++++++++++ third_party/wasm2c/src/wat-writer.cc | 1789 +++++++ 64 files changed, 41695 insertions(+) create mode 100644 third_party/wasm2c/src/apply-names.cc create mode 100644 third_party/wasm2c/src/binary-reader-ir.cc create mode 100644 third_party/wasm2c/src/binary-reader-logging.cc create mode 100644 third_party/wasm2c/src/binary-reader-objdump.cc create mode 100644 third_party/wasm2c/src/binary-reader-opcnt.cc create mode 100644 third_party/wasm2c/src/binary-reader.cc create mode 100644 third_party/wasm2c/src/binary-writer-spec.cc create mode 100644 third_party/wasm2c/src/binary-writer.cc create mode 100644 third_party/wasm2c/src/binary.cc create mode 100644 third_party/wasm2c/src/binding-hash.cc create mode 100644 third_party/wasm2c/src/c-writer.cc create mode 100644 third_party/wasm2c/src/color.cc create mode 100644 third_party/wasm2c/src/common.cc create mode 100644 third_party/wasm2c/src/config.cc create mode 100644 third_party/wasm2c/src/config.h.in create mode 100644 third_party/wasm2c/src/decompiler.cc create mode 100644 third_party/wasm2c/src/emscripten-exports.txt create mode 100644 third_party/wasm2c/src/emscripten-helpers.cc create mode 100644 third_party/wasm2c/src/error-formatter.cc create mode 100644 third_party/wasm2c/src/expr-visitor.cc create mode 100644 third_party/wasm2c/src/feature.cc create mode 100644 third_party/wasm2c/src/filenames.cc create mode 100644 third_party/wasm2c/src/generate-names.cc create mode 100644 third_party/wasm2c/src/ir-util.cc create mode 100644 third_party/wasm2c/src/ir.cc create mode 100644 third_party/wasm2c/src/leb128.cc create mode 100644 third_party/wasm2c/src/lexer-keywords.txt create mode 100644 third_party/wasm2c/src/lexer-source-line-finder.cc create mode 100644 third_party/wasm2c/src/lexer-source.cc create mode 100644 third_party/wasm2c/src/literal.cc create mode 100644 third_party/wasm2c/src/opcode-code-table.c create mode 100644 third_party/wasm2c/src/opcode.cc create mode 100644 third_party/wasm2c/src/option-parser.cc create mode 100644 third_party/wasm2c/src/prebuilt/.clang-format create mode 100644 third_party/wasm2c/src/prebuilt/lexer-keywords.cc create mode 100644 third_party/wasm2c/src/prebuilt/wasm2c_header_bottom.cc create mode 100644 third_party/wasm2c/src/prebuilt/wasm2c_header_top.cc create mode 100644 third_party/wasm2c/src/prebuilt/wasm2c_simd_source_declarations.cc create mode 100644 third_party/wasm2c/src/prebuilt/wasm2c_source_declarations.cc create mode 100644 third_party/wasm2c/src/prebuilt/wasm2c_source_includes.cc create mode 100644 third_party/wasm2c/src/resolve-names.cc create mode 100644 third_party/wasm2c/src/sha256.cc create mode 100644 third_party/wasm2c/src/shared-validator.cc create mode 100644 third_party/wasm2c/src/stream.cc create mode 100644 third_party/wasm2c/src/test-binary-reader.cc create mode 100644 third_party/wasm2c/src/test-circular-array.cc create mode 100644 third_party/wasm2c/src/test-filenames.cc create mode 100644 third_party/wasm2c/src/test-hexfloat.cc create mode 100644 third_party/wasm2c/src/test-interp.cc create mode 100644 third_party/wasm2c/src/test-intrusive-list.cc create mode 100644 third_party/wasm2c/src/test-literal.cc create mode 100644 third_party/wasm2c/src/test-option-parser.cc create mode 100644 third_party/wasm2c/src/test-utf8.cc create mode 100644 third_party/wasm2c/src/test-wast-parser.cc create mode 100644 third_party/wasm2c/src/token.cc create mode 100644 third_party/wasm2c/src/tools/wasm2c.cc create mode 100644 third_party/wasm2c/src/tracing.cc create mode 100644 third_party/wasm2c/src/type-checker.cc create mode 100644 third_party/wasm2c/src/utf8.cc create mode 100644 third_party/wasm2c/src/validator.cc create mode 100644 third_party/wasm2c/src/wabt.post.js create mode 100644 third_party/wasm2c/src/wast-lexer.cc create mode 100644 third_party/wasm2c/src/wast-parser.cc create mode 100644 third_party/wasm2c/src/wat-writer.cc (limited to 'third_party/wasm2c/src') diff --git a/third_party/wasm2c/src/apply-names.cc b/third_party/wasm2c/src/apply-names.cc new file mode 100644 index 0000000000..506ab1fe71 --- /dev/null +++ b/third_party/wasm2c/src/apply-names.cc @@ -0,0 +1,581 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/apply-names.h" + +#include +#include +#include +#include + +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" + +namespace wabt { + +namespace { + +class NameApplier : public ExprVisitor::DelegateNop { + public: + NameApplier(); + + Result VisitModule(Module* module); + + // Implementation of ExprVisitor::DelegateNop. + Result BeginBlockExpr(BlockExpr*) override; + Result EndBlockExpr(BlockExpr*) override; + Result OnBrExpr(BrExpr*) override; + Result OnBrIfExpr(BrIfExpr*) override; + Result OnBrTableExpr(BrTableExpr*) override; + Result OnCallExpr(CallExpr*) override; + Result OnRefFuncExpr(RefFuncExpr*) override; + Result OnCallIndirectExpr(CallIndirectExpr*) override; + Result OnReturnCallExpr(ReturnCallExpr*) override; + Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override; + Result OnGlobalGetExpr(GlobalGetExpr*) override; + Result OnGlobalSetExpr(GlobalSetExpr*) override; + Result BeginIfExpr(IfExpr*) override; + Result EndIfExpr(IfExpr*) override; + Result OnLoadExpr(LoadExpr*) override; + Result OnLocalGetExpr(LocalGetExpr*) override; + Result OnLocalSetExpr(LocalSetExpr*) override; + Result OnLocalTeeExpr(LocalTeeExpr*) override; + Result BeginLoopExpr(LoopExpr*) override; + Result EndLoopExpr(LoopExpr*) override; + Result OnMemoryCopyExpr(MemoryCopyExpr*) override; + Result OnDataDropExpr(DataDropExpr*) override; + Result OnMemoryFillExpr(MemoryFillExpr*) override; + Result OnMemoryGrowExpr(MemoryGrowExpr*) override; + Result OnMemoryInitExpr(MemoryInitExpr*) override; + Result OnMemorySizeExpr(MemorySizeExpr*) override; + Result OnElemDropExpr(ElemDropExpr*) override; + Result OnTableCopyExpr(TableCopyExpr*) override; + Result OnTableInitExpr(TableInitExpr*) override; + Result OnTableGetExpr(TableGetExpr*) override; + Result OnTableSetExpr(TableSetExpr*) override; + Result OnTableGrowExpr(TableGrowExpr*) override; + Result OnTableSizeExpr(TableSizeExpr*) override; + Result OnTableFillExpr(TableFillExpr*) override; + Result OnStoreExpr(StoreExpr*) override; + Result BeginTryExpr(TryExpr*) override; + Result EndTryExpr(TryExpr*) override; + Result OnCatchExpr(TryExpr*, Catch*) override; + Result OnDelegateExpr(TryExpr*) override; + Result OnThrowExpr(ThrowExpr*) override; + Result OnRethrowExpr(RethrowExpr*) override; + Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; + Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override; + + private: + void PushLabel(const std::string& label); + void PopLabel(); + std::string_view FindLabelByVar(Var* var); + void UseNameForVar(std::string_view name, Var* var); + Result UseNameForFuncTypeVar(Var* var); + Result UseNameForFuncVar(Var* var); + Result UseNameForGlobalVar(Var* var); + Result UseNameForTableVar(Var* var); + Result UseNameForMemoryVar(Var* var); + Result UseNameForTagVar(Var* var); + Result UseNameForDataSegmentVar(Var* var); + Result UseNameForElemSegmentVar(Var* var); + Result UseNameForParamAndLocalVar(Func* func, Var* var); + Result VisitFunc(Index func_index, Func* func); + Result VisitGlobal(Global* global); + Result VisitTag(Tag* tag); + Result VisitExport(Index export_index, Export* export_); + Result VisitElemSegment(Index elem_segment_index, ElemSegment* segment); + Result VisitDataSegment(Index data_segment_index, DataSegment* segment); + Result VisitStart(Var* start_var); + + Module* module_ = nullptr; + Func* current_func_ = nullptr; + ExprVisitor visitor_; + std::vector param_and_local_index_to_name_; + std::vector labels_; +}; + +NameApplier::NameApplier() : visitor_(this) {} + +void NameApplier::PushLabel(const std::string& label) { + labels_.push_back(label); +} + +void NameApplier::PopLabel() { + labels_.pop_back(); +} + +std::string_view NameApplier::FindLabelByVar(Var* var) { + if (var->is_name()) { + for (int i = labels_.size() - 1; i >= 0; --i) { + const std::string& label = labels_[i]; + if (label == var->name()) { + return label; + } + } + return std::string_view(); + } else { + if (var->index() >= labels_.size()) { + return std::string_view(); + } + return labels_[labels_.size() - 1 - var->index()]; + } +} + +void NameApplier::UseNameForVar(std::string_view name, Var* var) { + if (var->is_name()) { + assert(name == var->name()); + return; + } + + if (!name.empty()) { + var->set_name(name); + } +} + +Result NameApplier::UseNameForFuncTypeVar(Var* var) { + FuncType* func_type = module_->GetFuncType(*var); + if (!func_type) { + return Result::Error; + } + UseNameForVar(func_type->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForFuncVar(Var* var) { + Func* func = module_->GetFunc(*var); + if (!func) { + return Result::Error; + } + UseNameForVar(func->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForGlobalVar(Var* var) { + Global* global = module_->GetGlobal(*var); + if (!global) { + return Result::Error; + } + UseNameForVar(global->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForTableVar(Var* var) { + Table* table = module_->GetTable(*var); + if (!table) { + return Result::Error; + } + UseNameForVar(table->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForMemoryVar(Var* var) { + Memory* memory = module_->GetMemory(*var); + if (!memory) { + return Result::Error; + } + UseNameForVar(memory->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForTagVar(Var* var) { + Tag* tag = module_->GetTag(*var); + if (!tag) { + return Result::Error; + } + UseNameForVar(tag->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForDataSegmentVar(Var* var) { + DataSegment* data_segment = module_->GetDataSegment(*var); + if (!data_segment) { + return Result::Error; + } + UseNameForVar(data_segment->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForElemSegmentVar(Var* var) { + ElemSegment* elem_segment = module_->GetElemSegment(*var); + if (!elem_segment) { + return Result::Error; + } + UseNameForVar(elem_segment->name, var); + return Result::Ok; +} + +Result NameApplier::UseNameForParamAndLocalVar(Func* func, Var* var) { + Index local_index = func->GetLocalIndex(*var); + if (local_index >= func->GetNumParamsAndLocals()) { + return Result::Error; + } + + std::string name = param_and_local_index_to_name_[local_index]; + if (var->is_name()) { + assert(name == var->name()); + return Result::Ok; + } + + if (!name.empty()) { + var->set_name(name); + } + return Result::Ok; +} + +Result NameApplier::BeginBlockExpr(BlockExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndBlockExpr(BlockExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::BeginLoopExpr(LoopExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndLoopExpr(LoopExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnDataDropExpr(DataDropExpr* expr) { + CHECK_RESULT(UseNameForDataSegmentVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnMemoryCopyExpr(MemoryCopyExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->srcmemidx)); + CHECK_RESULT(UseNameForMemoryVar(&expr->destmemidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryFillExpr(MemoryFillExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryGrowExpr(MemoryGrowExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemoryInitExpr(MemoryInitExpr* expr) { + CHECK_RESULT(UseNameForDataSegmentVar(&expr->var)); + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnMemorySizeExpr(MemorySizeExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnElemDropExpr(ElemDropExpr* expr) { + CHECK_RESULT(UseNameForElemSegmentVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableCopyExpr(TableCopyExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->dst_table)); + CHECK_RESULT(UseNameForTableVar(&expr->src_table)); + return Result::Ok; +} + +Result NameApplier::OnTableInitExpr(TableInitExpr* expr) { + CHECK_RESULT(UseNameForElemSegmentVar(&expr->segment_index)); + CHECK_RESULT(UseNameForTableVar(&expr->table_index)); + return Result::Ok; +} + +Result NameApplier::OnTableGetExpr(TableGetExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableSetExpr(TableSetExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableGrowExpr(TableGrowExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableSizeExpr(TableSizeExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnTableFillExpr(TableFillExpr* expr) { + CHECK_RESULT(UseNameForTableVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnStoreExpr(StoreExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnBrExpr(BrExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrIfExpr(BrIfExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnBrTableExpr(BrTableExpr* expr) { + for (Var& target : expr->targets) { + std::string_view label = FindLabelByVar(&target); + UseNameForVar(label, &target); + } + + std::string_view label = FindLabelByVar(&expr->default_target); + UseNameForVar(label, &expr->default_target); + return Result::Ok; +} + +Result NameApplier::BeginTryExpr(TryExpr* expr) { + PushLabel(expr->block.label); + return Result::Ok; +} + +Result NameApplier::EndTryExpr(TryExpr*) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnCatchExpr(TryExpr*, Catch* expr) { + if (!expr->IsCatchAll()) { + CHECK_RESULT(UseNameForTagVar(&expr->var)); + } + return Result::Ok; +} + +Result NameApplier::OnDelegateExpr(TryExpr* expr) { + PopLabel(); + std::string_view label = FindLabelByVar(&expr->delegate_target); + UseNameForVar(label, &expr->delegate_target); + return Result::Ok; +} + +Result NameApplier::OnThrowExpr(ThrowExpr* expr) { + CHECK_RESULT(UseNameForTagVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnRethrowExpr(RethrowExpr* expr) { + std::string_view label = FindLabelByVar(&expr->var); + UseNameForVar(label, &expr->var); + return Result::Ok; +} + +Result NameApplier::OnCallExpr(CallExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnRefFuncExpr(RefFuncExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnCallIndirectExpr(CallIndirectExpr* expr) { + if (expr->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var)); + } + CHECK_RESULT(UseNameForTableVar(&expr->table)); + return Result::Ok; +} + +Result NameApplier::OnReturnCallExpr(ReturnCallExpr* expr) { + CHECK_RESULT(UseNameForFuncVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) { + if (expr->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&expr->decl.type_var)); + } + CHECK_RESULT(UseNameForTableVar(&expr->table)); + return Result::Ok; +} + +Result NameApplier::OnGlobalGetExpr(GlobalGetExpr* expr) { + CHECK_RESULT(UseNameForGlobalVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalGetExpr(LocalGetExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::BeginIfExpr(IfExpr* expr) { + PushLabel(expr->true_.label); + return Result::Ok; +} + +Result NameApplier::EndIfExpr(IfExpr* expr) { + PopLabel(); + return Result::Ok; +} + +Result NameApplier::OnLoadExpr(LoadExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnGlobalSetExpr(GlobalSetExpr* expr) { + CHECK_RESULT(UseNameForGlobalVar(&expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalSetExpr(LocalSetExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::OnLocalTeeExpr(LocalTeeExpr* expr) { + CHECK_RESULT(UseNameForParamAndLocalVar(current_func_, &expr->var)); + return Result::Ok; +} + +Result NameApplier::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { + CHECK_RESULT(UseNameForMemoryVar(&expr->memidx)); + return Result::Ok; +} + +Result NameApplier::VisitFunc(Index func_index, Func* func) { + current_func_ = func; + if (func->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&func->decl.type_var)); + } + + MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings, + ¶m_and_local_index_to_name_); + + CHECK_RESULT(visitor_.VisitFunc(func)); + current_func_ = nullptr; + return Result::Ok; +} + +Result NameApplier::VisitGlobal(Global* global) { + CHECK_RESULT(visitor_.VisitExprList(global->init_expr)); + return Result::Ok; +} + +Result NameApplier::VisitTag(Tag* tag) { + if (tag->decl.has_func_type) { + CHECK_RESULT(UseNameForFuncTypeVar(&tag->decl.type_var)); + } + return Result::Ok; +} + +Result NameApplier::VisitExport(Index export_index, Export* export_) { + switch (export_->kind) { + case ExternalKind::Func: + UseNameForFuncVar(&export_->var); + break; + + case ExternalKind::Table: + UseNameForTableVar(&export_->var); + break; + + case ExternalKind::Memory: + UseNameForMemoryVar(&export_->var); + break; + + case ExternalKind::Global: + UseNameForGlobalVar(&export_->var); + break; + + case ExternalKind::Tag: + UseNameForTagVar(&export_->var); + break; + } + return Result::Ok; +} + +Result NameApplier::VisitElemSegment(Index elem_segment_index, + ElemSegment* segment) { + CHECK_RESULT(UseNameForTableVar(&segment->table_var)); + CHECK_RESULT(visitor_.VisitExprList(segment->offset)); + for (ExprList& elem_expr : segment->elem_exprs) { + Expr* expr = &elem_expr.front(); + if (expr->type() == ExprType::RefFunc) { + CHECK_RESULT(UseNameForFuncVar(&cast(expr)->var)); + } + } + return Result::Ok; +} + +Result NameApplier::VisitDataSegment(Index data_segment_index, + DataSegment* segment) { + CHECK_RESULT(UseNameForMemoryVar(&segment->memory_var)); + CHECK_RESULT(visitor_.VisitExprList(segment->offset)); + return Result::Ok; +} + +Result NameApplier::VisitStart(Var* start_var) { + CHECK_RESULT(UseNameForFuncVar(start_var)); + return Result::Ok; +} + +Result NameApplier::VisitModule(Module* module) { + module_ = module; + for (size_t i = 0; i < module->funcs.size(); ++i) + CHECK_RESULT(VisitFunc(i, module->funcs[i])); + for (size_t i = 0; i < module->globals.size(); ++i) + CHECK_RESULT(VisitGlobal(module->globals[i])); + for (size_t i = 0; i < module->tags.size(); ++i) + CHECK_RESULT(VisitTag(module->tags[i])); + for (size_t i = 0; i < module->exports.size(); ++i) + CHECK_RESULT(VisitExport(i, module->exports[i])); + for (size_t i = 0; i < module->elem_segments.size(); ++i) + CHECK_RESULT(VisitElemSegment(i, module->elem_segments[i])); + for (size_t i = 0; i < module->data_segments.size(); ++i) + CHECK_RESULT(VisitDataSegment(i, module->data_segments[i])); + for (size_t i = 0; i < module->starts.size(); ++i) + CHECK_RESULT(VisitStart(module->starts[i])); + module_ = nullptr; + return Result::Ok; +} + +} // end anonymous namespace + +Result ApplyNames(Module* module) { + NameApplier applier; + return applier.VisitModule(module); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-ir.cc b/third_party/wasm2c/src/binary-reader-ir.cc new file mode 100644 index 0000000000..5736e3021e --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-ir.cc @@ -0,0 +1,1775 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-ir.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "wabt/binary-reader-nop.h" +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/ir.h" + +namespace wabt { + +namespace { + +struct LabelNode { + LabelNode(LabelType, ExprList* exprs, Expr* context = nullptr); + + LabelType label_type; + ExprList* exprs; + Expr* context; +}; + +LabelNode::LabelNode(LabelType label_type, ExprList* exprs, Expr* context) + : label_type(label_type), exprs(exprs), context(context) {} + +class CodeMetadataExprQueue { + private: + struct Entry { + Func* func; + std::deque> func_queue; + Entry(Func* f) : func(f) {} + }; + std::deque entries; + + public: + CodeMetadataExprQueue() {} + void push_func(Func* f) { entries.emplace_back(f); } + void push_metadata(std::unique_ptr meta) { + assert(!entries.empty()); + entries.back().func_queue.push_back(std::move(meta)); + } + + std::unique_ptr pop_match(Func* f, Offset offset) { + std::unique_ptr ret; + if (entries.empty()) { + return ret; + } + + auto& current_entry = entries.front(); + + if (current_entry.func != f) + return ret; + if (current_entry.func_queue.empty()) { + entries.pop_front(); + return ret; + } + + auto& current_metadata = current_entry.func_queue.front(); + if (current_metadata->loc.offset + current_entry.func->loc.offset != + offset) { + return ret; + } + + current_metadata->loc = Location(offset); + ret = std::move(current_metadata); + current_entry.func_queue.pop_front(); + + return ret; + } +}; + +class BinaryReaderIR : public BinaryReaderNop { + static constexpr size_t kMaxNestingDepth = 16384; // max depth of label stack + static constexpr size_t kMaxFunctionLocals = 50000; // matches V8 + static constexpr size_t kMaxFunctionParams = 1000; // matches V8 + static constexpr size_t kMaxFunctionResults = 1000; // matches V8 + + public: + BinaryReaderIR(Module* out_module, const char* filename, Errors* errors); + + bool OnError(const Error&) override; + + Result OnTypeCount(Index count) override; + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override; + Result OnStructType(Index index, Index field_count, TypeMut* fields) override; + Result OnArrayType(Index index, TypeMut field) override; + + Result OnImportCount(Index count) override; + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + std::string_view module_name, + std::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 OnExportCount(Index count) override; + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override; + + Result OnStartFunction(Index func_index) override; + + Result OnFunctionBodyCount(Index count) override; + Result BeginFunctionBody(Index index, Offset size) override; + Result OnLocalDecl(Index decl_index, Index count, Type type) override; + + Result OnOpcode(Opcode opcode) override; + Result OnAtomicLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnAtomicFenceExpr(uint32_t consistency_model) override; + Result OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + 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 OnCatchExpr(Index tag_index) override; + Result OnCatchAllExpr() override; + Result OnCallIndirectExpr(Index sig_index, Index table_index) override; + Result OnCallRefExpr() 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 OnDelegateExpr(Index depth) 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 OnMemoryFillExpr(Index memidx) override; + Result OnMemoryGrowExpr(Index memidx) override; + Result OnMemoryInitExpr(Index segment_index, Index memidx) override; + Result OnMemorySizeExpr(Index memidx) override; + Result OnTableCopyExpr(Index dst_index, Index src_index) override; + Result OnElemDropExpr(Index segment_index) override; + Result OnTableInitExpr(Index segment_index, Index table_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 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 OnThrowExpr(Index tag_index) override; + Result OnTryExpr(Type sig_type) override; + Result OnUnaryExpr(Opcode opcode) override; + Result OnTernaryExpr(Opcode opcode) override; + Result OnUnreachableExpr() override; + Result EndFunctionBody(Index index) override; + Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; + Result OnSimdLoadLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) override; + Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override; + Result OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) override; + Result OnLoadZeroExpr(Opcode opcode, + Index memidx, + 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 OnDataSegmentCount(Index count) override; + Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) override; + Result BeginDataSegmentInitExpr(Index index) override; + Result EndDataSegmentInitExpr(Index index) override; + Result OnDataSegmentData(Index index, + const void* data, + Address size) override; + + Result OnModuleName(std::string_view module_name) override; + Result OnFunctionNamesCount(Index num_functions) override; + Result OnFunctionName(Index function_index, + std::string_view function_name) override; + Result OnLocalNameLocalCount(Index function_index, Index num_locals) override; + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override; + + Result BeginTagSection(Offset size) override { return Result::Ok; } + Result OnTagCount(Index count) override { return Result::Ok; } + Result OnTagType(Index index, Index sig_index) override; + Result EndTagSection() override { return Result::Ok; } + + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override; + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override; + /* Code Metadata sections */ + Result BeginCodeMetadataSection(std::string_view name, Offset size) override; + Result OnCodeMetadataFuncCount(Index count) override; + Result OnCodeMetadataCount(Index function_index, Index count) override; + Result OnCodeMetadata(Offset offset, const void* data, Address size) override; + + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override; + + private: + Location GetLocation() const; + void PrintError(const char* format, ...); + Result PushLabel(LabelType label_type, + ExprList* first, + Expr* context = nullptr); + Result BeginInitExpr(ExprList* init_expr); + Result EndInitExpr(); + Result PopLabel(); + Result GetLabelAt(LabelNode** label, Index depth); + Result TopLabel(LabelNode** label); + Result TopLabelExpr(LabelNode** label, Expr** expr); + Result AppendExpr(std::unique_ptr expr); + Result AppendCatch(Catch&& catch_); + void SetFuncDeclaration(FuncDeclaration* decl, Var var); + void SetBlockDeclaration(BlockDeclaration* decl, Type sig_type); + Result SetMemoryName(Index index, std::string_view name); + Result SetTableName(Index index, std::string_view name); + Result SetFunctionName(Index index, std::string_view name); + Result SetTypeName(Index index, std::string_view name); + Result SetGlobalName(Index index, std::string_view name); + Result SetDataSegmentName(Index index, std::string_view name); + Result SetElemSegmentName(Index index, std::string_view name); + Result SetTagName(Index index, std::string_view name); + + std::string GetUniqueName(BindingHash* bindings, + const std::string& original_name); + + Errors* errors_ = nullptr; + Module* module_ = nullptr; + + Func* current_func_ = nullptr; + std::vector label_stack_; + const char* filename_; + + CodeMetadataExprQueue code_metadata_queue_; + std::string_view current_metadata_name_; +}; + +BinaryReaderIR::BinaryReaderIR(Module* out_module, + const char* filename, + Errors* errors) + : errors_(errors), module_(out_module), filename_(filename) {} + +Location BinaryReaderIR::GetLocation() const { + Location loc; + loc.filename = filename_; + loc.offset = state->offset; + return loc; +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReaderIR::PrintError(const char* format, + ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + errors_->emplace_back(ErrorLevel::Error, Location(kInvalidOffset), buffer); +} + +Result BinaryReaderIR::PushLabel(LabelType label_type, + ExprList* first, + Expr* context) { + if (label_stack_.size() >= kMaxNestingDepth) { + PrintError("label stack exceeds max nesting depth"); + return Result::Error; + } + label_stack_.emplace_back(label_type, first, context); + return Result::Ok; +} + +Result BinaryReaderIR::PopLabel() { + if (label_stack_.size() == 0) { + PrintError("popping empty label stack"); + return Result::Error; + } + + label_stack_.pop_back(); + return Result::Ok; +} + +Result BinaryReaderIR::GetLabelAt(LabelNode** label, Index depth) { + if (depth >= label_stack_.size()) { + PrintError("accessing stack depth: %" PRIindex " >= max: %" PRIzd, depth, + label_stack_.size()); + return Result::Error; + } + + *label = &label_stack_[label_stack_.size() - depth - 1]; + return Result::Ok; +} + +Result BinaryReaderIR::TopLabel(LabelNode** label) { + return GetLabelAt(label, 0); +} + +Result BinaryReaderIR::TopLabelExpr(LabelNode** label, Expr** expr) { + CHECK_RESULT(TopLabel(label)); + LabelNode* parent_label; + CHECK_RESULT(GetLabelAt(&parent_label, 1)); + if (parent_label->exprs->empty()) { + PrintError("TopLabelExpr: parent label has empty expr list"); + return Result::Error; + } + *expr = &parent_label->exprs->back(); + return Result::Ok; +} + +Result BinaryReaderIR::AppendExpr(std::unique_ptr expr) { + expr->loc = GetLocation(); + LabelNode* label; + CHECK_RESULT(TopLabel(&label)); + label->exprs->push_back(std::move(expr)); + return Result::Ok; +} + +void BinaryReaderIR::SetFuncDeclaration(FuncDeclaration* decl, Var var) { + decl->has_func_type = true; + decl->type_var = var; + if (auto* func_type = module_->GetFuncType(var)) { + decl->sig = func_type->sig; + } +} + +void BinaryReaderIR::SetBlockDeclaration(BlockDeclaration* decl, + Type sig_type) { + if (sig_type.IsIndex()) { + Index type_index = sig_type.GetIndex(); + SetFuncDeclaration(decl, Var(type_index, GetLocation())); + } else { + decl->has_func_type = false; + decl->sig.param_types.clear(); + decl->sig.result_types = sig_type.GetInlineVector(); + } +} + +std::string BinaryReaderIR::GetUniqueName(BindingHash* bindings, + const std::string& orig_name) { + int counter = 1; + std::string unique_name = orig_name; + while (bindings->count(unique_name) != 0) { + unique_name = orig_name + "." + std::to_string(counter++); + } + return unique_name; +} + +bool BinaryReaderIR::OnError(const Error& error) { + errors_->push_back(error); + return true; +} + +Result BinaryReaderIR::OnTypeCount(Index count) { + WABT_TRY + module_->types.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + if (param_count > kMaxFunctionParams) { + PrintError("FuncType param count exceeds maximum value"); + return Result::Error; + } + + if (result_count > kMaxFunctionResults) { + PrintError("FuncType result count exceeds maximum value"); + return Result::Error; + } + + auto field = std::make_unique(GetLocation()); + auto func_type = std::make_unique(); + func_type->sig.param_types.assign(param_types, param_types + param_count); + func_type->sig.result_types.assign(result_types, result_types + result_count); + + module_->features_used.simd |= + std::any_of(func_type->sig.param_types.begin(), + func_type->sig.param_types.end(), + [](auto x) { return x == Type::V128; }) || + std::any_of(func_type->sig.result_types.begin(), + func_type->sig.result_types.end(), + [](auto x) { return x == Type::V128; }); + + field->type = std::move(func_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + auto field = std::make_unique(GetLocation()); + auto struct_type = std::make_unique(); + struct_type->fields.resize(field_count); + for (Index i = 0; i < field_count; ++i) { + struct_type->fields[i].type = fields[i].type; + struct_type->fields[i].mutable_ = fields[i].mutable_; + module_->features_used.simd |= (fields[i].type == Type::V128); + } + field->type = std::move(struct_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) { + auto field = std::make_unique(GetLocation()); + auto array_type = std::make_unique(); + array_type->field.type = type_mut.type; + array_type->field.mutable_ = type_mut.mutable_; + module_->features_used.simd |= (type_mut.type == Type::V128); + field->type = std::move(array_type); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportCount(Index count) { + WABT_TRY + module_->imports.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + auto import = std::make_unique(); + import->module_name = module_name; + import->field_name = field_name; + SetFuncDeclaration(&import->func.decl, Var(sig_index, GetLocation())); + module_->AppendField( + std::make_unique(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + auto import = std::make_unique(); + import->module_name = module_name; + import->field_name = field_name; + import->table.elem_limits = *elem_limits; + import->table.elem_type = elem_type; + module_->AppendField( + std::make_unique(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + auto import = std::make_unique(); + import->module_name = module_name; + import->field_name = field_name; + import->memory.page_limits = *page_limits; + module_->AppendField( + std::make_unique(std::move(import), GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + auto import = std::make_unique(); + import->module_name = module_name; + import->field_name = field_name; + import->global.type = type; + import->global.mutable_ = mutable_; + module_->AppendField( + std::make_unique(std::move(import), GetLocation())); + module_->features_used.simd |= (type == Type::V128); + return Result::Ok; +} + +Result BinaryReaderIR::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + auto import = std::make_unique(); + import->module_name = module_name; + import->field_name = field_name; + SetFuncDeclaration(&import->tag.decl, Var(sig_index, GetLocation())); + module_->AppendField( + std::make_unique(std::move(import), GetLocation())); + module_->features_used.exceptions = true; + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionCount(Index count) { + WABT_TRY + module_->funcs.reserve(module_->num_func_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnFunction(Index index, Index sig_index) { + auto field = std::make_unique(GetLocation()); + Func& func = field->func; + SetFuncDeclaration(&func.decl, Var(sig_index, GetLocation())); + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTableCount(Index count) { + WABT_TRY + module_->tables.reserve(module_->num_table_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + auto field = std::make_unique(GetLocation()); + Table& table = field->table; + table.elem_limits = *elem_limits; + table.elem_type = elem_type; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnMemoryCount(Index count) { + WABT_TRY + module_->memories.reserve(module_->num_memory_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnMemory(Index index, const Limits* page_limits) { + auto field = std::make_unique(GetLocation()); + Memory& memory = field->memory; + memory.page_limits = *page_limits; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnGlobalCount(Index count) { + WABT_TRY + module_->globals.reserve(module_->num_global_imports + count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginGlobal(Index index, Type type, bool mutable_) { + auto field = std::make_unique(GetLocation()); + Global& global = field->global; + global.type = type; + global.mutable_ = mutable_; + module_->AppendField(std::move(field)); + module_->features_used.simd |= (type == Type::V128); + return Result::Ok; +} + +Result BinaryReaderIR::BeginGlobalInitExpr(Index index) { + assert(index == module_->globals.size() - 1); + Global* global = module_->globals[index]; + return BeginInitExpr(&global->init_expr); +} + +Result BinaryReaderIR::EndGlobalInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnExportCount(Index count) { + WABT_TRY + module_->exports.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + auto field = std::make_unique(GetLocation()); + Export& export_ = field->export_; + export_.name = name; + export_.var = Var(item_index, GetLocation()); + export_.kind = kind; + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::OnStartFunction(Index func_index) { + Var start(func_index, GetLocation()); + module_->AppendField( + std::make_unique(start, GetLocation())); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionBodyCount(Index count) { + // Can hit this case on a malformed module if we don't stop on first error. + if (module_->num_func_imports + count != module_->funcs.size()) { + PrintError( + "number of imported func + func count in code section does not match " + "actual number of funcs in module"); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) { + current_func_ = module_->funcs[index]; + current_func_->loc = GetLocation(); + return PushLabel(LabelType::Func, ¤t_func_->exprs); +} + +Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) { + current_func_->local_types.AppendDecl(type, count); + + if (current_func_->GetNumLocals() > kMaxFunctionLocals) { + PrintError("function local count exceeds maximum value"); + return Result::Error; + } + + module_->features_used.simd |= (type == Type::V128); + return Result::Ok; +} + +Result BinaryReaderIR::OnOpcode(Opcode opcode) { + std::unique_ptr metadata = + code_metadata_queue_.pop_match(current_func_, GetLocation().offset - 1); + if (metadata) { + return AppendExpr(std::move(metadata)); + } + module_->features_used.simd |= (opcode.GetResultType() == Type::V128); + return Result::Ok; +} + +Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicRmwCmpxchgExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicWaitExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnAtomicFenceExpr(uint32_t consistency_model) { + return AppendExpr(std::make_unique(consistency_model)); +} + +Result BinaryReaderIR::OnAtomicNotifyExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnBinaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + +Result BinaryReaderIR::OnBlockExpr(Type sig_type) { + auto expr = std::make_unique(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::Block, expr_list); +} + +Result BinaryReaderIR::OnBrExpr(Index depth) { + return AppendExpr(std::make_unique(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnBrIfExpr(Index depth) { + return AppendExpr(std::make_unique(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + auto expr = std::make_unique(); + expr->default_target = Var(default_target_depth, GetLocation()); + expr->targets.resize(num_targets); + for (Index i = 0; i < num_targets; ++i) { + expr->targets[i] = Var(target_depths[i], GetLocation()); + } + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallExpr(Index func_index) { + return AppendExpr(std::make_unique(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnCallIndirectExpr(Index sig_index, Index table_index) { + auto expr = std::make_unique(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index, GetLocation()); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCallRefExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::OnReturnCallExpr(Index func_index) { + return AppendExpr( + std::make_unique(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index, + Index table_index) { + auto expr = std::make_unique(); + SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation())); + expr->table = Var(table_index, GetLocation()); + return AppendExpr(std::move(expr)); +} + +Result BinaryReaderIR::OnCompareExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + +Result BinaryReaderIR::OnConvertExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + +Result BinaryReaderIR::OnDropExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::OnElseExpr() { + LabelNode* label; + Expr* expr; + CHECK_RESULT(TopLabelExpr(&label, &expr)); + + if (label->label_type == LabelType::If) { + auto* if_expr = cast(expr); + if_expr->true_.end_loc = GetLocation(); + label->exprs = &if_expr->false_; + label->label_type = LabelType::Else; + } else { + PrintError("else expression without matching if"); + return Result::Error; + } + + return Result::Ok; +} + +Result BinaryReaderIR::OnEndExpr() { + if (label_stack_.size() > 1) { + LabelNode* label; + Expr* expr; + CHECK_RESULT(TopLabelExpr(&label, &expr)); + switch (label->label_type) { + case LabelType::Block: + cast(expr)->block.end_loc = GetLocation(); + break; + case LabelType::Loop: + cast(expr)->block.end_loc = GetLocation(); + break; + case LabelType::If: + cast(expr)->true_.end_loc = GetLocation(); + break; + case LabelType::Else: + cast(expr)->false_end_loc = GetLocation(); + break; + case LabelType::Try: + cast(expr)->block.end_loc = GetLocation(); + break; + + case LabelType::InitExpr: + case LabelType::Func: + case LabelType::Catch: + break; + } + } + + return PopLabel(); +} + +Result BinaryReaderIR::OnF32ConstExpr(uint32_t value_bits) { + return AppendExpr( + std::make_unique(Const::F32(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnF64ConstExpr(uint64_t value_bits) { + return AppendExpr( + std::make_unique(Const::F64(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) { + return AppendExpr( + std::make_unique(Const::V128(value_bits, GetLocation()))); +} + +Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) { + return AppendExpr( + std::make_unique(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalGetExpr(Index local_index) { + return AppendExpr( + std::make_unique(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnI32ConstExpr(uint32_t value) { + return AppendExpr( + std::make_unique(Const::I32(value, GetLocation()))); +} + +Result BinaryReaderIR::OnI64ConstExpr(uint64_t value) { + return AppendExpr( + std::make_unique(Const::I64(value, GetLocation()))); +} + +Result BinaryReaderIR::OnIfExpr(Type sig_type) { + auto expr = std::make_unique(); + SetBlockDeclaration(&expr->true_.decl, sig_type); + ExprList* expr_list = &expr->true_.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::If, expr_list); +} + +Result BinaryReaderIR::OnLoadExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoopExpr(Type sig_type) { + auto expr = std::make_unique(); + SetBlockDeclaration(&expr->block.decl, sig_type); + ExprList* expr_list = &expr->block.exprs; + CHECK_RESULT(AppendExpr(std::move(expr))); + return PushLabel(LabelType::Loop, expr_list); +} + +Result BinaryReaderIR::OnMemoryCopyExpr(Index srcmemidx, Index destmemidx) { + return AppendExpr(std::make_unique( + Var(srcmemidx, GetLocation()), Var(destmemidx, GetLocation()))); +} + +Result BinaryReaderIR::OnDataDropExpr(Index segment) { + return AppendExpr( + std::make_unique(Var(segment, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryFillExpr(Index memidx) { + return AppendExpr( + std::make_unique(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryGrowExpr(Index memidx) { + return AppendExpr( + std::make_unique(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemoryInitExpr(Index segment, Index memidx) { + return AppendExpr(std::make_unique( + Var(segment, GetLocation()), Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnMemorySizeExpr(Index memidx) { + return AppendExpr( + std::make_unique(Var(memidx, GetLocation()))); +} + +Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) { + return AppendExpr(std::make_unique( + Var(dst_index, GetLocation()), Var(src_index, GetLocation()))); +} + +Result BinaryReaderIR::OnElemDropExpr(Index segment) { + return AppendExpr( + std::make_unique(Var(segment, GetLocation()))); +} + +Result BinaryReaderIR::OnTableInitExpr(Index segment, Index table_index) { + return AppendExpr(std::make_unique( + Var(segment, GetLocation()), Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableGetExpr(Index table_index) { + return AppendExpr( + std::make_unique(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableSetExpr(Index table_index) { + return AppendExpr( + std::make_unique(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableGrowExpr(Index table_index) { + return AppendExpr( + std::make_unique(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableSizeExpr(Index table_index) { + return AppendExpr( + std::make_unique(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTableFillExpr(Index table_index) { + return AppendExpr( + std::make_unique(Var(table_index, GetLocation()))); +} + +Result BinaryReaderIR::OnRefFuncExpr(Index func_index) { + return AppendExpr( + std::make_unique(Var(func_index, GetLocation()))); +} + +Result BinaryReaderIR::OnRefNullExpr(Type type) { + return AppendExpr(std::make_unique(type)); +} + +Result BinaryReaderIR::OnRefIsNullExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::OnNopExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::OnRethrowExpr(Index depth) { + return AppendExpr(std::make_unique(Var(depth, GetLocation()))); +} + +Result BinaryReaderIR::OnReturnExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) { + TypeVector results; + results.assign(result_types, result_types + result_count); + return AppendExpr(std::make_unique(results)); +} + +Result BinaryReaderIR::OnGlobalSetExpr(Index global_index) { + return AppendExpr( + std::make_unique(Var(global_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalSetExpr(Index local_index) { + return AppendExpr( + std::make_unique(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnStoreExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnThrowExpr(Index tag_index) { + return AppendExpr(std::make_unique(Var(tag_index, GetLocation()))); +} + +Result BinaryReaderIR::OnLocalTeeExpr(Index local_index) { + return AppendExpr( + std::make_unique(Var(local_index, GetLocation()))); +} + +Result BinaryReaderIR::OnTryExpr(Type sig_type) { + auto expr_ptr = std::make_unique(); + // Save expr so it can be used below, after expr_ptr has been moved. + TryExpr* expr = expr_ptr.get(); + ExprList* expr_list = &expr->block.exprs; + SetBlockDeclaration(&expr->block.decl, sig_type); + CHECK_RESULT(AppendExpr(std::move(expr_ptr))); + module_->features_used.exceptions = true; + return PushLabel(LabelType::Try, expr_list, expr); +} + +Result BinaryReaderIR::AppendCatch(Catch&& catch_) { + LabelNode* label = nullptr; + CHECK_RESULT(TopLabel(&label)); + + if (label->label_type != LabelType::Try) { + PrintError("catch not inside try block"); + return Result::Error; + } + + auto* try_ = cast(label->context); + + if (catch_.IsCatchAll() && !try_->catches.empty() && + try_->catches.back().IsCatchAll()) { + PrintError("only one catch_all allowed in try block"); + return Result::Error; + } + + if (try_->kind == TryKind::Plain) { + try_->kind = TryKind::Catch; + } else if (try_->kind != TryKind::Catch) { + PrintError("catch not allowed in try-delegate"); + return Result::Error; + } + + try_->catches.push_back(std::move(catch_)); + label->exprs = &try_->catches.back().exprs; + return Result::Ok; +} + +Result BinaryReaderIR::OnCatchExpr(Index except_index) { + return AppendCatch(Catch(Var(except_index, GetLocation()))); +} + +Result BinaryReaderIR::OnCatchAllExpr() { + return AppendCatch(Catch(GetLocation())); +} + +Result BinaryReaderIR::OnDelegateExpr(Index depth) { + LabelNode* label = nullptr; + CHECK_RESULT(TopLabel(&label)); + + if (label->label_type != LabelType::Try) { + PrintError("delegate not inside try block"); + return Result::Error; + } + + auto* try_ = cast(label->context); + + if (try_->kind == TryKind::Plain) { + try_->kind = TryKind::Delegate; + } else if (try_->kind != TryKind::Delegate) { + PrintError("delegate not allowed in try-catch"); + return Result::Error; + } + + try_->delegate_target = Var(depth, GetLocation()); + + PopLabel(); + return Result::Ok; +} + +Result BinaryReaderIR::OnUnaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + +Result BinaryReaderIR::OnTernaryExpr(Opcode opcode) { + return AppendExpr(std::make_unique(opcode)); +} + +Result BinaryReaderIR::OnUnreachableExpr() { + return AppendExpr(std::make_unique()); +} + +Result BinaryReaderIR::EndFunctionBody(Index index) { + current_func_ = nullptr; + if (!label_stack_.empty()) { + PrintError("function %" PRIindex " missing end marker", index); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { + return AppendExpr(std::make_unique(opcode, value)); +} + +Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset, + uint64_t value) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset, value)); +} + +Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { + return AppendExpr(std::make_unique(opcode, value)); +} + +Result BinaryReaderIR::OnLoadSplatExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnLoadZeroExpr(Opcode opcode, + Index memidx, + Address alignment_log2, + Address offset) { + return AppendExpr(std::make_unique( + opcode, Var(memidx, GetLocation()), 1 << alignment_log2, offset)); +} + +Result BinaryReaderIR::OnElemSegmentCount(Index count) { + WABT_TRY + module_->elem_segments.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + auto field = std::make_unique(GetLocation()); + ElemSegment& elem_segment = field->elem_segment; + elem_segment.table_var = Var(table_index, GetLocation()); + if ((flags & SegDeclared) == SegDeclared) { + elem_segment.kind = SegmentKind::Declared; + } else if ((flags & SegPassive) == SegPassive) { + elem_segment.kind = SegmentKind::Passive; + } else { + elem_segment.kind = SegmentKind::Active; + } + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginInitExpr(ExprList* expr) { + return PushLabel(LabelType::InitExpr, expr); +} + +Result BinaryReaderIR::BeginElemSegmentInitExpr(Index index) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + return BeginInitExpr(&segment->offset); +} + +Result BinaryReaderIR::EndInitExpr() { + if (!label_stack_.empty()) { + PrintError("init expression missing end marker"); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnElemSegmentElemType(Index index, Type elem_type) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + segment->elem_type = elem_type; + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExprCount(Index index, Index count) { + assert(index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[index]; + WABT_TRY + segment->elem_exprs.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) { + assert(segment_index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[segment_index]; + Location loc = GetLocation(); + ExprList init_expr; + init_expr.push_back(std::make_unique(type, loc)); + segment->elem_exprs.push_back(std::move(init_expr)); + return Result::Ok; +} + +Result BinaryReaderIR::OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) { + assert(segment_index == module_->elem_segments.size() - 1); + ElemSegment* segment = module_->elem_segments[segment_index]; + Location loc = GetLocation(); + ExprList init_expr; + init_expr.push_back(std::make_unique(Var(func_index, loc), loc)); + segment->elem_exprs.push_back(std::move(init_expr)); + return Result::Ok; +} + +Result BinaryReaderIR::OnDataSegmentCount(Index count) { + WABT_TRY + module_->data_segments.reserve(count); + WABT_CATCH_BAD_ALLOC + return Result::Ok; +} + +Result BinaryReaderIR::BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) { + auto field = std::make_unique(GetLocation()); + DataSegment& data_segment = field->data_segment; + data_segment.memory_var = Var(memory_index, GetLocation()); + if ((flags & SegPassive) == SegPassive) { + data_segment.kind = SegmentKind::Passive; + } else { + data_segment.kind = SegmentKind::Active; + } + module_->AppendField(std::move(field)); + return Result::Ok; +} + +Result BinaryReaderIR::BeginDataSegmentInitExpr(Index index) { + assert(index == module_->data_segments.size() - 1); + DataSegment* segment = module_->data_segments[index]; + return BeginInitExpr(&segment->offset); +} + +Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) { + return EndInitExpr(); +} + +Result BinaryReaderIR::OnDataSegmentData(Index index, + const void* data, + Address size) { + assert(index == module_->data_segments.size() - 1); + DataSegment* segment = module_->data_segments[index]; + segment->data.resize(size); + if (size > 0) { + memcpy(segment->data.data(), data, size); + } + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionNamesCount(Index count) { + if (count > module_->funcs.size()) { + PrintError("expected function name count (%" PRIindex + ") <= function count (%" PRIzd ")", + count, module_->funcs.size()); + return Result::Error; + } + return Result::Ok; +} + +static std::string MakeDollarName(std::string_view name) { + return std::string("$") + std::string(name); +} + +Result BinaryReaderIR::OnModuleName(std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + + module_->name = MakeDollarName(name); + return Result::Ok; +} + +Result BinaryReaderIR::SetGlobalName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->globals.size()) { + PrintError("invalid global index: %" PRIindex, index); + return Result::Error; + } + Global* glob = module_->globals[index]; + std::string dollar_name = + GetUniqueName(&module_->global_bindings, MakeDollarName(name)); + glob->name = dollar_name; + module_->global_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetFunctionName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->funcs.size()) { + PrintError("invalid function index: %" PRIindex, index); + return Result::Error; + } + Func* func = module_->funcs[index]; + std::string dollar_name = + GetUniqueName(&module_->func_bindings, MakeDollarName(name)); + func->name = dollar_name; + module_->func_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTypeName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->types.size()) { + PrintError("invalid type index: %" PRIindex, index); + return Result::Error; + } + TypeEntry* type = module_->types[index]; + std::string dollar_name = + GetUniqueName(&module_->type_bindings, MakeDollarName(name)); + type->name = dollar_name; + module_->type_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTableName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->tables.size()) { + PrintError("invalid table index: %" PRIindex, index); + return Result::Error; + } + Table* table = module_->tables[index]; + std::string dollar_name = + GetUniqueName(&module_->table_bindings, MakeDollarName(name)); + table->name = dollar_name; + module_->table_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetDataSegmentName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->data_segments.size()) { + PrintError("invalid data segment index: %" PRIindex, index); + return Result::Error; + } + DataSegment* segment = module_->data_segments[index]; + std::string dollar_name = + GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name)); + segment->name = dollar_name; + module_->data_segment_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetElemSegmentName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->elem_segments.size()) { + PrintError("invalid elem segment index: %" PRIindex, index); + return Result::Error; + } + ElemSegment* segment = module_->elem_segments[index]; + std::string dollar_name = + GetUniqueName(&module_->elem_segment_bindings, MakeDollarName(name)); + segment->name = dollar_name; + module_->elem_segment_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetMemoryName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->memories.size()) { + PrintError("invalid memory index: %" PRIindex, index); + return Result::Error; + } + Memory* memory = module_->memories[index]; + std::string dollar_name = + GetUniqueName(&module_->memory_bindings, MakeDollarName(name)); + memory->name = dollar_name; + module_->memory_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::SetTagName(Index index, std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + if (index >= module_->tags.size()) { + PrintError("invalid tag index: %" PRIindex, index); + return Result::Error; + } + Tag* tag = module_->tags[index]; + std::string dollar_name = + GetUniqueName(&module_->tag_bindings, MakeDollarName(name)); + tag->name = dollar_name; + module_->tag_bindings.emplace(dollar_name, Binding(index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionName(Index index, std::string_view name) { + return SetFunctionName(index, name); +} + +Result BinaryReaderIR::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + switch (type) { + // TODO(sbc): remove OnFunctionName in favor of just using + // OnNameEntry so that this works + case NameSectionSubsection::Function: + case NameSectionSubsection::Local: + case NameSectionSubsection::Module: + case NameSectionSubsection::Label: + break; + case NameSectionSubsection::Type: + SetTypeName(index, name); + break; + case NameSectionSubsection::Tag: + SetTagName(index, name); + break; + case NameSectionSubsection::Global: + SetGlobalName(index, name); + break; + case NameSectionSubsection::Table: + SetTableName(index, name); + break; + case NameSectionSubsection::DataSegment: + SetDataSegmentName(index, name); + break; + case NameSectionSubsection::Memory: + SetMemoryName(index, name); + break; + case NameSectionSubsection::ElemSegment: + SetElemSegmentName(index, name); + break; + } + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalNameLocalCount(Index index, Index count) { + assert(index < module_->funcs.size()); + Func* func = module_->funcs[index]; + Index num_params_and_locals = func->GetNumParamsAndLocals(); + if (count > num_params_and_locals) { + PrintError("expected local name count (%" PRIindex + ") <= local count (%" PRIindex ")", + count, num_params_and_locals); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReaderIR::BeginCodeMetadataSection(std::string_view name, + Offset size) { + current_metadata_name_ = name; + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadataFuncCount(Index count) { + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadataCount(Index function_index, Index count) { + code_metadata_queue_.push_func(module_->funcs[function_index]); + return Result::Ok; +} + +Result BinaryReaderIR::OnCodeMetadata(Offset offset, + const void* data, + Address size) { + std::vector data_(static_cast(data), + static_cast(data) + size); + auto meta = std::make_unique(current_metadata_name_, + std::move(data_)); + meta->loc.offset = offset; + code_metadata_queue_.push_metadata(std::move(meta)); + return Result::Ok; +} + +Result BinaryReaderIR::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + if (name.empty()) { + return Result::Ok; + } + + Func* func = module_->funcs[func_index]; + func->bindings.emplace(GetUniqueName(&func->bindings, MakeDollarName(name)), + Binding(local_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTagType(Index index, Index sig_index) { + auto field = std::make_unique(GetLocation()); + Tag& tag = field->tag; + SetFuncDeclaration(&tag.decl, Var(sig_index, GetLocation())); + module_->AppendField(std::move(field)); + module_->features_used.exceptions = true; + return Result::Ok; +} + +Result BinaryReaderIR::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + if (name.empty()) { + return Result::Ok; + } + if (flags & WABT_SYMBOL_FLAG_UNDEFINED) { + // Refers to data in another file, `segment` not valid. + return Result::Ok; + } + if (offset) { + // If it is pointing into the data segment, then it's not really naming + // the whole segment. + return Result::Ok; + } + if (segment >= module_->data_segments.size()) { + PrintError("invalid data segment index: %" PRIindex, segment); + return Result::Error; + } + DataSegment* seg = module_->data_segments[segment]; + std::string dollar_name = + GetUniqueName(&module_->data_segment_bindings, MakeDollarName(name)); + seg->name = dollar_name; + module_->data_segment_bindings.emplace(dollar_name, Binding(segment)); + return Result::Ok; +} + +Result BinaryReaderIR::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + if (name.empty()) { + return Result::Ok; + } + if (func_index >= module_->funcs.size()) { + PrintError("invalid function index: %" PRIindex, func_index); + return Result::Error; + } + Func* func = module_->funcs[func_index]; + if (!func->name.empty()) { + // The name section has already named this function. + return Result::Ok; + } + std::string dollar_name = + GetUniqueName(&module_->func_bindings, MakeDollarName(name)); + func->name = dollar_name; + module_->func_bindings.emplace(dollar_name, Binding(func_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + return SetGlobalName(global_index, name); +} + +Result BinaryReaderIR::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + return Result::Ok; +} + +Result BinaryReaderIR::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + if (name.empty()) { + return Result::Ok; + } + if (tag_index >= module_->tags.size()) { + PrintError("invalid tag index: %" PRIindex, tag_index); + return Result::Error; + } + Tag* tag = module_->tags[tag_index]; + std::string dollar_name = + GetUniqueName(&module_->tag_bindings, MakeDollarName(name)); + tag->name = dollar_name; + module_->tag_bindings.emplace(dollar_name, Binding(tag_index)); + return Result::Ok; +} + +Result BinaryReaderIR::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + return SetTableName(table_index, name); +} + +} // end anonymous namespace + +Result ReadBinaryIr(const char* filename, + const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors* errors, + Module* out_module) { + BinaryReaderIR reader(out_module, filename, errors); + return ReadBinary(data, size, &reader, options); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-logging.cc b/third_party/wasm2c/src/binary-reader-logging.cc new file mode 100644 index 0000000000..83a14e9ff7 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-logging.cc @@ -0,0 +1,971 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-logging.h" + +#include + +#include "wabt/stream.h" + +namespace wabt { + +#define INDENT_SIZE 2 + +#define LOGF_NOINDENT(...) stream_->Writef(__VA_ARGS__) + +#define LOGF(...) \ + do { \ + WriteIndent(); \ + LOGF_NOINDENT(__VA_ARGS__); \ + } while (0) + +namespace { + +void SPrintLimits(char* dst, size_t size, const Limits* limits) { + int result; + if (limits->has_max) { + result = wabt_snprintf(dst, size, "initial: %" PRIu64 ", max: %" PRIu64, + limits->initial, limits->max); + } else { + result = wabt_snprintf(dst, size, "initial: %" PRIu64, limits->initial); + } + WABT_USE(result); + assert(static_cast(result) < size); +} + +} // end anonymous namespace + +BinaryReaderLogging::BinaryReaderLogging(Stream* stream, + BinaryReaderDelegate* forward) + : stream_(stream), reader_(forward), indent_(0) {} + +void BinaryReaderLogging::Indent() { + indent_ += INDENT_SIZE; +} + +void BinaryReaderLogging::Dedent() { + indent_ -= INDENT_SIZE; + assert(indent_ >= 0); +} + +void BinaryReaderLogging::WriteIndent() { + static char s_indent[] = + " " + " "; + static const size_t s_indent_len = sizeof(s_indent) - 1; + size_t i = indent_; + while (i > s_indent_len) { + stream_->WriteData(s_indent, s_indent_len); + i -= s_indent_len; + } + if (i > 0) { + stream_->WriteData(s_indent, indent_); + } +} + +void BinaryReaderLogging::LogType(Type type) { + if (type.IsIndex()) { + LOGF_NOINDENT("typeidx[%d]", type.GetIndex()); + } else { + LOGF_NOINDENT("%s", type.GetName().c_str()); + } +} + +void BinaryReaderLogging::LogTypes(Index type_count, Type* types) { + LOGF_NOINDENT("["); + for (Index i = 0; i < type_count; ++i) { + LogType(types[i]); + if (i != type_count - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("]"); +} + +void BinaryReaderLogging::LogTypes(TypeVector& types) { + LogTypes(types.size(), types.data()); +} + +void BinaryReaderLogging::LogField(TypeMut field) { + if (field.mutable_) { + LOGF_NOINDENT("(mut "); + } + LogType(field.type); + if (field.mutable_) { + LOGF_NOINDENT(")"); + } +} + +bool BinaryReaderLogging::OnError(const Error& error) { + return reader_->OnError(error); +} + +void BinaryReaderLogging::OnSetState(const State* s) { + BinaryReaderDelegate::OnSetState(s); + reader_->OnSetState(s); +} + +Result BinaryReaderLogging::BeginModule(uint32_t version) { + LOGF("BeginModule(version: %u)\n", version); + Indent(); + return reader_->BeginModule(version); +} + +Result BinaryReaderLogging::BeginSection(Index section_index, + BinarySection section_type, + Offset size) { + return reader_->BeginSection(section_index, section_type, size); +} + +Result BinaryReaderLogging::BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) { + LOGF("BeginCustomSection('" PRIstringview "', size: %" PRIzd ")\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name), size); + Indent(); + return reader_->BeginCustomSection(section_index, size, section_name); +} + +Result BinaryReaderLogging::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + LOGF("OnFuncType(index: %" PRIindex ", params: ", index); + LogTypes(param_count, param_types); + LOGF_NOINDENT(", results: "); + LogTypes(result_count, result_types); + LOGF_NOINDENT(")\n"); + return reader_->OnFuncType(index, param_count, param_types, result_count, + result_types); +} + +Result BinaryReaderLogging::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + LOGF("OnStructType(index: %" PRIindex ", fields: ", index); + LOGF_NOINDENT("["); + for (Index i = 0; i < field_count; ++i) { + LogField(fields[i]); + if (i != field_count - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("])\n"); + return reader_->OnStructType(index, field_count, fields); +} + +Result BinaryReaderLogging::OnArrayType(Index index, TypeMut field) { + LOGF("OnArrayType(index: %" PRIindex ", field: ", index); + LogField(field); + LOGF_NOINDENT(")\n"); + return reader_->OnArrayType(index, field); +} + +Result BinaryReaderLogging::OnImport(Index index, + ExternalKind kind, + std::string_view module_name, + std::string_view field_name) { + LOGF("OnImport(index: %" PRIindex ", kind: %s, module: \"" PRIstringview + "\", field: \"" PRIstringview "\")\n", + index, GetKindName(kind), WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return reader_->OnImport(index, kind, module_name, field_name); +} + +Result BinaryReaderLogging::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + LOGF("OnImportFunc(import_index: %" PRIindex ", func_index: %" PRIindex + ", sig_index: %" PRIindex ")\n", + import_index, func_index, sig_index); + return reader_->OnImportFunc(import_index, module_name, field_name, + func_index, sig_index); +} + +Result BinaryReaderLogging::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), elem_limits); + LOGF("OnImportTable(import_index: %" PRIindex ", table_index: %" PRIindex + ", elem_type: %s, %s)\n", + import_index, table_index, elem_type.GetName().c_str(), buf); + return reader_->OnImportTable(import_index, module_name, field_name, + table_index, elem_type, elem_limits); +} + +Result BinaryReaderLogging::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), page_limits); + LOGF("OnImportMemory(import_index: %" PRIindex ", memory_index: %" PRIindex + ", %s)\n", + import_index, memory_index, buf); + return reader_->OnImportMemory(import_index, module_name, field_name, + memory_index, page_limits); +} + +Result BinaryReaderLogging::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + LOGF("OnImportGlobal(import_index: %" PRIindex ", global_index: %" PRIindex + ", type: %s, mutable: " + "%s)\n", + import_index, global_index, type.GetName().c_str(), + mutable_ ? "true" : "false"); + return reader_->OnImportGlobal(import_index, module_name, field_name, + global_index, type, mutable_); +} + +Result BinaryReaderLogging::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + LOGF("OnImportTag(import_index: %" PRIindex ", tag_index: %" PRIindex + ", sig_index: %" PRIindex ")\n", + import_index, tag_index, sig_index); + return reader_->OnImportTag(import_index, module_name, field_name, tag_index, + sig_index); +} + +Result BinaryReaderLogging::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), elem_limits); + LOGF("OnTable(index: %" PRIindex ", elem_type: %s, %s)\n", index, + elem_type.GetName().c_str(), buf); + return reader_->OnTable(index, elem_type, elem_limits); +} + +Result BinaryReaderLogging::OnMemory(Index index, const Limits* page_limits) { + char buf[100]; + SPrintLimits(buf, sizeof(buf), page_limits); + LOGF("OnMemory(index: %" PRIindex ", %s)\n", index, buf); + return reader_->OnMemory(index, page_limits); +} + +Result BinaryReaderLogging::BeginGlobal(Index index, Type type, bool mutable_) { + LOGF("BeginGlobal(index: %" PRIindex ", type: %s, mutable: %s)\n", index, + type.GetName().c_str(), mutable_ ? "true" : "false"); + return reader_->BeginGlobal(index, type, mutable_); +} + +Result BinaryReaderLogging::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + LOGF("OnExport(index: %" PRIindex ", kind: %s, item_index: %" PRIindex + ", name: \"" PRIstringview "\")\n", + index, GetKindName(kind), item_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnExport(index, kind, item_index, name); +} + +Result BinaryReaderLogging::BeginFunctionBody(Index value, Offset size) { + LOGF("BeginFunctionBody(%" PRIindex ", size:%" PRIzd ")\n", value, size); + return reader_->BeginFunctionBody(value, size); +} + +Result BinaryReaderLogging::OnLocalDecl(Index decl_index, + Index count, + Type type) { + LOGF("OnLocalDecl(index: %" PRIindex ", count: %" PRIindex ", type: %s)\n", + decl_index, count, type.GetName().c_str()); + return reader_->OnLocalDecl(decl_index, count, type); +} + +Result BinaryReaderLogging::OnBlockExpr(Type sig_type) { + LOGF("OnBlockExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnBlockExpr(sig_type); +} + +Result BinaryReaderLogging::OnBrExpr(Index depth) { + LOGF("OnBrExpr(depth: %" PRIindex ")\n", depth); + return reader_->OnBrExpr(depth); +} + +Result BinaryReaderLogging::OnBrIfExpr(Index depth) { + LOGF("OnBrIfExpr(depth: %" PRIindex ")\n", depth); + return reader_->OnBrIfExpr(depth); +} + +Result BinaryReaderLogging::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + LOGF("OnBrTableExpr(num_targets: %" PRIindex ", depths: [", num_targets); + for (Index i = 0; i < num_targets; ++i) { + LOGF_NOINDENT("%" PRIindex, target_depths[i]); + if (i != num_targets - 1) { + LOGF_NOINDENT(", "); + } + } + LOGF_NOINDENT("], default: %" PRIindex ")\n", default_target_depth); + return reader_->OnBrTableExpr(num_targets, target_depths, + default_target_depth); +} + +Result BinaryReaderLogging::OnF32ConstExpr(uint32_t value_bits) { + float value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnF32ConstExpr(%g (0x%08x))\n", value, value_bits); + return reader_->OnF32ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnF64ConstExpr(uint64_t value_bits) { + double value; + memcpy(&value, &value_bits, sizeof(value)); + LOGF("OnF64ConstExpr(%g (0x%016" PRIx64 "))\n", value, value_bits); + return reader_->OnF64ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnV128ConstExpr(v128 value_bits) { + LOGF("OnV128ConstExpr(0x%08x 0x%08x 0x%08x 0x%08x)\n", value_bits.u32(0), + value_bits.u32(1), value_bits.u32(2), value_bits.u32(3)); + return reader_->OnV128ConstExpr(value_bits); +} + +Result BinaryReaderLogging::OnI32ConstExpr(uint32_t value) { + LOGF("OnI32ConstExpr(%u (0x%x))\n", value, value); + return reader_->OnI32ConstExpr(value); +} + +Result BinaryReaderLogging::OnI64ConstExpr(uint64_t value) { + LOGF("OnI64ConstExpr(%" PRIu64 " (0x%" PRIx64 "))\n", value, value); + return reader_->OnI64ConstExpr(value); +} + +Result BinaryReaderLogging::OnIfExpr(Type sig_type) { + LOGF("OnIfExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnIfExpr(sig_type); +} + +Result BinaryReaderLogging::OnLoopExpr(Type sig_type) { + LOGF("OnLoopExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnLoopExpr(sig_type); +} + +Result BinaryReaderLogging::OnSelectExpr(Index result_count, + Type* result_types) { + LOGF("OnSelectExpr(return_type: "); + LogTypes(result_count, result_types); + LOGF_NOINDENT(")\n"); + return reader_->OnSelectExpr(result_count, result_types); +} + +Result BinaryReaderLogging::OnTryExpr(Type sig_type) { + LOGF("OnTryExpr(sig: "); + LogType(sig_type); + LOGF_NOINDENT(")\n"); + return reader_->OnTryExpr(sig_type); +} + +Result BinaryReaderLogging::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { + LOGF("OnSimdLaneOpExpr (lane: %" PRIu64 ")\n", value); + return reader_->OnSimdLaneOpExpr(opcode, value); +} + +Result BinaryReaderLogging::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { + LOGF("OnSimdShuffleOpExpr (lane: 0x%08x %08x %08x %08x)\n", value.u32(0), + value.u32(1), value.u32(2), value.u32(3)); + return reader_->OnSimdShuffleOpExpr(opcode, value); +} + +Result BinaryReaderLogging::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + LOGF("BeginElemSegment(index: %" PRIindex ", table_index: %" PRIindex + ", flags: %d)\n", + index, table_index, flags); + return reader_->BeginElemSegment(index, table_index, flags); +} + +Result BinaryReaderLogging::OnElemSegmentElemType(Index index, Type elem_type) { + LOGF("OnElemSegmentElemType(index: %" PRIindex ", type: %s)\n", index, + elem_type.GetName().c_str()); + return reader_->OnElemSegmentElemType(index, elem_type); +} + +Result BinaryReaderLogging::OnDataSegmentData(Index index, + const void* data, + Address size) { + LOGF("OnDataSegmentData(index:%" PRIindex ", size:%" PRIaddress ")\n", index, + size); + return reader_->OnDataSegmentData(index, data, size); +} + +Result BinaryReaderLogging::OnModuleNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnModuleNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnModuleNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnModuleName(std::string_view name) { + LOGF("OnModuleName(name: \"" PRIstringview "\")\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnModuleName(name); +} + +Result BinaryReaderLogging::OnFunctionNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnFunctionNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnFunctionNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnFunctionName(Index index, std::string_view name) { + LOGF("OnFunctionName(index: %" PRIindex ", name: \"" PRIstringview "\")\n", + index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnFunctionName(index, name); +} + +Result BinaryReaderLogging::OnLocalNameSubsection(Index index, + uint32_t name_type, + Offset subsection_size) { + LOGF("OnLocalNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd + ")\n", + index, name_type, subsection_size); + return reader_->OnLocalNameSubsection(index, name_type, subsection_size); +} + +Result BinaryReaderLogging::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + LOGF("OnLocalName(func_index: %" PRIindex ", local_index: %" PRIindex + ", name: \"" PRIstringview "\")\n", + func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnLocalName(func_index, local_index, name); +} + +Result BinaryReaderLogging::OnNameSubsection( + Index index, + NameSectionSubsection subsection_type, + Offset subsection_size) { + LOGF("OnNameSubsection(index: %" PRIindex ", type: %s, size:%" PRIzd ")\n", + index, GetNameSectionSubsectionName(subsection_type), subsection_size); + return reader_->OnNameSubsection(index, subsection_type, subsection_size); +} + +Result BinaryReaderLogging::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + LOGF("OnNameEntry(type: %s, index: %" PRIindex ", name: \"" PRIstringview + "\")\n", + GetNameSectionSubsectionName(type), index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnNameEntry(type, index, name); +} + +Result BinaryReaderLogging::OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align, + uint32_t table_size, + uint32_t table_align) { + LOGF( + "OnDylinkInfo(mem_size: %u, mem_align: %u, table_size: %u, table_align: " + "%u)\n", + mem_size, mem_align, table_size, table_align); + return reader_->OnDylinkInfo(mem_size, mem_align, table_size, table_align); +} + +Result BinaryReaderLogging::OnDylinkNeeded(std::string_view so_name) { + LOGF("OnDylinkNeeded(name: " PRIstringview ")\n", + WABT_PRINTF_STRING_VIEW_ARG(so_name)); + return reader_->OnDylinkNeeded(so_name); +} + +Result BinaryReaderLogging::OnDylinkExport(std::string_view name, + uint32_t flags) { + LOGF("OnDylinkExport(name: " PRIstringview ", flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags); + return reader_->OnDylinkExport(name, flags); +} + +Result BinaryReaderLogging::OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) { + LOGF("OnDylinkImport(module: " PRIstringview ", name: " PRIstringview + ", flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(module), WABT_PRINTF_STRING_VIEW_ARG(name), + flags); + return reader_->OnDylinkImport(module, name, flags); +} + +Result BinaryReaderLogging::OnRelocCount(Index count, Index section_index) { + LOGF("OnRelocCount(count: %" PRIindex ", section: %" PRIindex ")\n", count, + section_index); + return reader_->OnRelocCount(count, section_index); +} + +Result BinaryReaderLogging::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + int32_t signed_addend = static_cast(addend); + LOGF("OnReloc(type: %s, offset: %" PRIzd ", index: %" PRIindex + ", addend: %d)\n", + GetRelocTypeName(type), offset, index, signed_addend); + return reader_->OnReloc(type, offset, index, addend); +} + +Result BinaryReaderLogging::OnFeature(uint8_t prefix, std::string_view name) { + LOGF("OnFeature(prefix: '%c', name: '" PRIstringview "')\n", prefix, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return reader_->OnFeature(prefix, name); +} + +Result BinaryReaderLogging::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + LOGF("OnDataSymbol(name: " PRIstringview " flags: 0x%x)\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags); + return reader_->OnDataSymbol(index, flags, name, segment, offset, size); +} + +Result BinaryReaderLogging::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + LOGF("OnFunctionSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, func_index); + return reader_->OnFunctionSymbol(index, flags, name, func_index); +} + +Result BinaryReaderLogging::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + LOGF("OnGlobalSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, global_index); + return reader_->OnGlobalSymbol(index, flags, name, global_index); +} + +Result BinaryReaderLogging::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + LOGF("OnSectionSymbol(flags: 0x%x index: %" PRIindex ")\n", flags, + section_index); + return reader_->OnSectionSymbol(index, flags, section_index); +} + +Result BinaryReaderLogging::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + LOGF("OnTagSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, tag_index); + return reader_->OnTagSymbol(index, flags, name, tag_index); +} + +Result BinaryReaderLogging::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + LOGF("OnTableSymbol(name: " PRIstringview " flags: 0x%x index: %" PRIindex + ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, table_index); + return reader_->OnTableSymbol(index, flags, name, table_index); +} + +Result BinaryReaderLogging::OnSegmentInfo(Index index, + std::string_view name, + Address alignment, + uint32_t flags) { + LOGF("OnSegmentInfo(%d name: " PRIstringview ", alignment: %" PRIaddress + ", flags: 0x%x)\n", + index, WABT_PRINTF_STRING_VIEW_ARG(name), alignment, flags); + return reader_->OnSegmentInfo(index, name, alignment, flags); +} + +Result BinaryReaderLogging::OnInitFunction(uint32_t priority, + Index symbol_index) { + LOGF("OnInitFunction(%d priority: %d)\n", symbol_index, priority); + return reader_->OnInitFunction(priority, symbol_index); +} + +Result BinaryReaderLogging::OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) { + LOGF("OnComdatBegin(" PRIstringview ", flags: %d, count: %" PRIindex ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), flags, count); + return reader_->OnComdatBegin(name, flags, count); +} + +Result BinaryReaderLogging::OnComdatEntry(ComdatType kind, Index index) { + LOGF("OnComdatEntry(kind: %d, index: %" PRIindex ")\n", + static_cast(kind), index); + return reader_->OnComdatEntry(kind, index); +} + +Result BinaryReaderLogging::BeginCodeMetadataSection(std::string_view name, + Offset size) { + LOGF("BeginCodeMetadataSection('" PRIstringview "', size:%" PRIzd ")\n", + WABT_PRINTF_STRING_VIEW_ARG(name), size); + Indent(); + return reader_->BeginCodeMetadataSection(name, size); +} +Result BinaryReaderLogging::OnCodeMetadata(Offset code_offset, + const void* data, + Address size) { + std::string_view content(static_cast(data), size); + LOGF("OnCodeMetadata(offset: %" PRIzd ", data: \"" PRIstringview "\")\n", + code_offset, WABT_PRINTF_STRING_VIEW_ARG(content)); + return reader_->OnCodeMetadata(code_offset, data, size); +} + +#define DEFINE_BEGIN(name) \ + Result BinaryReaderLogging::name(Offset size) { \ + LOGF(#name "(%" PRIzd ")\n", size); \ + Indent(); \ + return reader_->name(size); \ + } + +#define DEFINE_END(name) \ + Result BinaryReaderLogging::name() { \ + Dedent(); \ + LOGF(#name "\n"); \ + return reader_->name(); \ + } + +#define DEFINE_INDEX(name) \ + Result BinaryReaderLogging::name(Index value) { \ + LOGF(#name "(%" PRIindex ")\n", value); \ + return reader_->name(value); \ + } + +#define DEFINE_TYPE(name) \ + Result BinaryReaderLogging::name(Type type) { \ + LOGF(#name "(%s)\n", type.GetName().c_str()); \ + return reader_->name(type); \ + } + +#define DEFINE_INDEX_DESC(name, desc) \ + Result BinaryReaderLogging::name(Index value) { \ + LOGF(#name "(" desc ": %" PRIindex ")\n", value); \ + return reader_->name(value); \ + } + +#define DEFINE_INDEX_TYPE(name) \ + Result BinaryReaderLogging::name(Index value, Type type) { \ + LOGF(#name "(index: %" PRIindex ", type: %s)\n", value, \ + type.GetName().c_str()); \ + return reader_->name(value, type); \ + } + +#define DEFINE_INDEX_INDEX(name, desc0, desc1) \ + Result BinaryReaderLogging::name(Index value0, Index value1) { \ + LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ")\n", \ + value0, value1); \ + return reader_->name(value0, value1); \ + } + +#define DEFINE_INDEX_INDEX_U8(name, desc0, desc1, desc2) \ + Result BinaryReaderLogging::name(Index value0, Index value1, \ + uint8_t value2) { \ + LOGF(#name "(" desc0 ": %" PRIindex ", " desc1 ": %" PRIindex ", " desc2 \ + ": %d)\n", \ + value0, value1, value2); \ + return reader_->name(value0, value1, value2); \ + } + +#define DEFINE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode) { \ + LOGF(#name "(\"%s\" (%u))\n", opcode.GetName(), opcode.GetCode()); \ + return reader_->name(opcode); \ + } + +#define DEFINE_LOAD_STORE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \ + Address alignment_log2, Address offset) { \ + LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \ + ", align log2: %" PRIaddress ", offset: %" PRIaddress ")\n", \ + opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset); \ + return reader_->name(opcode, memidx, alignment_log2, offset); \ + } + +#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \ + Result BinaryReaderLogging::name(Opcode opcode, Index memidx, \ + Address alignment_log2, Address offset, \ + uint64_t value) { \ + LOGF(#name "(opcode: \"%s\" (%u), memidx: %" PRIindex \ + ", align log2: %" PRIaddress ", offset: %" PRIaddress \ + ", lane: %" PRIu64 ")\n", \ + opcode.GetName(), opcode.GetCode(), memidx, alignment_log2, offset, \ + value); \ + return reader_->name(opcode, memidx, alignment_log2, offset, value); \ + } + +#define DEFINE0(name) \ + Result BinaryReaderLogging::name() { \ + LOGF(#name "\n"); \ + return reader_->name(); \ + } + +DEFINE_END(EndModule) + +DEFINE_END(EndCustomSection) + +DEFINE_BEGIN(BeginTypeSection) +DEFINE_INDEX(OnTypeCount) +DEFINE_END(EndTypeSection) + +DEFINE_BEGIN(BeginImportSection) +DEFINE_INDEX(OnImportCount) +DEFINE_END(EndImportSection) + +DEFINE_BEGIN(BeginFunctionSection) +DEFINE_INDEX(OnFunctionCount) +DEFINE_INDEX_INDEX(OnFunction, "index", "sig_index") +DEFINE_END(EndFunctionSection) + +DEFINE_BEGIN(BeginTableSection) +DEFINE_INDEX(OnTableCount) +DEFINE_END(EndTableSection) + +DEFINE_BEGIN(BeginMemorySection) +DEFINE_INDEX(OnMemoryCount) +DEFINE_END(EndMemorySection) + +DEFINE_BEGIN(BeginGlobalSection) +DEFINE_INDEX(OnGlobalCount) +DEFINE_INDEX(BeginGlobalInitExpr) +DEFINE_INDEX(EndGlobalInitExpr) +DEFINE_INDEX(EndGlobal) +DEFINE_END(EndGlobalSection) + +DEFINE_BEGIN(BeginExportSection) +DEFINE_INDEX(OnExportCount) +DEFINE_END(EndExportSection) + +DEFINE_BEGIN(BeginStartSection) +DEFINE_INDEX(OnStartFunction) +DEFINE_END(EndStartSection) + +DEFINE_BEGIN(BeginCodeSection) +DEFINE_INDEX(OnFunctionBodyCount) +DEFINE_INDEX(EndFunctionBody) +DEFINE_INDEX(OnLocalDeclCount) +DEFINE_LOAD_STORE_OPCODE(OnAtomicLoadExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicRmwCmpxchgExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicStoreExpr); +DEFINE_LOAD_STORE_OPCODE(OnAtomicWaitExpr); +DEFINE_INDEX_DESC(OnAtomicFenceExpr, "consistency_model"); +DEFINE_LOAD_STORE_OPCODE(OnAtomicNotifyExpr); +DEFINE_OPCODE(OnBinaryExpr) +DEFINE_INDEX_DESC(OnCallExpr, "func_index") +DEFINE_INDEX_INDEX(OnCallIndirectExpr, "sig_index", "table_index") +DEFINE0(OnCallRefExpr) +DEFINE_INDEX_DESC(OnCatchExpr, "tag_index"); +DEFINE0(OnCatchAllExpr); +DEFINE_OPCODE(OnCompareExpr) +DEFINE_OPCODE(OnConvertExpr) +DEFINE_INDEX_DESC(OnDelegateExpr, "depth"); +DEFINE0(OnDropExpr) +DEFINE0(OnElseExpr) +DEFINE0(OnEndExpr) +DEFINE_INDEX_DESC(OnGlobalGetExpr, "index") +DEFINE_INDEX_DESC(OnGlobalSetExpr, "index") +DEFINE_LOAD_STORE_OPCODE(OnLoadExpr); +DEFINE_INDEX_DESC(OnLocalGetExpr, "index") +DEFINE_INDEX_DESC(OnLocalSetExpr, "index") +DEFINE_INDEX_DESC(OnLocalTeeExpr, "index") +DEFINE_INDEX_INDEX(OnMemoryCopyExpr, "src_memory_index", "dest_memory_index") +DEFINE_INDEX(OnDataDropExpr) +DEFINE_INDEX(OnMemoryFillExpr) +DEFINE_INDEX(OnMemoryGrowExpr) +DEFINE_INDEX_INDEX(OnMemoryInitExpr, "segment_index", "memory_index") +DEFINE_INDEX(OnMemorySizeExpr) +DEFINE_INDEX_INDEX(OnTableCopyExpr, "dst_index", "src_index") +DEFINE_INDEX(OnElemDropExpr) +DEFINE_INDEX_INDEX(OnTableInitExpr, "segment_index", "table_index") +DEFINE_INDEX(OnTableSetExpr) +DEFINE_INDEX(OnTableGetExpr) +DEFINE_INDEX(OnTableGrowExpr) +DEFINE_INDEX(OnTableSizeExpr) +DEFINE_INDEX_DESC(OnTableFillExpr, "table index") +DEFINE_INDEX(OnRefFuncExpr) +DEFINE_TYPE(OnRefNullExpr) +DEFINE0(OnRefIsNullExpr) +DEFINE0(OnNopExpr) +DEFINE_INDEX_DESC(OnRethrowExpr, "depth"); +DEFINE_INDEX_DESC(OnReturnCallExpr, "func_index") + +DEFINE_INDEX_INDEX(OnReturnCallIndirectExpr, "sig_index", "table_index") +DEFINE0(OnReturnExpr) +DEFINE_LOAD_STORE_OPCODE(OnLoadSplatExpr); +DEFINE_LOAD_STORE_OPCODE(OnLoadZeroExpr); +DEFINE_LOAD_STORE_OPCODE(OnStoreExpr); +DEFINE_INDEX_DESC(OnThrowExpr, "tag_index") +DEFINE0(OnUnreachableExpr) +DEFINE_OPCODE(OnUnaryExpr) +DEFINE_OPCODE(OnTernaryExpr) +DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdLoadLaneExpr); +DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(OnSimdStoreLaneExpr); +DEFINE_END(EndCodeSection) + +DEFINE_BEGIN(BeginElemSection) +DEFINE_INDEX(OnElemSegmentCount) +DEFINE_INDEX(BeginElemSegmentInitExpr) +DEFINE_INDEX(EndElemSegmentInitExpr) +DEFINE_INDEX_INDEX(OnElemSegmentElemExprCount, "index", "count") +DEFINE_INDEX_TYPE(OnElemSegmentElemExpr_RefNull) +DEFINE_INDEX_INDEX(OnElemSegmentElemExpr_RefFunc, "index", "func_index") +DEFINE_INDEX(EndElemSegment) +DEFINE_END(EndElemSection) + +DEFINE_BEGIN(BeginDataSection) +DEFINE_INDEX(OnDataSegmentCount) +DEFINE_INDEX_INDEX_U8(BeginDataSegment, "index", "memory_index", "flags") +DEFINE_INDEX(BeginDataSegmentInitExpr) +DEFINE_INDEX(EndDataSegmentInitExpr) +DEFINE_INDEX(EndDataSegment) +DEFINE_END(EndDataSection) + +DEFINE_BEGIN(BeginDataCountSection) +DEFINE_INDEX(OnDataCount) +DEFINE_END(EndDataCountSection) + +DEFINE_BEGIN(BeginNamesSection) +DEFINE_INDEX(OnFunctionNamesCount) +DEFINE_INDEX(OnLocalNameFunctionCount) +DEFINE_INDEX_INDEX(OnLocalNameLocalCount, "index", "count") +DEFINE_INDEX(OnNameCount); +DEFINE_END(EndNamesSection) + +DEFINE_BEGIN(BeginRelocSection) +DEFINE_END(EndRelocSection) + +DEFINE_BEGIN(BeginDylinkSection) +DEFINE_INDEX(OnDylinkNeededCount) +DEFINE_INDEX(OnDylinkExportCount) +DEFINE_INDEX(OnDylinkImportCount) +DEFINE_END(EndDylinkSection) + +DEFINE_BEGIN(BeginTargetFeaturesSection) +DEFINE_INDEX(OnFeatureCount) +DEFINE_END(EndTargetFeaturesSection) + +DEFINE_BEGIN(BeginLinkingSection) +DEFINE_INDEX(OnSymbolCount) +DEFINE_INDEX(OnSegmentInfoCount) +DEFINE_INDEX(OnInitFunctionCount) +DEFINE_INDEX(OnComdatCount) +DEFINE_END(EndLinkingSection) + +DEFINE_BEGIN(BeginTagSection); +DEFINE_INDEX(OnTagCount); +DEFINE_INDEX_INDEX(OnTagType, "index", "sig_index") +DEFINE_END(EndTagSection); + +DEFINE_INDEX(OnCodeMetadataFuncCount); +DEFINE_INDEX_INDEX(OnCodeMetadataCount, "func_index", "count"); +DEFINE_END(EndCodeMetadataSection); + +// We don't need to log these (the individual opcodes are logged instead), but +// we still need to forward the calls. +Result BinaryReaderLogging::OnOpcode(Opcode opcode) { + return reader_->OnOpcode(opcode); +} + +Result BinaryReaderLogging::OnOpcodeBare() { + return reader_->OnOpcodeBare(); +} + +Result BinaryReaderLogging::OnOpcodeIndex(Index value) { + return reader_->OnOpcodeIndex(value); +} + +Result BinaryReaderLogging::OnOpcodeIndexIndex(Index value, Index value2) { + return reader_->OnOpcodeIndexIndex(value, value2); +} + +Result BinaryReaderLogging::OnOpcodeUint32(uint32_t value) { + return reader_->OnOpcodeUint32(value); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32(uint32_t value, + uint32_t value2) { + return reader_->OnOpcodeUint32Uint32(value, value2); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) { + return reader_->OnOpcodeUint32Uint32Uint32(value, value2, value3); +} + +Result BinaryReaderLogging::OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) { + return reader_->OnOpcodeUint32Uint32Uint32Uint32(value, value2, value3, + value4); +} + +Result BinaryReaderLogging::OnOpcodeUint64(uint64_t value) { + return reader_->OnOpcodeUint64(value); +} + +Result BinaryReaderLogging::OnOpcodeF32(uint32_t value) { + return reader_->OnOpcodeF32(value); +} + +Result BinaryReaderLogging::OnOpcodeF64(uint64_t value) { + return reader_->OnOpcodeF64(value); +} + +Result BinaryReaderLogging::OnOpcodeV128(v128 value) { + return reader_->OnOpcodeV128(value); +} + +Result BinaryReaderLogging::OnOpcodeBlockSig(Type sig_type) { + return reader_->OnOpcodeBlockSig(sig_type); +} + +Result BinaryReaderLogging::OnOpcodeType(Type type) { + return reader_->OnOpcodeType(type); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-objdump.cc b/third_party/wasm2c/src/binary-reader-objdump.cc new file mode 100644 index 0000000000..96ece0689d --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-objdump.cc @@ -0,0 +1,2408 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-objdump.h" + +#include +#include +#include +#include +#include +#include + +#if HAVE_STRCASECMP +#include +#endif + +#include "wabt/binary-reader-nop.h" +#include "wabt/filenames.h" +#include "wabt/literal.h" +#include "wabt/string-util.h" + +namespace wabt { + +namespace { + +class BinaryReaderObjdumpBase : public BinaryReaderNop { + public: + BinaryReaderObjdumpBase(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state); + + bool OnError(const Error&) override; + + Result BeginModule(uint32_t version) override; + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnRelocCount(Index count, Index section_index) override; + + protected: + std::string_view GetTypeName(Index index) const; + std::string_view GetFunctionName(Index index) const; + std::string_view GetGlobalName(Index index) const; + std::string_view GetLocalName(Index function_index, Index local_index) const; + std::string_view GetSectionName(Index index) const; + std::string_view GetTagName(Index index) const; + std::string_view GetSymbolName(Index index) const; + std::string_view GetSegmentName(Index index) const; + std::string_view GetTableName(Index index) const; + void PrintRelocation(const Reloc& reloc, Offset offset) const; + Offset GetPrintOffset(Offset offset) const; + Offset GetSectionStart(BinarySection section_code) const { + return section_starts_[static_cast(section_code)]; + } + + ObjdumpOptions* options_; + ObjdumpState* objdump_state_; + const uint8_t* data_; + size_t size_; + bool print_details_ = false; + BinarySection reloc_section_ = BinarySection::Invalid; + Offset section_starts_[kBinarySectionCount]; + // Map of section index to section type + std::vector section_types_; + bool section_found_ = false; + std::string module_name_; + Opcode current_opcode = Opcode::Unreachable; + + std::unique_ptr err_stream_; +}; + +BinaryReaderObjdumpBase::BinaryReaderObjdumpBase(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* objdump_state) + : options_(options), + objdump_state_(objdump_state), + data_(data), + size_(size), + err_stream_(FileStream::CreateStderr()) { + ZeroMemory(section_starts_); +} + +Result BinaryReaderObjdumpBase::BeginSection(Index section_index, + BinarySection section_code, + Offset size) { + section_starts_[static_cast(section_code)] = state->offset; + section_types_.push_back(section_code); + return Result::Ok; +} + +bool BinaryReaderObjdumpBase::OnError(const Error&) { + // Tell the BinaryReader that this error is "handled" for all passes other + // than the prepass. When the error is handled the default message will be + // suppressed. + return options_->mode != ObjdumpMode::Prepass; +} + +Result BinaryReaderObjdumpBase::BeginModule(uint32_t version) { + switch (options_->mode) { + case ObjdumpMode::Headers: + printf("\n"); + printf("Sections:\n\n"); + break; + case ObjdumpMode::Details: + printf("\n"); + printf("Section Details:\n\n"); + break; + case ObjdumpMode::Disassemble: + printf("\n"); + printf("Code Disassembly:\n\n"); + break; + case ObjdumpMode::Prepass: { + std::string_view basename = GetBasename(options_->filename); + if (basename == "-") { + basename = ""; + } + printf("%s:\tfile format wasm %#x\n", std::string(basename).c_str(), + version); + break; + } + case ObjdumpMode::RawData: + break; + } + + return Result::Ok; +} + +std::string_view BinaryReaderObjdumpBase::GetTypeName(Index index) const { + return objdump_state_->type_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const { + return objdump_state_->function_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const { + return objdump_state_->global_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetLocalName( + Index function_index, + Index local_index) const { + return objdump_state_->local_names.Get(function_index, local_index); +} + +std::string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const { + return objdump_state_->section_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetTagName(Index index) const { + return objdump_state_->tag_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const { + return objdump_state_->segment_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetTableName(Index index) const { + return objdump_state_->table_names.Get(index); +} + +std::string_view BinaryReaderObjdumpBase::GetSymbolName( + Index symbol_index) const { + if (symbol_index >= objdump_state_->symtab.size()) + return ""; + ObjdumpSymbol& sym = objdump_state_->symtab[symbol_index]; + switch (sym.kind) { + case SymbolType::Function: + return GetFunctionName(sym.index); + case SymbolType::Data: + return sym.name; + case SymbolType::Global: + return GetGlobalName(sym.index); + case SymbolType::Section: + return GetSectionName(sym.index); + case SymbolType::Tag: + return GetTagName(sym.index); + case SymbolType::Table: + return GetTableName(sym.index); + } + WABT_UNREACHABLE; +} + +void BinaryReaderObjdumpBase::PrintRelocation(const Reloc& reloc, + Offset offset) const { + printf(" %06" PRIzx ": %-18s %" PRIindex, offset, + GetRelocTypeName(reloc.type), reloc.index); + if (reloc.addend) { + printf(" + %d", reloc.addend); + } + if (reloc.type != RelocType::TypeIndexLEB) { + printf(" <" PRIstringview ">", + WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(reloc.index))); + } + printf("\n"); +} + +Offset BinaryReaderObjdumpBase::GetPrintOffset(Offset offset) const { + return options_->section_offsets + ? offset - GetSectionStart(BinarySection::Code) + : offset; +} + +Result BinaryReaderObjdumpBase::OnOpcode(Opcode opcode) { + current_opcode = opcode; + return Result::Ok; +} + +Result BinaryReaderObjdumpBase::OnRelocCount(Index count, Index section_index) { + if (section_index >= section_types_.size()) { + err_stream_->Writef("invalid relocation section index: %" PRIindex "\n", + section_index); + reloc_section_ = BinarySection::Invalid; + return Result::Error; + } + reloc_section_ = section_types_[section_index]; + return Result::Ok; +} + +class BinaryReaderObjdumpPrepass : public BinaryReaderObjdumpBase { + public: + using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; + + Result BeginSection(Index section_index, + BinarySection section_code, + Offset size) override { + BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); + if (section_code != BinarySection::Custom) { + objdump_state_->section_names.Set(section_index, + wabt::GetSectionName(section_code)); + } + return Result::Ok; + } + + Result BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) override { + objdump_state_->section_names.Set(section_index, section_name); + return Result::Ok; + } + + Result OnFunctionName(Index index, std::string_view name) override { + SetFunctionName(index, name); + return Result::Ok; + } + + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override { + objdump_state_->function_param_counts[index] = param_count; + return Result::Ok; + } + + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override { + switch (type) { + // TODO(sbc): remove OnFunctionName in favor of just using + // OnNameEntry so that this works + /* + case NameSectionSubsection::Function: + SetFunctionName(index, name); + break; + */ + case NameSectionSubsection::Type: + SetTypeName(index, name); + break; + case NameSectionSubsection::Global: + SetGlobalName(index, name); + break; + case NameSectionSubsection::Table: + SetTableName(index, name); + break; + case NameSectionSubsection::DataSegment: + SetSegmentName(index, name); + break; + case NameSectionSubsection::Tag: + SetTagName(index, name); + break; + default: + break; + } + return Result::Ok; + } + + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override { + SetLocalName(function_index, local_index, local_name); + return Result::Ok; + } + + Result OnSymbolCount(Index count) override { + objdump_state_->symtab.resize(count); + return Result::Ok; + } + + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override { + objdump_state_->symtab[index] = {SymbolType::Data, std::string(name), 0}; + return Result::Ok; + } + + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override { + if (!name.empty()) { + SetFunctionName(func_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Function, std::string(name), + func_index}; + return Result::Ok; + } + + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override { + if (!name.empty()) { + SetGlobalName(global_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Global, std::string(name), + global_index}; + return Result::Ok; + } + + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override { + objdump_state_->symtab[index] = {SymbolType::Section, + std::string(GetSectionName(section_index)), + section_index}; + return Result::Ok; + } + + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override { + if (!name.empty()) { + SetTagName(tag_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Tag, std::string(name), + tag_index}; + return Result::Ok; + } + + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override { + if (!name.empty()) { + SetTableName(table_index, name); + } + objdump_state_->symtab[index] = {SymbolType::Table, std::string(name), + table_index}; + return Result::Ok; + } + + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override { + SetFunctionName(func_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) override { + SetTagName(tag_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override { + SetGlobalName(global_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override { + SetTableName(table_index, module_name + "." + field_name); + return Result::Ok; + } + + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override { + if (kind == ExternalKind::Func) { + SetFunctionName(item_index, name); + } else if (kind == ExternalKind::Global) { + SetGlobalName(item_index, name); + } + return Result::Ok; + } + + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + + Result OnModuleName(std::string_view name) override { + if (options_->mode == ObjdumpMode::Prepass) { + printf("module name: <" PRIstringview ">\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; + } + + Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) override { + SetSegmentName(index, name); + return Result::Ok; + } + + protected: + void SetTypeName(Index index, std::string_view name); + void SetFunctionName(Index index, std::string_view name); + void SetGlobalName(Index index, std::string_view name); + void SetLocalName(Index function_index, + Index local_index, + std::string_view name); + void SetTagName(Index index, std::string_view name); + void SetTableName(Index index, std::string_view name); + void SetSegmentName(Index index, std::string_view name); +}; + +void BinaryReaderObjdumpPrepass::SetTypeName(Index index, + std::string_view name) { + objdump_state_->type_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetFunctionName(Index index, + std::string_view name) { + objdump_state_->function_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetGlobalName(Index index, + std::string_view name) { + objdump_state_->global_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetLocalName(Index function_index, + Index local_index, + std::string_view name) { + objdump_state_->local_names.Set(function_index, local_index, name); +} + +void BinaryReaderObjdumpPrepass::SetTagName(Index index, + std::string_view name) { + objdump_state_->tag_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetTableName(Index index, + std::string_view name) { + objdump_state_->table_names.Set(index, name); +} + +void BinaryReaderObjdumpPrepass::SetSegmentName(Index index, + std::string_view name) { + objdump_state_->segment_names.Set(index, name); +} + +Result BinaryReaderObjdumpPrepass::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + BinaryReaderObjdumpBase::OnReloc(type, offset, index, addend); + if (reloc_section_ == BinarySection::Code) { + objdump_state_->code_relocations.emplace_back(type, offset, index, addend); + } else if (reloc_section_ == BinarySection::Data) { + objdump_state_->data_relocations.emplace_back(type, offset, index, addend); + } + return Result::Ok; +} + +class BinaryReaderObjdumpDisassemble : public BinaryReaderObjdumpBase { + public: + using BinaryReaderObjdumpBase::BinaryReaderObjdumpBase; + + std::string BlockSigToString(Type type) const; + + Result BeginFunctionBody(Index index, Offset size) override; + Result EndFunctionBody(Index index) override; + + Result OnLocalDeclCount(Index count) override; + Result OnLocalDecl(Index decl_index, Index count, Type type) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnOpcodeBare() override; + Result OnOpcodeIndex(Index value) override; + Result OnOpcodeIndexIndex(Index value, Index value2) override; + Result OnOpcodeUint32(uint32_t value) override; + Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override; + Result OnCallIndirectExpr(uint32_t sig_indix, uint32_t table_index) override; + Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) override; + Result OnOpcodeUint32Uint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) override; + Result OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeV128(v128 value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnOpcodeType(Type type) override; + + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnDelegateExpr(Index) override; + Result OnEndExpr() override; + + private: + void LogOpcode(const char* fmt, ...); + + Offset current_opcode_offset = 0; + Offset last_opcode_end = 0; + int indent_level = 0; + Index next_reloc = 0; + Index current_function_index = 0; + Index local_index_ = 0; + bool in_function_body = false; + bool skip_next_opcode_ = false; +}; + +std::string BinaryReaderObjdumpDisassemble::BlockSigToString(Type type) const { + if (type.IsIndex()) { + return StringPrintf("type[%d]", type.GetIndex()); + } else if (type == Type::Void) { + return ""; + } else { + return type.GetName(); + } +} + +Result BinaryReaderObjdumpDisassemble::OnOpcode(Opcode opcode) { + BinaryReaderObjdumpBase::OnOpcode(opcode); + if (!in_function_body) { + return Result::Ok; + } + if (options_->debug) { + const char* opcode_name = opcode.GetName(); + err_stream_->Writef("on_opcode: %#" PRIzx ": %s\n", state->offset, + opcode_name); + } + + if (last_opcode_end) { + // Takes care of cases where opcode's bytes was a non-canonical leb128 + // encoding. In this case, opcode.GetLength() under-reports the length, + // since it canonicalizes the opcode. + if (state->offset < last_opcode_end + opcode.GetLength()) { + Opcode missing_opcode = Opcode::FromCode(data_[last_opcode_end]); + const char* opcode_name = missing_opcode.GetName(); + fprintf(stderr, + "error: %#" PRIzx " missing opcode callback at %#" PRIzx + " (%#02x=%s)\n", + state->offset, last_opcode_end + 1, data_[last_opcode_end], + opcode_name); + return Result::Error; + } + } + + current_opcode_offset = state->offset; + return Result::Ok; +} + +#define IMMEDIATE_OCTET_COUNT 9 + +Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) { + if (!in_function_body) { + return Result::Ok; + } + current_opcode_offset = state->offset; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index, + Index count, + Type type) { + if (!in_function_body) { + return Result::Ok; + } + Offset offset = current_opcode_offset; + size_t data_size = state->offset - offset; + + printf(" %06" PRIzx ":", GetPrintOffset(offset)); + for (size_t i = 0; i < data_size && i < IMMEDIATE_OCTET_COUNT; + i++, offset++) { + printf(" %02x", data_[offset]); + } + for (size_t i = data_size; i < IMMEDIATE_OCTET_COUNT; i++) { + printf(" "); + } + printf(" | local[%" PRIindex, local_index_); + + if (count != 1) { + printf("..%" PRIindex "", local_index_ + count - 1); + } + local_index_ += count; + + printf("] type=%s\n", type.GetName().c_str()); + + last_opcode_end = current_opcode_offset + data_size; + current_opcode_offset = last_opcode_end; + + return Result::Ok; +} + +void BinaryReaderObjdumpDisassemble::LogOpcode(const char* fmt, ...) { + // BinaryReaderObjdumpDisassemble is only used to disassembly function bodies + // so this should never be called for instructions outside of function bodies + // (i.e. init expresions). + assert(in_function_body); + if (skip_next_opcode_) { + skip_next_opcode_ = false; + return; + } + const Offset immediate_len = state->offset - current_opcode_offset; + const Offset opcode_size = current_opcode.GetLength(); + const Offset total_size = opcode_size + immediate_len; + // current_opcode_offset has already read past this opcode; rewind it by the + // size of this opcode, which may be more than one byte. + Offset offset = current_opcode_offset - opcode_size; + const Offset offset_end = offset + total_size; + + bool first_line = true; + while (offset < offset_end) { + // Print bytes, but only display a maximum of IMMEDIATE_OCTET_COUNT on each + // line. + printf(" %06" PRIzx ":", GetPrintOffset(offset)); + size_t i; + for (i = 0; offset < offset_end && i < IMMEDIATE_OCTET_COUNT; + ++i, ++offset) { + printf(" %02x", data_[offset]); + } + // Fill the rest of the remaining space with spaces. + for (; i < IMMEDIATE_OCTET_COUNT; ++i) { + printf(" "); + } + printf(" | "); + + if (first_line) { + first_line = false; + + // Print disassembly. + int indent_level = this->indent_level; + switch (current_opcode) { + case Opcode::Else: + case Opcode::Catch: + case Opcode::CatchAll: + indent_level--; + break; + default: + break; + } + for (int j = 0; j < indent_level; j++) { + printf(" "); + } + + const char* opcode_name = current_opcode.GetName(); + printf("%s", opcode_name); + if (fmt) { + printf(" "); + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } + } + + printf("\n"); + } + + last_opcode_end = state->offset; + + // Print relocation after then full (potentially multi-line) instruction. + if (options_->relocs && + next_reloc < objdump_state_->code_relocations.size()) { + const Reloc& reloc = objdump_state_->code_relocations[next_reloc]; + Offset code_start = GetSectionStart(BinarySection::Code); + Offset abs_offset = code_start + reloc.offset; + if (last_opcode_end > abs_offset) { + PrintRelocation(reloc, abs_offset); + next_reloc++; + } + } +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeBare() { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) { + if (!in_function_body) { + return Result::Ok; + } + std::string_view name; + if (current_opcode == Opcode::Call && + !(name = GetFunctionName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if (current_opcode == Opcode::Throw && + !(name = GetTagName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if ((current_opcode == Opcode::GlobalGet || + current_opcode == Opcode::GlobalSet) && + !(name = GetGlobalName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else if ((current_opcode == Opcode::LocalGet || + current_opcode == Opcode::LocalSet) && + !(name = GetLocalName(current_function_index, value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%d", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value, + Index value2) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%" PRIindex " %" PRIindex, value, value2); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) { + if (!in_function_body) { + return Result::Ok; + } + std::string_view name; + if (current_opcode == Opcode::DataDrop && + !(name = GetSegmentName(value)).empty()) { + LogOpcode("%d <" PRIstringview ">", value, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%u", value); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value, + uint32_t value2) { + if (!in_function_body) + return Result::Ok; + std::string_view name; + if (current_opcode == Opcode::MemoryInit && + !(name = GetSegmentName(value)).empty()) { + LogOpcode("%u %u <" PRIstringview ">", value, value2, + WABT_PRINTF_STRING_VIEW_ARG(name)); + } else { + LogOpcode("%u %u", value, value2); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnCallIndirectExpr( + uint32_t sig_index, + uint32_t table_index) { + std::string_view table_name = GetTableName(table_index); + std::string_view type_name = GetTypeName(sig_index); + if (!type_name.empty() && !table_name.empty()) { + LogOpcode("%u <" PRIstringview "> (type %u <" PRIstringview ">)", + table_index, WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index, + WABT_PRINTF_STRING_VIEW_ARG(type_name)); + } else if (!table_name.empty()) { + LogOpcode("%u <" PRIstringview "> (type %u)", table_index, + WABT_PRINTF_STRING_VIEW_ARG(table_name), sig_index); + } else if (!type_name.empty()) { + LogOpcode("%u (type %u <" PRIstringview ">)", table_index, sig_index, + WABT_PRINTF_STRING_VIEW_ARG(type_name)); + } else { + LogOpcode("%u (type %u)", table_index, sig_index); + } + skip_next_opcode_ = true; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32( + uint32_t value, + uint32_t value2, + uint32_t value3) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%u %u %u", value, value2, value3); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32Uint32( + uint32_t value, + uint32_t value2, + uint32_t value3, + uint32_t value4) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%u %u %u %u", value, value2, value3, value4); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) { + if (!in_function_body) { + return Result::Ok; + } + LogOpcode("%" PRId64, value); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) { + if (!in_function_body) { + return Result::Ok; + } + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), value); + LogOpcode(buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) { + if (!in_function_body) { + return Result::Ok; + } + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), value); + LogOpcode(buffer); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) { + if (!in_function_body) { + return Result::Ok; + } + // v128 is always dumped as i32x4: + LogOpcode("0x%08x 0x%08x 0x%08x 0x%08x", value.u32(0), value.u32(1), + value.u32(2), value.u32(3)); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeType(Type type) { + if (!in_function_body) { + return Result::Ok; + } + if (current_opcode == Opcode::SelectT) { + LogOpcode(type.GetName().c_str()); + } else { + LogOpcode(type.GetRefKindName()); + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnBrTableExpr( + Index num_targets, + Index* target_depths, + Index default_target_depth) { + if (!in_function_body) { + return Result::Ok; + } + + std::string buffer = std::string(); + for (Index i = 0; i < num_targets; i++) { + buffer.append(std::to_string(target_depths[i])).append(" "); + } + buffer.append(std::to_string(default_target_depth)); + + LogOpcode("%s", buffer.c_str()); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) { + if (!in_function_body) { + return Result::Ok; + } + // Because `delegate` ends the block we need to dedent here, and + // we don't need to dedent it in LogOpcode. + if (indent_level > 0) { + indent_level--; + } + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnEndExpr() { + if (!in_function_body) { + return Result::Ok; + } + if (indent_level > 0) { + indent_level--; + } + LogOpcode(0, nullptr); + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::BeginFunctionBody(Index index, + Offset size) { + printf("%06" PRIzx " func[%" PRIindex "]", GetPrintOffset(state->offset), + index); + auto name = GetFunctionName(index); + if (!name.empty()) { + printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + printf(":\n"); + + last_opcode_end = 0; + in_function_body = true; + current_function_index = index; + local_index_ = objdump_state_->function_param_counts[index]; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::EndFunctionBody(Index index) { + assert(in_function_body); + in_function_body = false; + return Result::Ok; +} + +Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) { + if (!in_function_body) { + return Result::Ok; + } + if (sig_type != Type::Void) { + LogOpcode("%s", BlockSigToString(sig_type).c_str()); + } else { + LogOpcode(nullptr); + } + indent_level++; + return Result::Ok; +} + +enum class InitExprType { + I32, + F32, + I64, + F64, + V128, + Global, + FuncRef, + // TODO: There isn't a nullref anymore, this just represents ref.null of some + // type T. + NullRef, +}; + +struct InitInst { + Opcode opcode; + union { + Index index; + uint32_t i32; + uint32_t f32; + uint64_t i64; + uint64_t f64; + v128 v128_v; + Type type; + } imm; +}; + +struct InitExpr { + InitExprType type; + std::vector insts; +}; + +class BinaryReaderObjdump : public BinaryReaderObjdumpBase { + public: + BinaryReaderObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state); + + Result EndModule() override; + Result BeginSection(Index section_index, + BinarySection section_type, + Offset size) override; + Result BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) override; + + Result OnTypeCount(Index count) override; + Result OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) override; + Result OnStructType(Index index, Index field_count, TypeMut* fields) override; + Result OnArrayType(Index index, TypeMut field) override; + + Result OnImportCount(Index count) override; + Result OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) override; + Result OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) override; + Result OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) override; + Result OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) override; + Result OnImportTag(Index import_index, + std::string_view module_name, + std::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 OnExportCount(Index count) override; + Result OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) override; + + Result OnStartFunction(Index func_index) override; + Result OnDataCount(Index count) override; + + Result OnFunctionBodyCount(Index count) override; + Result BeginFunctionBody(Index index, Offset size) override; + + Result OnElemSegmentCount(Index count) override; + Result BeginElemSegment(Index index, + Index table_index, + uint8_t flags) 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; + + void BeginInitExpr() { current_init_expr_.insts.clear(); } + + Result BeginElemSegmentInitExpr(Index index) override { + reading_elem_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndElemSegmentInitExpr(Index index) override { return EndInitExpr(); } + + Result BeginDataSegmentInitExpr(Index index) override { + reading_data_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndDataSegmentInitExpr(Index index) override { return EndInitExpr(); } + + Result BeginGlobalInitExpr(Index index) override { + reading_global_init_expr_ = true; + BeginInitExpr(); + return Result::Ok; + } + + Result EndGlobalInitExpr(Index index) override { return EndInitExpr(); } + + Result OnDataSegmentCount(Index count) override; + Result BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) override; + Result OnDataSegmentData(Index index, + const void* data, + Address size) override; + + Result OnModuleName(std::string_view name) override; + Result OnFunctionName(Index function_index, + std::string_view function_name) override; + Result OnLocalName(Index function_index, + Index local_index, + std::string_view local_name) override; + Result OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) override; + + Result OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align_log2, + uint32_t table_size, + uint32_t table_align_log2) override; + Result OnDylinkNeededCount(Index count) override; + Result OnDylinkNeeded(std::string_view so_name) override; + Result OnDylinkImportCount(Index count) override; + Result OnDylinkExportCount(Index count) override; + Result OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) override; + Result OnDylinkExport(std::string_view name, uint32_t flags) override; + + Result OnRelocCount(Index count, Index section_index) override; + Result OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) override; + + Result OnFeature(uint8_t prefix, std::string_view name) override; + + Result OnSymbolCount(Index count) override; + Result OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) override; + Result OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) override; + Result OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) override; + Result OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) override; + Result OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) override; + Result OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) override; + Result OnSegmentInfoCount(Index count) override; + Result OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) override; + Result OnInitFunctionCount(Index count) override; + Result OnInitFunction(uint32_t priority, Index symbol_index) override; + Result OnComdatCount(Index count) override; + Result OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) override; + Result OnComdatEntry(ComdatType kind, Index index) override; + + Result OnTagCount(Index count) override; + Result OnTagType(Index index, Index sig_index) override; + + Result OnOpcode(Opcode Opcode) override; + Result OnI32ConstExpr(uint32_t value) override; + Result OnI64ConstExpr(uint64_t value) override; + Result OnF32ConstExpr(uint32_t value) override; + Result OnF64ConstExpr(uint64_t value) override; + Result OnGlobalGetExpr(Index global_index) override; + Result OnCodeMetadataCount(Index function_index, Index count) override; + Result OnCodeMetadata(Offset code_offset, + const void* data, + Address size) override; + + private: + Result EndInitExpr(); + bool ShouldPrintDetails(); + void PrintDetails(const char* fmt, ...); + Result PrintSymbolFlags(uint32_t flags); + Result PrintSegmentFlags(uint32_t flags); + void PrintInitExpr(const InitExpr& expr, bool as_unsigned = false); + Result OnCount(Index count); + + std::unique_ptr out_stream_; + Index elem_index_ = 0; + Index table_index_ = 0; + Index next_data_reloc_ = 0; + bool reading_elem_init_expr_ = false; + bool reading_data_init_expr_ = false; + bool reading_global_init_expr_ = false; + InitExpr current_init_expr_; + uint8_t data_flags_ = 0; + uint8_t elem_flags_ = 0; + Index data_mem_index_ = 0; + uint64_t data_offset_ = 0; + uint64_t elem_offset_ = 0; + + bool ReadingInitExpr() { + return reading_elem_init_expr_ || reading_data_init_expr_ || + reading_global_init_expr_; + } +}; + +BinaryReaderObjdump::BinaryReaderObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* objdump_state) + : BinaryReaderObjdumpBase(data, size, options, objdump_state), + out_stream_(FileStream::CreateStdout()) {} + +Result BinaryReaderObjdump::BeginCustomSection(Index section_index, + Offset size, + std::string_view section_name) { + PrintDetails(" - name: \"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name)); + if (options_->mode == ObjdumpMode::Headers) { + printf("\"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(section_name)); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::BeginSection(Index section_index, + BinarySection section_code, + Offset size) { + BinaryReaderObjdumpBase::BeginSection(section_index, section_code, size); + + // |section_name| and |match_name| are identical for known sections. For + // custom sections, |section_name| is "Custom", but |match_name| is the name + // of the custom section. + const char* section_name = wabt::GetSectionName(section_code); + std::string match_name(GetSectionName(section_index)); + + bool section_match = !options_->section_name || + !strcasecmp(options_->section_name, match_name.c_str()); + if (section_match) { + section_found_ = true; + } + + switch (options_->mode) { + case ObjdumpMode::Headers: + printf("%9s start=%#010" PRIzx " end=%#010" PRIzx " (size=%#010" PRIoffset + ") ", + section_name, state->offset, state->offset + size, size); + break; + case ObjdumpMode::Details: + if (section_match) { + printf("%s", section_name); + // All known section types except the Start and DataCount sections have + // a count in which case this line gets completed in OnCount(). + if (section_code == BinarySection::Start || + section_code == BinarySection::DataCount || + section_code == BinarySection::Custom) { + printf(":\n"); + } + print_details_ = true; + } else { + print_details_ = false; + } + break; + case ObjdumpMode::RawData: + if (section_match) { + printf("\nContents of section %s:\n", section_name); + out_stream_->WriteMemoryDump(data_ + state->offset, size, state->offset, + PrintChars::Yes); + } + break; + case ObjdumpMode::Prepass: + case ObjdumpMode::Disassemble: + break; + } + return Result::Ok; +} + +bool BinaryReaderObjdump::ShouldPrintDetails() { + if (options_->mode != ObjdumpMode::Details) { + return false; + } + return print_details_; +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReaderObjdump::PrintDetails(const char* fmt, + ...) { + if (!ShouldPrintDetails()) { + return; + } + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +Result BinaryReaderObjdump::OnCount(Index count) { + if (options_->mode == ObjdumpMode::Headers) { + printf("count: %" PRIindex "\n", count); + } else if (options_->mode == ObjdumpMode::Details && print_details_) { + printf("[%" PRIindex "]:\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::EndModule() { + if (options_->section_name && !section_found_) { + err_stream_->Writef("Section not found: %s\n", options_->section_name); + return Result::Error; + } + + if (options_->relocs && ShouldPrintDetails()) { + if (next_data_reloc_ != objdump_state_->data_relocations.size()) { + err_stream_->Writef("Data reloctions outside of segments!:\n"); + for (size_t i = next_data_reloc_; + i < objdump_state_->data_relocations.size(); i++) { + const Reloc& reloc = objdump_state_->data_relocations[i]; + PrintRelocation(reloc, reloc.offset); + } + + return Result::Error; + } + } + + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTypeCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnFuncType(Index index, + Index param_count, + Type* param_types, + Index result_count, + Type* result_types) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (", index); + for (Index i = 0; i < param_count; i++) { + if (i != 0) { + printf(", "); + } + printf("%s", param_types[i].GetName().c_str()); + } + printf(") -> "); + switch (result_count) { + case 0: + printf("nil"); + break; + case 1: + printf("%s", result_types[0].GetName().c_str()); + break; + default: + printf("("); + for (Index i = 0; i < result_count; i++) { + if (i != 0) { + printf(", "); + } + printf("%s", result_types[i].GetName().c_str()); + } + printf(")"); + break; + } + printf("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnStructType(Index index, + Index field_count, + TypeMut* fields) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (struct", index); + for (Index i = 0; i < field_count; i++) { + if (fields[i].mutable_) { + printf(" (mut"); + } + printf(" %s", fields[i].type.GetName().c_str()); + if (fields[i].mutable_) { + printf(")"); + } + } + printf(")\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnArrayType(Index index, TypeMut field) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - type[%" PRIindex "] (array", index); + if (field.mutable_) { + printf(" (mut"); + } + printf(" %s", field.type.GetName().c_str()); + if (field.mutable_) { + printf(")"); + } + printf(")\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnFunction(Index index, Index sig_index) { + PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, index, sig_index); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionBodyCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginFunctionBody(Index index, Offset size) { + PrintDetails(" - func[%" PRIindex "] size=%" PRIzd, index, size); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnStartFunction(Index func_index) { + if (options_->mode == ObjdumpMode::Headers) { + printf("start: %" PRIindex "\n", func_index); + } else { + PrintDetails(" - start function: %" PRIindex, func_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataCount(Index count) { + if (options_->mode == ObjdumpMode::Headers) { + printf("count: %" PRIindex "\n", count); + } else { + PrintDetails(" - data count: %" PRIindex "\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnImportFunc(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index func_index, + Index sig_index) { + PrintDetails(" - func[%" PRIindex "] sig=%" PRIindex, func_index, sig_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportTable(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index table_index, + Type elem_type, + const Limits* elem_limits) { + PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, table_index, + elem_type.GetName().c_str(), elem_limits->initial); + if (elem_limits->has_max) { + PrintDetails(" max=%" PRId64, elem_limits->max); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportMemory(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index memory_index, + const Limits* page_limits) { + PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, memory_index, + page_limits->initial); + if (page_limits->has_max) { + PrintDetails(" max=%" PRId64, page_limits->max); + } + if (page_limits->is_shared) { + PrintDetails(" shared"); + } + if (page_limits->is_64) { + PrintDetails(" i64"); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportGlobal(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index global_index, + Type type, + bool mutable_) { + PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index, + type.GetName().c_str(), mutable_); + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnImportTag(Index import_index, + std::string_view module_name, + std::string_view field_name, + Index tag_index, + Index sig_index) { + PrintDetails(" - tag[%" PRIindex "] sig=%" PRIindex, tag_index, sig_index); + auto name = GetTagName(tag_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails(" <- " PRIstringview "." PRIstringview "\n", + WABT_PRINTF_STRING_VIEW_ARG(module_name), + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnMemoryCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnMemory(Index index, const Limits* page_limits) { + PrintDetails(" - memory[%" PRIindex "] pages: initial=%" PRId64, index, + page_limits->initial); + if (page_limits->has_max) { + PrintDetails(" max=%" PRId64, page_limits->max); + } + if (page_limits->is_shared) { + PrintDetails(" shared"); + } + if (page_limits->is_64) { + PrintDetails(" i64"); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTableCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnTable(Index index, + Type elem_type, + const Limits* elem_limits) { + PrintDetails(" - table[%" PRIindex "] type=%s initial=%" PRId64, index, + elem_type.GetName().c_str(), elem_limits->initial); + if (elem_limits->has_max) { + PrintDetails(" max=%" PRId64, elem_limits->max); + } + auto name = GetTableName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnExportCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnExport(Index index, + ExternalKind kind, + Index item_index, + std::string_view name) { + PrintDetails(" - %s[%" PRIindex "]", GetKindName(kind), item_index); + if (kind == ExternalKind::Func) { + auto name = GetFunctionName(item_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + } + + PrintDetails(" -> \"" PRIstringview "\"\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefNull(Index segment_index, + Type type) { + PrintDetails(" - elem[%" PRIu64 "] = ref.null %s\n", + elem_offset_ + elem_index_, type.GetName().c_str()); + elem_index_++; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index, + Index func_index) { + PrintDetails(" - elem[%" PRIu64 "] = func[%" PRIindex "]", + elem_offset_ + elem_index_, func_index); + auto name = GetFunctionName(func_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + elem_index_++; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginElemSegment(Index index, + Index table_index, + uint8_t flags) { + table_index_ = table_index; + elem_index_ = 0; + elem_flags_ = flags; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemType(Index index, Type elem_type) { + // TODO: Add support for this. + return Result::Ok; +} + +Result BinaryReaderObjdump::OnElemSegmentElemExprCount(Index index, + Index count) { + PrintDetails(" - segment[%" PRIindex "] flags=%d table=%" PRIindex + " count=%" PRIindex, + index, elem_flags_, table_index_, count); + if (elem_flags_ & SegPassive) { + PrintDetails("\n"); + } else { + PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnGlobalCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginGlobal(Index index, Type type, bool mutable_) { + PrintDetails(" - global[%" PRIindex "] %s mutable=%d", index, + type.GetName().c_str(), mutable_); + std::string_view name = GetGlobalName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; +} + +void BinaryReaderObjdump::PrintInitExpr(const InitExpr& expr, + bool as_unsigned) { + assert(expr.insts.size() > 0); + + // We have two different way to print init expressions. One for + // extended expressions involving more than one instruction, and + // a short form for the more traditional single instruction form. + if (expr.insts.size() > 1) { + PrintDetails(" - init ("); + bool first = true; + for (auto& inst : expr.insts) { + if (!first) { + PrintDetails(", "); + } + first = false; + PrintDetails("%s", inst.opcode.GetName()); + switch (inst.opcode) { + case Opcode::I32Const: + PrintDetails(" %d", inst.imm.i32); + break; + case Opcode::I64Const: + PrintDetails(" %" PRId64, inst.imm.i64); + break; + case Opcode::F32Const: { + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), inst.imm.f32); + PrintDetails(" %s\n", buffer); + break; + } + case Opcode::F64Const: { + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), inst.imm.f64); + PrintDetails(" %s\n", buffer); + break; + } + case Opcode::GlobalGet: { + PrintDetails(" %" PRIindex, inst.imm.index); + std::string_view name = GetGlobalName(inst.imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", + WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + default: + break; + } + } + PrintDetails(")\n"); + return; + } + + switch (expr.type) { + case InitExprType::I32: + if (as_unsigned) { + PrintDetails(" - init i32=%u\n", expr.insts[0].imm.i32); + } else { + PrintDetails(" - init i32=%d\n", expr.insts[0].imm.i32); + } + break; + case InitExprType::I64: + if (as_unsigned) { + PrintDetails(" - init i64=%" PRIu64 "\n", expr.insts[0].imm.i64); + } else { + PrintDetails(" - init i64=%" PRId64 "\n", expr.insts[0].imm.i64); + } + break; + case InitExprType::F64: { + char buffer[WABT_MAX_DOUBLE_HEX]; + WriteDoubleHex(buffer, sizeof(buffer), expr.insts[0].imm.f64); + PrintDetails(" - init f64=%s\n", buffer); + break; + } + case InitExprType::F32: { + char buffer[WABT_MAX_FLOAT_HEX]; + WriteFloatHex(buffer, sizeof(buffer), expr.insts[0].imm.f32); + PrintDetails(" - init f32=%s\n", buffer); + break; + } + case InitExprType::V128: { + PrintDetails( + " - init v128=0x%08x 0x%08x 0x%08x 0x%08x \n", + expr.insts[0].imm.v128_v.u32(0), expr.insts[0].imm.v128_v.u32(1), + expr.insts[0].imm.v128_v.u32(2), expr.insts[0].imm.v128_v.u32(3)); + break; + } + case InitExprType::Global: { + PrintDetails(" - init global=%" PRIindex, expr.insts[0].imm.index); + std::string_view name = GetGlobalName(expr.insts[0].imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::FuncRef: { + PrintDetails(" - init ref.func:%" PRIindex, expr.insts[0].imm.index); + std::string_view name = GetFunctionName(expr.insts[0].imm.index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + break; + } + case InitExprType::NullRef: + PrintDetails(" - init null\n"); + break; + break; + } +} + +static void InitExprToConstOffset(const InitExpr& expr, uint64_t* out_offset) { + if (expr.insts.size() == 1) { + switch (expr.type) { + case InitExprType::I32: + *out_offset = expr.insts[0].imm.i32; + break; + case InitExprType::I64: + *out_offset = expr.insts[0].imm.i64; + break; + default: + break; + } + } +} + +Result BinaryReaderObjdump::EndInitExpr() { + if (reading_data_init_expr_) { + reading_data_init_expr_ = false; + InitExprToConstOffset(current_init_expr_, &data_offset_); + } else if (reading_elem_init_expr_) { + reading_elem_init_expr_ = false; + InitExprToConstOffset(current_init_expr_, &elem_offset_); + } else if (reading_global_init_expr_) { + reading_global_init_expr_ = false; + PrintInitExpr(current_init_expr_); + } else { + WABT_UNREACHABLE; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnI32ConstExpr(uint32_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::I32; + current_init_expr_.insts.back().imm.i32 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnI64ConstExpr(uint64_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::I64; + current_init_expr_.insts.back().imm.i64 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnF32ConstExpr(uint32_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::F32; + current_init_expr_.insts.back().imm.f32 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnF64ConstExpr(uint64_t value) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::F64; + current_init_expr_.insts.back().imm.f64 = value; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnOpcode(Opcode opcode) { + BinaryReaderObjdumpBase::OnOpcode(opcode); + if (ReadingInitExpr() && opcode != Opcode::End) { + InitInst i; + i.opcode = current_opcode; + current_init_expr_.insts.push_back(i); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnGlobalGetExpr(Index global_index) { + if (ReadingInitExpr()) { + current_init_expr_.type = InitExprType::Global; + current_init_expr_.insts.back().imm.index = global_index; + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnModuleName(std::string_view name) { + PrintDetails(" - module <" PRIstringview ">\n", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFunctionName(Index index, std::string_view name) { + PrintDetails(" - func[%" PRIindex "] <" PRIstringview ">\n", index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnNameEntry(NameSectionSubsection type, + Index index, + std::string_view name) { + PrintDetails(" - %s[%" PRIindex "] <" PRIstringview ">\n", + GetNameSectionSubsectionName(type), index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnLocalName(Index func_index, + Index local_index, + std::string_view name) { + if (!name.empty()) { + PrintDetails(" - func[%" PRIindex "] local[%" PRIindex "] <" PRIstringview + ">\n", + func_index, local_index, WABT_PRINTF_STRING_VIEW_ARG(name)); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSegmentCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::BeginDataSegment(Index index, + Index memory_index, + uint8_t flags) { + data_mem_index_ = memory_index; + data_flags_ = flags; + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSegmentData(Index index, + const void* src_data, + Address size) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + + PrintDetails(" - segment[%" PRIindex "]", index); + auto name = GetSegmentName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + if (data_flags_ & SegPassive) { + PrintDetails(" passive"); + } else { + PrintDetails(" memory=%" PRIindex, data_mem_index_); + } + PrintDetails(" size=%" PRIaddress, size); + if (data_flags_ & SegPassive) { + PrintDetails("\n"); + } else { + PrintInitExpr(current_init_expr_, /*as_unsigned=*/true); + } + + out_stream_->WriteMemoryDump(src_data, size, data_offset_, PrintChars::Yes, + " - "); + + // Print relocations from this segment. + if (!options_->relocs) { + return Result::Ok; + } + + Offset data_start = GetSectionStart(BinarySection::Data); + Offset segment_start = state->offset - size; + Offset segment_offset = segment_start - data_start; + while (next_data_reloc_ < objdump_state_->data_relocations.size()) { + const Reloc& reloc = objdump_state_->data_relocations[next_data_reloc_]; + Offset abs_offset = data_start + reloc.offset; + if (abs_offset > state->offset) { + break; + } + PrintRelocation(reloc, reloc.offset - segment_offset + data_offset_); + next_data_reloc_++; + } + + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkInfo(uint32_t mem_size, + uint32_t mem_align_log2, + uint32_t table_size, + uint32_t table_align_log2) { + PrintDetails(" - mem_size : %u\n", mem_size); + PrintDetails(" - mem_p2align : %u\n", mem_align_log2); + PrintDetails(" - table_size : %u\n", table_size); + PrintDetails(" - table_p2align: %u\n", table_align_log2); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkNeededCount(Index count) { + if (count) { + PrintDetails(" - needed_dynlibs[%u]:\n", count); + } + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkImportCount(Index count) { + PrintDetails(" - imports[%u]:\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkExportCount(Index count) { + PrintDetails(" - exports[%u]:\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDylinkExport(std::string_view name, + uint32_t flags) { + PrintDetails(" - " PRIstringview, WABT_PRINTF_STRING_VIEW_ARG(name)); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnDylinkImport(std::string_view module, + std::string_view name, + uint32_t flags) { + PrintDetails(" - " PRIstringview "." PRIstringview, + WABT_PRINTF_STRING_VIEW_ARG(module), + WABT_PRINTF_STRING_VIEW_ARG(name)); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnDylinkNeeded(std::string_view so_name) { + PrintDetails(" - " PRIstringview "\n", WABT_PRINTF_STRING_VIEW_ARG(so_name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnRelocCount(Index count, Index section_index) { + BinaryReaderObjdumpBase::OnRelocCount(count, section_index); + PrintDetails(" - relocations for section: %d (" PRIstringview ") [%d]\n", + section_index, + WABT_PRINTF_STRING_VIEW_ARG(GetSectionName(section_index)), + count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnReloc(RelocType type, + Offset offset, + Index index, + uint32_t addend) { + Offset total_offset = GetSectionStart(reloc_section_) + offset; + PrintDetails(" - %-18s offset=%#08" PRIoffset "(file=%#08" PRIoffset ") ", + GetRelocTypeName(type), offset, total_offset); + if (type == RelocType::TypeIndexLEB) { + PrintDetails("type=%" PRIindex, index); + } else { + PrintDetails("symbol=%" PRIindex " <" PRIstringview ">", index, + WABT_PRINTF_STRING_VIEW_ARG(GetSymbolName(index))); + } + + if (addend) { + int32_t signed_addend = static_cast(addend); + if (signed_addend < 0) { + PrintDetails("-"); + signed_addend = -signed_addend; + } else { + PrintDetails("+"); + } + PrintDetails("%#x", signed_addend); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnFeature(uint8_t prefix, std::string_view name) { + PrintDetails(" - [%c] " PRIstringview "\n", prefix, + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnSymbolCount(Index count) { + PrintDetails(" - symbol table [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::PrintSymbolFlags(uint32_t flags) { + if (flags > WABT_SYMBOL_FLAG_MAX) { + err_stream_->Writef("Unknown symbols flags: %x\n", flags); + return Result::Error; + } + + const char* binding_name = nullptr; + SymbolBinding binding = + static_cast(flags & WABT_SYMBOL_MASK_BINDING); + switch (binding) { + case SymbolBinding::Global: + binding_name = "global"; + break; + case SymbolBinding::Local: + binding_name = "local"; + break; + case SymbolBinding::Weak: + binding_name = "weak"; + break; + } + flags &= ~WABT_SYMBOL_MASK_BINDING; + + const char* vis_name = nullptr; + SymbolVisibility vis = + static_cast(flags & WABT_SYMBOL_MASK_VISIBILITY); + switch (vis) { + case SymbolVisibility::Hidden: + vis_name = "hidden"; + break; + case SymbolVisibility::Default: + vis_name = "default"; + break; + } + flags &= ~WABT_SYMBOL_MASK_VISIBILITY; + + PrintDetails(" ["); + if (flags & WABT_SYMBOL_FLAG_UNDEFINED) { + PrintDetails(" undefined"); + flags &= ~WABT_SYMBOL_FLAG_UNDEFINED; + } + if (flags & WABT_SYMBOL_FLAG_EXPORTED) { + PrintDetails(" exported"); + flags &= ~WABT_SYMBOL_FLAG_EXPORTED; + } + if (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) { + PrintDetails(" explicit_name"); + flags &= ~WABT_SYMBOL_FLAG_EXPLICIT_NAME; + } + if (flags & WABT_SYMBOL_FLAG_NO_STRIP) { + PrintDetails(" no_strip"); + flags &= ~WABT_SYMBOL_FLAG_NO_STRIP; + } + if (flags & WABT_SYMBOL_FLAG_TLS) { + PrintDetails(" tls"); + flags &= ~WABT_SYMBOL_FLAG_TLS; + } + if (flags != 0) { + PrintDetails(" unknown_flags=%#x", flags); + } + PrintDetails(" binding=%s vis=%s ]\n", binding_name, vis_name); + return Result::Ok; +} + +Result BinaryReaderObjdump::PrintSegmentFlags(uint32_t flags) { + if (flags > WABT_SYMBOL_FLAG_MAX) { + err_stream_->Writef("Unknown symbols flags: %x\n", flags); + return Result::Error; + } + PrintDetails(" ["); + if (flags & WABT_SEGMENT_FLAG_STRINGS) { + PrintDetails(" STRINGS"); + flags &= ~WABT_SEGMENT_FLAG_STRINGS; + } + if (flags & WABT_SEGMENT_FLAG_TLS) { + PrintDetails(" TLS"); + flags &= ~WABT_SEGMENT_FLAG_TLS; + } + if (flags != 0) { + PrintDetails(" unknown_flags=%#x", flags); + } + PrintDetails(" ]\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnDataSymbol(Index index, + uint32_t flags, + std::string_view name, + Index segment, + uint32_t offset, + uint32_t size) { + PrintDetails(" - %d: D <" PRIstringview ">", index, + WABT_PRINTF_STRING_VIEW_ARG(name)); + if (!(flags & WABT_SYMBOL_FLAG_UNDEFINED)) + PrintDetails(" segment=%" PRIindex " offset=%d size=%d", segment, offset, + size); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnFunctionSymbol(Index index, + uint32_t flags, + std::string_view name, + Index func_index) { + if (name.empty()) { + name = GetFunctionName(func_index); + } + PrintDetails(" - %d: F <" PRIstringview "> func=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), func_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnGlobalSymbol(Index index, + uint32_t flags, + std::string_view name, + Index global_index) { + if (name.empty()) { + name = GetGlobalName(global_index); + } + PrintDetails(" - %d: G <" PRIstringview "> global=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), global_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnSectionSymbol(Index index, + uint32_t flags, + Index section_index) { + auto sym_name = GetSectionName(section_index); + assert(!sym_name.empty()); + PrintDetails(" - %d: S <" PRIstringview "> section=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(sym_name), section_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnTagSymbol(Index index, + uint32_t flags, + std::string_view name, + Index tag_index) { + if (name.empty()) { + name = GetTagName(tag_index); + } + PrintDetails(" - %d: E <" PRIstringview "> tag=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), tag_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnTableSymbol(Index index, + uint32_t flags, + std::string_view name, + Index table_index) { + if (name.empty()) { + name = GetTableName(table_index); + } + PrintDetails(" - %d: T <" PRIstringview "> table=%" PRIindex, index, + WABT_PRINTF_STRING_VIEW_ARG(name), table_index); + return PrintSymbolFlags(flags); +} + +Result BinaryReaderObjdump::OnSegmentInfoCount(Index count) { + PrintDetails(" - segment info [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnSegmentInfo(Index index, + std::string_view name, + Address alignment_log2, + uint32_t flags) { + PrintDetails(" - %d: " PRIstringview " p2align=%" PRIaddress, index, + WABT_PRINTF_STRING_VIEW_ARG(name), alignment_log2); + return PrintSegmentFlags(flags); +} + +Result BinaryReaderObjdump::OnInitFunctionCount(Index count) { + PrintDetails(" - init functions [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnInitFunction(uint32_t priority, + Index symbol_index) { + PrintDetails(" - %d: priority=%d", symbol_index, priority); + auto name = GetSymbolName(symbol_index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatCount(Index count) { + PrintDetails(" - comdat groups [count=%d]\n", count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatBegin(std::string_view name, + uint32_t flags, + Index count) { + PrintDetails(" - " PRIstringview ": [count=%d]\n", + WABT_PRINTF_STRING_VIEW_ARG(name), count); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnComdatEntry(ComdatType kind, Index index) { + switch (kind) { + case ComdatType::Data: { + PrintDetails(" - segment[%" PRIindex "]", index); + auto name = GetSegmentName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + case ComdatType::Function: { + PrintDetails(" - func[%" PRIindex "]", index); + auto name = GetFunctionName(index); + if (!name.empty()) { + PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + break; + } + } + PrintDetails("\n"); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnTagCount(Index count) { + return OnCount(count); +} + +Result BinaryReaderObjdump::OnTagType(Index index, Index sig_index) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - tag[%" PRIindex "] sig=%" PRIindex "\n", index, sig_index); + return Result::Ok; +} + +Result BinaryReaderObjdump::OnCodeMetadataCount(Index function_index, + Index count) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - func[%" PRIindex "]", function_index); + auto name = GetFunctionName(function_index); + if (!name.empty()) { + printf(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name)); + } + printf(":\n"); + return Result::Ok; +} +Result BinaryReaderObjdump::OnCodeMetadata(Offset code_offset, + const void* data, + Address size) { + if (!ShouldPrintDetails()) { + return Result::Ok; + } + printf(" - meta[%" PRIzx "]:\n", code_offset); + + out_stream_->WriteMemoryDump(data, size, 0, PrintChars::Yes, " - "); + return Result::Ok; +} + +} // end anonymous namespace + +std::string_view ObjdumpNames::Get(Index index) const { + auto iter = names.find(index); + if (iter == names.end()) + return std::string_view(); + return iter->second; +} + +void ObjdumpNames::Set(Index index, std::string_view name) { + names[index] = std::string(name); +} + +std::string_view ObjdumpLocalNames::Get(Index function_index, + Index local_index) const { + auto iter = names.find(std::pair(function_index, local_index)); + if (iter == names.end()) + return std::string_view(); + return iter->second; +} + +void ObjdumpLocalNames::Set(Index function_index, + Index local_index, + std::string_view name) { + names[std::pair(function_index, local_index)] = + std::string(name); +} + +Result ReadBinaryObjdump(const uint8_t* data, + size_t size, + ObjdumpOptions* options, + ObjdumpState* state) { + Features features; + features.EnableAll(); + const bool kReadDebugNames = true; + const bool kStopOnFirstError = false; + const bool kFailOnCustomSectionError = false; + ReadBinaryOptions read_options(features, options->log_stream, kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + + switch (options->mode) { + case ObjdumpMode::Prepass: { + read_options.skip_function_bodies = true; + BinaryReaderObjdumpPrepass reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + case ObjdumpMode::Disassemble: { + BinaryReaderObjdumpDisassemble reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + default: { + read_options.skip_function_bodies = true; + BinaryReaderObjdump reader(data, size, options, state); + return ReadBinary(data, size, &reader, read_options); + } + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader-opcnt.cc b/third_party/wasm2c/src/binary-reader-opcnt.cc new file mode 100644 index 0000000000..3177dbb7eb --- /dev/null +++ b/third_party/wasm2c/src/binary-reader-opcnt.cc @@ -0,0 +1,314 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader-opcnt.h" + +#include +#include +#include +#include +#include + +#include "wabt/binary-reader-nop.h" +#include "wabt/common.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +namespace wabt { + +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind) + : opcode_(opcode), kind_(kind) {} + +template +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count) + : OpcodeInfo(opcode, kind) { + if (count > 0) { + data_.resize(sizeof(T) * count); + memcpy(data_.data(), data, data_.size()); + } +} + +template +OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind, T* data, size_t count, T extra) + : OpcodeInfo(opcode, kind, data, count) { + data_.resize(data_.size() + sizeof(T)); + memcpy(data_.data() + data_.size() - sizeof(T), &extra, sizeof(T)); +} + +template +std::pair OpcodeInfo::GetDataArray() const { + if (data_.empty()) { + return std::pair(nullptr, 0); + } + + assert(data_.size() % sizeof(T) == 0); + return std::make_pair(reinterpret_cast(data_.data()), + data_.size() / sizeof(T)); +} + +template +const T* OpcodeInfo::GetData(size_t expected_size) const { + auto [data, size] = GetDataArray(); + assert(size == expected_size); + return data; +} + +template +void OpcodeInfo::WriteArray(Stream& stream, F&& write_func) { + auto [data, size] = GetDataArray(); + for (size_t i = 0; i < size; ++i) { + // Write an initial space (to separate from the opcode name) first, then + // comma-separate. + stream.Writef("%s", i == 0 ? " " : ", "); + write_func(data[i]); + } +} + +void OpcodeInfo::Write(Stream& stream) { + stream.Writef("%s", opcode_.GetName()); + + switch (kind_) { + case Kind::Bare: + break; + + case Kind::Uint32: + stream.Writef(" %u (0x%x)", *GetData(), *GetData()); + break; + + case Kind::Uint64: + stream.Writef(" %" PRIu64 " (0x%" PRIx64 ")", *GetData(), + *GetData()); + break; + + case Kind::Index: + stream.Writef(" %" PRIindex, *GetData()); + break; + + case Kind::Float32: { + stream.Writef(" %g", *GetData()); + char buffer[WABT_MAX_FLOAT_HEX + 1]; + WriteFloatHex(buffer, sizeof(buffer), *GetData()); + stream.Writef(" (%s)", buffer); + break; + } + + case Kind::Float64: { + stream.Writef(" %g", *GetData()); + char buffer[WABT_MAX_DOUBLE_HEX + 1]; + WriteDoubleHex(buffer, sizeof(buffer), *GetData()); + stream.Writef(" (%s)", buffer); + break; + } + + case Kind::V128: { + auto data = *GetData(); + auto l0 = data.u32(0); + auto l1 = data.u32(1); + auto l2 = data.u32(2); + auto l3 = data.u32(3); + stream.Writef(" %u %u %u %u (0x%x 0x%x 0x%x 0x%x)", l0, l1, l2, l3, l0, + l1, l2, l3); + break; + } + + case Kind::Uint32Uint32: + case Kind::Uint32Uint32Uint32: + case Kind::Uint32Uint32Uint32Uint32: + WriteArray( + stream, [&stream](uint32_t value) { stream.Writef("%u", value); }); + break; + + case Kind::BlockSig: { + auto type = *GetData(); + if (type.IsIndex()) { + stream.Writef(" type:%d", type.GetIndex()); + } else if (type != Type::Void) { + stream.Writef(" %s", type.GetName().c_str()); + } + break; + } + + case Kind::BrTable: { + WriteArray(stream, [&stream](Index index) { + stream.Writef("%" PRIindex, index); + }); + break; + } + } +} + +bool operator==(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return lhs.opcode_ == rhs.opcode_ && lhs.kind_ == rhs.kind_ && + lhs.data_ == rhs.data_; +} + +bool operator!=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs == rhs); +} + +bool operator<(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + if (lhs.opcode_ < rhs.opcode_) { + return true; + } + if (lhs.opcode_ > rhs.opcode_) { + return false; + } + if (lhs.kind_ < rhs.kind_) { + return true; + } + if (lhs.kind_ > rhs.kind_) { + return false; + } + if (lhs.data_ < rhs.data_) { + return true; + } + if (lhs.data_ > rhs.data_) { + return false; + } + return false; +} + +bool operator<=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return lhs < rhs || lhs == rhs; +} + +bool operator>(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs <= rhs); +} + +bool operator>=(const OpcodeInfo& lhs, const OpcodeInfo& rhs) { + return !(lhs < rhs); +} + +namespace { + +class BinaryReaderOpcnt : public BinaryReaderNop { + public: + explicit BinaryReaderOpcnt(OpcodeInfoCounts* counts); + + Result OnOpcode(Opcode opcode) override; + Result OnOpcodeBare() override; + Result OnOpcodeUint32(uint32_t value) override; + Result OnOpcodeIndex(Index value) override; + Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override; + Result OnOpcodeUint32Uint32Uint32(uint32_t value, + uint32_t value2, + uint32_t value3) override; + Result OnOpcodeUint64(uint64_t value) override; + Result OnOpcodeF32(uint32_t value) override; + Result OnOpcodeF64(uint64_t value) override; + Result OnOpcodeV128(v128 value) override; + Result OnOpcodeBlockSig(Type sig_type) override; + Result OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) override; + Result OnEndExpr() override; + + private: + template + Result Emplace(Args&&... args); + + OpcodeInfoCounts* opcode_counts_; + Opcode current_opcode_; +}; + +template +Result BinaryReaderOpcnt::Emplace(Args&&... args) { + auto pair = opcode_counts_->emplace( + std::piecewise_construct, std::make_tuple(std::forward(args)...), + std::make_tuple(0)); + + auto& count = pair.first->second; + count++; + return Result::Ok; +} + +BinaryReaderOpcnt::BinaryReaderOpcnt(OpcodeInfoCounts* counts) + : opcode_counts_(counts) {} + +Result BinaryReaderOpcnt::OnOpcode(Opcode opcode) { + current_opcode_ = opcode; + return Result::Ok; +} + +Result BinaryReaderOpcnt::OnOpcodeBare() { + return Emplace(current_opcode_, OpcodeInfo::Kind::Bare); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32(uint32_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeIndex(Index value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Index, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32Uint32(uint32_t value0, + uint32_t value1) { + uint32_t array[2] = {value0, value1}; + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32, array, 2); +} + +Result BinaryReaderOpcnt::OnOpcodeUint32Uint32Uint32(uint32_t value0, + uint32_t value1, + uint32_t value2) { + uint32_t array[3] = {value0, value1, value2}; + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint32Uint32Uint32, array, + 3); +} + +Result BinaryReaderOpcnt::OnOpcodeUint64(uint64_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Uint64, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeF32(uint32_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Float32, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeF64(uint64_t value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::Float64, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeV128(v128 value) { + return Emplace(current_opcode_, OpcodeInfo::Kind::V128, &value); +} + +Result BinaryReaderOpcnt::OnOpcodeBlockSig(Type sig_type) { + return Emplace(current_opcode_, OpcodeInfo::Kind::BlockSig, &sig_type); +} + +Result BinaryReaderOpcnt::OnBrTableExpr(Index num_targets, + Index* target_depths, + Index default_target_depth) { + return Emplace(current_opcode_, OpcodeInfo::Kind::BrTable, target_depths, + num_targets, default_target_depth); +} + +Result BinaryReaderOpcnt::OnEndExpr() { + return Emplace(Opcode::End, OpcodeInfo::Kind::Bare); +} + +} // end anonymous namespace + +Result ReadBinaryOpcnt(const void* data, + size_t size, + const ReadBinaryOptions& options, + OpcodeInfoCounts* counts) { + BinaryReaderOpcnt reader(counts); + return ReadBinary(data, size, &reader, options); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc new file mode 100644 index 0000000000..8e86383db4 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader.cc @@ -0,0 +1,3027 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-reader.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "wabt/config.h" + +#include "wabt/binary-reader-logging.h" +#include "wabt/binary.h" +#include "wabt/leb128.h" +#include "wabt/stream.h" +#include "wabt/utf8.h" + +#if HAVE_ALLOCA +#include +#endif + +#define ERROR_IF(expr, ...) \ + do { \ + if (expr) { \ + PrintError(__VA_ARGS__); \ + return Result::Error; \ + } \ + } while (0) + +#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__) + +#define ERROR_UNLESS_OPCODE_ENABLED(opcode) \ + do { \ + if (!opcode.IsEnabled(options_.features)) { \ + return ReportUnexpectedOpcode(opcode); \ + } \ + } while (0) + +#define CALLBACK0(member) \ + ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed") + +#define CALLBACK(member, ...) \ + ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \ + #member " callback failed") + +namespace wabt { + +namespace { + +class BinaryReader { + public: + struct ReadModuleOptions { + bool stop_on_first_error; + }; + + BinaryReader(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options); + + Result ReadModule(const ReadModuleOptions& options); + + private: + template + struct ValueRestoreGuard { + explicit ValueRestoreGuard(BinaryReader* this_) + : this_(this_), previous_value_(this_->*member) {} + ~ValueRestoreGuard() { this_->*member = previous_value_; } + + BinaryReader* this_; + T previous_value_; + }; + + struct ReadSectionsOptions { + bool stop_on_first_error; + }; + + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); + [[nodiscard]] Result ReadOpcode(Opcode* out_value, const char* desc); + template + [[nodiscard]] Result ReadT(T* out_value, + const char* type_name, + const char* desc); + [[nodiscard]] Result ReadU8(uint8_t* out_value, const char* desc); + [[nodiscard]] Result ReadU32(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadF32(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadF64(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadV128(v128* out_value, const char* desc); + [[nodiscard]] Result ReadU32Leb128(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadU64Leb128(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadS32Leb128(uint32_t* out_value, const char* desc); + [[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc); + [[nodiscard]] Result ReadType(Type* out_value, const char* desc); + [[nodiscard]] Result ReadRefType(Type* out_value, const char* desc); + [[nodiscard]] Result ReadExternalKind(ExternalKind* out_value, + const char* desc); + [[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc); + [[nodiscard]] Result ReadBytes(const void** out_data, + Address* out_data_size, + const char* desc); + [[nodiscard]] Result ReadIndex(Index* index, const char* desc); + [[nodiscard]] Result ReadOffset(Offset* offset, const char* desc); + [[nodiscard]] Result ReadAlignment(Address* align_log2, const char* desc); + [[nodiscard]] Result ReadMemidx(Index* memidx, const char* desc); + [[nodiscard]] Result ReadMemLocation(Address* alignment_log2, + Index* memidx, + Address* offset, + const char* desc_align, + const char* desc_memidx, + const char* desc_offset, + uint8_t* lane_val = nullptr); + [[nodiscard]] Result CallbackMemLocation(const Address* alignment_log2, + const Index* memidx, + const Address* offset, + const uint8_t* lane_val = nullptr); + [[nodiscard]] Result ReadCount(Index* index, const char* desc); + [[nodiscard]] Result ReadField(TypeMut* out_value); + + bool IsConcreteType(Type); + bool IsBlockType(Type); + + Index NumTotalFuncs(); + + [[nodiscard]] Result ReadInitExpr(Index index); + [[nodiscard]] Result ReadTable(Type* out_elem_type, Limits* out_elem_limits); + [[nodiscard]] Result ReadMemory(Limits* out_page_limits); + [[nodiscard]] Result ReadGlobalHeader(Type* out_type, bool* out_mutable); + [[nodiscard]] Result ReadTagType(Index* out_sig_index); + [[nodiscard]] Result ReadAddress(Address* out_value, + Index memory, + const char* desc); + [[nodiscard]] Result ReadFunctionBody(Offset end_offset); + // ReadInstructions either until and END instruction, or until + // the given end_offset. + [[nodiscard]] Result ReadInstructions(bool stop_on_end, + Offset end_offset, + Opcode* final_opcode); + [[nodiscard]] Result ReadNameSection(Offset section_size); + [[nodiscard]] Result ReadRelocSection(Offset section_size); + [[nodiscard]] Result ReadDylinkSection(Offset section_size); + [[nodiscard]] Result ReadDylink0Section(Offset section_size); + [[nodiscard]] Result ReadTargetFeaturesSections(Offset section_size); + [[nodiscard]] Result ReadLinkingSection(Offset section_size); + [[nodiscard]] Result ReadCodeMetadataSection(std::string_view name, + Offset section_size); + [[nodiscard]] Result ReadCustomSection(Index section_index, + Offset section_size); + [[nodiscard]] Result ReadTypeSection(Offset section_size); + [[nodiscard]] Result ReadImportSection(Offset section_size); + [[nodiscard]] Result ReadFunctionSection(Offset section_size); + [[nodiscard]] Result ReadTableSection(Offset section_size); + [[nodiscard]] Result ReadMemorySection(Offset section_size); + [[nodiscard]] Result ReadGlobalSection(Offset section_size); + [[nodiscard]] Result ReadExportSection(Offset section_size); + [[nodiscard]] Result ReadStartSection(Offset section_size); + [[nodiscard]] Result ReadElemSection(Offset section_size); + [[nodiscard]] Result ReadCodeSection(Offset section_size); + [[nodiscard]] Result ReadDataSection(Offset section_size); + [[nodiscard]] Result ReadDataCountSection(Offset section_size); + [[nodiscard]] Result ReadTagSection(Offset section_size); + [[nodiscard]] Result ReadSections(const ReadSectionsOptions& options); + Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr); + + size_t read_end_ = 0; // Either the section end or data_size. + BinaryReaderDelegate::State state_; + BinaryReaderLogging logging_delegate_; + BinaryReaderDelegate* delegate_ = nullptr; + TypeVector param_types_; + TypeVector result_types_; + TypeMutVector fields_; + std::vector target_depths_; + const ReadBinaryOptions& options_; + BinarySection last_known_section_ = BinarySection::Invalid; + bool did_read_names_section_ = false; + bool reading_custom_section_ = false; + Index num_func_imports_ = 0; + Index num_table_imports_ = 0; + Index num_memory_imports_ = 0; + Index num_global_imports_ = 0; + Index num_tag_imports_ = 0; + Index num_function_signatures_ = 0; + Index num_function_bodies_ = 0; + Index data_count_ = kInvalidIndex; + std::vector memories; + + using ReadEndRestoreGuard = + ValueRestoreGuard; +}; + +BinaryReader::BinaryReader(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options) + : read_end_(size), + state_(static_cast(data), size), + logging_delegate_(options.log_stream, delegate), + delegate_(options.log_stream ? &logging_delegate_ : delegate), + options_(options), + last_known_section_(BinarySection::Invalid) { + delegate->OnSetState(&state_); +} + +void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format, + ...) { + ErrorLevel error_level = + reading_custom_section_ && !options_.fail_on_custom_section_error + ? ErrorLevel::Warning + : ErrorLevel::Error; + + WABT_SNPRINTF_ALLOCA(buffer, length, format); + Error error(error_level, Location(state_.offset), buffer); + bool handled = delegate_->OnError(error); + + if (!handled) { + // Not great to just print, but we don't want to eat the error either. + fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset, + GetErrorLevelName(error_level), buffer); + } +} + +Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) { + std::string message = "unexpected opcode"; + if (where) { + message += ' '; + message += where; + } + + message += ":"; + + std::vector bytes = opcode.GetBytes(); + assert(bytes.size() > 0); + + for (uint8_t byte : bytes) { + message += StringPrintf(" 0x%x", byte); + } + + PrintError("%s", message.c_str()); + return Result::Error; +} + +Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) { + uint8_t value = 0; + CHECK_RESULT(ReadU8(&value, desc)); + + if (Opcode::IsPrefixByte(value)) { + uint32_t code; + CHECK_RESULT(ReadU32Leb128(&code, desc)); + *out_value = Opcode::FromCode(value, code); + } else { + *out_value = Opcode::FromCode(value); + } + return Result::Ok; +} + +template +Result BinaryReader::ReadT(T* out_value, + const char* type_name, + const char* desc) { + if (state_.offset + sizeof(T) > read_end_) { + PrintError("unable to read %s: %s", type_name, desc); + return Result::Error; + } +#if WABT_BIG_ENDIAN + uint8_t tmp[sizeof(T)]; + memcpy(tmp, state_.data + state_.offset, sizeof(tmp)); + SwapBytesSized(tmp, sizeof(tmp)); + memcpy(out_value, tmp, sizeof(T)); +#else + memcpy(out_value, state_.data + state_.offset, sizeof(T)); +#endif + state_.offset += sizeof(T); + return Result::Ok; +} + +Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) { + return ReadT(out_value, "uint8_t", desc); +} + +Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) { + return ReadT(out_value, "uint32_t", desc); +} + +Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) { + return ReadT(out_value, "float", desc); +} + +Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) { + return ReadT(out_value, "double", desc); +} + +Result BinaryReader::ReadV128(v128* out_value, const char* desc) { + return ReadT(out_value, "v128", desc); +} + +Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) { + const uint8_t* p = state_.data + state_.offset; + const uint8_t* end = state_.data + read_end_; + size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value); + ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc); + state_.offset += bytes_read; + return Result::Ok; +} + +Result BinaryReader::ReadType(Type* out_value, const char* desc) { + uint32_t type = 0; + CHECK_RESULT(ReadS32Leb128(&type, desc)); + if (static_cast(type) == Type::Reference) { + uint32_t heap_type = 0; + CHECK_RESULT(ReadS32Leb128(&heap_type, desc)); + *out_value = Type(Type::Reference, heap_type); + } else { + *out_value = static_cast(type); + } + return Result::Ok; +} + +Result BinaryReader::ReadRefType(Type* out_value, const char* desc) { + uint32_t type = 0; + CHECK_RESULT(ReadS32Leb128(&type, desc)); + *out_value = static_cast(type); + ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc); + return Result::Ok; +} + +Result BinaryReader::ReadExternalKind(ExternalKind* out_value, + const char* desc) { + uint8_t value = 0; + CHECK_RESULT(ReadU8(&value, desc)); + ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d", + value); + *out_value = static_cast(value); + return Result::Ok; +} + +Result BinaryReader::ReadStr(std::string_view* out_str, const char* desc) { + uint32_t str_len = 0; + CHECK_RESULT(ReadU32Leb128(&str_len, "string length")); + + ERROR_UNLESS(state_.offset + str_len <= read_end_, + "unable to read string: %s", desc); + + *out_str = std::string_view( + reinterpret_cast(state_.data) + state_.offset, str_len); + state_.offset += str_len; + + ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()), + "invalid utf-8 encoding: %s", desc); + return Result::Ok; +} + +Result BinaryReader::ReadBytes(const void** out_data, + Address* out_data_size, + const char* desc) { + uint32_t data_size = 0; + CHECK_RESULT(ReadU32Leb128(&data_size, "data size")); + + ERROR_UNLESS(state_.offset + data_size <= read_end_, + "unable to read data: %s", desc); + + *out_data = static_cast(state_.data) + state_.offset; + *out_data_size = data_size; + state_.offset += data_size; + return Result::Ok; +} + +Result BinaryReader::ReadIndex(Index* index, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + *index = value; + return Result::Ok; +} + +Result BinaryReader::ReadOffset(Offset* offset, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + *offset = value; + return Result::Ok; +} + +Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) { + uint32_t value; + CHECK_RESULT(ReadU32Leb128(&value, desc)); + if (value >= 128 || + (value >= 32 && !options_.features.multi_memory_enabled())) { + PrintError("invalid %s: %u", desc, value); + return Result::Error; + } + *alignment_log2 = value; + return Result::Ok; +} + +Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) { + CHECK_RESULT(ReadIndex(memidx, desc)); + ERROR_UNLESS(*memidx < memories.size(), "memory index %u out of range", + *memidx); + return Result::Ok; +} + +Result BinaryReader::ReadMemLocation(Address* alignment_log2, + Index* memidx, + Address* offset, + const char* desc_align, + const char* desc_memidx, + const char* desc_offset, + uint8_t* lane_val) { + CHECK_RESULT(ReadAlignment(alignment_log2, desc_align)); + *memidx = 0; + if (*alignment_log2 >> 6) { + ERROR_IF(!options_.features.multi_memory_enabled(), + "multi_memory not allowed"); + *alignment_log2 = *alignment_log2 & ((1 << 6) - 1); + CHECK_RESULT(ReadMemidx(memidx, desc_memidx)); + } + CHECK_RESULT(ReadAddress(offset, 0, desc_offset)); + + if (lane_val) { + CHECK_RESULT(ReadU8(lane_val, "Lane idx")); + } + + return Result::Ok; +} + +Result BinaryReader::CallbackMemLocation(const Address* alignment_log2, + const Index* memidx, + const Address* offset, + const uint8_t* lane_val) { + if (lane_val) { + if (*memidx) { + CALLBACK(OnOpcodeUint32Uint32Uint32Uint32, *alignment_log2, *memidx, + *offset, *lane_val); + } else { + CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *offset, *lane_val); + } + } else { + if (*memidx) { + CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *memidx, *offset); + } else { + CALLBACK(OnOpcodeUint32Uint32, *alignment_log2, *offset); + } + } + + return Result::Ok; +} + +Result BinaryReader::ReadCount(Index* count, const char* desc) { + CHECK_RESULT(ReadIndex(count, desc)); + + // This check assumes that each item follows in this section, and takes at + // least 1 byte. It's possible that this check passes but reading fails + // later. It is still useful to check here, though, because it early-outs + // when an erroneous large count is used, before allocating memory for it. + size_t section_remaining = read_end_ - state_.offset; + if (*count > section_remaining) { + PrintError("invalid %s %" PRIindex ", only %" PRIzd + " bytes left in section", + desc, *count, section_remaining); + return Result::Error; + } + return Result::Ok; +} + +Result BinaryReader::ReadField(TypeMut* out_value) { + // TODO: Reuse for global header too? + Type field_type; + CHECK_RESULT(ReadType(&field_type, "field type")); + ERROR_UNLESS(IsConcreteType(field_type), + "expected valid field type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(field_type)); + + uint8_t mutable_ = 0; + CHECK_RESULT(ReadU8(&mutable_, "field mutability")); + ERROR_UNLESS(mutable_ <= 1, "field mutability must be 0 or 1"); + out_value->type = field_type; + out_value->mutable_ = mutable_; + return Result::Ok; +} + +bool BinaryReader::IsConcreteType(Type type) { + switch (type) { + case Type::I32: + case Type::I64: + case Type::F32: + case Type::F64: + return true; + + case Type::V128: + return options_.features.simd_enabled(); + + case Type::FuncRef: + case Type::ExternRef: + return options_.features.reference_types_enabled(); + + case Type::Reference: + return options_.features.function_references_enabled(); + + default: + return false; + } +} + +bool BinaryReader::IsBlockType(Type type) { + if (IsConcreteType(type) || type == Type::Void) { + return true; + } + + if (!(options_.features.multi_value_enabled() && type.IsIndex())) { + return false; + } + + return true; +} + +Index BinaryReader::NumTotalFuncs() { + return num_func_imports_ + num_function_signatures_; +} + +Result BinaryReader::ReadInitExpr(Index index) { + // Read instructions until END opcode is reached. + Opcode final_opcode(Opcode::Invalid); + CHECK_RESULT( + ReadInstructions(/*stop_on_end=*/true, read_end_, &final_opcode)); + ERROR_UNLESS(state_.offset <= read_end_, + "init expression longer than given size"); + ERROR_UNLESS(final_opcode == Opcode::End, + "init expression must end with END opcode"); + return Result::Ok; +} + +Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) { + CHECK_RESULT(ReadRefType(out_elem_type, "table elem type")); + + uint8_t flags; + uint32_t initial; + uint32_t max = 0; + CHECK_RESULT(ReadU8(&flags, "table flags")); + bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; + bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG; + bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG; + const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS; + ERROR_IF(is_shared, "tables may not be shared"); + ERROR_IF(is_64, "tables may not be 64-bit"); + ERROR_UNLESS(unknown_flags == 0, "malformed table limits flag: %d", flags); + CHECK_RESULT(ReadU32Leb128(&initial, "table initial elem count")); + if (has_max) { + CHECK_RESULT(ReadU32Leb128(&max, "table max elem count")); + } + + out_elem_limits->has_max = has_max; + out_elem_limits->initial = initial; + out_elem_limits->max = max; + return Result::Ok; +} + +Result BinaryReader::ReadMemory(Limits* out_page_limits) { + uint8_t flags; + uint64_t initial; + uint64_t max = 0; + CHECK_RESULT(ReadU8(&flags, "memory flags")); + bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; + bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG; + bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG; + const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS; + ERROR_UNLESS(unknown_flags == 0, "malformed memory limits flag: %d", flags); + ERROR_IF(is_shared && !options_.features.threads_enabled(), + "memory may not be shared: threads not allowed"); + ERROR_IF(is_64 && !options_.features.memory64_enabled(), + "memory64 not allowed"); + if (is_64) { + CHECK_RESULT(ReadU64Leb128(&initial, "memory initial page count")); + if (has_max) { + CHECK_RESULT(ReadU64Leb128(&max, "memory max page count")); + } + } else { + uint32_t initial32; + CHECK_RESULT(ReadU32Leb128(&initial32, "memory initial page count")); + initial = initial32; + if (has_max) { + uint32_t max32; + CHECK_RESULT(ReadU32Leb128(&max32, "memory max page count")); + max = max32; + } + } + + out_page_limits->has_max = has_max; + out_page_limits->is_shared = is_shared; + out_page_limits->is_64 = is_64; + out_page_limits->initial = initial; + out_page_limits->max = max; + + // Have to keep a copy of these, to know how to interpret load/stores. + memories.push_back(*out_page_limits); + return Result::Ok; +} + +Result BinaryReader::ReadGlobalHeader(Type* out_type, bool* out_mutable) { + Type global_type = Type::Void; + uint8_t mutable_ = 0; + CHECK_RESULT(ReadType(&global_type, "global type")); + ERROR_UNLESS(IsConcreteType(global_type), "invalid global type: %#x", + static_cast(global_type)); + + CHECK_RESULT(ReadU8(&mutable_, "global mutability")); + ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1"); + + *out_type = global_type; + *out_mutable = mutable_; + return Result::Ok; +} + +Result BinaryReader::ReadAddress(Address* out_value, + Index memory, + const char* desc) { + ERROR_UNLESS(memory < memories.size(), + "load/store memory %u out of range %zu", memory, + memories.size()); + if (memories[memory].is_64) { + return ReadU64Leb128(out_value, desc); + } else { + uint32_t val; + Result res = ReadU32Leb128(&val, desc); + *out_value = val; + return res; + } +} + +Result BinaryReader::ReadFunctionBody(Offset end_offset) { + Opcode final_opcode(Opcode::Invalid); + CHECK_RESULT( + ReadInstructions(/*stop_on_end=*/false, end_offset, &final_opcode)); + ERROR_UNLESS(state_.offset == end_offset, + "function body longer than given size"); + ERROR_UNLESS(final_opcode == Opcode::End, + "function body must end with END opcode"); + return Result::Ok; +} + +Result BinaryReader::ReadInstructions(bool stop_on_end, + Offset end_offset, + Opcode* final_opcode) { + while (state_.offset < end_offset) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + CALLBACK(OnOpcode, opcode); + ERROR_UNLESS_OPCODE_ENABLED(opcode); + if (final_opcode) { + *final_opcode = opcode; + } + + switch (opcode) { + case Opcode::Unreachable: + CALLBACK0(OnUnreachableExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Block: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "block signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnBlockExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Loop: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "loop signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnLoopExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::If: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "if signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnIfExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Else: + CALLBACK0(OnElseExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::SelectT: { + Index num_results; + CHECK_RESULT(ReadCount(&num_results, "num result types")); + + result_types_.resize(num_results); + for (Index i = 0; i < num_results; ++i) { + Type result_type; + CHECK_RESULT(ReadType(&result_type, "select result type")); + ERROR_UNLESS(IsConcreteType(result_type), + "expected valid select result type (got " PRItypecode + ")", + WABT_PRINTF_TYPE_CODE(result_type)); + result_types_[i] = result_type; + } + + if (num_results) { + CALLBACK(OnSelectExpr, num_results, result_types_.data()); + CALLBACK(OnOpcodeType, result_types_[0]); + } else { + CALLBACK(OnSelectExpr, 0, NULL); + CALLBACK0(OnOpcodeBare); + } + break; + } + + case Opcode::Select: + CALLBACK(OnSelectExpr, 0, nullptr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Br: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "br depth")); + CALLBACK(OnBrExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::BrIf: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "br_if depth")); + CALLBACK(OnBrIfExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::BrTable: { + Index num_targets; + CHECK_RESULT(ReadCount(&num_targets, "br_table target count")); + target_depths_.resize(num_targets); + + for (Index i = 0; i < num_targets; ++i) { + Index target_depth; + CHECK_RESULT(ReadIndex(&target_depth, "br_table target depth")); + target_depths_[i] = target_depth; + } + + Index default_target_depth; + CHECK_RESULT( + ReadIndex(&default_target_depth, "br_table default target depth")); + + Index* target_depths = num_targets ? target_depths_.data() : nullptr; + + CALLBACK(OnBrTableExpr, num_targets, target_depths, + default_target_depth); + break; + } + + case Opcode::Return: + CALLBACK0(OnReturnExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Nop: + CALLBACK0(OnNopExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Drop: + CALLBACK0(OnDropExpr); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::End: + CALLBACK0(OnEndExpr); + if (stop_on_end) { + return Result::Ok; + } + break; + + case Opcode::I32Const: { + uint32_t value; + CHECK_RESULT(ReadS32Leb128(&value, "i32.const value")); + CALLBACK(OnI32ConstExpr, value); + CALLBACK(OnOpcodeUint32, value); + break; + } + + case Opcode::I64Const: { + uint64_t value; + CHECK_RESULT(ReadS64Leb128(&value, "i64.const value")); + CALLBACK(OnI64ConstExpr, value); + CALLBACK(OnOpcodeUint64, value); + break; + } + + case Opcode::F32Const: { + uint32_t value_bits = 0; + CHECK_RESULT(ReadF32(&value_bits, "f32.const value")); + CALLBACK(OnF32ConstExpr, value_bits); + CALLBACK(OnOpcodeF32, value_bits); + break; + } + + case Opcode::F64Const: { + uint64_t value_bits = 0; + CHECK_RESULT(ReadF64(&value_bits, "f64.const value")); + CALLBACK(OnF64ConstExpr, value_bits); + CALLBACK(OnOpcodeF64, value_bits); + break; + } + + case Opcode::V128Const: { + v128 value_bits; + ZeroMemory(value_bits); + CHECK_RESULT(ReadV128(&value_bits, "v128.const value")); + CALLBACK(OnV128ConstExpr, value_bits); + CALLBACK(OnOpcodeV128, value_bits); + break; + } + + case Opcode::GlobalGet: { + Index global_index; + CHECK_RESULT(ReadIndex(&global_index, "global.get global index")); + CALLBACK(OnGlobalGetExpr, global_index); + CALLBACK(OnOpcodeIndex, global_index); + break; + } + + case Opcode::LocalGet: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.get local index")); + CALLBACK(OnLocalGetExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::GlobalSet: { + Index global_index; + CHECK_RESULT(ReadIndex(&global_index, "global.set global index")); + CALLBACK(OnGlobalSetExpr, global_index); + CALLBACK(OnOpcodeIndex, global_index); + break; + } + + case Opcode::LocalSet: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.set local index")); + CALLBACK(OnLocalSetExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::Call: { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "call function index")); + CALLBACK(OnCallExpr, func_index); + CALLBACK(OnOpcodeIndex, func_index); + break; + } + + case Opcode::CallIndirect: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "call_indirect signature index")); + Index table_index = 0; + if (options_.features.reference_types_enabled()) { + CHECK_RESULT(ReadIndex(&table_index, "call_indirect table index")); + } else { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "call_indirect reserved")); + ERROR_UNLESS(reserved == 0, "call_indirect reserved value must be 0"); + } + CALLBACK(OnCallIndirectExpr, sig_index, table_index); + CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index); + break; + } + + case Opcode::ReturnCall: { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "return_call")); + CALLBACK(OnReturnCallExpr, func_index); + CALLBACK(OnOpcodeIndex, func_index); + break; + } + + case Opcode::ReturnCallIndirect: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "return_call_indirect")); + Index table_index = 0; + if (options_.features.reference_types_enabled()) { + CHECK_RESULT( + ReadIndex(&table_index, "return_call_indirect table index")); + } else { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "return_call_indirect reserved")); + ERROR_UNLESS(reserved == 0, + "return_call_indirect reserved value must be 0"); + } + CALLBACK(OnReturnCallIndirectExpr, sig_index, table_index); + CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index); + break; + } + + case Opcode::LocalTee: { + Index local_index; + CHECK_RESULT(ReadIndex(&local_index, "local.tee local index")); + CALLBACK(OnLocalTeeExpr, local_index); + CALLBACK(OnOpcodeIndex, local_index); + break; + } + + case Opcode::I32Load8S: + case Opcode::I32Load8U: + case Opcode::I32Load16S: + case Opcode::I32Load16U: + case Opcode::I64Load8S: + case Opcode::I64Load8U: + case Opcode::I64Load16S: + case Opcode::I64Load16U: + case Opcode::I64Load32S: + case Opcode::I64Load32U: + case Opcode::I32Load: + case Opcode::I64Load: + case Opcode::F32Load: + case Opcode::F64Load: + case Opcode::V128Load: + case Opcode::V128Load8X8S: + case Opcode::V128Load8X8U: + case Opcode::V128Load16X4S: + case Opcode::V128Load16X4U: + case Opcode::V128Load32X2S: + case Opcode::V128Load32X2U: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32Store8: + case Opcode::I32Store16: + case Opcode::I64Store8: + case Opcode::I64Store16: + case Opcode::I64Store32: + case Opcode::I32Store: + case Opcode::I64Store: + case Opcode::F32Store: + case Opcode::F64Store: + case Opcode::V128Store: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset")); + CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::MemorySize: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.size reserved")); + ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx")); + } + CALLBACK(OnMemorySizeExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::MemoryGrow: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved")); + ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx")); + } + CALLBACK(OnMemoryGrowExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::I32Add: + case Opcode::I32Sub: + case Opcode::I32Mul: + case Opcode::I32DivS: + case Opcode::I32DivU: + case Opcode::I32RemS: + case Opcode::I32RemU: + case Opcode::I32And: + case Opcode::I32Or: + case Opcode::I32Xor: + case Opcode::I32Shl: + case Opcode::I32ShrU: + case Opcode::I32ShrS: + case Opcode::I32Rotr: + case Opcode::I32Rotl: + case Opcode::I64Add: + case Opcode::I64Sub: + case Opcode::I64Mul: + case Opcode::I64DivS: + case Opcode::I64DivU: + case Opcode::I64RemS: + case Opcode::I64RemU: + case Opcode::I64And: + case Opcode::I64Or: + case Opcode::I64Xor: + case Opcode::I64Shl: + case Opcode::I64ShrU: + case Opcode::I64ShrS: + case Opcode::I64Rotr: + case Opcode::I64Rotl: + case Opcode::F32Add: + case Opcode::F32Sub: + case Opcode::F32Mul: + case Opcode::F32Div: + case Opcode::F32Min: + case Opcode::F32Max: + case Opcode::F32Copysign: + case Opcode::F64Add: + case Opcode::F64Sub: + case Opcode::F64Mul: + case Opcode::F64Div: + case Opcode::F64Min: + case Opcode::F64Max: + case Opcode::F64Copysign: + case Opcode::I8X16Add: + case Opcode::I16X8Add: + case Opcode::I32X4Add: + case Opcode::I64X2Add: + case Opcode::I8X16Sub: + case Opcode::I16X8Sub: + case Opcode::I32X4Sub: + case Opcode::I64X2Sub: + case Opcode::I16X8Mul: + case Opcode::I32X4Mul: + case Opcode::I64X2Mul: + case Opcode::I8X16AddSatS: + case Opcode::I8X16AddSatU: + case Opcode::I16X8AddSatS: + case Opcode::I16X8AddSatU: + case Opcode::I8X16SubSatS: + case Opcode::I8X16SubSatU: + case Opcode::I16X8SubSatS: + case Opcode::I16X8SubSatU: + case Opcode::I8X16MinS: + case Opcode::I16X8MinS: + case Opcode::I32X4MinS: + case Opcode::I8X16MinU: + case Opcode::I16X8MinU: + case Opcode::I32X4MinU: + case Opcode::I8X16MaxS: + case Opcode::I16X8MaxS: + case Opcode::I32X4MaxS: + case Opcode::I8X16MaxU: + case Opcode::I16X8MaxU: + case Opcode::I32X4MaxU: + case Opcode::I8X16Shl: + case Opcode::I16X8Shl: + case Opcode::I32X4Shl: + case Opcode::I64X2Shl: + case Opcode::I8X16ShrS: + case Opcode::I8X16ShrU: + case Opcode::I16X8ShrS: + case Opcode::I16X8ShrU: + case Opcode::I32X4ShrS: + case Opcode::I32X4ShrU: + case Opcode::I64X2ShrS: + case Opcode::I64X2ShrU: + case Opcode::V128And: + case Opcode::V128Or: + case Opcode::V128Xor: + case Opcode::F32X4Min: + case Opcode::F32X4PMin: + case Opcode::F64X2Min: + case Opcode::F64X2PMin: + case Opcode::F32X4Max: + case Opcode::F32X4PMax: + case Opcode::F64X2Max: + case Opcode::F64X2PMax: + case Opcode::F32X4Add: + case Opcode::F64X2Add: + case Opcode::F32X4Sub: + case Opcode::F64X2Sub: + case Opcode::F32X4Div: + case Opcode::F64X2Div: + case Opcode::F32X4Mul: + case Opcode::F64X2Mul: + case Opcode::I8X16Swizzle: + case Opcode::I8X16NarrowI16X8S: + case Opcode::I8X16NarrowI16X8U: + case Opcode::I16X8NarrowI32X4S: + case Opcode::I16X8NarrowI32X4U: + case Opcode::V128Andnot: + case Opcode::I8X16AvgrU: + case Opcode::I16X8AvgrU: + case Opcode::I16X8ExtmulLowI8X16S: + case Opcode::I16X8ExtmulHighI8X16S: + case Opcode::I16X8ExtmulLowI8X16U: + case Opcode::I16X8ExtmulHighI8X16U: + case Opcode::I32X4ExtmulLowI16X8S: + case Opcode::I32X4ExtmulHighI16X8S: + case Opcode::I32X4ExtmulLowI16X8U: + case Opcode::I32X4ExtmulHighI16X8U: + case Opcode::I64X2ExtmulLowI32X4S: + case Opcode::I64X2ExtmulHighI32X4S: + case Opcode::I64X2ExtmulLowI32X4U: + case Opcode::I64X2ExtmulHighI32X4U: + case Opcode::I16X8Q15mulrSatS: + case Opcode::I32X4DotI16X8S: + case Opcode::I8X16RelaxedSwizzle: + case Opcode::F32X4RelaxedMin: + case Opcode::F32X4RelaxedMax: + case Opcode::F64X2RelaxedMin: + case Opcode::F64X2RelaxedMax: + case Opcode::I16X8RelaxedQ15mulrS: + case Opcode::I16X8DotI8X16I7X16S: + CALLBACK(OnBinaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32Eq: + case Opcode::I32Ne: + case Opcode::I32LtS: + case Opcode::I32LeS: + case Opcode::I32LtU: + case Opcode::I32LeU: + case Opcode::I32GtS: + case Opcode::I32GeS: + case Opcode::I32GtU: + case Opcode::I32GeU: + case Opcode::I64Eq: + case Opcode::I64Ne: + case Opcode::I64LtS: + case Opcode::I64LeS: + case Opcode::I64LtU: + case Opcode::I64LeU: + case Opcode::I64GtS: + case Opcode::I64GeS: + case Opcode::I64GtU: + case Opcode::I64GeU: + case Opcode::F32Eq: + case Opcode::F32Ne: + case Opcode::F32Lt: + case Opcode::F32Le: + case Opcode::F32Gt: + case Opcode::F32Ge: + case Opcode::F64Eq: + case Opcode::F64Ne: + case Opcode::F64Lt: + case Opcode::F64Le: + case Opcode::F64Gt: + case Opcode::F64Ge: + case Opcode::I8X16Eq: + case Opcode::I16X8Eq: + case Opcode::I32X4Eq: + case Opcode::I64X2Eq: + case Opcode::F32X4Eq: + case Opcode::F64X2Eq: + case Opcode::I8X16Ne: + case Opcode::I16X8Ne: + case Opcode::I32X4Ne: + case Opcode::I64X2Ne: + case Opcode::F32X4Ne: + case Opcode::F64X2Ne: + case Opcode::I8X16LtS: + case Opcode::I8X16LtU: + case Opcode::I16X8LtS: + case Opcode::I16X8LtU: + case Opcode::I32X4LtS: + case Opcode::I32X4LtU: + case Opcode::I64X2LtS: + case Opcode::F32X4Lt: + case Opcode::F64X2Lt: + case Opcode::I8X16LeS: + case Opcode::I8X16LeU: + case Opcode::I16X8LeS: + case Opcode::I16X8LeU: + case Opcode::I32X4LeS: + case Opcode::I32X4LeU: + case Opcode::I64X2LeS: + case Opcode::F32X4Le: + case Opcode::F64X2Le: + case Opcode::I8X16GtS: + case Opcode::I8X16GtU: + case Opcode::I16X8GtS: + case Opcode::I16X8GtU: + case Opcode::I32X4GtS: + case Opcode::I32X4GtU: + case Opcode::I64X2GtS: + case Opcode::F32X4Gt: + case Opcode::F64X2Gt: + case Opcode::I8X16GeS: + case Opcode::I8X16GeU: + case Opcode::I16X8GeS: + case Opcode::I16X8GeU: + case Opcode::I32X4GeS: + case Opcode::I32X4GeU: + case Opcode::I64X2GeS: + case Opcode::F32X4Ge: + case Opcode::F64X2Ge: + CALLBACK(OnCompareExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32Clz: + case Opcode::I32Ctz: + case Opcode::I32Popcnt: + case Opcode::I64Clz: + case Opcode::I64Ctz: + case Opcode::I64Popcnt: + case Opcode::F32Abs: + case Opcode::F32Neg: + case Opcode::F32Ceil: + case Opcode::F32Floor: + case Opcode::F32Trunc: + case Opcode::F32Nearest: + case Opcode::F32Sqrt: + case Opcode::F64Abs: + case Opcode::F64Neg: + case Opcode::F64Ceil: + case Opcode::F64Floor: + case Opcode::F64Trunc: + case Opcode::F64Nearest: + case Opcode::F64Sqrt: + case Opcode::I8X16Splat: + case Opcode::I16X8Splat: + case Opcode::I32X4Splat: + case Opcode::I64X2Splat: + case Opcode::F32X4Splat: + case Opcode::F64X2Splat: + case Opcode::I8X16Neg: + case Opcode::I16X8Neg: + case Opcode::I32X4Neg: + case Opcode::I64X2Neg: + case Opcode::V128Not: + case Opcode::V128AnyTrue: + case Opcode::I8X16Bitmask: + case Opcode::I16X8Bitmask: + case Opcode::I32X4Bitmask: + case Opcode::I64X2Bitmask: + case Opcode::I8X16AllTrue: + case Opcode::I16X8AllTrue: + case Opcode::I32X4AllTrue: + case Opcode::I64X2AllTrue: + case Opcode::F32X4Ceil: + case Opcode::F64X2Ceil: + case Opcode::F32X4Floor: + case Opcode::F64X2Floor: + case Opcode::F32X4Trunc: + case Opcode::F64X2Trunc: + case Opcode::F32X4Nearest: + case Opcode::F64X2Nearest: + case Opcode::F32X4Neg: + case Opcode::F64X2Neg: + case Opcode::F32X4Abs: + case Opcode::F64X2Abs: + case Opcode::F32X4Sqrt: + case Opcode::F64X2Sqrt: + case Opcode::I16X8ExtendLowI8X16S: + case Opcode::I16X8ExtendHighI8X16S: + case Opcode::I16X8ExtendLowI8X16U: + case Opcode::I16X8ExtendHighI8X16U: + case Opcode::I32X4ExtendLowI16X8S: + case Opcode::I32X4ExtendHighI16X8S: + case Opcode::I32X4ExtendLowI16X8U: + case Opcode::I32X4ExtendHighI16X8U: + case Opcode::I64X2ExtendLowI32X4S: + case Opcode::I64X2ExtendHighI32X4S: + case Opcode::I64X2ExtendLowI32X4U: + case Opcode::I64X2ExtendHighI32X4U: + case Opcode::I8X16Abs: + case Opcode::I16X8Abs: + case Opcode::I32X4Abs: + case Opcode::I64X2Abs: + case Opcode::I8X16Popcnt: + case Opcode::I16X8ExtaddPairwiseI8X16S: + case Opcode::I16X8ExtaddPairwiseI8X16U: + case Opcode::I32X4ExtaddPairwiseI16X8S: + case Opcode::I32X4ExtaddPairwiseI16X8U: + case Opcode::I32X4RelaxedTruncF32X4S: + case Opcode::I32X4RelaxedTruncF32X4U: + case Opcode::I32X4RelaxedTruncF64X2SZero: + case Opcode::I32X4RelaxedTruncF64X2UZero: + CALLBACK(OnUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::V128BitSelect: + case Opcode::F32X4RelaxedMadd: + case Opcode::F32X4RelaxedNmadd: + case Opcode::F64X2RelaxedMadd: + case Opcode::F64X2RelaxedNmadd: + case Opcode::I8X16RelaxedLaneSelect: + case Opcode::I16X8RelaxedLaneSelect: + case Opcode::I32X4RelaxedLaneSelect: + case Opcode::I64X2RelaxedLaneSelect: + case Opcode::I32X4DotI8X16I7X16AddS: + CALLBACK(OnTernaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: { + uint8_t lane_val; + CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); + CALLBACK(OnSimdLaneOpExpr, opcode, lane_val); + CALLBACK(OnOpcodeUint64, lane_val); + break; + } + + case Opcode::I8X16Shuffle: { + v128 value; + CHECK_RESULT(ReadV128(&value, "Lane idx [16]")); + CALLBACK(OnSimdShuffleOpExpr, opcode, value); + CALLBACK(OnOpcodeV128, value); + break; + } + + case Opcode::V128Load8Splat: + case Opcode::V128Load16Splat: + case Opcode::V128Load32Splat: + case Opcode::V128Load64Splat: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadSplatExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + case Opcode::V128Load8Lane: + case Opcode::V128Load16Lane: + case Opcode::V128Load32Lane: + case Opcode::V128Load64Lane: { + Address alignment_log2; + Index memidx; + Address offset; + uint8_t lane_val; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset", &lane_val)); + CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset, + lane_val); + CHECK_RESULT( + CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val)); + break; + } + case Opcode::V128Store8Lane: + case Opcode::V128Store16Lane: + case Opcode::V128Store32Lane: + case Opcode::V128Store64Lane: { + Address alignment_log2; + Index memidx; + Address offset; + uint8_t lane_val; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset", &lane_val)); + CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset, + lane_val); + CHECK_RESULT( + CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val)); + break; + } + case Opcode::V128Load32Zero: + case Opcode::V128Load64Zero: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnLoadZeroExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + case Opcode::I32TruncF32S: + case Opcode::I32TruncF64S: + case Opcode::I32TruncF32U: + case Opcode::I32TruncF64U: + case Opcode::I32WrapI64: + case Opcode::I64TruncF32S: + case Opcode::I64TruncF64S: + case Opcode::I64TruncF32U: + case Opcode::I64TruncF64U: + case Opcode::I64ExtendI32S: + case Opcode::I64ExtendI32U: + case Opcode::F32ConvertI32S: + case Opcode::F32ConvertI32U: + case Opcode::F32ConvertI64S: + case Opcode::F32ConvertI64U: + case Opcode::F32DemoteF64: + case Opcode::F32ReinterpretI32: + case Opcode::F64ConvertI32S: + case Opcode::F64ConvertI32U: + case Opcode::F64ConvertI64S: + case Opcode::F64ConvertI64U: + case Opcode::F64PromoteF32: + case Opcode::F64ReinterpretI64: + case Opcode::I32ReinterpretF32: + case Opcode::I64ReinterpretF64: + case Opcode::I32Eqz: + case Opcode::I64Eqz: + case Opcode::F32X4ConvertI32X4S: + case Opcode::F32X4ConvertI32X4U: + case Opcode::I32X4TruncSatF32X4S: + case Opcode::I32X4TruncSatF32X4U: + case Opcode::F32X4DemoteF64X2Zero: + case Opcode::F64X2PromoteLowF32X4: + case Opcode::I32X4TruncSatF64X2SZero: + case Opcode::I32X4TruncSatF64X2UZero: + case Opcode::F64X2ConvertLowI32X4S: + case Opcode::F64X2ConvertLowI32X4U: + CALLBACK(OnConvertExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::Try: { + Type sig_type; + CHECK_RESULT(ReadType(&sig_type, "try signature type")); + ERROR_UNLESS(IsBlockType(sig_type), + "expected valid block signature type"); + CALLBACK(OnTryExpr, sig_type); + CALLBACK(OnOpcodeBlockSig, sig_type); + break; + } + + case Opcode::Catch: { + Index index; + CHECK_RESULT(ReadIndex(&index, "tag index")); + CALLBACK(OnCatchExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::CatchAll: { + CALLBACK(OnCatchAllExpr); + CALLBACK(OnOpcodeBare); + break; + } + + case Opcode::Delegate: { + Index index; + CHECK_RESULT(ReadIndex(&index, "depth")); + CALLBACK(OnDelegateExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::Rethrow: { + Index depth; + CHECK_RESULT(ReadIndex(&depth, "catch depth")); + CALLBACK(OnRethrowExpr, depth); + CALLBACK(OnOpcodeIndex, depth); + break; + } + + case Opcode::Throw: { + Index index; + CHECK_RESULT(ReadIndex(&index, "tag index")); + CALLBACK(OnThrowExpr, index); + CALLBACK(OnOpcodeIndex, index); + break; + } + + case Opcode::I32Extend8S: + case Opcode::I32Extend16S: + case Opcode::I64Extend8S: + case Opcode::I64Extend16S: + case Opcode::I64Extend32S: + CALLBACK(OnUnaryExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::I32TruncSatF32S: + case Opcode::I32TruncSatF32U: + case Opcode::I32TruncSatF64S: + case Opcode::I32TruncSatF64U: + case Opcode::I64TruncSatF32S: + case Opcode::I64TruncSatF32U: + case Opcode::I64TruncSatF64S: + case Opcode::I64TruncSatF64U: + CALLBACK(OnConvertExpr, opcode); + CALLBACK0(OnOpcodeBare); + break; + + case Opcode::MemoryAtomicNotify: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "notify alignment", "notify memidx", + "notify offset")); + CALLBACK(OnAtomicNotifyExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::MemoryAtomicWait32: + case Opcode::MemoryAtomicWait64: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "wait alignment", "wait memidx", + "wait offset")); + CALLBACK(OnAtomicWaitExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::AtomicFence: { + uint8_t consistency_model; + CHECK_RESULT(ReadU8(&consistency_model, "consistency model")); + ERROR_UNLESS(consistency_model == 0, + "atomic.fence consistency model must be 0"); + CALLBACK(OnAtomicFenceExpr, consistency_model); + CALLBACK(OnOpcodeUint32, consistency_model); + break; + } + + case Opcode::I32AtomicLoad8U: + case Opcode::I32AtomicLoad16U: + case Opcode::I64AtomicLoad8U: + case Opcode::I64AtomicLoad16U: + case Opcode::I64AtomicLoad32U: + case Opcode::I32AtomicLoad: + case Opcode::I64AtomicLoad: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "load alignment", "load memidx", + "load offset")); + CALLBACK(OnAtomicLoadExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicStore8: + case Opcode::I32AtomicStore16: + case Opcode::I64AtomicStore8: + case Opcode::I64AtomicStore16: + case Opcode::I64AtomicStore32: + case Opcode::I32AtomicStore: + case Opcode::I64AtomicStore: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "store alignment", "store memidx", + "store offset")); + CALLBACK(OnAtomicStoreExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicRmwAdd: + case Opcode::I64AtomicRmwAdd: + case Opcode::I32AtomicRmw8AddU: + case Opcode::I32AtomicRmw16AddU: + case Opcode::I64AtomicRmw8AddU: + case Opcode::I64AtomicRmw16AddU: + case Opcode::I64AtomicRmw32AddU: + case Opcode::I32AtomicRmwSub: + case Opcode::I64AtomicRmwSub: + case Opcode::I32AtomicRmw8SubU: + case Opcode::I32AtomicRmw16SubU: + case Opcode::I64AtomicRmw8SubU: + case Opcode::I64AtomicRmw16SubU: + case Opcode::I64AtomicRmw32SubU: + case Opcode::I32AtomicRmwAnd: + case Opcode::I64AtomicRmwAnd: + case Opcode::I32AtomicRmw8AndU: + case Opcode::I32AtomicRmw16AndU: + case Opcode::I64AtomicRmw8AndU: + case Opcode::I64AtomicRmw16AndU: + case Opcode::I64AtomicRmw32AndU: + case Opcode::I32AtomicRmwOr: + case Opcode::I64AtomicRmwOr: + case Opcode::I32AtomicRmw8OrU: + case Opcode::I32AtomicRmw16OrU: + case Opcode::I64AtomicRmw8OrU: + case Opcode::I64AtomicRmw16OrU: + case Opcode::I64AtomicRmw32OrU: + case Opcode::I32AtomicRmwXor: + case Opcode::I64AtomicRmwXor: + case Opcode::I32AtomicRmw8XorU: + case Opcode::I32AtomicRmw16XorU: + case Opcode::I64AtomicRmw8XorU: + case Opcode::I64AtomicRmw16XorU: + case Opcode::I64AtomicRmw32XorU: + case Opcode::I32AtomicRmwXchg: + case Opcode::I64AtomicRmwXchg: + case Opcode::I32AtomicRmw8XchgU: + case Opcode::I32AtomicRmw16XchgU: + case Opcode::I64AtomicRmw8XchgU: + case Opcode::I64AtomicRmw16XchgU: + case Opcode::I64AtomicRmw32XchgU: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "memory alignment", "memory memidx", + "memory offset")); + CALLBACK(OnAtomicRmwExpr, opcode, memidx, alignment_log2, offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::I32AtomicRmwCmpxchg: + case Opcode::I64AtomicRmwCmpxchg: + case Opcode::I32AtomicRmw8CmpxchgU: + case Opcode::I32AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw8CmpxchgU: + case Opcode::I64AtomicRmw16CmpxchgU: + case Opcode::I64AtomicRmw32CmpxchgU: { + Address alignment_log2; + Index memidx; + Address offset; + CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset, + "memory alignment", "memory memidx", + "memory offset")); + CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, memidx, alignment_log2, + offset); + CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset)); + break; + } + + case Opcode::TableInit: { + Index segment; + CHECK_RESULT(ReadIndex(&segment, "elem segment index")); + Index table_index; + CHECK_RESULT(ReadIndex(&table_index, "reserved table index")); + CALLBACK(OnTableInitExpr, segment, table_index); + CALLBACK(OnOpcodeUint32Uint32, segment, table_index); + break; + } + + case Opcode::MemoryInit: { + Index segment; + ERROR_IF(data_count_ == kInvalidIndex, + "memory.init requires data count section"); + CHECK_RESULT(ReadIndex(&segment, "elem segment index")); + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx")); + } + CALLBACK(OnMemoryInitExpr, segment, memidx); + CALLBACK(OnOpcodeUint32Uint32, segment, memidx); + break; + } + + case Opcode::DataDrop: + ERROR_IF(data_count_ == kInvalidIndex, + "data.drop requires data count section"); + [[fallthrough]]; + case Opcode::ElemDrop: { + Index segment; + CHECK_RESULT(ReadIndex(&segment, "segment index")); + if (opcode == Opcode::DataDrop) { + CALLBACK(OnDataDropExpr, segment); + } else { + CALLBACK(OnElemDropExpr, segment); + } + CALLBACK(OnOpcodeUint32, segment); + break; + } + + case Opcode::MemoryFill: { + Index memidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved")); + ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx")); + } + CALLBACK(OnMemoryFillExpr, memidx); + CALLBACK(OnOpcodeUint32, memidx); + break; + } + + case Opcode::MemoryCopy: { + Index srcmemidx = 0; + Index destmemidx = 0; + if (!options_.features.multi_memory_enabled()) { + uint8_t reserved; + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + CHECK_RESULT(ReadU8(&reserved, "reserved memory index")); + ERROR_UNLESS(reserved == 0, "reserved value must be 0"); + } else { + CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx")); + CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex")); + } + CALLBACK(OnMemoryCopyExpr, srcmemidx, destmemidx); + CALLBACK(OnOpcodeUint32Uint32, srcmemidx, destmemidx); + break; + } + + case Opcode::TableCopy: { + Index table_dst; + Index table_src; + CHECK_RESULT(ReadIndex(&table_dst, "reserved table index")); + CHECK_RESULT(ReadIndex(&table_src, "table src")); + CALLBACK(OnTableCopyExpr, table_dst, table_src); + CALLBACK(OnOpcodeUint32Uint32, table_dst, table_src); + break; + } + + case Opcode::TableGet: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableGetExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableSet: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableSetExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableGrow: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableGrowExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableSize: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableSizeExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::TableFill: { + Index table; + CHECK_RESULT(ReadIndex(&table, "table index")); + CALLBACK(OnTableFillExpr, table); + CALLBACK(OnOpcodeUint32, table); + break; + } + + case Opcode::RefFunc: { + Index func; + CHECK_RESULT(ReadIndex(&func, "func index")); + CALLBACK(OnRefFuncExpr, func); + CALLBACK(OnOpcodeUint32, func); + break; + } + + case Opcode::RefNull: { + Type type; + CHECK_RESULT(ReadRefType(&type, "ref.null type")); + CALLBACK(OnRefNullExpr, type); + CALLBACK(OnOpcodeType, type); + break; + } + + case Opcode::RefIsNull: + CALLBACK(OnRefIsNullExpr); + CALLBACK(OnOpcodeBare); + break; + + case Opcode::CallRef: + CALLBACK(OnCallRefExpr); + CALLBACK(OnOpcodeBare); + break; + + default: + return ReportUnexpectedOpcode(opcode); + } + } + return Result::Ok; +} + +Result BinaryReader::ReadNameSection(Offset section_size) { + CALLBACK(BeginNamesSection, section_size); + Index i = 0; + uint32_t previous_subsection_type = 0; + while (state_.offset < read_end_) { + uint32_t name_type; + Offset subsection_size; + CHECK_RESULT(ReadU32Leb128(&name_type, "name type")); + if (i != 0) { + ERROR_UNLESS(name_type != previous_subsection_type, + "duplicate sub-section"); + ERROR_UNLESS(name_type >= previous_subsection_type, + "out-of-order sub-section"); + } + previous_subsection_type = name_type; + CHECK_RESULT(ReadOffset(&subsection_size, "subsection size")); + size_t subsection_end = state_.offset + subsection_size; + ERROR_UNLESS(subsection_end <= read_end_, + "invalid sub-section size: extends past end"); + ReadEndRestoreGuard guard(this); + read_end_ = subsection_end; + + NameSectionSubsection type = static_cast(name_type); + if (type <= NameSectionSubsection::Last) { + CALLBACK(OnNameSubsection, i, type, subsection_size); + } + + switch (type) { + case NameSectionSubsection::Module: + CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + std::string_view name; + CHECK_RESULT(ReadStr(&name, "module name")); + CALLBACK(OnModuleName, name); + } + break; + case NameSectionSubsection::Function: + CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + Index num_names; + CHECK_RESULT(ReadCount(&num_names, "name count")); + CALLBACK(OnFunctionNamesCount, num_names); + Index last_function_index = kInvalidIndex; + + for (Index j = 0; j < num_names; ++j) { + Index function_index; + std::string_view function_name; + + CHECK_RESULT(ReadIndex(&function_index, "function index")); + ERROR_UNLESS(function_index != last_function_index, + "duplicate function name: %u", function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "function index out of order: %u", function_index); + last_function_index = function_index; + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %" PRIindex, function_index); + CHECK_RESULT(ReadStr(&function_name, "function name")); + CALLBACK(OnFunctionName, function_index, function_name); + } + } + break; + case NameSectionSubsection::Local: + CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size); + if (subsection_size) { + Index num_funcs; + CHECK_RESULT(ReadCount(&num_funcs, "function count")); + CALLBACK(OnLocalNameFunctionCount, num_funcs); + Index last_function_index = kInvalidIndex; + for (Index j = 0; j < num_funcs; ++j) { + Index function_index; + CHECK_RESULT(ReadIndex(&function_index, "function index")); + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %u", function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "locals function index out of order: %u", + function_index); + last_function_index = function_index; + Index num_locals; + CHECK_RESULT(ReadCount(&num_locals, "local count")); + CALLBACK(OnLocalNameLocalCount, function_index, num_locals); + Index last_local_index = kInvalidIndex; + for (Index k = 0; k < num_locals; ++k) { + Index local_index; + std::string_view local_name; + + CHECK_RESULT(ReadIndex(&local_index, "named index")); + ERROR_UNLESS(local_index != last_local_index, + "duplicate local index: %u", local_index); + ERROR_UNLESS(last_local_index == kInvalidIndex || + local_index > last_local_index, + "local index out of order: %u", local_index); + last_local_index = local_index; + CHECK_RESULT(ReadStr(&local_name, "name")); + CALLBACK(OnLocalName, function_index, local_index, local_name); + } + } + } + break; + case NameSectionSubsection::Label: + // TODO(sbc): Implement label names. These are slightly more complicated + // since they refer to offsets in the code section / instruction stream. + state_.offset = subsection_end; + break; + case NameSectionSubsection::Type: + case NameSectionSubsection::Table: + case NameSectionSubsection::Memory: + case NameSectionSubsection::Global: + case NameSectionSubsection::ElemSegment: + case NameSectionSubsection::DataSegment: + case NameSectionSubsection::Tag: + if (subsection_size) { + Index num_names; + CHECK_RESULT(ReadCount(&num_names, "name count")); + CALLBACK(OnNameCount, num_names); + for (Index j = 0; j < num_names; ++j) { + Index index; + std::string_view name; + + CHECK_RESULT(ReadIndex(&index, "index")); + CHECK_RESULT(ReadStr(&name, "name")); + CALLBACK(OnNameEntry, type, index, name); + } + } + state_.offset = subsection_end; + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ++i; + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + CALLBACK0(EndNamesSection); + return Result::Ok; +} + +Result BinaryReader::ReadRelocSection(Offset section_size) { + CALLBACK(BeginRelocSection, section_size); + uint32_t section_index; + CHECK_RESULT(ReadU32Leb128(§ion_index, "section index")); + Index num_relocs; + CHECK_RESULT(ReadCount(&num_relocs, "relocation count")); + CALLBACK(OnRelocCount, num_relocs, section_index); + for (Index i = 0; i < num_relocs; ++i) { + Offset offset; + Index index; + uint32_t reloc_type, addend = 0; + CHECK_RESULT(ReadU32Leb128(&reloc_type, "relocation type")); + CHECK_RESULT(ReadOffset(&offset, "offset")); + CHECK_RESULT(ReadIndex(&index, "index")); + RelocType type = static_cast(reloc_type); + switch (type) { + case RelocType::MemoryAddressLEB: + case RelocType::MemoryAddressLEB64: + case RelocType::MemoryAddressSLEB: + case RelocType::MemoryAddressSLEB64: + case RelocType::MemoryAddressRelSLEB: + case RelocType::MemoryAddressRelSLEB64: + case RelocType::MemoryAddressI32: + case RelocType::MemoryAddressI64: + case RelocType::FunctionOffsetI32: + case RelocType::SectionOffsetI32: + case RelocType::MemoryAddressTLSSLEB: + case RelocType::MemoryAddressTLSI32: + CHECK_RESULT(ReadS32Leb128(&addend, "addend")); + break; + + case RelocType::FuncIndexLEB: + case RelocType::TableIndexSLEB: + case RelocType::TableIndexSLEB64: + case RelocType::TableIndexI32: + case RelocType::TableIndexI64: + case RelocType::TypeIndexLEB: + case RelocType::GlobalIndexLEB: + case RelocType::GlobalIndexI32: + case RelocType::TagIndexLEB: + case RelocType::TableIndexRelSLEB: + case RelocType::TableNumberLEB: + break; + + default: + PrintError("unknown reloc type: %s", GetRelocTypeName(type)); + return Result::Error; + } + CALLBACK(OnReloc, type, offset, index, addend); + } + CALLBACK0(EndRelocSection); + return Result::Ok; +} + +Result BinaryReader::ReadDylink0Section(Offset section_size) { + CALLBACK(BeginDylinkSection, section_size); + + while (state_.offset < read_end_) { + uint32_t dylink_type; + Offset subsection_size; + CHECK_RESULT(ReadU32Leb128(&dylink_type, "type")); + CHECK_RESULT(ReadOffset(&subsection_size, "subsection size")); + size_t subsection_end = state_.offset + subsection_size; + ERROR_UNLESS(subsection_end <= read_end_, + "invalid sub-section size: extends past end"); + ReadEndRestoreGuard guard(this); + read_end_ = subsection_end; + + uint32_t count; + switch (static_cast(dylink_type)) { + case DylinkEntryType::MemInfo: { + uint32_t mem_size; + uint32_t mem_align; + uint32_t table_size; + uint32_t table_align; + + CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size")); + CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align")); + CHECK_RESULT(ReadU32Leb128(&table_size, "table_size")); + CHECK_RESULT(ReadU32Leb128(&table_align, "table_align")); + CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align); + break; + } + case DylinkEntryType::Needed: + CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs")); + CALLBACK(OnDylinkNeededCount, count); + while (count--) { + std::string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + break; + case DylinkEntryType::ImportInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnDylinkImportCount, count); + for (Index i = 0; i < count; ++i) { + uint32_t flags = 0; + std::string_view module; + std::string_view field; + CHECK_RESULT(ReadStr(&module, "module")); + CHECK_RESULT(ReadStr(&field, "field")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CALLBACK(OnDylinkImport, module, field, flags); + } + break; + case DylinkEntryType::ExportInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnDylinkExportCount, count); + for (Index i = 0; i < count; ++i) { + uint32_t flags = 0; + std::string_view name; + CHECK_RESULT(ReadStr(&name, "name")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CALLBACK(OnDylinkExport, name, flags); + } + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + + CALLBACK0(EndDylinkSection); + return Result::Ok; +} + +Result BinaryReader::ReadDylinkSection(Offset section_size) { + CALLBACK(BeginDylinkSection, section_size); + uint32_t mem_size; + uint32_t mem_align; + uint32_t table_size; + uint32_t table_align; + + CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size")); + CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align")); + CHECK_RESULT(ReadU32Leb128(&table_size, "table_size")); + CHECK_RESULT(ReadU32Leb128(&table_align, "table_align")); + CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align); + + uint32_t count; + CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs")); + CALLBACK(OnDylinkNeededCount, count); + while (count--) { + std::string_view so_name; + CHECK_RESULT(ReadStr(&so_name, "dylib so_name")); + CALLBACK(OnDylinkNeeded, so_name); + } + + CALLBACK0(EndDylinkSection); + return Result::Ok; +} + +Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) { + CALLBACK(BeginTargetFeaturesSection, section_size); + uint32_t count; + CHECK_RESULT(ReadU32Leb128(&count, "sym count")); + CALLBACK(OnFeatureCount, count); + while (count--) { + uint8_t prefix; + std::string_view name; + CHECK_RESULT(ReadU8(&prefix, "prefix")); + CHECK_RESULT(ReadStr(&name, "feature name")); + CALLBACK(OnFeature, prefix, name); + } + CALLBACK0(EndTargetFeaturesSection); + return Result::Ok; +} + +Result BinaryReader::ReadLinkingSection(Offset section_size) { + CALLBACK(BeginLinkingSection, section_size); + uint32_t version; + CHECK_RESULT(ReadU32Leb128(&version, "version")); + ERROR_UNLESS(version == 2, "invalid linking metadata version: %u", version); + while (state_.offset < read_end_) { + uint32_t linking_type; + Offset subsection_size; + CHECK_RESULT(ReadU32Leb128(&linking_type, "type")); + CHECK_RESULT(ReadOffset(&subsection_size, "subsection size")); + size_t subsection_end = state_.offset + subsection_size; + ERROR_UNLESS(subsection_end <= read_end_, + "invalid sub-section size: extends past end"); + ReadEndRestoreGuard guard(this); + read_end_ = subsection_end; + + uint32_t count; + switch (static_cast(linking_type)) { + case LinkingEntryType::SymbolTable: + CHECK_RESULT(ReadU32Leb128(&count, "sym count")); + CALLBACK(OnSymbolCount, count); + for (Index i = 0; i < count; ++i) { + std::string_view name; + uint32_t flags = 0; + uint32_t kind = 0; + CHECK_RESULT(ReadU32Leb128(&kind, "sym type")); + CHECK_RESULT(ReadU32Leb128(&flags, "sym flags")); + SymbolType sym_type = static_cast(kind); + switch (sym_type) { + case SymbolType::Function: + case SymbolType::Global: + case SymbolType::Tag: + case SymbolType::Table: { + uint32_t index = 0; + CHECK_RESULT(ReadU32Leb128(&index, "index")); + if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0 || + (flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) != 0) + CHECK_RESULT(ReadStr(&name, "symbol name")); + switch (sym_type) { + case SymbolType::Function: + CALLBACK(OnFunctionSymbol, i, flags, name, index); + break; + case SymbolType::Global: + CALLBACK(OnGlobalSymbol, i, flags, name, index); + break; + case SymbolType::Tag: + CALLBACK(OnTagSymbol, i, flags, name, index); + break; + case SymbolType::Table: + CALLBACK(OnTableSymbol, i, flags, name, index); + break; + default: + WABT_UNREACHABLE; + } + break; + } + case SymbolType::Data: { + uint32_t segment = 0; + uint32_t offset = 0; + uint32_t size = 0; + CHECK_RESULT(ReadStr(&name, "symbol name")); + if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0) { + CHECK_RESULT(ReadU32Leb128(&segment, "segment")); + CHECK_RESULT(ReadU32Leb128(&offset, "offset")); + CHECK_RESULT(ReadU32Leb128(&size, "size")); + } + CALLBACK(OnDataSymbol, i, flags, name, segment, offset, size); + break; + } + case SymbolType::Section: { + uint32_t index = 0; + CHECK_RESULT(ReadU32Leb128(&index, "index")); + CALLBACK(OnSectionSymbol, i, flags, index); + break; + } + } + } + break; + case LinkingEntryType::SegmentInfo: + CHECK_RESULT(ReadU32Leb128(&count, "info count")); + CALLBACK(OnSegmentInfoCount, count); + for (Index i = 0; i < count; i++) { + std::string_view name; + Address alignment_log2; + uint32_t flags; + CHECK_RESULT(ReadStr(&name, "segment name")); + CHECK_RESULT(ReadAlignment(&alignment_log2, "segment alignment")); + CHECK_RESULT(ReadU32Leb128(&flags, "segment flags")); + CALLBACK(OnSegmentInfo, i, name, alignment_log2, flags); + } + break; + case LinkingEntryType::InitFunctions: + CHECK_RESULT(ReadU32Leb128(&count, "info count")); + CALLBACK(OnInitFunctionCount, count); + while (count--) { + uint32_t priority; + uint32_t symbol; + CHECK_RESULT(ReadU32Leb128(&priority, "priority")); + CHECK_RESULT(ReadU32Leb128(&symbol, "symbol index")); + CALLBACK(OnInitFunction, priority, symbol); + } + break; + case LinkingEntryType::ComdatInfo: + CHECK_RESULT(ReadU32Leb128(&count, "count")); + CALLBACK(OnComdatCount, count); + while (count--) { + uint32_t flags; + uint32_t entry_count; + std::string_view name; + CHECK_RESULT(ReadStr(&name, "comdat name")); + CHECK_RESULT(ReadU32Leb128(&flags, "flags")); + CHECK_RESULT(ReadU32Leb128(&entry_count, "entry count")); + CALLBACK(OnComdatBegin, name, flags, entry_count); + while (entry_count--) { + uint32_t kind; + uint32_t index; + CHECK_RESULT(ReadU32Leb128(&kind, "kind")); + CHECK_RESULT(ReadU32Leb128(&index, "index")); + ComdatType comdat_type = static_cast(kind); + CALLBACK(OnComdatEntry, comdat_type, index); + } + } + break; + default: + // Unknown subsection, skip it. + state_.offset = subsection_end; + break; + } + ERROR_UNLESS(state_.offset == subsection_end, + "unfinished sub-section (expected end: 0x%" PRIzx ")", + subsection_end); + } + CALLBACK0(EndLinkingSection); + return Result::Ok; +} + +Result BinaryReader::ReadTagType(Index* out_sig_index) { + uint8_t attribute; + CHECK_RESULT(ReadU8(&attribute, "tag attribute")); + ERROR_UNLESS(attribute == 0, "tag attribute must be 0"); + CHECK_RESULT(ReadIndex(out_sig_index, "tag signature index")); + return Result::Ok; +} + +Result BinaryReader::ReadTagSection(Offset section_size) { + CALLBACK(BeginTagSection, section_size); + Index num_tags; + CHECK_RESULT(ReadCount(&num_tags, "tag count")); + CALLBACK(OnTagCount, num_tags); + + for (Index i = 0; i < num_tags; ++i) { + Index tag_index = num_tag_imports_ + i; + Index sig_index; + CHECK_RESULT(ReadTagType(&sig_index)); + CALLBACK(OnTagType, tag_index, sig_index); + } + + CALLBACK(EndTagSection); + return Result::Ok; +} + +Result BinaryReader::ReadCodeMetadataSection(std::string_view name, + Offset section_size) { + CALLBACK(BeginCodeMetadataSection, name, section_size); + + Index num_functions; + CHECK_RESULT(ReadCount(&num_functions, "function count")); + CALLBACK(OnCodeMetadataFuncCount, num_functions); + + Index last_function_index = kInvalidIndex; + for (Index i = 0; i < num_functions; ++i) { + Index function_index; + CHECK_RESULT(ReadCount(&function_index, "function index")); + ERROR_UNLESS(function_index >= num_func_imports_, + "function import can't have metadata (got %" PRIindex ")", + function_index); + ERROR_UNLESS(function_index < NumTotalFuncs(), + "invalid function index: %" PRIindex, function_index); + ERROR_UNLESS(function_index != last_function_index, + "duplicate function index: %" PRIindex, function_index); + ERROR_UNLESS(last_function_index == kInvalidIndex || + function_index > last_function_index, + "function index out of order: %" PRIindex, function_index); + last_function_index = function_index; + + Index num_metadata; + CHECK_RESULT(ReadCount(&num_metadata, "metadata instances count")); + + CALLBACK(OnCodeMetadataCount, function_index, num_metadata); + + Offset last_code_offset = kInvalidOffset; + for (Index j = 0; j < num_metadata; ++j) { + Offset code_offset; + CHECK_RESULT(ReadOffset(&code_offset, "code offset")); + ERROR_UNLESS(code_offset != last_code_offset, + "duplicate code offset: %" PRIzx, code_offset); + ERROR_UNLESS( + last_code_offset == kInvalidOffset || code_offset > last_code_offset, + "code offset out of order: %" PRIzx, code_offset); + last_code_offset = code_offset; + + Address data_size; + const void* data; + CHECK_RESULT(ReadBytes(&data, &data_size, "instance data")); + CALLBACK(OnCodeMetadata, code_offset, data, data_size); + } + } + + CALLBACK(EndCodeMetadataSection); + return Result::Ok; +} + +Result BinaryReader::ReadCustomSection(Index section_index, + Offset section_size) { + std::string_view section_name; + CHECK_RESULT(ReadStr(§ion_name, "section name")); + CALLBACK(BeginCustomSection, section_index, section_size, section_name); + ValueRestoreGuard guard(this); + reading_custom_section_ = true; + + if (options_.read_debug_names && section_name == WABT_BINARY_SECTION_NAME) { + CHECK_RESULT(ReadNameSection(section_size)); + did_read_names_section_ = true; + } else if (section_name == WABT_BINARY_SECTION_DYLINK0) { + CHECK_RESULT(ReadDylink0Section(section_size)); + } else if (section_name == WABT_BINARY_SECTION_DYLINK) { + CHECK_RESULT(ReadDylinkSection(section_size)); + } else if (section_name.rfind(WABT_BINARY_SECTION_RELOC, 0) == 0) { + // Reloc sections always begin with "reloc." + CHECK_RESULT(ReadRelocSection(section_size)); + } else if (section_name == WABT_BINARY_SECTION_TARGET_FEATURES) { + CHECK_RESULT(ReadTargetFeaturesSections(section_size)); + } else if (section_name == WABT_BINARY_SECTION_LINKING) { + CHECK_RESULT(ReadLinkingSection(section_size)); + } else if (options_.features.code_metadata_enabled() && + section_name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0) { + std::string_view metadata_name = section_name; + metadata_name.remove_prefix(sizeof(WABT_BINARY_SECTION_CODE_METADATA) - 1); + CHECK_RESULT(ReadCodeMetadataSection(metadata_name, section_size)); + } else { + // This is an unknown custom section, skip it. + state_.offset = read_end_; + } + CALLBACK0(EndCustomSection); + return Result::Ok; +} + +Result BinaryReader::ReadTypeSection(Offset section_size) { + CALLBACK(BeginTypeSection, section_size); + Index num_signatures; + CHECK_RESULT(ReadCount(&num_signatures, "type count")); + CALLBACK(OnTypeCount, num_signatures); + + for (Index i = 0; i < num_signatures; ++i) { + Type form; + if (options_.features.gc_enabled()) { + CHECK_RESULT(ReadType(&form, "type form")); + } else { + uint8_t type; + CHECK_RESULT(ReadU8(&type, "type form")); + ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type); + form = Type::Func; + } + + switch (form) { + case Type::Func: { + Index num_params; + CHECK_RESULT(ReadCount(&num_params, "function param count")); + + param_types_.resize(num_params); + + for (Index j = 0; j < num_params; ++j) { + Type param_type; + CHECK_RESULT(ReadType(¶m_type, "function param type")); + ERROR_UNLESS(IsConcreteType(param_type), + "expected valid param type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(param_type)); + param_types_[j] = param_type; + } + + Index num_results; + CHECK_RESULT(ReadCount(&num_results, "function result count")); + + result_types_.resize(num_results); + + for (Index j = 0; j < num_results; ++j) { + Type result_type; + CHECK_RESULT(ReadType(&result_type, "function result type")); + ERROR_UNLESS(IsConcreteType(result_type), + "expected valid result type (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(result_type)); + result_types_[j] = result_type; + } + + Type* param_types = num_params ? param_types_.data() : nullptr; + Type* result_types = num_results ? result_types_.data() : nullptr; + + CALLBACK(OnFuncType, i, num_params, param_types, num_results, + result_types); + break; + } + + case Type::Struct: { + ERROR_UNLESS(options_.features.gc_enabled(), + "invalid type form: struct not allowed"); + Index num_fields; + CHECK_RESULT(ReadCount(&num_fields, "field count")); + + fields_.resize(num_fields); + for (Index j = 0; j < num_fields; ++j) { + CHECK_RESULT(ReadField(&fields_[j])); + } + + CALLBACK(OnStructType, i, fields_.size(), fields_.data()); + break; + } + + case Type::Array: { + ERROR_UNLESS(options_.features.gc_enabled(), + "invalid type form: array not allowed"); + + TypeMut field; + CHECK_RESULT(ReadField(&field)); + CALLBACK(OnArrayType, i, field); + break; + }; + + default: + PrintError("unexpected type form (got " PRItypecode ")", + WABT_PRINTF_TYPE_CODE(form)); + return Result::Error; + } + } + CALLBACK0(EndTypeSection); + return Result::Ok; +} + +Result BinaryReader::ReadImportSection(Offset section_size) { + CALLBACK(BeginImportSection, section_size); + Index num_imports; + CHECK_RESULT(ReadCount(&num_imports, "import count")); + CALLBACK(OnImportCount, num_imports); + for (Index i = 0; i < num_imports; ++i) { + std::string_view module_name; + CHECK_RESULT(ReadStr(&module_name, "import module name")); + std::string_view field_name; + CHECK_RESULT(ReadStr(&field_name, "import field name")); + + uint8_t kind; + CHECK_RESULT(ReadU8(&kind, "import kind")); + CALLBACK(OnImport, i, static_cast(kind), module_name, + field_name); + switch (static_cast(kind)) { + case ExternalKind::Func: { + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "import signature index")); + CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_, + sig_index); + num_func_imports_++; + break; + } + + case ExternalKind::Table: { + Type elem_type; + Limits elem_limits; + CHECK_RESULT(ReadTable(&elem_type, &elem_limits)); + CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_, + elem_type, &elem_limits); + num_table_imports_++; + break; + } + + case ExternalKind::Memory: { + Limits page_limits; + CHECK_RESULT(ReadMemory(&page_limits)); + CALLBACK(OnImportMemory, i, module_name, field_name, + num_memory_imports_, &page_limits); + num_memory_imports_++; + break; + } + + case ExternalKind::Global: { + Type type; + bool mutable_; + CHECK_RESULT(ReadGlobalHeader(&type, &mutable_)); + CALLBACK(OnImportGlobal, i, module_name, field_name, + num_global_imports_, type, mutable_); + num_global_imports_++; + break; + } + + case ExternalKind::Tag: { + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid import tag kind: exceptions not allowed"); + Index sig_index; + CHECK_RESULT(ReadTagType(&sig_index)); + CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_, + sig_index); + num_tag_imports_++; + break; + } + + default: + PrintError("malformed import kind: %d", kind); + return Result::Error; + } + } + + CALLBACK0(EndImportSection); + return Result::Ok; +} + +Result BinaryReader::ReadFunctionSection(Offset section_size) { + CALLBACK(BeginFunctionSection, section_size); + CHECK_RESULT( + ReadCount(&num_function_signatures_, "function signature count")); + CALLBACK(OnFunctionCount, num_function_signatures_); + for (Index i = 0; i < num_function_signatures_; ++i) { + Index func_index = num_func_imports_ + i; + Index sig_index; + CHECK_RESULT(ReadIndex(&sig_index, "function signature index")); + CALLBACK(OnFunction, func_index, sig_index); + } + CALLBACK0(EndFunctionSection); + return Result::Ok; +} + +Result BinaryReader::ReadTableSection(Offset section_size) { + CALLBACK(BeginTableSection, section_size); + Index num_tables; + CHECK_RESULT(ReadCount(&num_tables, "table count")); + CALLBACK(OnTableCount, num_tables); + for (Index i = 0; i < num_tables; ++i) { + Index table_index = num_table_imports_ + i; + Type elem_type; + Limits elem_limits; + CHECK_RESULT(ReadTable(&elem_type, &elem_limits)); + CALLBACK(OnTable, table_index, elem_type, &elem_limits); + } + CALLBACK0(EndTableSection); + return Result::Ok; +} + +Result BinaryReader::ReadMemorySection(Offset section_size) { + CALLBACK(BeginMemorySection, section_size); + Index num_memories; + CHECK_RESULT(ReadCount(&num_memories, "memory count")); + CALLBACK(OnMemoryCount, num_memories); + for (Index i = 0; i < num_memories; ++i) { + Index memory_index = num_memory_imports_ + i; + Limits page_limits; + CHECK_RESULT(ReadMemory(&page_limits)); + CALLBACK(OnMemory, memory_index, &page_limits); + } + CALLBACK0(EndMemorySection); + return Result::Ok; +} + +Result BinaryReader::ReadGlobalSection(Offset section_size) { + CALLBACK(BeginGlobalSection, section_size); + Index num_globals; + CHECK_RESULT(ReadCount(&num_globals, "global count")); + CALLBACK(OnGlobalCount, num_globals); + for (Index i = 0; i < num_globals; ++i) { + Index global_index = num_global_imports_ + i; + Type global_type; + bool mutable_; + CHECK_RESULT(ReadGlobalHeader(&global_type, &mutable_)); + CALLBACK(BeginGlobal, global_index, global_type, mutable_); + CALLBACK(BeginGlobalInitExpr, global_index); + CHECK_RESULT(ReadInitExpr(global_index)); + CALLBACK(EndGlobalInitExpr, global_index); + CALLBACK(EndGlobal, global_index); + } + CALLBACK0(EndGlobalSection); + return Result::Ok; +} + +Result BinaryReader::ReadExportSection(Offset section_size) { + CALLBACK(BeginExportSection, section_size); + Index num_exports; + CHECK_RESULT(ReadCount(&num_exports, "export count")); + CALLBACK(OnExportCount, num_exports); + for (Index i = 0; i < num_exports; ++i) { + std::string_view name; + CHECK_RESULT(ReadStr(&name, "export item name")); + + ExternalKind kind; + CHECK_RESULT(ReadExternalKind(&kind, "export kind")); + + Index item_index; + CHECK_RESULT(ReadIndex(&item_index, "export item index")); + if (kind == ExternalKind::Tag) { + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid export tag kind: exceptions not allowed"); + } + + CALLBACK(OnExport, i, static_cast(kind), item_index, name); + } + CALLBACK0(EndExportSection); + return Result::Ok; +} + +Result BinaryReader::ReadStartSection(Offset section_size) { + CALLBACK(BeginStartSection, section_size); + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "start function index")); + CALLBACK(OnStartFunction, func_index); + CALLBACK0(EndStartSection); + return Result::Ok; +} + +Result BinaryReader::ReadElemSection(Offset section_size) { + CALLBACK(BeginElemSection, section_size); + Index num_elem_segments; + CHECK_RESULT(ReadCount(&num_elem_segments, "elem segment count")); + CALLBACK(OnElemSegmentCount, num_elem_segments); + for (Index i = 0; i < num_elem_segments; ++i) { + uint32_t flags; + CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags")); + ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags); + Index table_index(0); + if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) { + CHECK_RESULT(ReadIndex(&table_index, "elem segment table index")); + } + Type elem_type = Type::FuncRef; + + CALLBACK(BeginElemSegment, i, table_index, flags); + + if (!(flags & SegPassive)) { + CALLBACK(BeginElemSegmentInitExpr, i); + CHECK_RESULT(ReadInitExpr(i)); + CALLBACK(EndElemSegmentInitExpr, i); + } + + // For backwards compat we support not declaring the element kind. + if (flags & (SegPassive | SegExplicitIndex)) { + if (flags & SegUseElemExprs) { + CHECK_RESULT(ReadRefType(&elem_type, "table elem type")); + } else { + ExternalKind kind; + CHECK_RESULT(ReadExternalKind(&kind, "export kind")); + ERROR_UNLESS(kind == ExternalKind::Func, + "segment elem type must be func (%s)", + elem_type.GetName().c_str()); + elem_type = Type::FuncRef; + } + } + + CALLBACK(OnElemSegmentElemType, i, elem_type); + + Index num_elem_exprs; + CHECK_RESULT(ReadCount(&num_elem_exprs, "elem count")); + + CALLBACK(OnElemSegmentElemExprCount, i, num_elem_exprs); + for (Index j = 0; j < num_elem_exprs; ++j) { + if (flags & SegUseElemExprs) { + Opcode opcode; + CHECK_RESULT(ReadOpcode(&opcode, "elem expr opcode")); + if (opcode == Opcode::RefNull) { + Type type; + CHECK_RESULT(ReadRefType(&type, "elem expr ref.null type")); + CALLBACK(OnElemSegmentElemExpr_RefNull, i, type); + } else if (opcode == Opcode::RefFunc) { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "elem expr func index")); + CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index); + } else { + PrintError( + "expected ref.null or ref.func in passive element segment"); + } + CHECK_RESULT(ReadOpcode(&opcode, "opcode")); + ERROR_UNLESS(opcode == Opcode::End, + "expected END opcode after element expression"); + } else { + Index func_index; + CHECK_RESULT(ReadIndex(&func_index, "elem expr func index")); + CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index); + } + } + CALLBACK(EndElemSegment, i); + } + CALLBACK0(EndElemSection); + return Result::Ok; +} + +Result BinaryReader::ReadCodeSection(Offset section_size) { + CALLBACK(BeginCodeSection, section_size); + CHECK_RESULT(ReadCount(&num_function_bodies_, "function body count")); + ERROR_UNLESS(num_function_signatures_ == num_function_bodies_, + "function signature count != function body count"); + CALLBACK(OnFunctionBodyCount, num_function_bodies_); + for (Index i = 0; i < num_function_bodies_; ++i) { + Index func_index = num_func_imports_ + i; + Offset func_offset = state_.offset; + state_.offset = func_offset; + uint32_t body_size; + CHECK_RESULT(ReadU32Leb128(&body_size, "function body size")); + Offset body_start_offset = state_.offset; + Offset end_offset = body_start_offset + body_size; + CALLBACK(BeginFunctionBody, func_index, body_size); + + uint64_t total_locals = 0; + Index num_local_decls; + CHECK_RESULT(ReadCount(&num_local_decls, "local declaration count")); + CALLBACK(OnLocalDeclCount, num_local_decls); + for (Index k = 0; k < num_local_decls; ++k) { + Index num_local_types; + CHECK_RESULT(ReadIndex(&num_local_types, "local type count")); + total_locals += num_local_types; + ERROR_UNLESS(total_locals <= UINT32_MAX, "local count must be <= 0x%x", + UINT32_MAX); + Type local_type; + CHECK_RESULT(ReadType(&local_type, "local type")); + ERROR_UNLESS(IsConcreteType(local_type), "expected valid local type"); + CALLBACK(OnLocalDecl, k, num_local_types, local_type); + } + + if (options_.skip_function_bodies) { + state_.offset = end_offset; + } else { + CHECK_RESULT(ReadFunctionBody(end_offset)); + } + + CALLBACK(EndFunctionBody, func_index); + } + CALLBACK0(EndCodeSection); + return Result::Ok; +} + +Result BinaryReader::ReadDataSection(Offset section_size) { + CALLBACK(BeginDataSection, section_size); + Index num_data_segments; + CHECK_RESULT(ReadCount(&num_data_segments, "data segment count")); + CALLBACK(OnDataSegmentCount, num_data_segments); + // If the DataCount section is not present, then data_count_ will be invalid. + ERROR_UNLESS(data_count_ == kInvalidIndex || data_count_ == num_data_segments, + "data segment count does not equal count in DataCount section"); + for (Index i = 0; i < num_data_segments; ++i) { + uint32_t flags; + CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags")); + ERROR_IF(flags != 0 && !options_.features.bulk_memory_enabled(), + "invalid memory index %d: bulk memory not allowed", flags); + ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags); + Index memory_index(0); + if (flags & SegExplicitIndex) { + CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index")); + } + CALLBACK(BeginDataSegment, i, memory_index, flags); + if (!(flags & SegPassive)) { + ERROR_UNLESS(memories.size() > 0, "no memory to copy data to"); + CALLBACK(BeginDataSegmentInitExpr, i); + CHECK_RESULT(ReadInitExpr(i)); + CALLBACK(EndDataSegmentInitExpr, i); + } + + Address data_size; + const void* data; + CHECK_RESULT(ReadBytes(&data, &data_size, "data segment data")); + CALLBACK(OnDataSegmentData, i, data, data_size); + CALLBACK(EndDataSegment, i); + } + CALLBACK0(EndDataSection); + return Result::Ok; +} + +Result BinaryReader::ReadDataCountSection(Offset section_size) { + CALLBACK(BeginDataCountSection, section_size); + Index data_count; + CHECK_RESULT(ReadIndex(&data_count, "data count")); + CALLBACK(OnDataCount, data_count); + CALLBACK0(EndDataCountSection); + data_count_ = data_count; + return Result::Ok; +} + +Result BinaryReader::ReadSections(const ReadSectionsOptions& options) { + Result result = Result::Ok; + Index section_index = 0; + bool seen_section_code[static_cast(BinarySection::Last) + 1] = {false}; + + for (; state_.offset < state_.size; ++section_index) { + uint8_t section_code; + Offset section_size; + CHECK_RESULT(ReadU8(§ion_code, "section code")); + CHECK_RESULT(ReadOffset(§ion_size, "section size")); + ReadEndRestoreGuard guard(this); + read_end_ = state_.offset + section_size; + if (section_code >= kBinarySectionCount) { + PrintError("invalid section code: %u", section_code); + if (options.stop_on_first_error) { + return Result::Error; + } + // If we don't have to stop on first error, continue reading + // sections, because although we could not understand the + // current section, we can continue and correctly parse + // subsequent sections, so we can give back as much information + // as we can understand. + result = Result::Error; + state_.offset = read_end_; + continue; + } + + BinarySection section = static_cast(section_code); + if (section != BinarySection::Custom) { + if (seen_section_code[section_code]) { + PrintError("multiple %s sections", GetSectionName(section)); + return Result::Error; + } + seen_section_code[section_code] = true; + } + + ERROR_UNLESS(read_end_ <= state_.size, + "invalid section size: extends past end"); + + ERROR_UNLESS( + last_known_section_ == BinarySection::Invalid || + section == BinarySection::Custom || + GetSectionOrder(section) > GetSectionOrder(last_known_section_), + "section %s out of order", GetSectionName(section)); + + ERROR_UNLESS(!did_read_names_section_ || section == BinarySection::Custom, + "%s section can not occur after Name section", + GetSectionName(section)); + + CALLBACK(BeginSection, section_index, section, section_size); + + bool stop_on_first_error = options_.stop_on_first_error; + Result section_result = Result::Error; + switch (section) { + case BinarySection::Custom: + section_result = ReadCustomSection(section_index, section_size); + if (options_.fail_on_custom_section_error) { + result |= section_result; + } else { + stop_on_first_error = false; + } + break; + case BinarySection::Type: + section_result = ReadTypeSection(section_size); + result |= section_result; + break; + case BinarySection::Import: + section_result = ReadImportSection(section_size); + result |= section_result; + break; + case BinarySection::Function: + section_result = ReadFunctionSection(section_size); + result |= section_result; + break; + case BinarySection::Table: + section_result = ReadTableSection(section_size); + result |= section_result; + break; + case BinarySection::Memory: + section_result = ReadMemorySection(section_size); + result |= section_result; + break; + case BinarySection::Global: + section_result = ReadGlobalSection(section_size); + result |= section_result; + break; + case BinarySection::Export: + section_result = ReadExportSection(section_size); + result |= section_result; + break; + case BinarySection::Start: + section_result = ReadStartSection(section_size); + result |= section_result; + break; + case BinarySection::Elem: + section_result = ReadElemSection(section_size); + result |= section_result; + break; + case BinarySection::Code: + section_result = ReadCodeSection(section_size); + result |= section_result; + break; + case BinarySection::Data: + section_result = ReadDataSection(section_size); + result |= section_result; + break; + case BinarySection::Tag: + ERROR_UNLESS(options_.features.exceptions_enabled(), + "invalid section code: %u", + static_cast(section)); + section_result = ReadTagSection(section_size); + result |= section_result; + break; + case BinarySection::DataCount: + ERROR_UNLESS(options_.features.bulk_memory_enabled(), + "invalid section code: %u", + static_cast(section)); + section_result = ReadDataCountSection(section_size); + result |= section_result; + break; + case BinarySection::Invalid: + WABT_UNREACHABLE; + } + + if (Succeeded(section_result) && state_.offset != read_end_) { + PrintError("unfinished section (expected end: 0x%" PRIzx ")", read_end_); + section_result = Result::Error; + result |= section_result; + } + + if (Failed(section_result)) { + if (stop_on_first_error) { + return Result::Error; + } + + // If we're continuing after failing to read this section, move the + // offset to the expected section end. This way we may be able to read + // further sections. + state_.offset = read_end_; + } + + if (section != BinarySection::Custom) { + last_known_section_ = section; + } + } + + return result; +} + +Result BinaryReader::ReadModule(const ReadModuleOptions& options) { + uint32_t magic = 0; + CHECK_RESULT(ReadU32(&magic, "magic")); + ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value"); + uint32_t version = 0; + CHECK_RESULT(ReadU32(&version, "version")); + ERROR_UNLESS(version == WABT_BINARY_VERSION, + "bad wasm file version: %#x (expected %#x)", version, + WABT_BINARY_VERSION); + + CALLBACK(BeginModule, version); + CHECK_RESULT(ReadSections(ReadSectionsOptions{options.stop_on_first_error})); + // This is checked in ReadCodeSection, but it must be checked at the end too, + // in case the code section was omitted. + ERROR_UNLESS(num_function_signatures_ == num_function_bodies_, + "function signature count != function body count"); + CALLBACK0(EndModule); + + return Result::Ok; +} + +} // end anonymous namespace + +Result ReadBinary(const void* data, + size_t size, + BinaryReaderDelegate* delegate, + const ReadBinaryOptions& options) { + BinaryReader reader(data, size, delegate, options); + return reader.ReadModule( + BinaryReader::ReadModuleOptions{options.stop_on_first_error}); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-writer-spec.cc b/third_party/wasm2c/src/binary-writer-spec.cc new file mode 100644 index 0000000000..3cb6674de3 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer-spec.cc @@ -0,0 +1,672 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-writer-spec.h" + +#include +#include +#include + +#include "wabt/config.h" + +#include "wabt/binary-writer.h" +#include "wabt/binary.h" +#include "wabt/cast.h" +#include "wabt/filenames.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +namespace wabt { + +namespace { + +class BinaryWriterSpec { + public: + BinaryWriterSpec(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options); + + Result WriteScript(const Script& script); + + private: + std::string GetModuleFilename(const char* extension); + void WriteString(const char* s); + void WriteKey(const char* key); + void WriteSeparator(); + void WriteEscapedString(std::string_view); + void WriteCommandType(const Command& command); + void WriteLocation(const Location& loc); + void WriteVar(const Var& var); + void WriteTypeObject(Type type); + void WriteF32(uint32_t, ExpectedNan); + void WriteF64(uint64_t, ExpectedNan); + void WriteRefBits(uintptr_t ref_bits); + void WriteConst(const Const& const_); + void WriteConstVector(const ConstVector& consts); + void WriteAction(const Action& action); + void WriteActionResultType(const Action& action); + void WriteModule(std::string_view filename, const Module& module); + void WriteScriptModule(std::string_view filename, + const ScriptModule& script_module); + void WriteInvalidModule(const ScriptModule& module, std::string_view text); + void WriteCommands(); + + const Script* script_ = nullptr; + Stream* json_stream_ = nullptr; + WriteBinarySpecStreamFactory module_stream_factory_; + std::string source_filename_; + std::string module_filename_noext_; + const WriteBinaryOptions& options_; + Result result_ = Result::Ok; + size_t num_modules_ = 0; +}; + +BinaryWriterSpec::BinaryWriterSpec( + Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) + : json_stream_(json_stream), + module_stream_factory_(module_stream_factory), + source_filename_(source_filename), + module_filename_noext_(module_filename_noext), + options_(options) {} + +std::string BinaryWriterSpec::GetModuleFilename(const char* extension) { + std::string result = module_filename_noext_; + result += '.'; + result += std::to_string(num_modules_); + result += extension; + ConvertBackslashToSlash(&result); + return result; +} + +void BinaryWriterSpec::WriteString(const char* s) { + json_stream_->Writef("\"%s\"", s); +} + +void BinaryWriterSpec::WriteKey(const char* key) { + json_stream_->Writef("\"%s\": ", key); +} + +void BinaryWriterSpec::WriteSeparator() { + json_stream_->Writef(", "); +} + +void BinaryWriterSpec::WriteEscapedString(std::string_view s) { + json_stream_->WriteChar('"'); + for (size_t i = 0; i < s.length(); ++i) { + uint8_t c = s[i]; + if (c < 0x20 || c == '\\' || c == '"') { + json_stream_->Writef("\\u%04x", c); + } else { + json_stream_->WriteChar(c); + } + } + json_stream_->WriteChar('"'); +} + +void BinaryWriterSpec::WriteCommandType(const Command& command) { + static const char* s_command_names[] = { + "module", + "module", + "action", + "register", + "assert_malformed", + "assert_invalid", + "assert_unlinkable", + "assert_uninstantiable", + "assert_return", + "assert_trap", + "assert_exhaustion", + "assert_exception", + }; + WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount); + + WriteKey("type"); + assert(s_command_names[static_cast(command.type)]); + WriteString(s_command_names[static_cast(command.type)]); +} + +void BinaryWriterSpec::WriteLocation(const Location& loc) { + WriteKey("line"); + json_stream_->Writef("%d", loc.line); +} + +void BinaryWriterSpec::WriteVar(const Var& var) { + if (var.is_index()) { + json_stream_->Writef("\"%" PRIindex "\"", var.index()); + } else { + WriteEscapedString(var.name()); + } +} + +void BinaryWriterSpec::WriteTypeObject(Type type) { + json_stream_->Writef("{"); + WriteKey("type"); + WriteString(type.GetName().c_str()); + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%u\"", f32_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%" PRIu64 "\"", f64_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteRefBits(uintptr_t ref_bits) { + if (ref_bits == Const::kRefNullBits) { + json_stream_->Writef("\"null\""); + } else { + json_stream_->Writef("\"%" PRIuPTR "\"", ref_bits); + } +} + +void BinaryWriterSpec::WriteConst(const Const& const_) { + json_stream_->Writef("{"); + WriteKey("type"); + + /* Always write the values as strings, even though they may be representable + * as JSON numbers. This way the formatting is consistent. */ + switch (const_.type()) { + case Type::I32: + WriteString("i32"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%u\"", const_.u32()); + break; + + case Type::I64: + WriteString("i64"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%" PRIu64 "\"", const_.u64()); + break; + + case Type::F32: + WriteString("f32"); + WriteSeparator(); + WriteKey("value"); + WriteF32(const_.f32_bits(), const_.expected_nan()); + break; + + case Type::F64: + WriteString("f64"); + WriteSeparator(); + WriteKey("value"); + WriteF64(const_.f64_bits(), const_.expected_nan()); + break; + + case Type::FuncRef: { + WriteString("funcref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::ExternRef: { + WriteString("externref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::V128: { + WriteString("v128"); + WriteSeparator(); + WriteKey("lane_type"); + WriteString(const_.lane_type().GetName().c_str()); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("["); + + for (int lane = 0; lane < const_.lane_count(); ++lane) { + switch (const_.lane_type()) { + case Type::I8: + json_stream_->Writef("\"%u\"", const_.v128_lane(lane)); + break; + + case Type::I16: + json_stream_->Writef("\"%u\"", const_.v128_lane(lane)); + break; + + case Type::I32: + json_stream_->Writef("\"%u\"", const_.v128_lane(lane)); + break; + + case Type::I64: + json_stream_->Writef("\"%" PRIu64 "\"", + const_.v128_lane(lane)); + break; + + case Type::F32: + WriteF32(const_.v128_lane(lane), + const_.expected_nan(lane)); + break; + + case Type::F64: + WriteF64(const_.v128_lane(lane), + const_.expected_nan(lane)); + break; + + default: + WABT_UNREACHABLE; + } + + if (lane != const_.lane_count() - 1) { + WriteSeparator(); + } + } + + json_stream_->Writef("]"); + + break; + } + + default: + WABT_UNREACHABLE; + } + + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteConstVector(const ConstVector& consts) { + json_stream_->Writef("["); + for (size_t i = 0; i < consts.size(); ++i) { + const Const& const_ = consts[i]; + WriteConst(const_); + if (i != consts.size() - 1) { + WriteSeparator(); + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteAction(const Action& action) { + WriteKey("action"); + json_stream_->Writef("{"); + WriteKey("type"); + if (action.type() == ActionType::Invoke) { + WriteString("invoke"); + } else { + assert(action.type() == ActionType::Get); + WriteString("get"); + } + WriteSeparator(); + if (action.module_var.is_name()) { + WriteKey("module"); + WriteVar(action.module_var); + WriteSeparator(); + } + if (action.type() == ActionType::Invoke) { + WriteKey("field"); + WriteEscapedString(action.name); + WriteSeparator(); + WriteKey("args"); + WriteConstVector(cast(&action)->args); + } else { + WriteKey("field"); + WriteEscapedString(action.name); + } + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteActionResultType(const Action& action) { + const Module* module = script_->GetModule(action.module_var); + const Export* export_; + json_stream_->Writef("["); + switch (action.type()) { + case ActionType::Invoke: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Func); + const Func* func = module->GetFunc(export_->var); + Index num_results = func->GetNumResults(); + for (Index i = 0; i < num_results; ++i) + WriteTypeObject(func->GetResultType(i)); + break; + } + + case ActionType::Get: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Global); + const Global* global = module->GetGlobal(export_->var); + WriteTypeObject(global->type); + break; + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteModule(std::string_view filename, + const Module& module) { + result_ |= + WriteBinaryModule(module_stream_factory_(filename), &module, options_); +} + +void BinaryWriterSpec::WriteScriptModule(std::string_view filename, + const ScriptModule& script_module) { + switch (script_module.type()) { + case ScriptModuleType::Text: + WriteModule(filename, cast(&script_module)->module); + break; + + case ScriptModuleType::Binary: + module_stream_factory_(filename)->WriteData( + cast(&script_module)->data, ""); + break; + + case ScriptModuleType::Quoted: + module_stream_factory_(filename)->WriteData( + cast(&script_module)->data, ""); + break; + } +} + +void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module, + std::string_view text) { + const char* extension = ""; + const char* module_type = ""; + switch (module.type()) { + case ScriptModuleType::Text: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Binary: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Quoted: + extension = kWatExtension; + module_type = "text"; + break; + } + + WriteLocation(module.location()); + WriteSeparator(); + std::string filename = GetModuleFilename(extension); + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(text); + WriteSeparator(); + WriteKey("module_type"); + WriteString(module_type); + WriteScriptModule(filename, module); +} + +void BinaryWriterSpec::WriteCommands() { + json_stream_->Writef("{\"source_filename\": "); + WriteEscapedString(source_filename_); + json_stream_->Writef(",\n \"commands\": [\n"); + Index last_module_index = kInvalidIndex; + for (size_t i = 0; i < script_->commands.size(); ++i) { + const Command* command = script_->commands[i].get(); + + if (i != 0) { + WriteSeparator(); + json_stream_->Writef("\n"); + } + + json_stream_->Writef(" {"); + WriteCommandType(*command); + WriteSeparator(); + + switch (command->type) { + case CommandType::Module: { + const Module& module = cast(command)->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteModule(filename, module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::ScriptModule: { + auto* script_module_command = cast(command); + const auto& module = script_module_command->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteScriptModule(filename, *script_module_command->script_module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::Action: { + const Action& action = *cast(command)->action; + WriteLocation(action.loc); + WriteSeparator(); + WriteAction(action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(action); + break; + } + + case CommandType::Register: { + auto* register_command = cast(command); + const Var& var = register_command->var; + WriteLocation(var.loc); + WriteSeparator(); + if (var.is_name()) { + WriteKey("name"); + WriteVar(var); + WriteSeparator(); + } else { + /* If we're not registering by name, then we should only be + * registering the last module. */ + WABT_USE(last_module_index); + assert(var.index() == last_module_index); + } + WriteKey("as"); + WriteEscapedString(register_command->module_name); + break; + } + + case CommandType::AssertMalformed: { + auto* assert_malformed_command = cast(command); + WriteInvalidModule(*assert_malformed_command->module, + assert_malformed_command->text); + num_modules_++; + break; + } + + case CommandType::AssertInvalid: { + auto* assert_invalid_command = cast(command); + WriteInvalidModule(*assert_invalid_command->module, + assert_invalid_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUnlinkable: { + auto* assert_unlinkable_command = + cast(command); + WriteInvalidModule(*assert_unlinkable_command->module, + assert_unlinkable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUninstantiable: { + auto* assert_uninstantiable_command = + cast(command); + WriteInvalidModule(*assert_uninstantiable_command->module, + assert_uninstantiable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertReturn: { + auto* assert_return_command = cast(command); + WriteLocation(assert_return_command->action->loc); + WriteSeparator(); + WriteAction(*assert_return_command->action); + WriteSeparator(); + const Expectation* expectation = assert_return_command->expected.get(); + switch (expectation->type()) { + case ExpectationType::Values: + WriteKey("expected"); + break; + + case ExpectationType::Either: + WriteKey("either"); + break; + } + WriteConstVector(expectation->expected); + break; + } + + case CommandType::AssertTrap: { + auto* assert_trap_command = cast(command); + WriteLocation(assert_trap_command->action->loc); + WriteSeparator(); + WriteAction(*assert_trap_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_trap_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_trap_command->action); + break; + } + + case CommandType::AssertExhaustion: { + auto* assert_exhaustion_command = + cast(command); + WriteLocation(assert_exhaustion_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exhaustion_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_exhaustion_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exhaustion_command->action); + break; + } + + case CommandType::AssertException: { + auto* assert_exception_command = cast(command); + WriteLocation(assert_exception_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exception_command->action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exception_command->action); + break; + } + } + + json_stream_->Writef("}"); + } + json_stream_->Writef("]}\n"); +} + +Result BinaryWriterSpec::WriteScript(const Script& script) { + script_ = &script; + WriteCommands(); + return result_; +} + +} // end anonymous namespace + +Result WriteBinarySpecScript(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) { + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +Result WriteBinarySpecScript( + Stream* json_stream, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options, + std::vector* out_module_streams, + Stream* log_stream) { + WriteBinarySpecStreamFactory module_stream_factory = + [&](std::string_view filename) { + out_module_streams->emplace_back( + filename, std::make_unique(log_stream)); + return out_module_streams->back().stream.get(); + }; + + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary-writer.cc b/third_party/wasm2c/src/binary-writer.cc new file mode 100644 index 0000000000..2eb2884684 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer.cc @@ -0,0 +1,1798 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary-writer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wabt/config.h" + +#include "wabt/binary.h" +#include "wabt/cast.h" +#include "wabt/expr-visitor.h" +#include "wabt/ir.h" +#include "wabt/leb128.h" +#include "wabt/stream.h" + +#define PRINT_HEADER_NO_INDEX -1 +#define MAX_U32_LEB128_BYTES 5 + +namespace wabt { + +void WriteStr(Stream* stream, + std::string_view s, + const char* desc, + PrintChars print_chars) { + WriteU32Leb128(stream, s.length(), "string length"); + stream->WriteData(s.data(), s.length(), desc, print_chars); +} + +void WriteOpcode(Stream* stream, Opcode opcode) { + if (opcode.HasPrefix()) { + stream->WriteU8(opcode.GetPrefix(), "prefix"); + WriteU32Leb128(stream, opcode.GetCode(), opcode.GetName()); + } else { + stream->WriteU8(opcode.GetCode(), opcode.GetName()); + } +} + +void WriteType(Stream* stream, Type type, const char* desc) { + WriteS32Leb128(stream, type, desc ? desc : type.GetName().c_str()); + if (type.IsReferenceWithIndex()) { + WriteS32Leb128(stream, type.GetReferenceIndex(), + desc ? desc : type.GetName().c_str()); + } +} + +void WriteLimits(Stream* stream, const Limits* limits) { + uint32_t flags = limits->has_max ? WABT_BINARY_LIMITS_HAS_MAX_FLAG : 0; + flags |= limits->is_shared ? WABT_BINARY_LIMITS_IS_SHARED_FLAG : 0; + flags |= limits->is_64 ? WABT_BINARY_LIMITS_IS_64_FLAG : 0; + WriteU32Leb128(stream, flags, "limits: flags"); + if (limits->is_64) { + WriteU64Leb128(stream, limits->initial, "limits: initial"); + if (limits->has_max) { + WriteU64Leb128(stream, limits->max, "limits: max"); + } + } else { + WriteU32Leb128(stream, limits->initial, "limits: initial"); + if (limits->has_max) { + WriteU32Leb128(stream, limits->max, "limits: max"); + } + } +} + +void WriteDebugName(Stream* stream, std::string_view name, const char* desc) { + std::string_view stripped_name = name; + if (!stripped_name.empty()) { + // Strip leading $ from name + assert(stripped_name.front() == '$'); + stripped_name.remove_prefix(1); + } + WriteStr(stream, stripped_name, desc, PrintChars::Yes); +} + +namespace { + +/* TODO(binji): better leb size guess. Some sections we know will only be 1 + byte, but others we can be fairly certain will be larger. */ +constexpr size_t LEB_SECTION_SIZE_GUESS = 1; + +#define ALLOC_FAILURE \ + fprintf(stderr, "%s:%d: allocation failed\n", __FILE__, __LINE__) + +struct RelocSection { + RelocSection(const char* name, Index index) + : name(name), section_index(index) {} + + const char* name; + Index section_index; + std::vector relocations; +}; + +class Symbol { + public: + struct Function { + static const SymbolType type = SymbolType::Function; + Index index; + }; + struct Data { + static const SymbolType type = SymbolType::Data; + Index index; + Offset offset; + Address size; + }; + struct Global { + static const SymbolType type = SymbolType::Global; + Index index; + }; + struct Section { + static const SymbolType type = SymbolType::Section; + Index section; + }; + struct Tag { + static const SymbolType type = SymbolType::Tag; + Index index; + }; + struct Table { + static const SymbolType type = SymbolType::Table; + Index index; + }; + + private: + SymbolType type_; + std::string_view name_; + uint8_t flags_; + union { + Function function_; + Data data_; + Global global_; + Section section_; + Tag tag_; + Table table_; + }; + + public: + Symbol(const std::string_view& name, uint8_t flags, const Function& f) + : type_(Function::type), name_(name), flags_(flags), function_(f) {} + Symbol(const std::string_view& name, uint8_t flags, const Data& d) + : type_(Data::type), name_(name), flags_(flags), data_(d) {} + Symbol(const std::string_view& name, uint8_t flags, const Global& g) + : type_(Global::type), name_(name), flags_(flags), global_(g) {} + Symbol(const std::string_view& name, uint8_t flags, const Section& s) + : type_(Section::type), name_(name), flags_(flags), section_(s) {} + Symbol(const std::string_view& name, uint8_t flags, const Tag& e) + : type_(Tag::type), name_(name), flags_(flags), tag_(e) {} + Symbol(const std::string_view& name, uint8_t flags, const Table& t) + : type_(Table::type), name_(name), flags_(flags), table_(t) {} + + SymbolType type() const { return type_; } + const std::string_view& name() const { return name_; } + uint8_t flags() const { return flags_; } + + SymbolVisibility visibility() const { + return static_cast(flags() & WABT_SYMBOL_MASK_VISIBILITY); + } + SymbolBinding binding() const { + return static_cast(flags() & WABT_SYMBOL_MASK_BINDING); + } + bool undefined() const { return flags() & WABT_SYMBOL_FLAG_UNDEFINED; } + bool defined() const { return !undefined(); } + bool exported() const { return flags() & WABT_SYMBOL_FLAG_EXPORTED; } + bool explicit_name() const { + return flags() & WABT_SYMBOL_FLAG_EXPLICIT_NAME; + } + bool no_strip() const { return flags() & WABT_SYMBOL_FLAG_NO_STRIP; } + + bool IsFunction() const { return type() == Function::type; } + bool IsData() const { return type() == Data::type; } + bool IsGlobal() const { return type() == Global::type; } + bool IsSection() const { return type() == Section::type; } + bool IsTag() const { return type() == Tag::type; } + bool IsTable() const { return type() == Table::type; } + + const Function& AsFunction() const { + assert(IsFunction()); + return function_; + } + const Data& AsData() const { + assert(IsData()); + return data_; + } + const Global& AsGlobal() const { + assert(IsGlobal()); + return global_; + } + const Section& AsSection() const { + assert(IsSection()); + return section_; + } + const Tag& AsTag() const { + assert(IsTag()); + return tag_; + } + const Table& AsTable() const { + assert(IsTable()); + return table_; + } +}; + +class SymbolTable { + WABT_DISALLOW_COPY_AND_ASSIGN(SymbolTable); + + std::vector symbols_; + + std::vector functions_; + std::vector tables_; + std::vector globals_; + + std::set seen_names_; + + Result EnsureUnique(const std::string_view& name) { + if (seen_names_.count(name)) { + fprintf(stderr, + "error: duplicate symbol when writing relocatable " + "binary: %s\n", + &name[0]); + return Result::Error; + } + seen_names_.insert(name); + return Result::Ok; + }; + + template + Result AddSymbol(std::vector* map, + std::string_view name, + bool imported, + bool exported, + T&& sym) { + uint8_t flags = 0; + if (imported) { + flags |= WABT_SYMBOL_FLAG_UNDEFINED; + // Wabt currently has no way for a user to explicitly specify the name of + // an import, so never set the EXPLICIT_NAME flag, and ignore any display + // name fabricated by wabt. + name = std::string_view(); + } else { + if (name.empty()) { + // Definitions without a name are local. + flags |= uint8_t(SymbolBinding::Local); + flags |= uint8_t(SymbolVisibility::Hidden); + } else { + // Otherwise, strip the dollar off the name; a definition $foo is + // available for linking as "foo". + assert(name[0] == '$'); + name.remove_prefix(1); + } + + if (exported) { + CHECK_RESULT(EnsureUnique(name)); + flags |= uint8_t(SymbolVisibility::Hidden); + flags |= WABT_SYMBOL_FLAG_NO_STRIP; + } + } + if (exported) { + flags |= WABT_SYMBOL_FLAG_EXPORTED; + } + + map->push_back(symbols_.size()); + symbols_.emplace_back(name, flags, sym); + return Result::Ok; + }; + + Index SymbolIndex(const std::vector& table, Index index) const { + // For well-formed modules, an index into (e.g.) functions_ will always be + // within bounds; the out-of-bounds case here is just to allow --relocatable + // to write known-invalid modules. + return index < table.size() ? table[index] : kInvalidIndex; + } + + public: + SymbolTable() {} + + Result Populate(const Module* module) { + std::set exported_funcs; + std::set exported_globals; + std::set exported_tags; + std::set exported_tables; + + for (const Export* export_ : module->exports) { + switch (export_->kind) { + case ExternalKind::Func: + exported_funcs.insert(module->GetFuncIndex(export_->var)); + break; + case ExternalKind::Table: + exported_tables.insert(module->GetTableIndex(export_->var)); + break; + case ExternalKind::Memory: + break; + case ExternalKind::Global: + exported_globals.insert(module->GetGlobalIndex(export_->var)); + break; + case ExternalKind::Tag: + exported_tags.insert(module->GetTagIndex(export_->var)); + break; + } + } + + // We currently only create symbol table entries for function, table, and + // global symbols. + for (size_t i = 0; i < module->funcs.size(); ++i) { + const Func* func = module->funcs[i]; + bool imported = i < module->num_func_imports; + bool exported = exported_funcs.count(i); + CHECK_RESULT(AddSymbol(&functions_, func->name, imported, exported, + Symbol::Function{Index(i)})); + } + + for (size_t i = 0; i < module->tables.size(); ++i) { + const Table* table = module->tables[i]; + bool imported = i < module->num_table_imports; + bool exported = exported_tables.count(i); + CHECK_RESULT(AddSymbol(&tables_, table->name, imported, exported, + Symbol::Table{Index(i)})); + } + + for (size_t i = 0; i < module->globals.size(); ++i) { + const Global* global = module->globals[i]; + bool imported = i < module->num_global_imports; + bool exported = exported_globals.count(i); + CHECK_RESULT(AddSymbol(&globals_, global->name, imported, exported, + Symbol::Global{Index(i)})); + } + + return Result::Ok; + } + + const std::vector& symbols() const { return symbols_; } + Index FunctionSymbolIndex(Index index) const { + return SymbolIndex(functions_, index); + } + Index TableSymbolIndex(Index index) const { + return SymbolIndex(tables_, index); + } + Index GlobalSymbolIndex(Index index) const { + return SymbolIndex(globals_, index); + } +}; + +struct CodeMetadata { + Offset offset; + std::vector data; + CodeMetadata(Offset offset, std::vector data) + : offset(offset), data(std::move(data)) {} +}; +struct FuncCodeMetadata { + Index func_idx; + std::vector entries; + FuncCodeMetadata(Index func_idx) : func_idx(func_idx) {} +}; +struct CodeMetadataSection { + std::vector entries; +}; +using CodeMetadataSections = + std::unordered_map; + +class BinaryWriter { + WABT_DISALLOW_COPY_AND_ASSIGN(BinaryWriter); + + public: + BinaryWriter(Stream*, + const WriteBinaryOptions& options, + const Module* module); + + Result WriteModule(); + + private: + void WriteHeader(const char* name, int index); + Offset WriteU32Leb128Space(Offset leb_size_guess, const char* desc); + Offset WriteFixupU32Leb128Size(Offset offset, + Offset leb_size_guess, + const char* desc); + void BeginKnownSection(BinarySection section_code); + void BeginCustomSection(const char* name); + void WriteSectionHeader(const char* desc, BinarySection section_code); + void EndSection(); + void BeginSubsection(const char* name); + void EndSubsection(); + Index GetLabelVarDepth(const Var* var); + Index GetTagVarDepth(const Var* var); + Index GetLocalIndex(const Func* func, const Var& var); + Index GetSymbolIndex(RelocType reloc_type, Index index); + void AddReloc(RelocType reloc_type, Index index); + void WriteBlockDecl(const BlockDeclaration& decl); + void WriteU32Leb128WithReloc(Index index, + const char* desc, + RelocType reloc_type); + void WriteS32Leb128WithReloc(int32_t value, + const char* desc, + RelocType reloc_type); + void WriteTableNumberWithReloc(Index table_number, const char* desc); + template + void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc); + template + void WriteSimdLoadStoreLaneExpr(const Func* func, + const Expr* expr, + const char* desc); + void WriteExpr(const Func* func, const Expr* expr); + void WriteExprList(const Func* func, const ExprList& exprs); + void WriteInitExpr(const ExprList& expr); + void WriteFuncLocals(const Func* func, const LocalTypes& local_types); + void WriteFunc(const Func* func); + void WriteTable(const Table* table); + void WriteMemory(const Memory* memory); + void WriteGlobalHeader(const Global* global); + void WriteTagType(const Tag* tag); + void WriteRelocSection(const RelocSection* reloc_section); + void WriteLinkingSection(); + template + void WriteNames(const std::vector& elems, NameSectionSubsection type); + void WriteCodeMetadataSections(); + + Stream* stream_; + const WriteBinaryOptions& options_; + const Module* module_; + + SymbolTable symtab_; + std::vector reloc_sections_; + RelocSection* current_reloc_section_ = nullptr; + + Index section_count_ = 0; + size_t last_section_offset_ = 0; + size_t last_section_leb_size_guess_ = 0; + BinarySection last_section_type_ = BinarySection::Invalid; + size_t last_section_payload_offset_ = 0; + + size_t last_subsection_offset_ = 0; + size_t last_subsection_leb_size_guess_ = 0; + size_t last_subsection_payload_offset_ = 0; + + // Information about the data count section, so it can be removed if it is + // not needed, and relocs relative to the code section patched up. + size_t code_start_ = 0; + size_t data_count_start_ = 0; + size_t data_count_end_ = 0; + bool has_data_segment_instruction_ = false; + + CodeMetadataSections code_metadata_sections_; + Offset cur_func_start_offset_; + Index cur_func_index_; +}; + +static uint8_t log2_u32(uint32_t x) { + uint8_t result = 0; + while (x > 1) { + x >>= 1; + result++; + } + return result; +} + +BinaryWriter::BinaryWriter(Stream* stream, + const WriteBinaryOptions& options, + const Module* module) + : stream_(stream), options_(options), module_(module) {} + +void BinaryWriter::WriteHeader(const char* name, int index) { + if (stream_->has_log_stream()) { + if (index == PRINT_HEADER_NO_INDEX) { + stream_->log_stream().Writef("; %s\n", name); + } else { + stream_->log_stream().Writef("; %s %d\n", name, index); + } + } +} + +/* returns offset of leb128 */ +Offset BinaryWriter::WriteU32Leb128Space(Offset leb_size_guess, + const char* desc) { + assert(leb_size_guess <= MAX_U32_LEB128_BYTES); + uint8_t data[MAX_U32_LEB128_BYTES] = {0}; + Offset result = stream_->offset(); + Offset bytes_to_write = + options_.canonicalize_lebs ? leb_size_guess : MAX_U32_LEB128_BYTES; + stream_->WriteData(data, bytes_to_write, desc); + return result; +} + +Offset BinaryWriter::WriteFixupU32Leb128Size(Offset offset, + Offset leb_size_guess, + const char* desc) { + if (options_.canonicalize_lebs) { + Offset size = stream_->offset() - offset - leb_size_guess; + Offset leb_size = U32Leb128Length(size); + Offset delta = leb_size - leb_size_guess; + if (delta != 0) { + Offset src_offset = offset + leb_size_guess; + Offset dst_offset = offset + leb_size; + stream_->MoveData(dst_offset, src_offset, size); + } + WriteU32Leb128At(stream_, offset, size, desc); + stream_->AddOffset(delta); + return delta; + } else { + Offset size = stream_->offset() - offset - MAX_U32_LEB128_BYTES; + WriteFixedU32Leb128At(stream_, offset, size, desc); + return 0; + } +} + +void BinaryWriter::WriteBlockDecl(const BlockDeclaration& decl) { + if (decl.sig.GetNumParams() == 0 && decl.sig.GetNumResults() <= 1) { + if (decl.sig.GetNumResults() == 0) { + WriteType(stream_, Type::Void); + } else if (decl.sig.GetNumResults() == 1) { + WriteType(stream_, decl.sig.GetResultType(0)); + } + return; + } + + Index index = decl.has_func_type ? module_->GetFuncTypeIndex(decl.type_var) + : module_->GetFuncTypeIndex(decl.sig); + assert(index != kInvalidIndex); + WriteS32Leb128WithReloc(index, "block type function index", + RelocType::TypeIndexLEB); +} + +void BinaryWriter::WriteSectionHeader(const char* desc, + BinarySection section_code) { + assert(last_section_leb_size_guess_ == 0); + WriteHeader(desc, PRINT_HEADER_NO_INDEX); + stream_->WriteU8Enum(section_code, "section code"); + last_section_type_ = section_code; + last_section_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; + last_section_offset_ = + WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "section size (guess)"); + last_section_payload_offset_ = stream_->offset(); +} + +void BinaryWriter::BeginKnownSection(BinarySection section_code) { + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "section \"%s\" (%u)", + GetSectionName(section_code), + static_cast(section_code)); + WriteSectionHeader(desc, section_code); +} + +void BinaryWriter::BeginCustomSection(const char* name) { + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "section \"%s\"", name); + WriteSectionHeader(desc, BinarySection::Custom); + WriteStr(stream_, name, "custom section name", PrintChars::Yes); +} + +void BinaryWriter::EndSection() { + assert(last_section_leb_size_guess_ != 0); + WriteFixupU32Leb128Size(last_section_offset_, last_section_leb_size_guess_, + "FIXUP section size"); + last_section_leb_size_guess_ = 0; + section_count_++; +} + +void BinaryWriter::BeginSubsection(const char* name) { + assert(last_subsection_leb_size_guess_ == 0); + last_subsection_leb_size_guess_ = LEB_SECTION_SIZE_GUESS; + last_subsection_offset_ = + WriteU32Leb128Space(LEB_SECTION_SIZE_GUESS, "subsection size (guess)"); + last_subsection_payload_offset_ = stream_->offset(); +} + +void BinaryWriter::EndSubsection() { + assert(last_subsection_leb_size_guess_ != 0); + WriteFixupU32Leb128Size(last_subsection_offset_, + last_subsection_leb_size_guess_, + "FIXUP subsection size"); + last_subsection_leb_size_guess_ = 0; +} + +Index BinaryWriter::GetLabelVarDepth(const Var* var) { + return var->index(); +} + +Index BinaryWriter::GetTagVarDepth(const Var* var) { + return var->index(); +} + +Index BinaryWriter::GetSymbolIndex(RelocType reloc_type, Index index) { + switch (reloc_type) { + case RelocType::FuncIndexLEB: + return symtab_.FunctionSymbolIndex(index); + case RelocType::TableNumberLEB: + return symtab_.TableSymbolIndex(index); + case RelocType::GlobalIndexLEB: + return symtab_.GlobalSymbolIndex(index); + case RelocType::TypeIndexLEB: + // Type indexes don't create entries in the symbol table; instead their + // index is used directly. + return index; + default: + fprintf(stderr, "warning: unsupported relocation type: %s\n", + GetRelocTypeName(reloc_type)); + return kInvalidIndex; + } +} + +void BinaryWriter::AddReloc(RelocType reloc_type, Index index) { + // Add a new reloc section if needed + if (!current_reloc_section_ || + current_reloc_section_->section_index != section_count_) { + reloc_sections_.emplace_back(GetSectionName(last_section_type_), + section_count_); + current_reloc_section_ = &reloc_sections_.back(); + } + + // Add a new relocation to the curent reloc section + size_t offset = stream_->offset() - last_section_payload_offset_; + Index symbol_index = GetSymbolIndex(reloc_type, index); + if (symbol_index == kInvalidIndex) { + // The file is invalid, for example a reference to function 42 where only 10 + // functions are defined. The user must have already passed --no-check, so + // no extra warning here is needed. + return; + } + current_reloc_section_->relocations.emplace_back(reloc_type, offset, + symbol_index); +} + +void BinaryWriter::WriteU32Leb128WithReloc(Index index, + const char* desc, + RelocType reloc_type) { + if (options_.relocatable) { + AddReloc(reloc_type, index); + WriteFixedU32Leb128(stream_, index, desc); + } else { + WriteU32Leb128(stream_, index, desc); + } +} + +void BinaryWriter::WriteS32Leb128WithReloc(int32_t value, + const char* desc, + RelocType reloc_type) { + if (options_.relocatable) { + AddReloc(reloc_type, value); + WriteFixedS32Leb128(stream_, value, desc); + } else { + WriteS32Leb128(stream_, value, desc); + } +} + +void BinaryWriter::WriteTableNumberWithReloc(Index value, const char* desc) { + // Unless reference types are enabled, all references to tables refer to table + // 0, so no relocs need be emitted when making relocatable binaries. + if (options_.relocatable && options_.features.reference_types_enabled()) { + AddReloc(RelocType::TableNumberLEB, value); + WriteFixedS32Leb128(stream_, value, desc); + } else { + WriteS32Leb128(stream_, value, desc); + } +} + +Index BinaryWriter::GetLocalIndex(const Func* func, const Var& var) { + // func can be nullptr when using local.get/local.set/local.tee in an + // init_expr. + if (func) { + return func->GetLocalIndex(var); + } else if (var.is_index()) { + return var.index(); + } else { + return kInvalidIndex; + } +} + +// TODO(binji): Rename this, it is used for more than loads/stores now. +template +void BinaryWriter::WriteLoadStoreExpr(const Func* func, + const Expr* expr, + const char* desc) { + auto* typed_expr = cast(expr); + WriteOpcode(stream_, typed_expr->opcode); + Address align = typed_expr->opcode.GetAlignment(typed_expr->align); + Index memidx = module_->GetMemoryIndex(typed_expr->memidx); + if (memidx != 0) { + stream_->WriteU8(log2_u32(align) | (1 << 6), "alignment"); + WriteU32Leb128(stream_, memidx, "memidx"); + } else { + stream_->WriteU8(log2_u32(align), "alignment"); + } + WriteU32Leb128(stream_, typed_expr->offset, desc); +} + +template +void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func, + const Expr* expr, + const char* desc) { + WriteLoadStoreExpr(func, expr, desc); + auto* typed_expr = cast(expr); + stream_->WriteU8(static_cast(typed_expr->val), "Simd Lane literal"); +} + +void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) { + switch (expr->type()) { + case ExprType::AtomicLoad: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::AtomicRmw: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::AtomicRmwCmpxchg: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::AtomicStore: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::AtomicWait: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::AtomicFence: { + auto* fence_expr = cast(expr); + WriteOpcode(stream_, Opcode::AtomicFence); + WriteU32Leb128(stream_, fence_expr->consistency_model, + "consistency model"); + break; + } + case ExprType::AtomicNotify: + WriteLoadStoreExpr(func, expr, "memory offset"); + break; + case ExprType::Binary: + WriteOpcode(stream_, cast(expr)->opcode); + break; + case ExprType::Block: + WriteOpcode(stream_, Opcode::Block); + WriteBlockDecl(cast(expr)->block.decl); + WriteExprList(func, cast(expr)->block.exprs); + WriteOpcode(stream_, Opcode::End); + break; + case ExprType::Br: + WriteOpcode(stream_, Opcode::Br); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), + "break depth"); + break; + case ExprType::BrIf: + WriteOpcode(stream_, Opcode::BrIf); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), + "break depth"); + break; + case ExprType::BrTable: { + auto* br_table_expr = cast(expr); + WriteOpcode(stream_, Opcode::BrTable); + WriteU32Leb128(stream_, br_table_expr->targets.size(), "num targets"); + Index depth; + for (const Var& var : br_table_expr->targets) { + depth = GetLabelVarDepth(&var); + WriteU32Leb128(stream_, depth, "break depth"); + } + depth = GetLabelVarDepth(&br_table_expr->default_target); + WriteU32Leb128(stream_, depth, "break depth for default"); + break; + } + case ExprType::Call: { + Index index = module_->GetFuncIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::Call); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::ReturnCall: { + Index index = module_->GetFuncIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::ReturnCall); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::CallIndirect: { + Index sig_index = + module_->GetFuncTypeIndex(cast(expr)->decl); + Index table_index = + module_->GetTableIndex(cast(expr)->table); + WriteOpcode(stream_, Opcode::CallIndirect); + WriteU32Leb128WithReloc(sig_index, "signature index", + RelocType::TypeIndexLEB); + WriteTableNumberWithReloc(table_index, "table index"); + break; + } + case ExprType::CallRef: { + WriteOpcode(stream_, Opcode::CallRef); + break; + } + case ExprType::ReturnCallIndirect: { + Index sig_index = + module_->GetFuncTypeIndex(cast(expr)->decl); + Index table_index = + module_->GetTableIndex(cast(expr)->table); + WriteOpcode(stream_, Opcode::ReturnCallIndirect); + WriteU32Leb128WithReloc(sig_index, "signature index", + RelocType::TypeIndexLEB); + WriteTableNumberWithReloc(table_index, "table index"); + break; + } + case ExprType::Compare: + WriteOpcode(stream_, cast(expr)->opcode); + break; + case ExprType::Const: { + const Const& const_ = cast(expr)->const_; + switch (const_.type()) { + case Type::I32: { + WriteOpcode(stream_, Opcode::I32Const); + WriteS32Leb128(stream_, const_.u32(), "i32 literal"); + break; + } + case Type::I64: + WriteOpcode(stream_, Opcode::I64Const); + WriteS64Leb128(stream_, const_.u64(), "i64 literal"); + break; + case Type::F32: + WriteOpcode(stream_, Opcode::F32Const); + stream_->WriteU32(const_.f32_bits(), "f32 literal"); + break; + case Type::F64: + WriteOpcode(stream_, Opcode::F64Const); + stream_->WriteU64(const_.f64_bits(), "f64 literal"); + break; + case Type::V128: + WriteOpcode(stream_, Opcode::V128Const); + stream_->WriteU128(const_.vec128(), "v128 literal"); + break; + default: + assert(0); + } + break; + } + case ExprType::Convert: + WriteOpcode(stream_, cast(expr)->opcode); + break; + case ExprType::Drop: + WriteOpcode(stream_, Opcode::Drop); + break; + case ExprType::GlobalGet: { + Index index = module_->GetGlobalIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::GlobalGet); + WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); + break; + } + case ExprType::GlobalSet: { + Index index = module_->GetGlobalIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::GlobalSet); + WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB); + break; + } + case ExprType::If: { + auto* if_expr = cast(expr); + WriteOpcode(stream_, Opcode::If); + WriteBlockDecl(if_expr->true_.decl); + WriteExprList(func, if_expr->true_.exprs); + if (!if_expr->false_.empty()) { + WriteOpcode(stream_, Opcode::Else); + WriteExprList(func, if_expr->false_); + } + WriteOpcode(stream_, Opcode::End); + break; + } + case ExprType::Load: + WriteLoadStoreExpr(func, expr, "load offset"); + break; + case ExprType::LocalGet: { + Index index = GetLocalIndex(func, cast(expr)->var); + WriteOpcode(stream_, Opcode::LocalGet); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::LocalSet: { + Index index = GetLocalIndex(func, cast(expr)->var); + WriteOpcode(stream_, Opcode::LocalSet); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::LocalTee: { + Index index = GetLocalIndex(func, cast(expr)->var); + WriteOpcode(stream_, Opcode::LocalTee); + WriteU32Leb128(stream_, index, "local index"); + break; + } + case ExprType::Loop: + WriteOpcode(stream_, Opcode::Loop); + WriteBlockDecl(cast(expr)->block.decl); + WriteExprList(func, cast(expr)->block.exprs); + WriteOpcode(stream_, Opcode::End); + break; + case ExprType::MemoryCopy: { + Index srcmemidx = + module_->GetMemoryIndex(cast(expr)->srcmemidx); + Index destmemidx = + module_->GetMemoryIndex(cast(expr)->destmemidx); + WriteOpcode(stream_, Opcode::MemoryCopy); + WriteU32Leb128(stream_, srcmemidx, "memory.copy srcmemidx"); + WriteU32Leb128(stream_, destmemidx, "memory.copy destmemidx"); + break; + } + case ExprType::DataDrop: { + Index index = module_->GetDataSegmentIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::DataDrop); + WriteU32Leb128(stream_, index, "data.drop segment"); + has_data_segment_instruction_ = true; + break; + } + case ExprType::MemoryFill: { + Index memidx = + module_->GetMemoryIndex(cast(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryFill); + WriteU32Leb128(stream_, memidx, "memory.fill memidx"); + break; + } + case ExprType::MemoryGrow: { + Index memidx = + module_->GetMemoryIndex(cast(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryGrow); + WriteU32Leb128(stream_, memidx, "memory.grow memidx"); + break; + } + case ExprType::MemoryInit: { + Index index = + module_->GetDataSegmentIndex(cast(expr)->var); + Index memidx = + module_->GetMemoryIndex(cast(expr)->memidx); + WriteOpcode(stream_, Opcode::MemoryInit); + WriteU32Leb128(stream_, index, "memory.init segment"); + WriteU32Leb128(stream_, memidx, "memory.init memidx"); + has_data_segment_instruction_ = true; + break; + } + case ExprType::MemorySize: { + Index memidx = + module_->GetMemoryIndex(cast(expr)->memidx); + WriteOpcode(stream_, Opcode::MemorySize); + WriteU32Leb128(stream_, memidx, "memory.size memidx"); + break; + } + case ExprType::TableCopy: { + auto* copy_expr = cast(expr); + Index dst = module_->GetTableIndex(copy_expr->dst_table); + Index src = module_->GetTableIndex(copy_expr->src_table); + WriteOpcode(stream_, Opcode::TableCopy); + WriteTableNumberWithReloc(dst, "table.copy dst_table"); + WriteTableNumberWithReloc(src, "table.copy src_table"); + break; + } + case ExprType::ElemDrop: { + Index index = module_->GetElemSegmentIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::ElemDrop); + WriteU32Leb128(stream_, index, "elem.drop segment"); + break; + } + case ExprType::TableInit: { + auto* init_expr = cast(expr); + Index table_index = module_->GetTableIndex(init_expr->table_index); + Index segment_index = + module_->GetElemSegmentIndex(init_expr->segment_index); + WriteOpcode(stream_, Opcode::TableInit); + WriteU32Leb128(stream_, segment_index, "table.init segment"); + WriteTableNumberWithReloc(table_index, "table.init table"); + break; + } + case ExprType::TableGet: { + Index index = module_->GetTableIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::TableGet); + WriteTableNumberWithReloc(index, "table.get table index"); + break; + } + case ExprType::TableSet: { + Index index = module_->GetTableIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::TableSet); + WriteTableNumberWithReloc(index, "table.set table index"); + break; + } + case ExprType::TableGrow: { + Index index = module_->GetTableIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::TableGrow); + WriteTableNumberWithReloc(index, "table.grow table index"); + break; + } + case ExprType::TableSize: { + Index index = module_->GetTableIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::TableSize); + WriteTableNumberWithReloc(index, "table.size table index"); + break; + } + case ExprType::TableFill: { + Index index = module_->GetTableIndex(cast(expr)->var); + WriteOpcode(stream_, Opcode::TableFill); + WriteTableNumberWithReloc(index, "table.fill table index"); + break; + } + case ExprType::RefFunc: { + WriteOpcode(stream_, Opcode::RefFunc); + Index index = module_->GetFuncIndex(cast(expr)->var); + WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB); + break; + } + case ExprType::RefNull: { + WriteOpcode(stream_, Opcode::RefNull); + WriteType(stream_, cast(expr)->type, "ref.null type"); + break; + } + case ExprType::RefIsNull: + WriteOpcode(stream_, Opcode::RefIsNull); + break; + case ExprType::Nop: + WriteOpcode(stream_, Opcode::Nop); + break; + case ExprType::Rethrow: + WriteOpcode(stream_, Opcode::Rethrow); + WriteU32Leb128(stream_, GetLabelVarDepth(&cast(expr)->var), + "rethrow depth"); + break; + case ExprType::Return: + WriteOpcode(stream_, Opcode::Return); + break; + case ExprType::Select: { + auto* select_expr = cast(expr); + if (select_expr->result_type.empty()) { + WriteOpcode(stream_, Opcode::Select); + } else { + WriteOpcode(stream_, Opcode::SelectT); + WriteU32Leb128(stream_, select_expr->result_type.size(), + "num result types"); + for (Type t : select_expr->result_type) { + WriteType(stream_, t, "result type"); + } + } + break; + } + case ExprType::Store: + WriteLoadStoreExpr(func, expr, "store offset"); + break; + case ExprType::Throw: + WriteOpcode(stream_, Opcode::Throw); + WriteU32Leb128(stream_, GetTagVarDepth(&cast(expr)->var), + "throw tag"); + break; + case ExprType::Try: { + auto* try_expr = cast(expr); + WriteOpcode(stream_, Opcode::Try); + WriteBlockDecl(try_expr->block.decl); + WriteExprList(func, try_expr->block.exprs); + switch (try_expr->kind) { + case TryKind::Catch: + for (const Catch& catch_ : try_expr->catches) { + if (catch_.IsCatchAll()) { + WriteOpcode(stream_, Opcode::CatchAll); + } else { + WriteOpcode(stream_, Opcode::Catch); + WriteU32Leb128(stream_, GetTagVarDepth(&catch_.var), "catch tag"); + } + WriteExprList(func, catch_.exprs); + } + WriteOpcode(stream_, Opcode::End); + break; + case TryKind::Delegate: + WriteOpcode(stream_, Opcode::Delegate); + WriteU32Leb128(stream_, GetLabelVarDepth(&try_expr->delegate_target), + "delegate depth"); + break; + case TryKind::Plain: + WriteOpcode(stream_, Opcode::End); + break; + } + break; + } + case ExprType::Unary: + WriteOpcode(stream_, cast(expr)->opcode); + break; + case ExprType::Ternary: + WriteOpcode(stream_, cast(expr)->opcode); + break; + case ExprType::SimdLaneOp: { + const Opcode opcode = cast(expr)->opcode; + WriteOpcode(stream_, opcode); + stream_->WriteU8(static_cast(cast(expr)->val), + "Simd Lane literal"); + break; + } + case ExprType::SimdLoadLane: { + WriteSimdLoadStoreLaneExpr(func, expr, "load offset"); + break; + } + case ExprType::SimdStoreLane: { + WriteSimdLoadStoreLaneExpr(func, expr, "store offset"); + break; + } + case ExprType::SimdShuffleOp: { + const Opcode opcode = cast(expr)->opcode; + WriteOpcode(stream_, opcode); + stream_->WriteU128(cast(expr)->val, + "Simd Lane[16] literal"); + break; + } + case ExprType::LoadSplat: + WriteLoadStoreExpr(func, expr, "load offset"); + break; + case ExprType::LoadZero: + WriteLoadStoreExpr(func, expr, "load offset"); + break; + case ExprType::Unreachable: + WriteOpcode(stream_, Opcode::Unreachable); + break; + case ExprType::CodeMetadata: { + auto* meta_expr = cast(expr); + auto& s = code_metadata_sections_[meta_expr->name]; + if (s.entries.empty() || s.entries.back().func_idx != cur_func_index_) { + s.entries.emplace_back(cur_func_index_); + } + auto& a = s.entries.back(); + Offset code_offset = stream_->offset() - cur_func_start_offset_; + a.entries.emplace_back(code_offset, meta_expr->data); + break; + } + } +} + +void BinaryWriter::WriteExprList(const Func* func, const ExprList& exprs) { + for (const Expr& expr : exprs) { + WriteExpr(func, &expr); + } +} + +void BinaryWriter::WriteInitExpr(const ExprList& expr) { + WriteExprList(nullptr, expr); + WriteOpcode(stream_, Opcode::End); +} + +void BinaryWriter::WriteFuncLocals(const Func* func, + const LocalTypes& local_types) { + if (local_types.size() == 0) { + WriteU32Leb128(stream_, 0, "local decl count"); + return; + } + + Index local_decl_count = local_types.decls().size(); + WriteU32Leb128(stream_, local_decl_count, "local decl count"); + for (auto decl : local_types.decls()) { + WriteU32Leb128(stream_, decl.second, "local type count"); + WriteType(stream_, decl.first); + } +} + +void BinaryWriter::WriteFunc(const Func* func) { + WriteFuncLocals(func, func->local_types); + WriteExprList(func, func->exprs); + WriteOpcode(stream_, Opcode::End); +} + +void BinaryWriter::WriteTable(const Table* table) { + WriteType(stream_, table->elem_type); + WriteLimits(stream_, &table->elem_limits); +} + +void BinaryWriter::WriteMemory(const Memory* memory) { + WriteLimits(stream_, &memory->page_limits); +} + +void BinaryWriter::WriteGlobalHeader(const Global* global) { + WriteType(stream_, global->type); + stream_->WriteU8(global->mutable_, "global mutability"); +} + +void BinaryWriter::WriteTagType(const Tag* tag) { + stream_->WriteU8(0, "tag attribute"); + WriteU32Leb128(stream_, module_->GetFuncTypeIndex(tag->decl), + "tag signature index"); +} + +void BinaryWriter::WriteRelocSection(const RelocSection* reloc_section) { + char section_name[128]; + wabt_snprintf(section_name, sizeof(section_name), "%s.%s", + WABT_BINARY_SECTION_RELOC, reloc_section->name); + BeginCustomSection(section_name); + WriteU32Leb128(stream_, reloc_section->section_index, "reloc section index"); + const std::vector& relocs = reloc_section->relocations; + WriteU32Leb128(stream_, relocs.size(), "num relocs"); + + for (const Reloc& reloc : relocs) { + WriteU32Leb128(stream_, reloc.type, "reloc type"); + WriteU32Leb128(stream_, reloc.offset, "reloc offset"); + WriteU32Leb128(stream_, reloc.index, "reloc index"); + switch (reloc.type) { + case RelocType::MemoryAddressLEB: + case RelocType::MemoryAddressLEB64: + case RelocType::MemoryAddressSLEB: + case RelocType::MemoryAddressSLEB64: + case RelocType::MemoryAddressRelSLEB: + case RelocType::MemoryAddressRelSLEB64: + case RelocType::MemoryAddressI32: + case RelocType::MemoryAddressI64: + case RelocType::FunctionOffsetI32: + case RelocType::SectionOffsetI32: + case RelocType::MemoryAddressTLSSLEB: + case RelocType::MemoryAddressTLSI32: + WriteU32Leb128(stream_, reloc.addend, "reloc addend"); + break; + case RelocType::FuncIndexLEB: + case RelocType::TableIndexSLEB: + case RelocType::TableIndexSLEB64: + case RelocType::TableIndexI32: + case RelocType::TableIndexI64: + case RelocType::TypeIndexLEB: + case RelocType::GlobalIndexLEB: + case RelocType::TagIndexLEB: + case RelocType::TableIndexRelSLEB: + case RelocType::TableNumberLEB: + break; + default: + fprintf(stderr, "warning: unsupported relocation type: %s\n", + GetRelocTypeName(reloc.type)); + } + } + + EndSection(); +} + +void BinaryWriter::WriteLinkingSection() { + BeginCustomSection(WABT_BINARY_SECTION_LINKING); + WriteU32Leb128(stream_, 2, "metadata version"); + const std::vector& symbols = symtab_.symbols(); + if (symbols.size()) { + stream_->WriteU8Enum(LinkingEntryType::SymbolTable, "symbol table"); + BeginSubsection("symbol table"); + WriteU32Leb128(stream_, symbols.size(), "num symbols"); + + for (const Symbol& sym : symbols) { + stream_->WriteU8Enum(sym.type(), "symbol type"); + WriteU32Leb128(stream_, sym.flags(), "symbol flags"); + switch (sym.type()) { + case SymbolType::Function: + WriteU32Leb128(stream_, sym.AsFunction().index, "function index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "function name", PrintChars::Yes); + } + break; + case SymbolType::Data: + WriteStr(stream_, sym.name(), "data name", PrintChars::Yes); + if (sym.defined()) { + WriteU32Leb128(stream_, sym.AsData().index, "data index"); + WriteU32Leb128(stream_, sym.AsData().offset, "data offset"); + WriteU32Leb128(stream_, sym.AsData().size, "data size"); + } + break; + case SymbolType::Global: + WriteU32Leb128(stream_, sym.AsGlobal().index, "global index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "global name", PrintChars::Yes); + } + break; + case SymbolType::Section: + WriteU32Leb128(stream_, sym.AsSection().section, "section index"); + break; + case SymbolType::Tag: + WriteU32Leb128(stream_, sym.AsTag().index, "tag index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "tag name", PrintChars::Yes); + } + break; + case SymbolType::Table: + WriteU32Leb128(stream_, sym.AsTable().index, "table index"); + if (sym.defined() || sym.explicit_name()) { + WriteStr(stream_, sym.name(), "table name", PrintChars::Yes); + } + break; + } + } + EndSubsection(); + } + EndSection(); +} + +template +void BinaryWriter::WriteNames(const std::vector& elems, + NameSectionSubsection type) { + size_t num_named_elems = 0; + for (const T* elem : elems) { + if (!elem->name.empty()) { + num_named_elems++; + } + } + + if (!num_named_elems) { + return; + } + + WriteU32Leb128(stream_, type, "name subsection type"); + BeginSubsection("name subsection"); + + char desc[100]; + WriteU32Leb128(stream_, num_named_elems, "num names"); + for (size_t i = 0; i < elems.size(); ++i) { + const T* elem = elems[i]; + if (elem->name.empty()) { + continue; + } + WriteU32Leb128(stream_, i, "elem index"); + wabt_snprintf(desc, sizeof(desc), "elem name %" PRIzd, i); + WriteDebugName(stream_, elem->name, desc); + } + EndSubsection(); +} + +Result BinaryWriter::WriteModule() { + stream_->WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC"); + stream_->WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION"); + + if (options_.relocatable) { + CHECK_RESULT(symtab_.Populate(module_)); + } + + if (module_->types.size()) { + BeginKnownSection(BinarySection::Type); + WriteU32Leb128(stream_, module_->types.size(), "num types"); + for (size_t i = 0; i < module_->types.size(); ++i) { + const TypeEntry* type = module_->types[i]; + switch (type->kind()) { + case TypeEntryKind::Func: { + const FuncType* func_type = cast(type); + const FuncSignature* sig = &func_type->sig; + WriteHeader("func type", i); + WriteType(stream_, Type::Func); + + Index num_params = sig->param_types.size(); + Index num_results = sig->result_types.size(); + WriteU32Leb128(stream_, num_params, "num params"); + for (size_t j = 0; j < num_params; ++j) { + WriteType(stream_, sig->param_types[j]); + } + + WriteU32Leb128(stream_, num_results, "num results"); + for (size_t j = 0; j < num_results; ++j) { + WriteType(stream_, sig->result_types[j]); + } + break; + } + + case TypeEntryKind::Struct: { + const StructType* struct_type = cast(type); + WriteHeader("struct type", i); + WriteType(stream_, Type::Struct); + Index num_fields = struct_type->fields.size(); + WriteU32Leb128(stream_, num_fields, "num fields"); + for (size_t j = 0; j < num_fields; ++j) { + const Field& field = struct_type->fields[j]; + WriteType(stream_, field.type); + stream_->WriteU8(field.mutable_, "field mutability"); + } + break; + } + + case TypeEntryKind::Array: { + const ArrayType* array_type = cast(type); + WriteHeader("array type", i); + WriteType(stream_, Type::Array); + WriteType(stream_, array_type->field.type); + stream_->WriteU8(array_type->field.mutable_, "field mutability"); + break; + } + } + } + EndSection(); + } + + if (module_->imports.size()) { + BeginKnownSection(BinarySection::Import); + WriteU32Leb128(stream_, module_->imports.size(), "num imports"); + + for (size_t i = 0; i < module_->imports.size(); ++i) { + const Import* import = module_->imports[i]; + WriteHeader("import header", i); + WriteStr(stream_, import->module_name, "import module name", + PrintChars::Yes); + WriteStr(stream_, import->field_name, "import field name", + PrintChars::Yes); + stream_->WriteU8Enum(import->kind(), "import kind"); + switch (import->kind()) { + case ExternalKind::Func: + WriteU32Leb128( + stream_, + module_->GetFuncTypeIndex(cast(import)->func.decl), + "import signature index"); + break; + + case ExternalKind::Table: + WriteTable(&cast(import)->table); + break; + + case ExternalKind::Memory: + WriteMemory(&cast(import)->memory); + break; + + case ExternalKind::Global: + WriteGlobalHeader(&cast(import)->global); + break; + + case ExternalKind::Tag: + WriteTagType(&cast(import)->tag); + break; + } + } + EndSection(); + } + + assert(module_->funcs.size() >= module_->num_func_imports); + Index num_funcs = module_->funcs.size() - module_->num_func_imports; + if (num_funcs) { + BeginKnownSection(BinarySection::Function); + WriteU32Leb128(stream_, num_funcs, "num functions"); + + for (size_t i = 0; i < num_funcs; ++i) { + const Func* func = module_->funcs[i + module_->num_func_imports]; + char desc[100]; + wabt_snprintf(desc, sizeof(desc), "function %" PRIzd " signature index", + i); + WriteU32Leb128(stream_, module_->GetFuncTypeIndex(func->decl), desc); + } + EndSection(); + } + + assert(module_->tables.size() >= module_->num_table_imports); + Index num_tables = module_->tables.size() - module_->num_table_imports; + if (num_tables) { + BeginKnownSection(BinarySection::Table); + WriteU32Leb128(stream_, num_tables, "num tables"); + for (size_t i = 0; i < num_tables; ++i) { + const Table* table = module_->tables[i + module_->num_table_imports]; + WriteHeader("table", i); + WriteTable(table); + } + EndSection(); + } + + assert(module_->memories.size() >= module_->num_memory_imports); + Index num_memories = module_->memories.size() - module_->num_memory_imports; + if (num_memories) { + BeginKnownSection(BinarySection::Memory); + WriteU32Leb128(stream_, num_memories, "num memories"); + for (size_t i = 0; i < num_memories; ++i) { + const Memory* memory = module_->memories[i + module_->num_memory_imports]; + WriteHeader("memory", i); + WriteMemory(memory); + } + EndSection(); + } + + assert(module_->tags.size() >= module_->num_tag_imports); + Index num_tags = module_->tags.size() - module_->num_tag_imports; + if (num_tags) { + BeginKnownSection(BinarySection::Tag); + WriteU32Leb128(stream_, num_tags, "tag count"); + for (size_t i = 0; i < num_tags; ++i) { + WriteHeader("tag", i); + const Tag* tag = module_->tags[i + module_->num_tag_imports]; + WriteTagType(tag); + } + EndSection(); + } + + assert(module_->globals.size() >= module_->num_global_imports); + Index num_globals = module_->globals.size() - module_->num_global_imports; + if (num_globals) { + BeginKnownSection(BinarySection::Global); + WriteU32Leb128(stream_, num_globals, "num globals"); + + for (size_t i = 0; i < num_globals; ++i) { + const Global* global = module_->globals[i + module_->num_global_imports]; + WriteGlobalHeader(global); + WriteInitExpr(global->init_expr); + } + EndSection(); + } + + if (module_->exports.size()) { + BeginKnownSection(BinarySection::Export); + WriteU32Leb128(stream_, module_->exports.size(), "num exports"); + + for (const Export* export_ : module_->exports) { + WriteStr(stream_, export_->name, "export name", PrintChars::Yes); + stream_->WriteU8Enum(export_->kind, "export kind"); + switch (export_->kind) { + case ExternalKind::Func: { + Index index = module_->GetFuncIndex(export_->var); + WriteU32Leb128(stream_, index, "export func index"); + break; + } + case ExternalKind::Table: { + Index index = module_->GetTableIndex(export_->var); + WriteU32Leb128(stream_, index, "export table index"); + break; + } + case ExternalKind::Memory: { + Index index = module_->GetMemoryIndex(export_->var); + WriteU32Leb128(stream_, index, "export memory index"); + break; + } + case ExternalKind::Global: { + Index index = module_->GetGlobalIndex(export_->var); + WriteU32Leb128(stream_, index, "export global index"); + break; + } + case ExternalKind::Tag: { + Index index = module_->GetTagIndex(export_->var); + WriteU32Leb128(stream_, index, "export tag index"); + break; + } + } + } + EndSection(); + } + + if (module_->starts.size()) { + Index start_func_index = module_->GetFuncIndex(*module_->starts[0]); + if (start_func_index != kInvalidIndex) { + BeginKnownSection(BinarySection::Start); + WriteU32Leb128(stream_, start_func_index, "start func index"); + EndSection(); + } + } + + if (module_->elem_segments.size()) { + BeginKnownSection(BinarySection::Elem); + WriteU32Leb128(stream_, module_->elem_segments.size(), "num elem segments"); + for (size_t i = 0; i < module_->elem_segments.size(); ++i) { + ElemSegment* segment = module_->elem_segments[i]; + WriteHeader("elem segment header", i); + // 1. flags + uint8_t flags = segment->GetFlags(module_); + stream_->WriteU8(flags, "segment flags"); + // 2. optional target table + if (flags & SegExplicitIndex && segment->kind != SegmentKind::Declared) { + WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var), + "table index"); + } + // 3. optional target location within the table (active segments only) + if (!(flags & SegPassive)) { + WriteInitExpr(segment->offset); + } + // 4. type of item in the following list (omitted for "legacy" segments) + if (flags & (SegPassive | SegExplicitIndex)) { + if (flags & SegUseElemExprs) { + WriteType(stream_, segment->elem_type, "elem expr list type"); + } else { + stream_->WriteU8Enum(ExternalKind::Func, "elem list type"); + } + } + // 5. actual list of elements (with extern indexes or elem expr's) + // preceeded by length + WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems"); + if (flags & SegUseElemExprs) { + for (const ExprList& elem_expr : segment->elem_exprs) { + WriteInitExpr(elem_expr); + } + } else { + for (const ExprList& elem_expr : segment->elem_exprs) { + assert(elem_expr.size() == 1); + const Expr* expr = &elem_expr.front(); + assert(expr->type() == ExprType::RefFunc); + WriteU32Leb128(stream_, + module_->GetFuncIndex(cast(expr)->var), + "elem function index"); + } + } + } + EndSection(); + } + + if (options_.features.bulk_memory_enabled() && + module_->data_segments.size()) { + // Keep track of the data count section offset so it can be removed if + // it isn't needed. + data_count_start_ = stream_->offset(); + BeginKnownSection(BinarySection::DataCount); + WriteU32Leb128(stream_, module_->data_segments.size(), "data count"); + EndSection(); + data_count_end_ = stream_->offset(); + } + + if (num_funcs) { + code_start_ = stream_->offset(); + BeginKnownSection(BinarySection::Code); + WriteU32Leb128(stream_, num_funcs, "num functions"); + + for (size_t i = 0; i < num_funcs; ++i) { + cur_func_index_ = i + module_->num_func_imports; + WriteHeader("function body", i); + const Func* func = module_->funcs[cur_func_index_]; + + /* TODO(binji): better guess of the size of the function body section */ + const Offset leb_size_guess = 1; + Offset body_size_offset = + WriteU32Leb128Space(leb_size_guess, "func body size (guess)"); + cur_func_start_offset_ = stream_->offset(); + WriteFunc(func); + auto func_start_offset = body_size_offset - last_section_payload_offset_; + auto func_end_offset = stream_->offset() - last_section_payload_offset_; + auto delta = WriteFixupU32Leb128Size(body_size_offset, leb_size_guess, + "FIXUP func body size"); + if (current_reloc_section_ && delta != 0) { + for (Reloc& reloc : current_reloc_section_->relocations) { + if (reloc.offset >= func_start_offset && + reloc.offset <= func_end_offset) { + reloc.offset += delta; + } + } + } + } + EndSection(); + } + + // Remove the DataCount section if there are no instructions that require it. + if (options_.features.bulk_memory_enabled() && + module_->data_segments.size() && !has_data_segment_instruction_) { + Offset size = stream_->offset() - data_count_end_; + if (size) { + // If the DataCount section was followed by anything, assert that it's + // only the Code section. This limits the amount of fixing-up that we + // need to do. + assert(data_count_end_ == code_start_); + assert(last_section_type_ == BinarySection::Code); + stream_->MoveData(data_count_start_, data_count_end_, size); + code_start_ = data_count_start_; + } + stream_->Truncate(data_count_start_ + size); + + --section_count_; + + // We just effectively decremented the code section's index; adjust anything + // that might have captured it. + for (RelocSection& section : reloc_sections_) { + if (section.section_index == section_count_) { + assert(last_section_type_ == BinarySection::Code); + --section.section_index; + } + } + } + + WriteCodeMetadataSections(); + + if (module_->data_segments.size()) { + BeginKnownSection(BinarySection::Data); + WriteU32Leb128(stream_, module_->data_segments.size(), "num data segments"); + for (size_t i = 0; i < module_->data_segments.size(); ++i) { + const DataSegment* segment = module_->data_segments[i]; + WriteHeader("data segment header", i); + uint8_t flags = segment->GetFlags(module_); + stream_->WriteU8(flags, "segment flags"); + if (!(flags & SegPassive)) { + if (options_.features.multi_memory_enabled() && + (flags & SegExplicitIndex)) { + WriteU32Leb128(stream_, module_->GetMemoryIndex(segment->memory_var), + "memidx"); + } else { + assert(module_->GetMemoryIndex(segment->memory_var) == 0); + } + WriteInitExpr(segment->offset); + } + WriteU32Leb128(stream_, segment->data.size(), "data segment size"); + WriteHeader("data segment data", i); + stream_->WriteData(segment->data, "data segment data"); + } + EndSection(); + } + + if (options_.write_debug_names) { + std::vector index_to_name; + + char desc[100]; + BeginCustomSection(WABT_BINARY_SECTION_NAME); + + if (!module_->name.empty()) { + WriteU32Leb128(stream_, NameSectionSubsection::Module, + "module name type"); + BeginSubsection("module name subsection"); + WriteDebugName(stream_, module_->name, "module name"); + EndSubsection(); + } + + WriteNames(module_->funcs, NameSectionSubsection::Function); + + WriteU32Leb128(stream_, 2, "local name type"); + + BeginSubsection("local name subsection"); + WriteU32Leb128(stream_, module_->funcs.size(), "num functions"); + for (size_t i = 0; i < module_->funcs.size(); ++i) { + const Func* func = module_->funcs[i]; + Index num_params_and_locals = func->GetNumParamsAndLocals(); + MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings, + &index_to_name); + Index num_named = 0; + for (auto s : index_to_name) { + if (!s.empty()) { + num_named++; + } + } + + WriteU32Leb128(stream_, i, "function index"); + WriteU32Leb128(stream_, num_named, "num locals"); + + for (size_t j = 0; j < num_params_and_locals; ++j) { + const std::string& name = index_to_name[j]; + if (!name.empty()) { + wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j); + WriteU32Leb128(stream_, j, "local index"); + WriteDebugName(stream_, name, desc); + } + } + } + EndSubsection(); + + WriteNames(module_->types, NameSectionSubsection::Type); + WriteNames(module_->tables, NameSectionSubsection::Table); + WriteNames(module_->memories, NameSectionSubsection::Memory); + WriteNames(module_->globals, NameSectionSubsection::Global); + WriteNames(module_->elem_segments, + NameSectionSubsection::ElemSegment); + WriteNames(module_->data_segments, + NameSectionSubsection::DataSegment); + WriteNames(module_->tags, NameSectionSubsection::Tag); + + EndSection(); + } + + if (options_.relocatable) { + WriteLinkingSection(); + for (RelocSection& section : reloc_sections_) { + WriteRelocSection(§ion); + } + } + + return stream_->result(); +} + +void BinaryWriter::WriteCodeMetadataSections() { + if (code_metadata_sections_.empty()) + return; + + section_count_ -= 1; + // We have to increment the code section's index; adjust anything + // that might have captured it. + for (RelocSection& section : reloc_sections_) { + if (section.section_index == section_count_) { + assert(last_section_type_ == BinarySection::Code); + section.section_index += code_metadata_sections_.size(); + } + } + + MemoryStream tmp_stream; + Stream* main_stream = stream_; + stream_ = &tmp_stream; + for (auto& s : code_metadata_sections_) { + std::string name = "metadata.code."; + name.append(s.first); + auto& section = s.second; + BeginCustomSection(name.c_str()); + WriteU32Leb128(stream_, section.entries.size(), "function count"); + for (auto& f : section.entries) { + WriteU32Leb128WithReloc(f.func_idx, "function index", + RelocType::FuncIndexLEB); + WriteU32Leb128(stream_, f.entries.size(), "instances count"); + for (auto& a : f.entries) { + WriteU32Leb128(stream_, a.offset, "code offset"); + WriteU32Leb128(stream_, a.data.size(), "data length"); + stream_->WriteData(a.data.data(), a.data.size(), "data", + PrintChars::Yes); + } + } + EndSection(); + } + stream_ = main_stream; + auto buf = tmp_stream.ReleaseOutputBuffer(); + stream_->MoveData(code_start_ + buf->data.size(), code_start_, + stream_->offset() - code_start_); + stream_->WriteDataAt(code_start_, buf->data.data(), buf->data.size()); + stream_->AddOffset(buf->data.size()); + code_start_ += buf->data.size(); + section_count_ += 1; + last_section_type_ = BinarySection::Code; +} + +} // end anonymous namespace + +Result WriteBinaryModule(Stream* stream, + const Module* module, + const WriteBinaryOptions& options) { + BinaryWriter binary_writer(stream, options, module); + return binary_writer.WriteModule(); +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binary.cc b/third_party/wasm2c/src/binary.cc new file mode 100644 index 0000000000..d4938a1939 --- /dev/null +++ b/third_party/wasm2c/src/binary.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binary.h" + +namespace wabt { + +BinarySectionOrder GetSectionOrder(BinarySection sec) { + switch (sec) { +#define V(Name, name, code) \ + case BinarySection::Name: \ + return BinarySectionOrder::Name; + WABT_FOREACH_BINARY_SECTION(V) +#undef V + default: + WABT_UNREACHABLE; + } +} + +const char* GetSectionName(BinarySection sec) { + switch (sec) { +#define V(Name, name, code) \ + case BinarySection::Name: \ + return #Name; + WABT_FOREACH_BINARY_SECTION(V) +#undef V + default: + WABT_UNREACHABLE; + } +} + +// clang-format off +const char* NameSubsectionName[] = { + "module", + "function", + "local", + "label", + "type", + "table", + "memory", + "global", + "elemseg", + "dataseg", + "tag", +}; +// clang-format on + +const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) { + static_assert(WABT_ENUM_COUNT(NameSectionSubsection) == + WABT_ARRAY_SIZE(NameSubsectionName), + "Malformed ExprTypeName array"); + return NameSubsectionName[size_t(subsec)]; +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/binding-hash.cc b/third_party/wasm2c/src/binding-hash.cc new file mode 100644 index 0000000000..cd1b5f5a3e --- /dev/null +++ b/third_party/wasm2c/src/binding-hash.cc @@ -0,0 +1,91 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/binding-hash.h" + +#include +#include + +#include "wabt/ir.h" + +namespace wabt { + +void BindingHash::FindDuplicates(DuplicateCallback callback) const { + if (size() > 0) { + ValueTypeVector duplicates; + CreateDuplicatesVector(&duplicates); + SortDuplicatesVectorByLocation(&duplicates); + CallCallbacks(duplicates, callback); + } +} + +Index BindingHash::FindIndex(const Var& var) const { + if (var.is_name()) { + return FindIndex(var.name()); + } + return var.index(); +} + +void BindingHash::CreateDuplicatesVector( + ValueTypeVector* out_duplicates) const { + // This relies on the fact that in an unordered_multimap, all values with the + // same key are adjacent in iteration order. + auto first = begin(); + bool is_first = true; + for (auto iter = std::next(first); iter != end(); ++iter) { + if (first->first == iter->first) { + if (is_first) { + out_duplicates->push_back(&*first); + } + out_duplicates->push_back(&*iter); + is_first = false; + } else { + is_first = true; + first = iter; + } + } +} + +void BindingHash::SortDuplicatesVectorByLocation( + ValueTypeVector* duplicates) const { + std::sort( + duplicates->begin(), duplicates->end(), + [](const value_type* lhs, const value_type* rhs) -> bool { + return lhs->second.loc.line < rhs->second.loc.line || + (lhs->second.loc.line == rhs->second.loc.line && + lhs->second.loc.first_column < rhs->second.loc.first_column); + }); +} + +void BindingHash::CallCallbacks(const ValueTypeVector& duplicates, + DuplicateCallback callback) const { + // Loop through all duplicates in order, and call callback with first + // occurrence. + for (auto iter = duplicates.begin(), end = duplicates.end(); iter != end; + ++iter) { + auto first = std::find_if(duplicates.begin(), duplicates.end(), + [iter](const value_type* x) -> bool { + return x->first == (*iter)->first; + }); + if (first == iter) { + continue; + } + assert(first != duplicates.end()); + callback(**first, **iter); + } +} + +} // namespace wabt diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc new file mode 100644 index 0000000000..2405c170ae --- /dev/null +++ b/third_party/wasm2c/src/c-writer.cc @@ -0,0 +1,5295 @@ +/* + * Copyright 2017 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wabt/c-writer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/sha256.h" +#include "wabt/stream.h" +#include "wabt/string-util.h" + +#define INDENT_SIZE 2 + +#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort() + +// code to be inserted into the generated output +extern const char* s_header_top; +extern const char* s_header_bottom; +extern const char* s_source_includes; +extern const char* s_source_declarations; +extern const char* s_simd_source_declarations; + +namespace wabt { + +namespace { + +struct Label { + Label(LabelType label_type, + const std::string& name, + const TypeVector& sig, + size_t type_stack_size, + size_t try_catch_stack_size, + bool used = false) + : label_type(label_type), + name(name), + sig(sig), + type_stack_size(type_stack_size), + try_catch_stack_size(try_catch_stack_size), + used(used) {} + + bool HasValue() const { return !sig.empty(); } + + LabelType label_type; + const std::string& name; + const TypeVector& sig; + size_t type_stack_size; + size_t try_catch_stack_size; + bool used = false; +}; + +struct LocalName { + explicit LocalName(const std::string& name) : name(name) {} + const std::string& name; +}; + +struct ParamName : LocalName { + using LocalName::LocalName; + ParamName(const Var& var) : LocalName(var.name()) {} +}; + +struct LabelName : LocalName { + using LocalName::LocalName; +}; + +struct GlobalName { + GlobalName(ModuleFieldType type, const std::string& name) + : type(type), name(name) {} + ModuleFieldType type; + const std::string& name; +}; + +struct ExternalPtr : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalRef : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalInstancePtr : GlobalName { + using GlobalName::GlobalName; +}; + +struct ExternalInstanceRef : GlobalName { + using GlobalName::GlobalName; +}; + +struct GotoLabel { + explicit GotoLabel(const Var& var) : var(var) {} + const Var& var; +}; + +struct LabelDecl { + explicit LabelDecl(const std::string& name) : name(name) {} + std::string name; +}; + +struct GlobalInstanceVar { + explicit GlobalInstanceVar(const Var& var) : var(var) {} + const Var& var; +}; + +struct StackVar { + explicit StackVar(Index index, Type type = Type::Any) + : index(index), type(type) {} + Index index; + Type type; +}; + +struct TypeEnum { + explicit TypeEnum(Type type) : type(type) {} + Type type; +}; + +struct SignedType { + explicit SignedType(Type type) : type(type) {} + Type type; +}; + +struct ResultType { + explicit ResultType(const TypeVector& types) : types(types) {} + const TypeVector& types; +}; + +struct TryCatchLabel { + TryCatchLabel(const std::string& name, size_t try_catch_stack_size) + : name(name), try_catch_stack_size(try_catch_stack_size), used(false) {} + std::string name; + size_t try_catch_stack_size; + bool used; +}; + +struct FuncTypeExpr { + const FuncType* func_type; + FuncTypeExpr(const FuncType* f) : func_type(f) {} +}; + +struct Newline {}; +struct OpenBrace {}; +struct CloseBrace {}; + +int GetShiftMask(Type type) { + // clang-format off + switch (type) { + case Type::I32: return 31; + case Type::I64: return 63; + default: WABT_UNREACHABLE; return 0; + } + // clang-format on +} + +/* + * This function is the default behavior for name_to_output_file_index_. For + * single .c output, this function returns a vector filled with 0. For multiple + * .c outputs, this function sorts all non-imported functions in the module by + * their names, and then divides all non-imported functions into equal-sized + * buckets (# of non-imported functions / # of .c outputs) based on the sorting. + */ +static std::vector default_name_to_output_file_index( + std::vector::const_iterator func_begin, + std::vector::const_iterator func_end, + size_t num_imports, + size_t num_streams) { + std::vector result; + result.resize(std::distance(func_begin, func_end)); + if (num_streams == 1) { + return result; + } + + std::map sorted_functions; + size_t non_imported_funcs = result.size() - num_imports; + size_t bucket_size = non_imported_funcs / num_streams + + (non_imported_funcs % num_streams ? 1 : 0); + Index func_index = 0; + for (auto func = func_begin; func != func_end; func++) { + sorted_functions.insert({(*func)->name, func_index}); + ++func_index; + } + Index sorted_func_index = 0; + for (const auto& [func_name, index] : sorted_functions) { + bool is_import = index < num_imports; + if (!is_import) { + result.at(index) = sorted_func_index / bucket_size; + ++sorted_func_index; + } + } + return result; +} + +class CWriter { + public: + CWriter(std::vector&& c_streams, + Stream* h_stream, + Stream* h_impl_stream, + const char* header_name, + const char* header_impl_name, + const WriteCOptions& options) + : options_(options), + c_streams_(std::move(c_streams)), + h_stream_(h_stream), + h_impl_stream_(h_impl_stream), + header_name_(header_name), + header_impl_name_(header_impl_name) { + module_prefix_ = MangleModuleName(options_.module_name); + if (c_streams_.size() != 1 && options.name_to_output_file_index) { + name_to_output_file_index_ = options.name_to_output_file_index; + } else { + name_to_output_file_index_ = default_name_to_output_file_index; + } + } + + Result WriteModule(const Module&); + + private: + using SymbolSet = std::set; + using SymbolMap = std::map; + using StackTypePair = std::pair; + using StackVarSymbolMap = std::map; + + void WriteCHeader(); + void WriteCSource(); + + size_t MarkTypeStack() const; + void ResetTypeStack(size_t mark); + Type StackType(Index) const; + void PushType(Type); + void PushTypes(const TypeVector&); + void DropTypes(size_t count); + + void PushLabel(LabelType, + const std::string& name, + const FuncSignature&, + bool used = false); + const Label* FindLabel(const Var& var, bool mark_used = true); + bool IsTopLabelUsed() const; + void PopLabel(); + + static constexpr char MangleType(Type); + static constexpr char MangleField(ModuleFieldType); + static std::string MangleMultivalueTypes(const TypeVector&); + static std::string MangleTagTypes(const TypeVector&); + static std::string Mangle(std::string_view name, bool double_underscores); + static std::string MangleName(std::string_view); + static std::string MangleModuleName(std::string_view); + std::string ExportName(std::string_view module_name, + std::string_view export_name); + std::string ExportName(std::string_view export_name); + std::string ModuleInstanceTypeName() const; + static std::string ModuleInstanceTypeName(std::string_view module_name); + void ClaimName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& c_name); + std::string FindUniqueName(SymbolSet& set, std::string_view proposed_name); + std::string ClaimUniqueName(SymbolSet& set, + SymbolMap& map, + char type_suffix, + std::string_view wasm_name, + const std::string& proposed_c_name); + void DefineImportName(const Import* import, + std::string_view module_name, + std::string_view field_name); + void ReserveExportNames(); + void ReserveExportName(std::string_view); + std::string DefineImportedModuleInstanceName(std::string_view name); + std::string DefineInstanceMemberName(ModuleFieldType, std::string_view); + std::string DefineGlobalScopeName(ModuleFieldType, std::string_view); + std::string DefineLocalScopeName(std::string_view name, bool is_label); + std::string DefineParamName(std::string_view); + std::string DefineLabelName(std::string_view); + std::string DefineStackVarName(Index, Type, std::string_view); + + static void SerializeFuncType(const FuncType&, std::string&); + + std::string GetGlobalName(ModuleFieldType, const std::string&) const; + std::string GetLocalName(const std::string&, bool is_label) const; + + void Indent(int size = INDENT_SIZE); + void Dedent(int size = INDENT_SIZE); + void WriteIndent(); + void WriteData(const char* src, size_t size); + void Writef(const char* format, ...); + + template + void Write(T&& t, U&& u, Args&&... args) { + Write(std::forward(t)); + Write(std::forward(u)); + Write(std::forward(args)...); + } + + static const char* GetReferenceTypeName(const Type& type); + static const char* GetReferenceNullValue(const Type& type); + static const char* GetCTypeName(const Type& type); + + const char* InternalSymbolScope() const; + + enum class CWriterPhase { + Declarations, + Definitions, + }; + + void Write() {} + void Write(Newline); + void Write(OpenBrace); + void Write(CloseBrace); + void Write(uint64_t); + void Write(std::string_view); + void Write(const ParamName&); + void Write(const LabelName&); + void Write(const GlobalName&); + void Write(const ExternalPtr&); + void Write(const ExternalRef&); + void Write(const ExternalInstancePtr&); + void Write(const ExternalInstanceRef&); + void Write(Type); + void Write(SignedType); + void Write(TypeEnum); + void Write(const GotoLabel&); + void Write(const LabelDecl&); + void Write(const GlobalInstanceVar&); + void Write(const StackVar&); + void Write(const ResultType&); + void Write(const Const&); + void WriteInitExpr(const ExprList&); + void WriteInitExprTerminal(const Expr*); + std::string GenerateHeaderGuard() const; + void WriteSourceTop(); + void WriteMultiCTop(); + void WriteMultiCTopEmpty(); + void WriteMultivalueTypes(); + void WriteTagTypes(); + void WriteFuncTypeDecls(); + void WriteFuncTypes(); + void Write(const FuncTypeExpr&); + void WriteTagDecls(); + void WriteTags(); + void ComputeUniqueImports(); + void BeginInstance(); + void WriteImports(); + void WriteFuncDeclarations(); + void WriteFuncDeclaration(const FuncDeclaration&, const std::string&); + void WriteImportFuncDeclaration(const FuncDeclaration&, + const std::string& module_name, + const std::string&); + void WriteCallIndirectFuncDeclaration(const FuncDeclaration&, + const std::string&); + void ComputeSimdScope(); + void WriteHeaderIncludes(); + void WriteV128Decl(); + void WriteModuleInstance(); + void WriteGlobals(); + void WriteGlobal(const Global&, const std::string&); + void WriteGlobalPtr(const Global&, const std::string&); + void WriteMemories(); + void WriteMemory(const std::string&); + void WriteMemoryPtr(const std::string&); + void WriteTables(); + void WriteTable(const std::string&, const wabt::Type&); + void WriteTablePtr(const std::string&, const Table&); + void WriteTableType(const wabt::Type&); + void WriteDataInstances(); + void WriteElemInstances(); + void WriteGlobalInitializers(); + void WriteDataInitializerDecls(); + void WriteDataInitializers(); + void WriteElemInitializerDecls(); + void WriteElemInitializers(); + void WriteElemTableInit(bool, const ElemSegment*, const Table*); + void WriteExports(CWriterPhase); + void WriteInitDecl(); + void WriteFreeDecl(); + void WriteGetFuncTypeDecl(); + void WriteInit(); + void WriteFree(); + void WriteGetFuncType(); + void WriteInitInstanceImport(); + void WriteImportProperties(CWriterPhase); + void WriteFuncs(); + void Write(const Func&); + void WriteParamsAndLocals(); + void WriteParams(const std::vector& index_to_name); + void WriteParamSymbols(const std::vector& index_to_name); + void WriteParamTypes(const FuncDeclaration& decl); + void WriteLocals(const std::vector& index_to_name); + void WriteStackVarDeclarations(); + void Write(const ExprList&); + + enum class AssignOp { + Disallowed, + Allowed, + }; + + void WriteSimpleUnaryExpr(Opcode, const char* op); + void WriteInfixBinaryExpr(Opcode, + const char* op, + AssignOp = AssignOp::Allowed); + void WritePrefixBinaryExpr(Opcode, const char* op); + void WriteSignedBinaryExpr(Opcode, const char* op); + void Write(const BinaryExpr&); + void Write(const CompareExpr&); + void Write(const ConvertExpr&); + void Write(const LoadExpr&); + void Write(const StoreExpr&); + void Write(const UnaryExpr&); + void Write(const TernaryExpr&); + void Write(const SimdLaneOpExpr&); + void Write(const SimdLoadLaneExpr&); + void Write(const SimdStoreLaneExpr&); + void Write(const SimdShuffleOpExpr&); + void Write(const LoadSplatExpr&); + void Write(const LoadZeroExpr&); + void Write(const Block&); + + size_t BeginTry(const TryExpr& tryexpr); + void WriteTryCatch(const TryExpr& tryexpr); + void WriteTryDelegate(const TryExpr& tryexpr); + void Write(const Catch& c); + void WriteThrow(); + + void PushTryCatch(const std::string& name); + void PopTryCatch(); + + void PushFuncSection(std::string_view include_condition = ""); + + const WriteCOptions& options_; + const Module* module_ = nullptr; + const Func* func_ = nullptr; + Stream* stream_ = nullptr; + std::vector c_streams_; + Stream* h_stream_ = nullptr; + Stream* h_impl_stream_ = nullptr; + std::string header_name_; + std::string header_impl_name_; + Result result_ = Result::Ok; + int indent_ = 0; + bool should_write_indent_next_ = false; + int consecutive_newline_count_ = 0; + + SymbolMap global_sym_map_; + SymbolMap local_sym_map_; + SymbolMap import_module_sym_map_; + StackVarSymbolMap stack_var_sym_map_; + SymbolSet global_syms_; + SymbolSet local_syms_; + SymbolSet import_syms_; + TypeVector type_stack_; + std::vector