summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/wasm2c/src')
-rw-r--r--third_party/wasm2c/src/apply-names.cc510
-rw-r--r--third_party/wasm2c/src/apply-names.h45
-rw-r--r--third_party/wasm2c/src/binary-reader-ir.cc1570
-rw-r--r--third_party/wasm2c/src/binary-reader-ir.h37
-rw-r--r--third_party/wasm2c/src/binary-reader-logging.cc960
-rw-r--r--third_party/wasm2c/src/binary-reader-logging.h398
-rw-r--r--third_party/wasm2c/src/binary-reader-nop.h560
-rw-r--r--third_party/wasm2c/src/binary-reader-objdump.cc2085
-rw-r--r--third_party/wasm2c/src/binary-reader-objdump.h88
-rw-r--r--third_party/wasm2c/src/binary-reader-opcnt.cc290
-rw-r--r--third_party/wasm2c/src/binary-reader-opcnt.h93
-rw-r--r--third_party/wasm2c/src/binary-reader.cc2821
-rw-r--r--third_party/wasm2c/src/binary-reader.h479
-rw-r--r--third_party/wasm2c/src/binary-writer-spec.cc630
-rw-r--r--third_party/wasm2c/src/binary-writer-spec.h60
-rw-r--r--third_party/wasm2c/src/binary-writer.cc1675
-rw-r--r--third_party/wasm2c/src/binary-writer.h62
-rw-r--r--third_party/wasm2c/src/binary.cc62
-rw-r--r--third_party/wasm2c/src/binary.h94
-rw-r--r--third_party/wasm2c/src/binding-hash.cc91
-rw-r--r--third_party/wasm2c/src/binding-hash.h72
-rw-r--r--third_party/wasm2c/src/c-writer.cc2595
-rw-r--r--third_party/wasm2c/src/c-writer.h41
-rw-r--r--third_party/wasm2c/src/cast.h109
-rw-r--r--third_party/wasm2c/src/circular-array.h123
-rw-r--r--third_party/wasm2c/src/color.cc84
-rw-r--r--third_party/wasm2c/src/color.h72
-rw-r--r--third_party/wasm2c/src/common.cc147
-rw-r--r--third_party/wasm2c/src/common.h490
-rw-r--r--third_party/wasm2c/src/config.cc162
-rw-r--r--third_party/wasm2c/src/config.h.in316
-rw-r--r--third_party/wasm2c/src/decompiler-ast.h386
-rw-r--r--third_party/wasm2c/src/decompiler-ls.h265
-rw-r--r--third_party/wasm2c/src/decompiler-naming.h211
-rw-r--r--third_party/wasm2c/src/decompiler.cc832
-rw-r--r--third_party/wasm2c/src/decompiler.h36
-rw-r--r--third_party/wasm2c/src/emscripten-exported.json59
-rw-r--r--third_party/wasm2c/src/emscripten-helpers.cc406
-rw-r--r--third_party/wasm2c/src/error-formatter.cc127
-rw-r--r--third_party/wasm2c/src/error-formatter.h54
-rw-r--r--third_party/wasm2c/src/error.h58
-rw-r--r--third_party/wasm2c/src/expr-visitor.cc469
-rw-r--r--third_party/wasm2c/src/expr-visitor.h220
-rw-r--r--third_party/wasm2c/src/feature.cc56
-rw-r--r--third_party/wasm2c/src/feature.def38
-rw-r--r--third_party/wasm2c/src/feature.h58
-rw-r--r--third_party/wasm2c/src/filenames.cc56
-rw-r--r--third_party/wasm2c/src/filenames.h51
-rw-r--r--third_party/wasm2c/src/generate-names.cc430
-rw-r--r--third_party/wasm2c/src/generate-names.h46
-rw-r--r--third_party/wasm2c/src/hash-util.cc38
-rw-r--r--third_party/wasm2c/src/hash-util.h51
-rw-r--r--third_party/wasm2c/src/intrusive-list.h633
-rw-r--r--third_party/wasm2c/src/ir-util.cc272
-rw-r--r--third_party/wasm2c/src/ir-util.h76
-rw-r--r--third_party/wasm2c/src/ir.cc695
-rw-r--r--third_party/wasm2c/src/ir.h1354
-rw-r--r--third_party/wasm2c/src/leb128.cc361
-rw-r--r--third_party/wasm2c/src/leb128.h70
-rw-r--r--third_party/wasm2c/src/lexer-keywords.txt631
-rw-r--r--third_party/wasm2c/src/lexer-source-line-finder.cc152
-rw-r--r--third_party/wasm2c/src/lexer-source-line-finder.h61
-rw-r--r--third_party/wasm2c/src/lexer-source.cc67
-rw-r--r--third_party/wasm2c/src/lexer-source.h53
-rw-r--r--third_party/wasm2c/src/literal.cc830
-rw-r--r--third_party/wasm2c/src/literal.h84
-rw-r--r--third_party/wasm2c/src/make-unique.h42
-rw-r--r--third_party/wasm2c/src/opcode-code-table.c41
-rw-r--r--third_party/wasm2c/src/opcode-code-table.h38
-rw-r--r--third_party/wasm2c/src/opcode.cc407
-rw-r--r--third_party/wasm2c/src/opcode.def569
-rw-r--r--third_party/wasm2c/src/opcode.h182
-rw-r--r--third_party/wasm2c/src/option-parser.cc356
-rw-r--r--third_party/wasm2c/src/option-parser.h99
-rw-r--r--third_party/wasm2c/src/prebuilt/.clang-format2
-rw-r--r--third_party/wasm2c/src/prebuilt/lexer-keywords.cc1856
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c.include.c439
-rw-r--r--third_party/wasm2c/src/prebuilt/wasm2c.include.h55
-rw-r--r--third_party/wasm2c/src/range.h37
-rw-r--r--third_party/wasm2c/src/resolve-names.cc595
-rw-r--r--third_party/wasm2c/src/resolve-names.h33
-rw-r--r--third_party/wasm2c/src/result.h63
-rw-r--r--third_party/wasm2c/src/shared-validator.cc1261
-rw-r--r--third_party/wasm2c/src/shared-validator.h314
-rw-r--r--third_party/wasm2c/src/stream.cc316
-rw-r--r--third_party/wasm2c/src/stream.h227
-rw-r--r--third_party/wasm2c/src/string-view.cc196
-rw-r--r--third_party/wasm2c/src/string-view.h347
-rw-r--r--third_party/wasm2c/src/test-binary-reader.cc75
-rw-r--r--third_party/wasm2c/src/test-circular-array.cc284
-rw-r--r--third_party/wasm2c/src/test-filenames.cc61
-rw-r--r--third_party/wasm2c/src/test-hexfloat.cc264
-rw-r--r--third_party/wasm2c/src/test-interp.cc693
-rw-r--r--third_party/wasm2c/src/test-intrusive-list.cc584
-rw-r--r--third_party/wasm2c/src/test-literal.cc838
-rw-r--r--third_party/wasm2c/src/test-option-parser.cc181
-rw-r--r--third_party/wasm2c/src/test-string-view.cc415
-rw-r--r--third_party/wasm2c/src/test-utf8.cc167
-rw-r--r--third_party/wasm2c/src/test-wast-parser.cc87
-rw-r--r--third_party/wasm2c/src/token.cc99
-rw-r--r--third_party/wasm2c/src/token.def172
-rw-r--r--third_party/wasm2c/src/token.h133
-rw-r--r--third_party/wasm2c/src/tools/spectest-interp.cc1852
-rw-r--r--third_party/wasm2c/src/tools/wasm-decompile.cc119
-rw-r--r--third_party/wasm2c/src/tools/wasm-interp.cc337
-rw-r--r--third_party/wasm2c/src/tools/wasm-objdump.cc149
-rw-r--r--third_party/wasm2c/src/tools/wasm-opcodecnt.cc188
-rw-r--r--third_party/wasm2c/src/tools/wasm-strip.cc114
-rw-r--r--third_party/wasm2c/src/tools/wasm-validate.cc99
-rw-r--r--third_party/wasm2c/src/tools/wasm2c.cc169
-rw-r--r--third_party/wasm2c/src/tools/wasm2wat-fuzz.cc30
-rw-r--r--third_party/wasm2c/src/tools/wasm2wat.cc149
-rw-r--r--third_party/wasm2c/src/tools/wast2json.cc162
-rw-r--r--third_party/wasm2c/src/tools/wat-desugar.cc130
-rw-r--r--third_party/wasm2c/src/tools/wat2wasm.cc170
-rw-r--r--third_party/wasm2c/src/tracing.cc71
-rw-r--r--third_party/wasm2c/src/tracing.h73
-rw-r--r--third_party/wasm2c/src/type-checker.cc941
-rw-r--r--third_party/wasm2c/src/type-checker.h196
-rw-r--r--third_party/wasm2c/src/type.h145
-rw-r--r--third_party/wasm2c/src/utf8.cc104
-rw-r--r--third_party/wasm2c/src/utf8.h28
-rw-r--r--third_party/wasm2c/src/validator.cc1072
-rw-r--r--third_party/wasm2c/src/validator.h36
-rw-r--r--third_party/wasm2c/src/wabt.post.js376
-rw-r--r--third_party/wasm2c/src/wasm2c.c.tmpl433
-rw-r--r--third_party/wasm2c/src/wasm2c.h.tmpl51
-rwxr-xr-xthird_party/wasm2c/src/wasm2c_tmpl.py77
-rw-r--r--third_party/wasm2c/src/wast-lexer.cc557
-rw-r--r--third_party/wasm2c/src/wast-lexer.h108
-rw-r--r--third_party/wasm2c/src/wast-parser.cc3278
-rw-r--r--third_party/wasm2c/src/wast-parser.h250
-rw-r--r--third_party/wasm2c/src/wat-writer.cc1723
-rw-r--r--third_party/wasm2c/src/wat-writer.h37
134 files changed, 50859 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/apply-names.cc b/third_party/wasm2c/src/apply-names.cc
new file mode 100644
index 0000000000..321e263e40
--- /dev/null
+++ b/third_party/wasm2c/src/apply-names.cc
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/apply-names.h"
+
+#include <cassert>
+#include <cstdio>
+#include <vector>
+
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/string-view.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 OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) 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 BeginTryExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+
+ private:
+ void PushLabel(const std::string& label);
+ void PopLabel();
+ string_view FindLabelByVar(Var* var);
+ void UseNameForVar(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<std::string> param_and_local_index_to_name_;
+ std::vector<std::string> labels_;
+};
+
+NameApplier::NameApplier() : visitor_(this) {}
+
+void NameApplier::PushLabel(const std::string& label) {
+ labels_.push_back(label);
+}
+
+void NameApplier::PopLabel() {
+ labels_.pop_back();
+}
+
+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 string_view();
+ } else {
+ if (var->index() >= labels_.size()) {
+ return string_view();
+ }
+ return labels_[labels_.size() - 1 - var->index()];
+ }
+}
+
+void NameApplier::UseNameForVar(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::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ CHECK_RESULT(UseNameForDataSegmentVar(&expr->var));
+ 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::OnBrExpr(BrExpr* expr) {
+ string_view label = FindLabelByVar(&expr->var);
+ UseNameForVar(label, &expr->var);
+ return Result::Ok;
+}
+
+Result NameApplier::OnBrIfExpr(BrIfExpr* expr) {
+ string_view label = FindLabelByVar(&expr->var);
+ UseNameForVar(label, &expr->var);
+ return Result::Ok;
+}
+
+Result NameApplier::OnBrTableExpr(BrTableExpr* expr) {
+ for (Var& target : expr->targets) {
+ string_view label = FindLabelByVar(&target);
+ UseNameForVar(label, &target);
+ }
+
+ 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) {
+ 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) {
+ 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::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::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,
+ &param_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_) {
+ if (export_->kind == ExternalKind::Func) {
+ UseNameForFuncVar(&export_->var);
+ }
+ 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 (ElemExpr& elem_expr : segment->elem_exprs) {
+ if (elem_expr.kind == ElemExprKind::RefFunc) {
+ CHECK_RESULT(UseNameForFuncVar(&elem_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/apply-names.h b/third_party/wasm2c/src/apply-names.h
new file mode 100644
index 0000000000..1837c37076
--- /dev/null
+++ b/third_party/wasm2c/src/apply-names.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_APPLY_NAMES_H_
+#define WABT_APPLY_NAMES_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+struct Module;
+
+/* Use function, import, function type, parameter and local names in Vars
+ * that reference them.
+ *
+ * e.g. transform this:
+ *
+ * (func $foo ...)
+ * ...
+ * (call 0 ...)
+ *
+ * to this:
+ *
+ * (func $foo ...)
+ * ...
+ * (call $foo ...)
+ */
+Result ApplyNames(struct Module*);
+
+} // namespace wabt
+
+#endif /* WABT_APPLY_NAMES_H_ */
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..8290b95f5c
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-ir.cc
@@ -0,0 +1,1570 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-reader-ir.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <vector>
+
+#include "src/binary-reader-nop.h"
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/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 BinaryReaderIR : public BinaryReaderNop {
+ 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,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) override;
+ Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override;
+ Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
+
+ Result OnFunctionCount(Index count) override;
+ Result OnFunction(Index index, Index sig_index) override;
+
+ Result OnTableCount(Index count) override;
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+
+ Result OnMemoryCount(Index count) override;
+ Result OnMemory(Index index, const Limits* limits) override;
+
+ Result OnGlobalCount(Index count) override;
+ Result BeginGlobal(Index index, Type type, bool mutable_) override;
+ Result BeginGlobalInitExpr(Index index) override;
+ Result EndGlobalInitExpr(Index index) override;
+
+ Result OnExportCount(Index count) override;
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ 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 OnAtomicLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicWaitExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicFenceExpr(uint32_t consistency_model) override;
+ Result OnAtomicNotifyExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnBinaryExpr(Opcode opcode) override;
+ Result OnBlockExpr(Type sig_type) override;
+ Result OnBrExpr(Index depth) override;
+ Result OnBrIfExpr(Index depth) override;
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnCallExpr(Index func_index) override;
+ Result 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,
+ 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() override;
+ Result OnDataDropExpr(Index segment_index) override;
+ Result OnMemoryFillExpr() override;
+ Result OnMemoryGrowExpr() override;
+ Result OnMemoryInitExpr(Index segment_index) override;
+ Result OnMemorySizeExpr() 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,
+ 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,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdStoreLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override;
+ Result OnLoadSplatExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnLoadZeroExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+
+ Result OnElemSegmentCount(Index count) override;
+ Result BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) override;
+ Result BeginElemSegmentInitExpr(Index index) override;
+ Result EndElemSegmentInitExpr(Index index) override;
+ Result OnElemSegmentElemType(Index index, Type elem_type) override;
+ Result OnElemSegmentElemExprCount(Index index, Index count) override;
+ Result OnElemSegmentElemExpr_RefNull(Index segment_index, Type type) override;
+ Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) override;
+
+ Result 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(string_view module_name) override;
+ Result OnFunctionNamesCount(Index num_functions) override;
+ Result OnFunctionName(Index function_index,
+ string_view function_name) override;
+ Result OnLocalNameLocalCount(Index function_index, Index num_locals) override;
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) override;
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ 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 OnInitExprF32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprV128ConstExpr(Index index, v128 value) override;
+ Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
+ Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprRefNull(Index index, Type type) override;
+ Result OnInitExprRefFunc(Index index, Index func_index) override;
+
+ Result OnDataSymbol(Index index, uint32_t flags, string_view name,
+ Index segment, uint32_t offset, uint32_t size) override;
+ Result OnFunctionSymbol(Index index, uint32_t flags, string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index, uint32_t flags, 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,
+ string_view name,
+ Index tag_index) override;
+ Result OnTableSymbol(Index index, uint32_t flags, string_view name,
+ Index table_index) override;
+
+ private:
+ Location GetLocation() const;
+ void PrintError(const char* format, ...);
+ void PushLabel(LabelType label_type,
+ ExprList* first,
+ Expr* context = nullptr);
+ Result PopLabel();
+ Result GetLabelAt(LabelNode** label, Index depth);
+ Result TopLabel(LabelNode** label);
+ Result TopLabelExpr(LabelNode** label, Expr** expr);
+ Result AppendExpr(std::unique_ptr<Expr> expr);
+ Result AppendCatch(Catch&& catch_);
+ void SetFuncDeclaration(FuncDeclaration* decl, Var var);
+ void SetBlockDeclaration(BlockDeclaration* decl, Type sig_type);
+ Result SetMemoryName(Index index, string_view name);
+ Result SetTableName(Index index, string_view name);
+ Result SetFunctionName(Index index, string_view name);
+ Result SetGlobalName(Index index, string_view name);
+ Result SetDataSegmentName(Index index, string_view name);
+ Result SetElemSegmentName(Index index, 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<LabelNode> label_stack_;
+ ExprList* current_init_expr_ = nullptr;
+ const char* filename_;
+};
+
+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);
+}
+
+void BinaryReaderIR::PushLabel(LabelType label_type,
+ ExprList* first,
+ Expr* context) {
+ label_stack_.emplace_back(label_type, first, context);
+}
+
+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));
+ *expr = &parent_label->exprs->back();
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::AppendExpr(std::unique_ptr<Expr> 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));
+ } 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) {
+ auto field = MakeUnique<TypeModuleField>(GetLocation());
+ auto func_type = MakeUnique<FuncType>();
+ func_type->sig.param_types.assign(param_types, param_types + param_count);
+ func_type->sig.result_types.assign(result_types, result_types + result_count);
+ 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 = MakeUnique<TypeModuleField>(GetLocation());
+ auto struct_type = MakeUnique<StructType>();
+ 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_;
+ }
+ field->type = std::move(struct_type);
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnArrayType(Index index, TypeMut type_mut) {
+ auto field = MakeUnique<TypeModuleField>(GetLocation());
+ auto array_type = MakeUnique<ArrayType>();
+ array_type->field.type = type_mut.type;
+ array_type->field.mutable_ = type_mut.mutable_;
+ 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,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) {
+ auto import = MakeUnique<FuncImport>();
+ import->module_name = module_name.to_string();
+ import->field_name = field_name.to_string();
+ SetFuncDeclaration(&import->func.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) {
+ auto import = MakeUnique<TableImport>();
+ import->module_name = module_name.to_string();
+ import->field_name = field_name.to_string();
+ import->table.elem_limits = *elem_limits;
+ import->table.elem_type = elem_type;
+ module_->AppendField(
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) {
+ auto import = MakeUnique<MemoryImport>();
+ import->module_name = module_name.to_string();
+ import->field_name = field_name.to_string();
+ import->memory.page_limits = *page_limits;
+ module_->AppendField(
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) {
+ auto import = MakeUnique<GlobalImport>();
+ import->module_name = module_name.to_string();
+ import->field_name = field_name.to_string();
+ import->global.type = type;
+ import->global.mutable_ = mutable_;
+ module_->AppendField(
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) {
+ auto import = MakeUnique<TagImport>();
+ import->module_name = module_name.to_string();
+ import->field_name = field_name.to_string();
+ SetFuncDeclaration(&import->tag.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation()));
+ 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 = MakeUnique<FuncModuleField>(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 = MakeUnique<TableModuleField>(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 = MakeUnique<MemoryModuleField>(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 = MakeUnique<GlobalModuleField>(GetLocation());
+ Global& global = field->global;
+ global.type = type;
+ global.mutable_ = mutable_;
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginGlobalInitExpr(Index index) {
+ assert(index == module_->globals.size() - 1);
+ Global* global = module_->globals[index];
+ current_init_expr_ = &global->init_expr;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::EndGlobalInitExpr(Index index) {
+ current_init_expr_ = nullptr;
+ return Result::Ok;
+}
+
+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,
+ string_view name) {
+ auto field = MakeUnique<ExportModuleField>(GetLocation());
+ Export& export_ = field->export_;
+ export_.name = name.to_string();
+ 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(MakeUnique<StartModuleField>(start, GetLocation()));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnFunctionBodyCount(Index count) {
+ assert(module_->num_func_imports + count == module_->funcs.size());
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::BeginFunctionBody(Index index, Offset size) {
+ current_func_ = module_->funcs[index];
+ PushLabel(LabelType::Func, &current_func_->exprs);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnLocalDecl(Index decl_index, Index count, Type type) {
+ current_func_->local_types.AppendDecl(type, count);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnAtomicLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicLoadExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicStoreExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicRmwExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicRmwExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicRmwCmpxchgExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicWaitExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicWaitExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnAtomicFenceExpr(uint32_t consistency_model) {
+ return AppendExpr(MakeUnique<AtomicFenceExpr>(consistency_model));
+}
+
+Result BinaryReaderIR::OnAtomicNotifyExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<AtomicNotifyExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnBinaryExpr(Opcode opcode) {
+ return AppendExpr(MakeUnique<BinaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnBlockExpr(Type sig_type) {
+ auto expr = MakeUnique<BlockExpr>();
+ SetBlockDeclaration(&expr->block.decl, sig_type);
+ ExprList* expr_list = &expr->block.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ PushLabel(LabelType::Block, expr_list);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnBrExpr(Index depth) {
+ return AppendExpr(MakeUnique<BrExpr>(Var(depth)));
+}
+
+Result BinaryReaderIR::OnBrIfExpr(Index depth) {
+ return AppendExpr(MakeUnique<BrIfExpr>(Var(depth)));
+}
+
+Result BinaryReaderIR::OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ auto expr = MakeUnique<BrTableExpr>();
+ expr->default_target = Var(default_target_depth);
+ expr->targets.resize(num_targets);
+ for (Index i = 0; i < num_targets; ++i) {
+ expr->targets[i] = Var(target_depths[i]);
+ }
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCallExpr(Index func_index) {
+ return AppendExpr(MakeUnique<CallExpr>(Var(func_index)));
+}
+
+Result BinaryReaderIR::OnCallIndirectExpr(Index sig_index, Index table_index) {
+ auto expr = MakeUnique<CallIndirectExpr>();
+ SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation()));
+ expr->table = Var(table_index);
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCallRefExpr() {
+ return AppendExpr(MakeUnique<CallRefExpr>());
+}
+
+Result BinaryReaderIR::OnReturnCallExpr(Index func_index) {
+ return AppendExpr(MakeUnique<ReturnCallExpr>(Var(func_index)));
+}
+
+Result BinaryReaderIR::OnReturnCallIndirectExpr(Index sig_index, Index table_index) {
+ auto expr = MakeUnique<ReturnCallIndirectExpr>();
+ SetFuncDeclaration(&expr->decl, Var(sig_index, GetLocation()));
+ expr->table = Var(table_index);
+ return AppendExpr(std::move(expr));
+}
+
+Result BinaryReaderIR::OnCompareExpr(Opcode opcode) {
+ return AppendExpr(MakeUnique<CompareExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnConvertExpr(Opcode opcode) {
+ return AppendExpr(MakeUnique<ConvertExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnDropExpr() {
+ return AppendExpr(MakeUnique<DropExpr>());
+}
+
+Result BinaryReaderIR::OnElseExpr() {
+ LabelNode* label;
+ Expr* expr;
+ CHECK_RESULT(TopLabelExpr(&label, &expr));
+
+ if (label->label_type == LabelType::If) {
+ auto* if_expr = cast<IfExpr>(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() {
+ LabelNode* label;
+ Expr* expr;
+ CHECK_RESULT(TopLabelExpr(&label, &expr));
+ switch (label->label_type) {
+ case LabelType::Block:
+ cast<BlockExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::Loop:
+ cast<LoopExpr>(expr)->block.end_loc = GetLocation();
+ break;
+ case LabelType::If:
+ cast<IfExpr>(expr)->true_.end_loc = GetLocation();
+ break;
+ case LabelType::Else:
+ cast<IfExpr>(expr)->false_end_loc = GetLocation();
+ break;
+ case LabelType::Try:
+ cast<TryExpr>(expr)->block.end_loc = GetLocation();
+ break;
+
+ case LabelType::Func:
+ case LabelType::Catch:
+ break;
+ }
+
+ return PopLabel();
+}
+
+Result BinaryReaderIR::OnF32ConstExpr(uint32_t value_bits) {
+ return AppendExpr(
+ MakeUnique<ConstExpr>(Const::F32(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnF64ConstExpr(uint64_t value_bits) {
+ return AppendExpr(
+ MakeUnique<ConstExpr>(Const::F64(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnV128ConstExpr(v128 value_bits) {
+ return AppendExpr(
+ MakeUnique<ConstExpr>(Const::V128(value_bits, GetLocation())));
+}
+
+Result BinaryReaderIR::OnGlobalGetExpr(Index global_index) {
+ return AppendExpr(
+ MakeUnique<GlobalGetExpr>(Var(global_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalGetExpr(Index local_index) {
+ return AppendExpr(MakeUnique<LocalGetExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnI32ConstExpr(uint32_t value) {
+ return AppendExpr(MakeUnique<ConstExpr>(Const::I32(value, GetLocation())));
+}
+
+Result BinaryReaderIR::OnI64ConstExpr(uint64_t value) {
+ return AppendExpr(MakeUnique<ConstExpr>(Const::I64(value, GetLocation())));
+}
+
+Result BinaryReaderIR::OnIfExpr(Type sig_type) {
+ auto expr = MakeUnique<IfExpr>();
+ SetBlockDeclaration(&expr->true_.decl, sig_type);
+ ExprList* expr_list = &expr->true_.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ PushLabel(LabelType::If, expr_list);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(MakeUnique<LoadExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnLoopExpr(Type sig_type) {
+ auto expr = MakeUnique<LoopExpr>();
+ SetBlockDeclaration(&expr->block.decl, sig_type);
+ ExprList* expr_list = &expr->block.exprs;
+ CHECK_RESULT(AppendExpr(std::move(expr)));
+ PushLabel(LabelType::Loop, expr_list);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnMemoryCopyExpr() {
+ return AppendExpr(MakeUnique<MemoryCopyExpr>());
+}
+
+Result BinaryReaderIR::OnDataDropExpr(Index segment) {
+ return AppendExpr(MakeUnique<DataDropExpr>(Var(segment)));
+}
+
+Result BinaryReaderIR::OnMemoryFillExpr() {
+ return AppendExpr(MakeUnique<MemoryFillExpr>());
+}
+
+Result BinaryReaderIR::OnMemoryGrowExpr() {
+ return AppendExpr(MakeUnique<MemoryGrowExpr>());
+}
+
+Result BinaryReaderIR::OnMemoryInitExpr(Index segment) {
+ return AppendExpr(MakeUnique<MemoryInitExpr>(Var(segment)));
+}
+
+Result BinaryReaderIR::OnMemorySizeExpr() {
+ return AppendExpr(MakeUnique<MemorySizeExpr>());
+}
+
+Result BinaryReaderIR::OnTableCopyExpr(Index dst_index, Index src_index) {
+ return AppendExpr(MakeUnique<TableCopyExpr>(Var(dst_index), Var(src_index)));
+}
+
+Result BinaryReaderIR::OnElemDropExpr(Index segment) {
+ return AppendExpr(MakeUnique<ElemDropExpr>(Var(segment)));
+}
+
+Result BinaryReaderIR::OnTableInitExpr(Index segment, Index table_index) {
+ return AppendExpr(MakeUnique<TableInitExpr>(Var(segment), Var(table_index)));
+}
+
+Result BinaryReaderIR::OnTableGetExpr(Index table_index) {
+ return AppendExpr(MakeUnique<TableGetExpr>(Var(table_index)));
+}
+
+Result BinaryReaderIR::OnTableSetExpr(Index table_index) {
+ return AppendExpr(MakeUnique<TableSetExpr>(Var(table_index)));
+}
+
+Result BinaryReaderIR::OnTableGrowExpr(Index table_index) {
+ return AppendExpr(MakeUnique<TableGrowExpr>(Var(table_index)));
+}
+
+Result BinaryReaderIR::OnTableSizeExpr(Index table_index) {
+ return AppendExpr(MakeUnique<TableSizeExpr>(Var(table_index)));
+}
+
+Result BinaryReaderIR::OnTableFillExpr(Index table_index) {
+ return AppendExpr(MakeUnique<TableFillExpr>(Var(table_index)));
+}
+
+Result BinaryReaderIR::OnRefFuncExpr(Index func_index) {
+ return AppendExpr(MakeUnique<RefFuncExpr>(Var(func_index)));
+}
+
+Result BinaryReaderIR::OnRefNullExpr(Type type) {
+ return AppendExpr(MakeUnique<RefNullExpr>(type));
+}
+
+Result BinaryReaderIR::OnRefIsNullExpr() {
+ return AppendExpr(MakeUnique<RefIsNullExpr>());
+}
+
+Result BinaryReaderIR::OnNopExpr() {
+ return AppendExpr(MakeUnique<NopExpr>());
+}
+
+Result BinaryReaderIR::OnRethrowExpr(Index depth) {
+ return AppendExpr(MakeUnique<RethrowExpr>(Var(depth, GetLocation())));
+}
+
+Result BinaryReaderIR::OnReturnExpr() {
+ return AppendExpr(MakeUnique<ReturnExpr>());
+}
+
+Result BinaryReaderIR::OnSelectExpr(Index result_count, Type* result_types) {
+ TypeVector results;
+ results.assign(result_types, result_types + result_count);
+ return AppendExpr(MakeUnique<SelectExpr>(results));
+}
+
+Result BinaryReaderIR::OnGlobalSetExpr(Index global_index) {
+ return AppendExpr(
+ MakeUnique<GlobalSetExpr>(Var(global_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalSetExpr(Index local_index) {
+ return AppendExpr(MakeUnique<LocalSetExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(MakeUnique<StoreExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnThrowExpr(Index tag_index) {
+ return AppendExpr(MakeUnique<ThrowExpr>(Var(tag_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnLocalTeeExpr(Index local_index) {
+ return AppendExpr(MakeUnique<LocalTeeExpr>(Var(local_index, GetLocation())));
+}
+
+Result BinaryReaderIR::OnTryExpr(Type sig_type) {
+ auto expr_ptr = MakeUnique<TryExpr>();
+ // 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)));
+ PushLabel(LabelType::Try, expr_list, expr);
+ return Result::Ok;
+}
+
+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<TryExpr>(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<TryExpr>(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(MakeUnique<UnaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnTernaryExpr(Opcode opcode) {
+ return AppendExpr(MakeUnique<TernaryExpr>(opcode));
+}
+
+Result BinaryReaderIR::OnUnreachableExpr() {
+ return AppendExpr(MakeUnique<UnreachableExpr>());
+}
+
+Result BinaryReaderIR::EndFunctionBody(Index index) {
+ CHECK_RESULT(PopLabel());
+ current_func_ = nullptr;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) {
+ return AppendExpr(MakeUnique<SimdLaneOpExpr>(opcode, value));
+}
+
+Result BinaryReaderIR::OnSimdLoadLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ return AppendExpr(
+ MakeUnique<SimdLoadLaneExpr>(opcode, 1 << alignment_log2, offset, value));
+}
+
+Result BinaryReaderIR::OnSimdStoreLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) {
+ return AppendExpr(
+ MakeUnique<SimdStoreLaneExpr>(opcode, 1 << alignment_log2, offset, value));
+}
+
+Result BinaryReaderIR::OnSimdShuffleOpExpr(Opcode opcode, v128 value) {
+ return AppendExpr(MakeUnique<SimdShuffleOpExpr>(opcode, value));
+}
+
+Result BinaryReaderIR::OnLoadSplatExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<LoadSplatExpr>(opcode, 1 << alignment_log2, offset));
+}
+
+Result BinaryReaderIR::OnLoadZeroExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) {
+ return AppendExpr(
+ MakeUnique<LoadZeroExpr>(opcode, 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 = MakeUnique<ElemSegmentModuleField>(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::BeginElemSegmentInitExpr(Index index) {
+ assert(index == module_->elem_segments.size() - 1);
+ ElemSegment* segment = module_->elem_segments[index];
+ current_init_expr_ = &segment->offset;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::EndElemSegmentInitExpr(Index index) {
+ current_init_expr_ = nullptr;
+ return Result::Ok;
+}
+
+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];
+ segment->elem_exprs.emplace_back(type);
+ 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];
+ segment->elem_exprs.emplace_back(Var(func_index, GetLocation()));
+ 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 = MakeUnique<DataSegmentModuleField>(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];
+ current_init_expr_ = &segment->offset;
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::EndDataSegmentInitExpr(Index index) {
+ current_init_expr_ = nullptr;
+ return Result::Ok;
+}
+
+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(string_view name) {
+ return std::string("$") + name.to_string();
+}
+
+Result BinaryReaderIR::OnModuleName(string_view name) {
+ if (name.empty()) {
+ return Result::Ok;
+ }
+
+ module_->name = MakeDollarName(name);
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::SetGlobalName(Index index, 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, 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::SetTableName(Index index, 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, 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, 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, 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::OnFunctionName(Index index, string_view name) {
+ return SetFunctionName(index, name);
+}
+
+Result BinaryReaderIR::OnNameEntry(NameSectionSubsection type,
+ Index index,
+ 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:
+ case NameSectionSubsection::Type:
+ 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::OnInitExprF32ConstExpr(Index index, uint32_t value) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<ConstExpr>(Const::F32(value, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprF64ConstExpr(Index index, uint64_t value) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<ConstExpr>(Const::F64(value, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprV128ConstExpr(Index index, v128 value) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<ConstExpr>(Const::V128(value, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprGlobalGetExpr(Index index,
+ Index global_index) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<GlobalGetExpr>(Var(global_index, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprI32ConstExpr(Index index, uint32_t value) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<ConstExpr>(Const::I32(value, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprI64ConstExpr(Index index, uint64_t value) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<ConstExpr>(Const::I64(value, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprRefNull(Index index, Type type) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(MakeUnique<RefNullExpr>(type, loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnInitExprRefFunc(Index index, Index func_index) {
+ Location loc = GetLocation();
+ current_init_expr_->push_back(
+ MakeUnique<RefFuncExpr>(Var(func_index, loc), loc));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnLocalName(Index func_index,
+ Index local_index,
+ 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 = MakeUnique<TagModuleField>(GetLocation());
+ Tag& tag = field->tag;
+ SetFuncDeclaration(&tag.decl, Var(sig_index, GetLocation()));
+ module_->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result BinaryReaderIR::OnDataSymbol(Index index, uint32_t flags,
+ 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,
+ 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,
+ 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,
+ 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,
+ string_view name, Index table_index) {
+ return SetTableName(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-ir.h b/third_party/wasm2c/src/binary-reader-ir.h
new file mode 100644
index 0000000000..18c15585b9
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-ir.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_IR_H_
+#define WABT_BINARY_READER_IR_H_
+
+#include "src/common.h"
+#include "src/error.h"
+
+namespace wabt {
+
+struct Module;
+struct ReadBinaryOptions;
+
+Result ReadBinaryIr(const char* filename,
+ const void* data,
+ size_t size,
+ const ReadBinaryOptions& options,
+ Errors*,
+ Module* out_module);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_READER_IR_H_ */
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..77ee5736cf
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-logging.cc
@@ -0,0 +1,960 @@
+/*
+ * 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 "src/binary-reader-logging.h"
+
+#include <cinttypes>
+
+#include "src/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<size_t>(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());
+ }
+}
+
+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,
+ 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,
+ string_view module_name,
+ 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,
+ string_view module_name,
+ 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,
+ string_view module_name,
+ 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(), buf);
+ return reader_->OnImportTable(import_index, module_name, field_name,
+ table_index, elem_type, elem_limits);
+}
+
+Result BinaryReaderLogging::OnImportMemory(Index import_index,
+ string_view module_name,
+ 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,
+ string_view module_name,
+ 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(), mutable_ ? "true" : "false");
+ return reader_->OnImportGlobal(import_index, module_name, field_name,
+ global_index, type, mutable_);
+}
+
+Result BinaryReaderLogging::OnImportTag(Index import_index,
+ string_view module_name,
+ 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(), 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(), mutable_ ? "true" : "false");
+ return reader_->BeginGlobal(index, type, mutable_);
+}
+
+Result BinaryReaderLogging::OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ 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());
+ 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());
+ 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(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, 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,
+ 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,
+ 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::OnInitExprF32ConstExpr(Index index,
+ uint32_t value_bits) {
+ float value;
+ memcpy(&value, &value_bits, sizeof(value));
+ LOGF("OnInitExprF32ConstExpr(index: %" PRIindex ", value: %g (0x04%x))\n",
+ index, value, value_bits);
+ return reader_->OnInitExprF32ConstExpr(index, value_bits);
+}
+
+Result BinaryReaderLogging::OnInitExprF64ConstExpr(Index index,
+ uint64_t value_bits) {
+ double value;
+ memcpy(&value, &value_bits, sizeof(value));
+ LOGF("OnInitExprF64ConstExpr(index: %" PRIindex " value: %g (0x08%" PRIx64
+ "))\n",
+ index, value, value_bits);
+ return reader_->OnInitExprF64ConstExpr(index, value_bits);
+}
+
+Result BinaryReaderLogging::OnInitExprV128ConstExpr(Index index,
+ v128 value_bits) {
+ LOGF("OnInitExprV128ConstExpr(index: %" PRIindex
+ " value: ( 0x%08x 0x%08x 0x%08x 0x%08x))\n",
+ index, value_bits.u32(0), value_bits.u32(1), value_bits.u32(2),
+ value_bits.u32(3));
+ return reader_->OnInitExprV128ConstExpr(index, value_bits);
+}
+
+Result BinaryReaderLogging::OnInitExprI32ConstExpr(Index index,
+ uint32_t value) {
+ LOGF("OnInitExprI32ConstExpr(index: %" PRIindex ", value: %u)\n", index,
+ value);
+ return reader_->OnInitExprI32ConstExpr(index, value);
+}
+
+Result BinaryReaderLogging::OnInitExprI64ConstExpr(Index index,
+ uint64_t value) {
+ LOGF("OnInitExprI64ConstExpr(index: %" PRIindex ", value: %" PRIu64 ")\n",
+ index, value);
+ return reader_->OnInitExprI64ConstExpr(index, value);
+}
+
+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(string_view so_name) {
+ LOGF("OnDylinkNeeded(name: " PRIstringview ")\n",
+ WABT_PRINTF_STRING_VIEW_ARG(so_name));
+ return reader_->OnDylinkNeeded(so_name);
+}
+
+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<int32_t>(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::OnDataSymbol(Index index,
+ uint32_t flags,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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 func_index) {
+ LOGF("OnInitFunction(%d priority: %d)\n", func_index, priority);
+ return reader_->OnInitFunction(priority, func_index);
+}
+
+Result BinaryReaderLogging::OnComdatBegin(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<int>(kind), index);
+ return reader_->OnComdatEntry(kind, index);
+}
+
+#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()); \
+ 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()); \
+ 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, Address alignment_log2, \
+ Address offset) { \
+ LOGF(#name "(opcode: \"%s\" (%u), align log2: %" PRIaddress \
+ ", offset: %" PRIaddress ")\n", \
+ opcode.GetName(), opcode.GetCode(), alignment_log2, offset); \
+ return reader_->name(opcode, alignment_log2, offset); \
+ }
+
+#define DEFINE_SIMD_LOAD_STORE_LANE_OPCODE(name) \
+ Result BinaryReaderLogging::name(Opcode opcode, Address alignment_log2, \
+ Address offset, uint64_t value) { \
+ LOGF(#name "(opcode: \"%s\" (%u), align log2: %" PRIaddress \
+ ", offset: %" PRIaddress ", lane: %" PRIu64 ")\n", \
+ opcode.GetName(), opcode.GetCode(), alignment_log2, offset, value); \
+ return reader_->name(opcode, 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")
+DEFINE0(OnMemoryCopyExpr)
+DEFINE_INDEX(OnDataDropExpr)
+DEFINE0(OnMemoryFillExpr)
+DEFINE0(OnMemoryGrowExpr)
+DEFINE_INDEX(OnMemoryInitExpr)
+DEFINE0(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_INDEX_INDEX(OnInitExprGlobalGetExpr, "index", "global_index")
+DEFINE_INDEX_TYPE(OnInitExprRefNull)
+DEFINE_INDEX_INDEX(OnInitExprRefFunc, "index", "func_index")
+
+DEFINE_BEGIN(BeginDylinkSection)
+DEFINE_INDEX(OnDylinkNeededCount)
+DEFINE_END(EndDylinkSection)
+
+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);
+
+// 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::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);
+}
+
+Result BinaryReaderLogging::OnEndFunc() {
+ return reader_->OnEndFunc();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-logging.h b/third_party/wasm2c/src/binary-reader-logging.h
new file mode 100644
index 0000000000..423a321cee
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-logging.h
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_LOGGING_H_
+#define WABT_BINARY_READER_LOGGING_H_
+
+#include "src/binary-reader.h"
+
+namespace wabt {
+
+class Stream;
+
+class BinaryReaderLogging : public BinaryReaderDelegate {
+ public:
+ BinaryReaderLogging(Stream*, BinaryReaderDelegate* forward);
+
+ bool OnError(const Error&) override;
+ void OnSetState(const State* s) override;
+
+ Result BeginModule(uint32_t version) override;
+ Result EndModule() override;
+
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override;
+
+ Result BeginCustomSection(Index section_index,
+ Offset size,
+ string_view section_name) override;
+ Result EndCustomSection() override;
+
+ Result BeginTypeSection(Offset size) 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 EndTypeSection() override;
+
+ Result BeginImportSection(Offset size) override;
+ Result OnImportCount(Index count) override;
+ Result OnImport(Index index,
+ ExternalKind kind,
+ string_view module_name,
+ string_view field_name) override;
+ Result OnImportFunc(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) override;
+ Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override;
+ Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
+ Result EndImportSection() override;
+
+ Result BeginFunctionSection(Offset size) override;
+ Result OnFunctionCount(Index count) override;
+ Result OnFunction(Index index, Index sig_index) override;
+ Result EndFunctionSection() override;
+
+ Result BeginTableSection(Offset size) override;
+ Result OnTableCount(Index count) override;
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result EndTableSection() override;
+
+ Result BeginMemorySection(Offset size) override;
+ Result OnMemoryCount(Index count) override;
+ Result OnMemory(Index index, const Limits* limits) override;
+ Result EndMemorySection() override;
+
+ Result BeginGlobalSection(Offset size) 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 EndGlobal(Index index) override;
+ Result EndGlobalSection() override;
+
+ Result BeginExportSection(Offset size) override;
+ Result OnExportCount(Index count) override;
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ string_view name) override;
+ Result EndExportSection() override;
+
+ Result BeginStartSection(Offset size) override;
+ Result OnStartFunction(Index func_index) override;
+ Result EndStartSection() override;
+
+ Result BeginCodeSection(Offset size) override;
+ Result OnFunctionBodyCount(Index count) override;
+ Result BeginFunctionBody(Index index, Offset size) override;
+ Result OnLocalDeclCount(Index count) override;
+ Result OnLocalDecl(Index decl_index, Index count, Type type) override;
+
+ Result OnOpcode(Opcode opcode) override;
+ Result 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 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 OnOpcodeType(Type type) override;
+ Result OnAtomicLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result 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 OnCompareExpr(Opcode opcode) override;
+ Result OnConvertExpr(Opcode opcode) override;
+ Result OnDelegateExpr(Index depth) override;
+ Result OnDropExpr() override;
+ Result OnElseExpr() override;
+ Result OnEndExpr() override;
+ Result OnEndFunc() 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,
+ 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() override;
+ Result OnDataDropExpr(Index segment_index) override;
+ Result OnMemoryFillExpr() override;
+ Result OnMemoryGrowExpr() override;
+ Result OnMemoryInitExpr(Index segment_index) override;
+ Result OnMemorySizeExpr() 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) override;
+ Result OnTableSetExpr(Index table) override;
+ Result OnTableGrowExpr(Index table) override;
+ Result OnTableSizeExpr(Index table) override;
+ Result OnTableFillExpr(Index table) override;
+ Result OnRefFuncExpr(Index index) override;
+ Result OnRefNullExpr(Type type) override;
+ Result OnRefIsNullExpr() override;
+ Result OnNopExpr() override;
+ Result OnRethrowExpr(Index depth) override;
+ Result OnReturnCallExpr(Index func_index) override;
+ Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override;
+ Result OnReturnExpr() override;
+ Result OnSelectExpr(Index result_count, Type* result_types) override;
+ Result OnStoreExpr(Opcode opcode,
+ 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 OnAtomicWaitExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnAtomicFenceExpr(uint32_t consistency_model) override;
+ Result OnAtomicNotifyExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result EndFunctionBody(Index index) override;
+ Result EndCodeSection() override;
+ Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override;
+ Result OnSimdLoadLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdStoreLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override;
+ Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override;
+ Result OnLoadSplatExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+ Result OnLoadZeroExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override;
+
+ Result BeginElemSection(Offset size) 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 EndElemSegment(Index index) override;
+ Result EndElemSection() override;
+
+ Result BeginDataSection(Offset size) 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 EndDataSegment(Index index) override;
+ Result EndDataSection() override;
+
+ Result BeginDataCountSection(Offset size) override;
+ Result OnDataCount(Index count) override;
+ Result EndDataCountSection() override;
+
+ Result BeginNamesSection(Offset size) override;
+ Result OnModuleNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override;
+ Result OnModuleName(string_view name) override;
+ Result OnFunctionNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override;
+ Result OnFunctionNamesCount(Index num_functions) override;
+ Result OnFunctionName(Index function_index,
+ string_view function_name) override;
+ Result OnLocalNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override;
+ Result OnLocalNameFunctionCount(Index num_functions) override;
+ Result OnLocalNameLocalCount(Index function_index, Index num_locals) override;
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) override;
+ Result OnNameSubsection(Index index,
+ NameSectionSubsection subsection_type,
+ Offset subsection_size) override;
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ string_view name) override;
+ Result OnNameCount(Index num_names) override;
+ Result EndNamesSection() override;
+
+ Result BeginRelocSection(Offset size) override;
+ Result OnRelocCount(Index count, Index section_index) override;
+ Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) override;
+ Result EndRelocSection() override;
+
+ Result BeginDylinkSection(Offset size) override;
+ Result OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align,
+ uint32_t table_size,
+ uint32_t table_align) override;
+ Result OnDylinkNeededCount(Index count) override;
+ Result OnDylinkNeeded(string_view needed) override;
+ Result EndDylinkSection() override;
+
+ Result BeginLinkingSection(Offset size) override;
+ Result OnSymbolCount(Index count) override;
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override;
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ 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,
+ string_view name,
+ Index tag_index) override;
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index tag_index) override;
+ Result OnSegmentInfoCount(Index count) override;
+ Result OnSegmentInfo(Index index,
+ string_view name,
+ Address alignment,
+ uint32_t flags) override;
+ Result OnInitFunctionCount(Index count) override;
+ Result OnInitFunction(uint32_t priority, Index function_index) override;
+ Result OnComdatCount(Index count) override;
+ Result OnComdatBegin(string_view name, uint32_t flags, Index count) override;
+ Result OnComdatEntry(ComdatType kind, Index index) override;
+ Result EndLinkingSection() override;
+
+ Result BeginTagSection(Offset size) override;
+ Result OnTagCount(Index count) override;
+ Result OnTagType(Index index, Index sig_index) override;
+ Result EndTagSection() override;
+
+ Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprV128ConstExpr(Index index, v128 value) override;
+ Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
+ Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprRefNull(Index index, Type type) override;
+ Result OnInitExprRefFunc(Index index, Index func_index) override;
+
+ private:
+ void Indent();
+ void Dedent();
+ void WriteIndent();
+ void LogType(Type type);
+ void LogTypes(Index type_count, Type* types);
+ void LogTypes(TypeVector& types);
+ void LogField(TypeMut field);
+
+ Stream* stream_;
+ BinaryReaderDelegate* reader_;
+ int indent_;
+};
+
+} // namespace wabt
+
+#endif // WABT_BINARY_READER_LOGGING_H_
diff --git a/third_party/wasm2c/src/binary-reader-nop.h b/third_party/wasm2c/src/binary-reader-nop.h
new file mode 100644
index 0000000000..f22b05ad5b
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-nop.h
@@ -0,0 +1,560 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_NOP_H_
+#define WABT_BINARY_READER_NOP_H_
+
+#include "src/binary-reader.h"
+
+namespace wabt {
+
+class BinaryReaderNop : public BinaryReaderDelegate {
+ public:
+ bool OnError(const Error&) override { return false; }
+
+ /* Module */
+ Result BeginModule(uint32_t version) override { return Result::Ok; }
+ Result EndModule() override { return Result::Ok; }
+
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override {
+ return Result::Ok;
+ }
+
+ /* Custom section */
+ Result BeginCustomSection(Index section_index,
+ Offset size,
+ string_view section_name) override {
+ return Result::Ok;
+ }
+ Result EndCustomSection() override { return Result::Ok; }
+
+ /* Type section */
+ Result BeginTypeSection(Offset size) override { return Result::Ok; }
+ Result OnTypeCount(Index count) override { return Result::Ok; }
+ Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) override {
+ return Result::Ok;
+ }
+ Result OnStructType(Index index,
+ Index field_count,
+ TypeMut* fields) override {
+ return Result::Ok;
+ }
+ Result OnArrayType(Index index, TypeMut field) override {
+ return Result::Ok;
+ }
+ Result EndTypeSection() override { return Result::Ok; }
+
+ /* Import section */
+ Result BeginImportSection(Offset size) override { return Result::Ok; }
+ Result OnImportCount(Index count) override { return Result::Ok; }
+ Result OnImport(Index index,
+ ExternalKind kind,
+ string_view module_name,
+ string_view field_name) override {
+ return Result::Ok;
+ }
+ Result OnImportFunc(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) override {
+ return Result::Ok;
+ }
+ Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override {
+ return Result::Ok;
+ }
+ Result OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override {
+ return Result::Ok;
+ }
+ Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override {
+ return Result::Ok;
+ }
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override {
+ return Result::Ok;
+ }
+ Result EndImportSection() override { return Result::Ok; }
+
+ /* Function section */
+ Result BeginFunctionSection(Offset size) override { return Result::Ok; }
+ Result OnFunctionCount(Index count) override { return Result::Ok; }
+ Result OnFunction(Index index, Index sig_index) override {
+ return Result::Ok;
+ }
+ Result EndFunctionSection() override { return Result::Ok; }
+
+ /* Table section */
+ Result BeginTableSection(Offset size) override { return Result::Ok; }
+ Result OnTableCount(Index count) override { return Result::Ok; }
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override {
+ return Result::Ok;
+ }
+ Result EndTableSection() override { return Result::Ok; }
+
+ /* Memory section */
+ Result BeginMemorySection(Offset size) override { return Result::Ok; }
+ Result OnMemoryCount(Index count) override { return Result::Ok; }
+ Result OnMemory(Index index, const Limits* limits) override {
+ return Result::Ok;
+ }
+ Result EndMemorySection() override { return Result::Ok; }
+
+ /* Global section */
+ Result BeginGlobalSection(Offset size) override { return Result::Ok; }
+ Result OnGlobalCount(Index count) override { return Result::Ok; }
+ Result BeginGlobal(Index index, Type type, bool mutable_) override {
+ return Result::Ok;
+ }
+ Result BeginGlobalInitExpr(Index index) override { return Result::Ok; }
+ Result EndGlobalInitExpr(Index index) override { return Result::Ok; }
+ Result EndGlobal(Index index) override { return Result::Ok; }
+ Result EndGlobalSection() override { return Result::Ok; }
+
+ /* Exports section */
+ Result BeginExportSection(Offset size) override { return Result::Ok; }
+ Result OnExportCount(Index count) override { return Result::Ok; }
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ string_view name) override {
+ return Result::Ok;
+ }
+ Result EndExportSection() override { return Result::Ok; }
+
+ /* Start section */
+ Result BeginStartSection(Offset size) override { return Result::Ok; }
+ Result OnStartFunction(Index func_index) override { return Result::Ok; }
+ Result EndStartSection() override { return Result::Ok; }
+
+ /* Code section */
+ Result BeginCodeSection(Offset size) override { return Result::Ok; }
+ Result OnFunctionBodyCount(Index count) override { return Result::Ok; }
+ Result BeginFunctionBody(Index index, Offset size) override {
+ return Result::Ok;
+ }
+ Result OnLocalDeclCount(Index count) override { return Result::Ok; }
+ Result OnLocalDecl(Index decl_index, Index count, Type type) override {
+ return Result::Ok;
+ }
+
+ /* Function expressions; called between BeginFunctionBody and
+ EndFunctionBody */
+ Result OnOpcode(Opcode Opcode) override { return Result::Ok; }
+ Result OnOpcodeBare() override { return Result::Ok; }
+ Result OnOpcodeIndex(Index value) override { return Result::Ok; }
+ Result OnOpcodeIndexIndex(Index value, Index value2) override {
+ return Result::Ok;
+ }
+ Result OnOpcodeUint32(uint32_t value) override { return Result::Ok; }
+ Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) override {
+ return Result::Ok;
+ }
+ Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) override {
+ return Result::Ok;
+ }
+ Result OnOpcodeUint64(uint64_t value) override { return Result::Ok; }
+ Result OnOpcodeF32(uint32_t value) override { return Result::Ok; }
+ Result OnOpcodeF64(uint64_t value) override { return Result::Ok; }
+ Result OnOpcodeV128(v128 value) override { return Result::Ok; }
+ Result OnOpcodeBlockSig(Type sig_type) override { return Result::Ok; }
+ Result OnOpcodeType(Type type) override { return Result::Ok; }
+ Result OnAtomicLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnAtomicStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnAtomicRmwExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnAtomicWaitExpr(Opcode, Address, Address) override {
+ return Result::Ok;
+ }
+ Result OnAtomicFenceExpr(uint32_t) override {
+ return Result::Ok;
+ }
+ Result OnAtomicNotifyExpr(Opcode, Address, Address) override {
+ return Result::Ok;
+ }
+ Result OnBinaryExpr(Opcode opcode) override { return Result::Ok; }
+ Result OnBlockExpr(Type sig_type) override { return Result::Ok; }
+ Result OnBrExpr(Index depth) override { return Result::Ok; }
+ Result OnBrIfExpr(Index depth) override { return Result::Ok; }
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override {
+ return Result::Ok;
+ }
+ Result OnCallExpr(Index func_index) override { return Result::Ok; }
+ Result OnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; }
+ Result OnCallRefExpr() override { return Result::Ok; }
+ Result OnCatchExpr(Index tag_index) override { return Result::Ok; }
+ Result OnCatchAllExpr() override { return Result::Ok; }
+ Result OnCompareExpr(Opcode opcode) override { return Result::Ok; }
+ Result OnConvertExpr(Opcode opcode) override { return Result::Ok; }
+ Result OnDelegateExpr(Index depth) override { return Result::Ok; }
+ Result OnDropExpr() override { return Result::Ok; }
+ Result OnElseExpr() override { return Result::Ok; }
+ Result OnEndExpr() override { return Result::Ok; }
+ Result OnEndFunc() override { return Result::Ok; }
+ Result OnF32ConstExpr(uint32_t value_bits) override { return Result::Ok; }
+ Result OnF64ConstExpr(uint64_t value_bits) override { return Result::Ok; }
+ Result OnV128ConstExpr(v128 value_bits) override { return Result::Ok; }
+ Result OnGlobalGetExpr(Index global_index) override { return Result::Ok; }
+ Result OnGlobalSetExpr(Index global_index) override { return Result::Ok; }
+ Result OnI32ConstExpr(uint32_t value) override { return Result::Ok; }
+ Result OnI64ConstExpr(uint64_t value) override { return Result::Ok; }
+ Result OnIfExpr(Type sig_type) override { return Result::Ok; }
+ Result OnLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnLocalGetExpr(Index local_index) override { return Result::Ok; }
+ Result OnLocalSetExpr(Index local_index) override { return Result::Ok; }
+ Result OnLocalTeeExpr(Index local_index) override { return Result::Ok; }
+ Result OnLoopExpr(Type sig_type) override { return Result::Ok; }
+ Result OnMemoryCopyExpr() override { return Result::Ok; }
+ Result OnDataDropExpr(Index segment_index) override { return Result::Ok; }
+ Result OnMemoryFillExpr() override { return Result::Ok; }
+ Result OnMemoryGrowExpr() override { return Result::Ok; }
+ Result OnMemoryInitExpr(Index segment_index) override { return Result::Ok; }
+ Result OnMemorySizeExpr() override { return Result::Ok; }
+ Result OnTableCopyExpr(Index dst_index, Index src_index) override {
+ return Result::Ok;
+ }
+ Result OnElemDropExpr(Index segment_index) override { return Result::Ok; }
+ Result OnTableInitExpr(Index segment_index, Index table_index) override {
+ return Result::Ok;
+ }
+ Result OnTableGetExpr(Index table_index) override { return Result::Ok; }
+ Result OnTableSetExpr(Index table_index) override { return Result::Ok; }
+ Result OnTableGrowExpr(Index table_index) override { return Result::Ok; }
+ Result OnTableSizeExpr(Index table_index) override { return Result::Ok; }
+ Result OnTableFillExpr(Index table_index) override { return Result::Ok; }
+ Result OnRefFuncExpr(Index func_index) override { return Result::Ok; }
+ Result OnRefNullExpr(Type type) override { return Result::Ok; }
+ Result OnRefIsNullExpr() override { return Result::Ok; }
+ Result OnNopExpr() override { return Result::Ok; }
+ Result OnRethrowExpr(Index depth) override { return Result::Ok; }
+ Result OnReturnCallExpr(Index sig_index) override { return Result::Ok; }
+ Result OnReturnCallIndirectExpr(Index sig_index, Index table_index) override { return Result::Ok; }
+ Result OnReturnExpr() override { return Result::Ok; }
+ Result OnSelectExpr(Index result_count, Type* result_types) override {
+ return Result::Ok;
+ }
+ Result OnStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnThrowExpr(Index depth) override { return Result::Ok; }
+ Result OnTryExpr(Type sig_type) override { return Result::Ok; }
+ Result OnUnaryExpr(Opcode opcode) override { return Result::Ok; }
+ Result OnTernaryExpr(Opcode opcode) override { return Result::Ok; }
+ Result OnUnreachableExpr() override { return Result::Ok; }
+ Result EndFunctionBody(Index index) override { return Result::Ok; }
+ Result EndCodeSection() override { return Result::Ok; }
+ Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override {
+ return Result::Ok;
+ }
+ Result OnSimdLoadLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override {
+ return Result::Ok;
+ }
+ Result OnSimdStoreLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) override {
+ return Result::Ok;
+ }
+ Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) override {
+ return Result::Ok;
+ }
+ Result OnLoadSplatExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+ Result OnLoadZeroExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) override {
+ return Result::Ok;
+ }
+
+ /* Elem section */
+ Result BeginElemSection(Offset size) override { return Result::Ok; }
+ Result OnElemSegmentCount(Index count) override { return Result::Ok; }
+ Result BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) override {
+ return Result::Ok;
+ }
+ Result BeginElemSegmentInitExpr(Index index) override { return Result::Ok; }
+ Result EndElemSegmentInitExpr(Index index) override { return Result::Ok; }
+ Result OnElemSegmentElemType(Index index, Type elem_type) override {
+ return Result::Ok;
+ }
+ Result OnElemSegmentElemExprCount(Index index, Index count) override {
+ return Result::Ok;
+ }
+ Result OnElemSegmentElemExpr_RefNull(Index segment_index,
+ Type type) override {
+ return Result::Ok;
+ }
+ Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) override {
+ return Result::Ok;
+ }
+ Result EndElemSegment(Index index) override { return Result::Ok; }
+ Result EndElemSection() override { return Result::Ok; }
+
+ /* Data section */
+ Result BeginDataSection(Offset size) override { return Result::Ok; }
+ Result OnDataSegmentCount(Index count) override { return Result::Ok; }
+ Result BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) override {
+ return Result::Ok;
+ }
+ Result BeginDataSegmentInitExpr(Index index) override { return Result::Ok; }
+ Result EndDataSegmentInitExpr(Index index) override { return Result::Ok; }
+ Result OnDataSegmentData(Index index,
+ const void* data,
+ Address size) override {
+ return Result::Ok;
+ }
+ Result EndDataSegment(Index index) override { return Result::Ok; }
+ Result EndDataSection() override { return Result::Ok; }
+
+ /* DataCount section */
+ Result BeginDataCountSection(Offset size) override { return Result::Ok; }
+ Result OnDataCount(Index count) override { return Result::Ok; }
+ Result EndDataCountSection() override { return Result::Ok; }
+
+ /* Names section */
+ Result BeginNamesSection(Offset size) override { return Result::Ok; }
+ Result OnModuleNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override {
+ return Result::Ok;
+ }
+ Result OnModuleName(string_view name) override { return Result::Ok; }
+ Result OnFunctionNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override {
+ return Result::Ok;
+ }
+ Result OnFunctionNamesCount(Index num_functions) override {
+ return Result::Ok;
+ }
+ Result OnFunctionName(Index function_index,
+ string_view function_name) override {
+ return Result::Ok;
+ }
+ Result OnLocalNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) override {
+ return Result::Ok;
+ }
+ Result OnLocalNameFunctionCount(Index num_functions) override {
+ return Result::Ok;
+ }
+ Result OnLocalNameLocalCount(Index function_index,
+ Index num_locals) override {
+ return Result::Ok;
+ }
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) override {
+ return Result::Ok;
+ }
+ Result EndNamesSection() override { return Result::Ok; }
+
+ Result OnNameSubsection(Index index,
+ NameSectionSubsection subsection_type,
+ Offset subsection_size) override {
+ return Result::Ok;
+ }
+ Result OnNameCount(Index num_names) override { return Result::Ok; }
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ string_view name) override {
+ return Result::Ok;
+ }
+
+ /* Reloc section */
+ Result BeginRelocSection(Offset size) override { return Result::Ok; }
+ Result OnRelocCount(Index count, Index section_code) override {
+ return Result::Ok;
+ }
+ Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) override {
+ return Result::Ok;
+ }
+ Result EndRelocSection() override { return Result::Ok; }
+
+ /* Tag section */
+ Result BeginTagSection(Offset size) override { return Result::Ok; }
+ Result OnTagCount(Index count) override { return Result::Ok; }
+ Result OnTagType(Index index, Index sig_index) override { return Result::Ok; }
+ Result EndTagSection() override { return Result::Ok; }
+
+ /* Dylink section */
+ Result BeginDylinkSection(Offset size) override { return Result::Ok; }
+ Result OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align,
+ uint32_t table_size,
+ uint32_t table_align) override {
+ return Result::Ok;
+ }
+ Result OnDylinkNeededCount(Index count) override { return Result::Ok; }
+ Result OnDylinkNeeded(string_view so_name) override { return Result::Ok; }
+ Result EndDylinkSection() override { return Result::Ok; }
+
+ /* Linking section */
+ Result BeginLinkingSection(Offset size) override { return Result::Ok; }
+ Result OnSymbolCount(Index count) override { return Result::Ok; }
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override {
+ return Result::Ok;
+ }
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) override {
+ return Result::Ok;
+ }
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index global_index) override {
+ return Result::Ok;
+ }
+ Result OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) override {
+ return Result::Ok;
+ }
+ Result OnTagSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index tag_index) override {
+ return Result::Ok;
+ }
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index table_index) override {
+ return Result::Ok;
+ }
+ Result OnSegmentInfoCount(Index count) override { return Result::Ok; }
+ Result OnSegmentInfo(Index index,
+ string_view name,
+ Address alignment,
+ uint32_t flags) override {
+ return Result::Ok;
+ }
+ Result OnInitFunctionCount(Index count) override { return Result::Ok; }
+ Result OnInitFunction(uint32_t priority, Index function_index) override {
+ return Result::Ok;
+ }
+ Result OnComdatCount(Index count) override { return Result::Ok; }
+ Result OnComdatBegin(string_view name, uint32_t flags, Index count) override {
+ return Result::Ok;
+ }
+ Result OnComdatEntry(ComdatType kind, Index index) override {
+ return Result::Ok;
+ }
+ Result EndLinkingSection() override { return Result::Ok; }
+
+ /* InitExpr - used by elem, data and global sections; these functions are
+ * only called between calls to Begin*InitExpr and End*InitExpr */
+ Result OnInitExprF32ConstExpr(Index index, uint32_t value) override {
+ return Result::Ok;
+ }
+ Result OnInitExprF64ConstExpr(Index index, uint64_t value) override {
+ return Result::Ok;
+ }
+ Result OnInitExprV128ConstExpr(Index index, v128 value) override {
+ return Result::Ok;
+ }
+ Result OnInitExprGlobalGetExpr(Index index, Index global_index) override {
+ return Result::Ok;
+ }
+ Result OnInitExprI32ConstExpr(Index index, uint32_t value) override {
+ return Result::Ok;
+ }
+ Result OnInitExprI64ConstExpr(Index index, uint64_t value) override {
+ return Result::Ok;
+ }
+ Result OnInitExprRefNull(Index index, Type type) override {
+ return Result::Ok;
+ }
+ Result OnInitExprRefFunc(Index index, Index func_index) override {
+ return Result::Ok;
+ }
+};
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_READER_H_ */
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..188cd4271e
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-objdump.cc
@@ -0,0 +1,2085 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-reader-objdump.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstring>
+#include <vector>
+
+#if HAVE_STRCASECMP
+#include <strings.h>
+#endif
+
+#include "src/binary-reader-nop.h"
+#include "src/filenames.h"
+#include "src/literal.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 OnRelocCount(Index count, Index section_index) override;
+
+ protected:
+ string_view GetFunctionName(Index index) const;
+ string_view GetGlobalName(Index index) const;
+ string_view GetSectionName(Index index) const;
+ string_view GetTagName(Index index) const;
+ string_view GetSymbolName(Index index) const;
+ string_view GetSegmentName(Index index) const;
+ 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<size_t>(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<BinarySection> section_types_;
+ bool section_found_ = false;
+ std::string module_name_;
+
+ std::unique_ptr<FileStream> 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<size_t>(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: {
+ string_view basename = GetBasename(options_->filename);
+ if (basename == "-") {
+ basename = "<stdin>";
+ }
+ printf("%s:\tfile format wasm %#x\n", basename.to_string().c_str(),
+ version);
+ break;
+ }
+ case ObjdumpMode::RawData:
+ break;
+ }
+
+ return Result::Ok;
+}
+
+string_view BinaryReaderObjdumpBase::GetFunctionName(Index index) const {
+ return objdump_state_->function_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetGlobalName(Index index) const {
+ return objdump_state_->global_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetSectionName(Index index) const {
+ return objdump_state_->section_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetTagName(Index index) const {
+ return objdump_state_->tag_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetSegmentName(Index index) const {
+ return objdump_state_->segment_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetTableName(Index index) const {
+ return objdump_state_->table_names.Get(index);
+}
+
+string_view BinaryReaderObjdumpBase::GetSymbolName(Index symbol_index) const {
+ if (symbol_index >= objdump_state_->symtab.size())
+ return "<illegal_symbol_index>";
+ 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::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,
+ string_view section_name) override {
+ objdump_state_->section_names.Set(section_index, section_name);
+ return Result::Ok;
+ }
+
+ Result OnFunctionName(Index index, string_view name) override {
+ SetFunctionName(index, name);
+ return Result::Ok;
+ }
+
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ 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::Global:
+ SetGlobalName(index, name);
+ break;
+ case NameSectionSubsection::Table:
+ SetTableName(index, name);
+ break;
+ case NameSectionSubsection::DataSegment:
+ SetSegmentName(index, name);
+ break;
+ default:
+ break;
+ }
+ return Result::Ok;
+ }
+
+ Result OnSymbolCount(Index count) override {
+ objdump_state_->symtab.resize(count);
+ return Result::Ok;
+ }
+
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override {
+ objdump_state_->symtab[index] = {SymbolType::Data, name.to_string(), 0};
+ return Result::Ok;
+ }
+
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) override {
+ if (!name.empty()) {
+ SetFunctionName(func_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Function, name.to_string(),
+ func_index};
+ return Result::Ok;
+ }
+
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index global_index) override {
+ if (!name.empty()) {
+ SetGlobalName(global_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Global, name.to_string(),
+ 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,
+ string_view name,
+ Index tag_index) override {
+ if (!name.empty()) {
+ SetTagName(tag_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Tag, name.to_string(),
+ tag_index};
+ return Result::Ok;
+ }
+
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index table_index) override {
+ if (!name.empty()) {
+ SetTableName(table_index, name);
+ }
+ objdump_state_->symtab[index] = {SymbolType::Table, name.to_string(),
+ table_index};
+ return Result::Ok;
+ }
+
+ Result OnImportFunc(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) override {
+ SetFunctionName(func_index,
+ module_name.to_string() + "." + field_name.to_string());
+ return Result::Ok;
+ }
+
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override {
+ SetTagName(tag_index,
+ module_name.to_string() + "." + field_name.to_string());
+ return Result::Ok;
+ }
+
+ Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override {
+ SetGlobalName(global_index,
+ module_name.to_string() + "." + field_name.to_string());
+ return Result::Ok;
+ }
+
+ Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override {
+ SetTableName(table_index,
+ module_name.to_string() + "." + field_name.to_string());
+ return Result::Ok;
+ }
+
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ 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(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,
+ string_view name,
+ Address alignment_log2,
+ uint32_t flags) override {
+ SetSegmentName(index, name);
+ return Result::Ok;
+ }
+
+ protected:
+ void SetFunctionName(Index index, string_view name);
+ void SetGlobalName(Index index, string_view name);
+ void SetTagName(Index index, string_view name);
+ void SetTableName(Index index, string_view name);
+ void SetSegmentName(Index index, string_view name);
+};
+
+void BinaryReaderObjdumpPrepass::SetFunctionName(Index index,
+ string_view name) {
+ objdump_state_->function_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetGlobalName(Index index, string_view name) {
+ objdump_state_->global_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetTagName(Index index, string_view name) {
+ objdump_state_->tag_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetTableName(Index index, string_view name) {
+ objdump_state_->table_names.Set(index, name);
+}
+
+void BinaryReaderObjdumpPrepass::SetSegmentName(Index index, 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 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 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 OnOpcodeType(Type type) override;
+
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnDelegateExpr(Index) override;
+ Result OnEndExpr() override;
+ Result OnEndFunc() override;
+
+ private:
+ void LogOpcode(size_t data_size, const char* fmt, ...);
+
+ Opcode current_opcode = Opcode::Unreachable;
+ Offset current_opcode_offset = 0;
+ Offset last_opcode_end = 0;
+ int indent_level = 0;
+ Index next_reloc = 0;
+ Index local_index_ = 0;
+};
+
+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) {
+ 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) {
+ 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,
+ "warning: %#" 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;
+ current_opcode = opcode;
+ return Result::Ok;
+}
+
+#define IMMEDIATE_OCTET_COUNT 9
+
+Result BinaryReaderObjdumpDisassemble::OnLocalDeclCount(Index count) {
+ local_index_ = 0;
+ current_opcode_offset = state->offset;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnLocalDecl(Index decl_index,
+ Index count,
+ Type type) {
+ 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());
+
+ last_opcode_end = current_opcode_offset + data_size;
+ current_opcode_offset = last_opcode_end;
+
+ return Result::Ok;
+}
+
+void BinaryReaderObjdumpDisassemble::LogOpcode(size_t data_size,
+ const char* fmt,
+ ...) {
+ const Offset opcode_size = current_opcode.GetLength();
+ const Offset total_size = opcode_size + data_size;
+ // 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--;
+ 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 = offset_end;
+
+ // 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() {
+ LogOpcode(0, nullptr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeIndex(Index value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ string_view name;
+ if (current_opcode == Opcode::Call &&
+ !(name = GetFunctionName(value)).empty()) {
+ LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else if ((current_opcode == Opcode::GlobalGet ||
+ current_opcode == Opcode::GlobalSet) &&
+ !(name = GetGlobalName(value)).empty()) {
+ LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode(immediate_len, "%d", value);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeIndexIndex(Index value,
+ Index value2) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ LogOpcode(immediate_len, "%" PRIindex " %" PRIindex, value, value2);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32(uint32_t value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ string_view name;
+ if (current_opcode == Opcode::DataDrop &&
+ !(name = GetSegmentName(value)).empty()) {
+ LogOpcode(immediate_len, "%d <" PRIstringview ">", value,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode(immediate_len, "%u", value);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32(uint32_t value,
+ uint32_t value2) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ string_view name;
+ if (current_opcode == Opcode::MemoryInit &&
+ !(name = GetSegmentName(value)).empty()) {
+ LogOpcode(immediate_len, "%u %u <" PRIstringview ">", value, value2,
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ } else {
+ LogOpcode(immediate_len, "%u %u", value, value2);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint32Uint32Uint32(
+ uint32_t value,
+ uint32_t value2,
+ uint32_t value3) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ LogOpcode(immediate_len, "%u %u %u", value, value2, value3);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeUint64(uint64_t value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ LogOpcode(immediate_len, "%" PRId64, value);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeF32(uint32_t value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ char buffer[WABT_MAX_FLOAT_HEX];
+ WriteFloatHex(buffer, sizeof(buffer), value);
+ LogOpcode(immediate_len, buffer);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeF64(uint64_t value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ char buffer[WABT_MAX_DOUBLE_HEX];
+ WriteDoubleHex(buffer, sizeof(buffer), value);
+ LogOpcode(immediate_len, buffer);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeV128(v128 value) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ // v128 is always dumped as i32x4:
+ LogOpcode(immediate_len, "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) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ LogOpcode(immediate_len, type.GetRefKindName());
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnBrTableExpr(
+ Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+
+ 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(immediate_len, "%s", buffer.c_str());
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnDelegateExpr(Index depth) {
+ // 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::OnEndFunc() {
+ LogOpcode(0, nullptr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnEndExpr() {
+ 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;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdumpDisassemble::OnOpcodeBlockSig(Type sig_type) {
+ Offset immediate_len = state->offset - current_opcode_offset;
+ if (sig_type != Type::Void) {
+ LogOpcode(immediate_len, "%s", BlockSigToString(sig_type).c_str());
+ } else {
+ LogOpcode(immediate_len, 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 InitExpr {
+ InitExprType type;
+ union {
+ Index index;
+ uint32_t i32;
+ uint32_t f32;
+ uint64_t i64;
+ uint64_t f64;
+ v128 v128_v;
+ Type type;
+ } value;
+};
+
+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,
+ 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,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) override;
+ Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+ Result OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) override;
+ Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) override;
+ Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) override;
+
+ Result OnFunctionCount(Index count) override;
+ Result OnFunction(Index index, Index sig_index) override;
+
+ Result OnTableCount(Index count) override;
+ Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) override;
+
+ Result OnMemoryCount(Index count) override;
+ Result OnMemory(Index index, const Limits* limits) override;
+
+ Result OnGlobalCount(Index count) override;
+ Result BeginGlobal(Index index, Type type, bool mutable_) override;
+
+ Result OnExportCount(Index count) override;
+ Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ 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 BeginElemSection(Offset size) override {
+ in_elem_section_ = true;
+ return Result::Ok;
+ }
+ Result EndElemSection() override {
+ in_elem_section_ = false;
+ return Result::Ok;
+ }
+
+ 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;
+
+ Result BeginDataSection(Offset size) override {
+ in_data_section_ = true;
+ return Result::Ok;
+ }
+ Result EndDataSection() override {
+ in_data_section_ = false;
+ return Result::Ok;
+ }
+
+ 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(string_view name) override;
+ Result OnFunctionName(Index function_index,
+ string_view function_name) override;
+ Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) override;
+ Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ string_view name) override;
+
+ Result OnInitExprF32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprF64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprV128ConstExpr(Index index, v128 value) override;
+ Result OnInitExprGlobalGetExpr(Index index, Index global_index) override;
+ Result OnInitExprI32ConstExpr(Index index, uint32_t value) override;
+ Result OnInitExprI64ConstExpr(Index index, uint64_t value) override;
+ Result OnInitExprRefNull(Index index, Type type) override;
+ Result OnInitExprRefFunc(Index index, Index func_index) 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(string_view so_name) override;
+
+ Result OnRelocCount(Index count, Index section_index) override;
+ Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) override;
+
+ Result OnSymbolCount(Index count) override;
+ Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) override;
+ Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index func_index) override;
+ Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ 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,
+ string_view name,
+ Index tag_index) override;
+ Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index table_index) override;
+ Result OnSegmentInfoCount(Index count) override;
+ Result OnSegmentInfo(Index index,
+ string_view name,
+ Address alignment_log2,
+ uint32_t flags) override;
+ Result OnInitFunctionCount(Index count) override;
+ Result OnInitFunction(uint32_t priority, Index function_index) override;
+ Result OnComdatCount(Index count) override;
+ Result OnComdatBegin(string_view name, uint32_t flags, Index count) override;
+ Result OnComdatEntry(ComdatType kind, Index index) override;
+ Result EndLinkingSection() override { return Result::Ok; }
+
+ Result OnTagCount(Index count) override;
+ Result OnTagType(Index index, Index sig_index) override;
+
+ private:
+ Result InitExprToConstOffset(const InitExpr& expr, uint32_t* out_offset);
+ Result HandleInitExpr(const InitExpr& expr);
+ bool ShouldPrintDetails();
+ void PrintDetails(const char* fmt, ...);
+ Result PrintSymbolFlags(uint32_t flags);
+ Result PrintSegmentFlags(uint32_t flags);
+ void PrintInitExpr(const InitExpr& expr);
+ Result OnCount(Index count);
+
+ std::unique_ptr<FileStream> out_stream_;
+ Index elem_index_ = 0;
+ Index table_index_ = 0;
+ Index next_data_reloc_ = 0;
+ bool in_data_section_ = false;
+ bool in_elem_section_ = false;
+ InitExpr data_init_expr_;
+ InitExpr elem_init_expr_;
+ uint8_t data_flags_ = 0;
+ uint8_t elem_flags_ = 0;
+ Index data_mem_index_ = 0;
+ uint32_t data_offset_ = 0;
+ uint32_t elem_offset_ = 0;
+};
+
+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,
+ 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).to_string();
+
+ 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) {
+ if (next_data_reloc_ != objdump_state_->data_relocations.size()) {
+ err_stream_->Writef("Data reloctions outside of segments\n");
+ 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());
+ }
+ printf(") -> ");
+ switch (result_count) {
+ case 0:
+ printf("nil");
+ break;
+ case 1:
+ printf("%s", result_types[0].GetName());
+ break;
+ default:
+ printf("(");
+ for (Index i = 0; i < result_count; i++) {
+ if (i != 0) {
+ printf(", ");
+ }
+ printf("%s", result_types[i].GetName());
+ }
+ 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());
+ 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());
+ 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,
+ string_view module_name,
+ 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,
+ string_view module_name,
+ 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(), 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,
+ string_view module_name,
+ 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,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) {
+ PrintDetails(" - global[%" PRIindex "] %s mutable=%d", global_index,
+ type.GetName(), 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,
+ string_view module_name,
+ 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(), 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,
+ 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[%" PRIindex "] = ref.null %s\n",
+ elem_offset_ + elem_index_, type.GetName());
+ elem_index_++;
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) {
+ PrintDetails(" - elem[%" PRIindex "] = 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(elem_init_expr_);
+ }
+ 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(),
+ mutable_);
+ 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) {
+ switch (expr.type) {
+ case InitExprType::I32:
+ PrintDetails(" - init i32=%d\n", expr.value.i32);
+ break;
+ case InitExprType::I64:
+ PrintDetails(" - init i64=%" PRId64 "\n", expr.value.i64);
+ break;
+ case InitExprType::F64: {
+ char buffer[WABT_MAX_DOUBLE_HEX];
+ WriteDoubleHex(buffer, sizeof(buffer), expr.value.f64);
+ PrintDetails(" - init f64=%s\n", buffer);
+ break;
+ }
+ case InitExprType::F32: {
+ char buffer[WABT_MAX_FLOAT_HEX];
+ WriteFloatHex(buffer, sizeof(buffer), expr.value.f32);
+ PrintDetails(" - init f32=%s\n", buffer);
+ break;
+ }
+ case InitExprType::V128: {
+ PrintDetails(" - init v128=0x%08x 0x%08x 0x%08x 0x%08x \n",
+ expr.value.v128_v.u32(0), expr.value.v128_v.u32(1),
+ expr.value.v128_v.u32(2), expr.value.v128_v.u32(3));
+ break;
+ }
+ case InitExprType::Global: {
+ PrintDetails(" - init global=%" PRIindex, expr.value.index);
+ string_view name = GetGlobalName(expr.value.index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ break;
+ }
+ case InitExprType::FuncRef: {
+ PrintDetails(" - init ref.func:%" PRIindex, expr.value.index);
+ string_view name = GetFunctionName(expr.value.index);
+ if (!name.empty()) {
+ PrintDetails(" <" PRIstringview ">", WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ PrintDetails("\n");
+ break;
+ }
+ case InitExprType::NullRef: {
+ PrintDetails(" - init null\n");
+ break;
+ }
+ }
+}
+
+Result BinaryReaderObjdump::InitExprToConstOffset(const InitExpr& expr,
+ uint32_t* out_offset) {
+ switch (expr.type) {
+ case InitExprType::I32:
+ *out_offset = expr.value.i32;
+ break;
+ case InitExprType::Global:
+ *out_offset = 0;
+ break;
+ case InitExprType::I64:
+ case InitExprType::F32:
+ case InitExprType::F64:
+ case InitExprType::V128:
+ case InitExprType::FuncRef:
+ case InitExprType::NullRef:
+ err_stream_->Writef("Segment/Elem offset must be an i32 init expr");
+ return Result::Error;
+ break;
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::HandleInitExpr(const InitExpr& expr) {
+ if (in_data_section_) {
+ data_init_expr_ = expr;
+ return InitExprToConstOffset(expr, &data_offset_);
+ } else if (in_elem_section_) {
+ elem_init_expr_ = expr;
+ return InitExprToConstOffset(expr, &elem_offset_);
+ } else {
+ PrintInitExpr(expr);
+ }
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprF32ConstExpr(Index index,
+ uint32_t value) {
+ InitExpr expr;
+ expr.type = InitExprType::F32;
+ expr.value.f32 = value;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprF64ConstExpr(Index index,
+ uint64_t value) {
+ InitExpr expr;
+ expr.type = InitExprType::F64;
+ expr.value.f64 = value;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprV128ConstExpr(Index index, v128 value) {
+ InitExpr expr;
+ expr.type = InitExprType::V128;
+ expr.value.v128_v = value;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprGlobalGetExpr(Index index,
+ Index global_index) {
+ InitExpr expr;
+ expr.type = InitExprType::Global;
+ expr.value.index = global_index;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprI32ConstExpr(Index index,
+ uint32_t value) {
+ InitExpr expr;
+ expr.type = InitExprType::I32;
+ expr.value.i32 = value;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprI64ConstExpr(Index index,
+ uint64_t value) {
+ InitExpr expr;
+ expr.type = InitExprType::I64;
+ expr.value.i64 = value;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprRefNull(Index index, Type type) {
+ InitExpr expr;
+ expr.type = InitExprType::NullRef;
+ expr.value.type = type;
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnInitExprRefFunc(Index index, Index func_index) {
+ InitExpr expr{InitExprType::FuncRef, {func_index}};
+ HandleInitExpr(expr);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnModuleName(string_view name) {
+ PrintDetails(" - module <" PRIstringview ">\n",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnFunctionName(Index index, 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,
+ 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,
+ 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(data_init_expr_);
+ }
+
+ 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::OnDylinkNeeded(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<int32_t>(addend);
+ if (signed_addend < 0) {
+ PrintDetails("-");
+ signed_addend = -signed_addend;
+ } else {
+ PrintDetails("+");
+ }
+ PrintDetails("%#x", signed_addend);
+ }
+ PrintDetails("\n");
+ 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<SymbolBinding>(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<SymbolVisibility>(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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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 function_index) {
+ PrintDetails(" - %d: priority=%d\n", function_index, priority);
+ return Result::Ok;
+}
+Result BinaryReaderObjdump::OnComdatCount(Index count) {
+ PrintDetails(" - comdat groups [count=%d]\n", count);
+ return Result::Ok;
+}
+
+Result BinaryReaderObjdump::OnComdatBegin(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;
+}
+
+} // end anonymous namespace
+
+string_view ObjdumpNames::Get(Index index) const {
+ auto iter = names.find(index);
+ if (iter == names.end())
+ return string_view();
+ return iter->second;
+}
+
+void ObjdumpNames::Set(Index index, string_view name) {
+ names[index] = name.to_string();
+}
+
+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: {
+ 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: {
+ BinaryReaderObjdump reader(data, size, options, state);
+ return ReadBinary(data, size, &reader, read_options);
+ }
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader-objdump.h b/third_party/wasm2c/src/binary-reader-objdump.h
new file mode 100644
index 0000000000..1c3d119e97
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-objdump.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_OBJDUMP_H_
+#define WABT_BINARY_READER_OBJDUMP_H_
+
+#include <map>
+#include <string>
+
+#include "src/common.h"
+#include "src/feature.h"
+#include "src/stream.h"
+
+namespace wabt {
+
+struct Module;
+struct ReadBinaryOptions;
+
+enum class ObjdumpMode {
+ Prepass,
+ Headers,
+ Details,
+ Disassemble,
+ RawData,
+};
+
+struct ObjdumpOptions {
+ Stream* log_stream;
+ bool headers;
+ bool details;
+ bool raw;
+ bool disassemble;
+ bool debug;
+ bool relocs;
+ bool section_offsets;
+ ObjdumpMode mode;
+ const char* filename;
+ const char* section_name;
+};
+
+struct ObjdumpSymbol {
+ wabt::SymbolType kind;
+ std::string name;
+ Index index;
+};
+
+struct ObjdumpNames {
+ string_view Get(Index index) const;
+ void Set(Index index, string_view name);
+
+ std::map<Index, std::string> names;
+};
+
+// read_binary_objdump uses this state to store information from previous runs
+// and use it to display more useful information.
+struct ObjdumpState {
+ std::vector<Reloc> code_relocations;
+ std::vector<Reloc> data_relocations;
+ ObjdumpNames function_names;
+ ObjdumpNames global_names;
+ ObjdumpNames section_names;
+ ObjdumpNames tag_names;
+ ObjdumpNames segment_names;
+ ObjdumpNames table_names;
+ std::vector<ObjdumpSymbol> symtab;
+};
+
+Result ReadBinaryObjdump(const uint8_t* data,
+ size_t size,
+ ObjdumpOptions* options,
+ ObjdumpState* state);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_READER_OBJDUMP_H_ */
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..3908660d00
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-opcnt.cc
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-reader-opcnt.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+
+#include "src/binary-reader-nop.h"
+#include "src/common.h"
+#include "src/literal.h"
+#include "src/stream.h"
+
+namespace wabt {
+
+OpcodeInfo::OpcodeInfo(Opcode opcode, Kind kind)
+ : opcode_(opcode), kind_(kind) {}
+
+template <typename T>
+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 <typename T>
+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 <typename T>
+std::pair<const T*, size_t> OpcodeInfo::GetDataArray() const {
+ if (data_.empty()) {
+ return std::pair<const T*, size_t>(nullptr, 0);
+ }
+
+ assert(data_.size() % sizeof(T) == 0);
+ return std::make_pair(reinterpret_cast<const T*>(data_.data()),
+ data_.size() / sizeof(T));
+}
+
+template <typename T>
+const T* OpcodeInfo::GetData(size_t expected_size) const {
+ auto pair = GetDataArray<T>();
+ assert(pair.second == expected_size);
+ return pair.first;
+}
+
+template <typename T, typename F>
+void OpcodeInfo::WriteArray(Stream& stream, F&& write_func) {
+ auto pair = GetDataArray<T>();
+ for (size_t i = 0; i < pair.second; ++i) {
+ // Write an initial space (to separate from the opcode name) first, then
+ // comma-separate.
+ stream.Writef("%s", i == 0 ? " " : ", ");
+ write_func(pair.first[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<uint32_t>(), *GetData<uint32_t>());
+ break;
+
+ case Kind::Uint64:
+ stream.Writef(" %" PRIu64 " (0x%" PRIx64 ")", *GetData<uint64_t>(),
+ *GetData<uint64_t>());
+ break;
+
+ case Kind::Index:
+ stream.Writef(" %" PRIindex, *GetData<Index>());
+ break;
+
+ case Kind::Float32: {
+ stream.Writef(" %g", *GetData<float>());
+ char buffer[WABT_MAX_FLOAT_HEX + 1];
+ WriteFloatHex(buffer, sizeof(buffer), *GetData<uint32_t>());
+ stream.Writef(" (%s)", buffer);
+ break;
+ }
+
+ case Kind::Float64: {
+ stream.Writef(" %g", *GetData<double>());
+ char buffer[WABT_MAX_DOUBLE_HEX + 1];
+ WriteDoubleHex(buffer, sizeof(buffer), *GetData<uint64_t>());
+ stream.Writef(" (%s)", buffer);
+ break;
+ }
+
+ case Kind::Uint32Uint32:
+ WriteArray<uint32_t>(
+ stream, [&stream](uint32_t value) { stream.Writef("%u", value); });
+ break;
+
+ case Kind::BlockSig: {
+ auto type = *GetData<Type>();
+ if (type.IsIndex()) {
+ stream.Writef(" type:%d", type.GetIndex());
+ } else if (type != Type::Void) {
+ stream.Writef(" %s", type.GetName());
+ }
+ break;
+ }
+
+ case Kind::BrTable: {
+ WriteArray<Index>(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 OnOpcodeUint64(uint64_t value) override;
+ Result OnOpcodeF32(uint32_t value) override;
+ Result OnOpcodeF64(uint64_t value) override;
+ Result OnOpcodeBlockSig(Type sig_type) override;
+ Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) override;
+ Result OnEndExpr() override;
+ Result OnEndFunc() override;
+
+ private:
+ template <typename... Args>
+ Result Emplace(Args&&... args);
+
+ OpcodeInfoCounts* opcode_counts_;
+ Opcode current_opcode_;
+};
+
+template <typename... Args>
+Result BinaryReaderOpcnt::Emplace(Args&&... args) {
+ auto pair = opcode_counts_->emplace(
+ std::piecewise_construct, std::make_tuple(std::forward<Args>(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::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::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);
+}
+
+Result BinaryReaderOpcnt::OnEndFunc() {
+ 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-opcnt.h b/third_party/wasm2c/src/binary-reader-opcnt.h
new file mode 100644
index 0000000000..cfdd570c0e
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader-opcnt.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_OPCNT_H_
+#define WABT_BINARY_READER_OPCNT_H_
+
+#include <map>
+#include <vector>
+
+#include "src/common.h"
+#include "src/opcode.h"
+
+namespace wabt {
+
+struct Module;
+struct ReadBinaryOptions;
+class Stream;
+
+class OpcodeInfo {
+ public:
+ enum class Kind {
+ Bare,
+ Uint32,
+ Uint64,
+ Index,
+ Float32,
+ Float64,
+ Uint32Uint32,
+ BlockSig,
+ BrTable,
+ };
+
+ explicit OpcodeInfo(Opcode, Kind);
+ template <typename T>
+ OpcodeInfo(Opcode, Kind, T* data, size_t count = 1);
+ template <typename T>
+ OpcodeInfo(Opcode, Kind, T* data, size_t count, T extra);
+
+ Opcode opcode() const { return opcode_; }
+
+ void Write(Stream&);
+
+ private:
+ template <typename T>
+ std::pair<const T*, size_t> GetDataArray() const;
+ template <typename T>
+ const T* GetData(size_t expected_size = 1) const;
+
+ template <typename T, typename F>
+ void WriteArray(Stream& stream, F&& write_func);
+
+ Opcode opcode_;
+ Kind kind_;
+ std::vector<uint8_t> data_;
+
+ friend bool operator==(const OpcodeInfo&, const OpcodeInfo&);
+ friend bool operator!=(const OpcodeInfo&, const OpcodeInfo&);
+ friend bool operator<(const OpcodeInfo&, const OpcodeInfo&);
+ friend bool operator<=(const OpcodeInfo&, const OpcodeInfo&);
+ friend bool operator>(const OpcodeInfo&, const OpcodeInfo&);
+ friend bool operator>=(const OpcodeInfo&, const OpcodeInfo&);
+};
+
+bool operator==(const OpcodeInfo&, const OpcodeInfo&);
+bool operator!=(const OpcodeInfo&, const OpcodeInfo&);
+bool operator<(const OpcodeInfo&, const OpcodeInfo&);
+bool operator<=(const OpcodeInfo&, const OpcodeInfo&);
+bool operator>(const OpcodeInfo&, const OpcodeInfo&);
+bool operator>=(const OpcodeInfo&, const OpcodeInfo&);
+
+typedef std::map<OpcodeInfo, size_t> OpcodeInfoCounts;
+
+Result ReadBinaryOpcnt(const void* data,
+ size_t size,
+ const ReadBinaryOptions& options,
+ OpcodeInfoCounts* opcode_counts);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_READER_OPCNT_H_ */
diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc
new file mode 100644
index 0000000000..b5a5b2fd2a
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader.cc
@@ -0,0 +1,2821 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-reader.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <vector>
+
+#include "config.h"
+
+#include "src/binary-reader-logging.h"
+#include "src/binary.h"
+#include "src/leb128.h"
+#include "src/stream.h"
+#include "src/utf8.h"
+
+#if HAVE_ALLOCA
+#include <alloca.h>
+#endif
+
+#define ERROR_IF(expr, ...) \
+ do { \
+ if (expr) { \
+ PrintError(__VA_ARGS__); \
+ return Result::Error; \
+ } \
+ } while (0)
+
+#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__)
+
+#define ERROR_UNLESS_OPCODE_ENABLED(opcode) \
+ do { \
+ if (!opcode.IsEnabled(options_.features)) { \
+ return ReportUnexpectedOpcode(opcode); \
+ } \
+ } while (0)
+
+#define CALLBACK0(member) \
+ ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed")
+
+#define CALLBACK(member, ...) \
+ ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \
+ #member " callback failed")
+
+namespace wabt {
+
+namespace {
+
+class BinaryReader {
+ public:
+ BinaryReader(const void* data,
+ size_t size,
+ BinaryReaderDelegate* delegate,
+ const ReadBinaryOptions& options);
+
+ Result ReadModule();
+
+ private:
+ template <typename T, T BinaryReader::*member>
+ struct ValueRestoreGuard {
+ explicit ValueRestoreGuard(BinaryReader* this_)
+ : this_(this_), previous_value_(this_->*member) {}
+ ~ValueRestoreGuard() { this_->*member = previous_value_; }
+
+ BinaryReader* this_;
+ T previous_value_;
+ };
+
+ void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
+ Result ReadOpcode(Opcode* out_value, const char* desc) WABT_WARN_UNUSED;
+ template <typename T>
+ Result ReadT(T* out_value,
+ const char* type_name,
+ const char* desc) WABT_WARN_UNUSED;
+ Result ReadU8(uint8_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadU32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadF32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadF64(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadV128(v128* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadU32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadU64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadS32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadS64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadType(Type* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadRefType(Type* out_value, const char* desc) WABT_WARN_UNUSED;
+ Result ReadExternalKind(ExternalKind* out_value,
+ const char* desc) WABT_WARN_UNUSED;
+ Result ReadStr(string_view* out_str, const char* desc) WABT_WARN_UNUSED;
+ Result ReadBytes(const void** out_data,
+ Address* out_data_size,
+ const char* desc) WABT_WARN_UNUSED;
+ Result ReadIndex(Index* index, const char* desc) WABT_WARN_UNUSED;
+ Result ReadOffset(Offset* offset, const char* desc) WABT_WARN_UNUSED;
+ Result ReadAlignment(Address* align_log2, const char* desc) WABT_WARN_UNUSED;
+ Result ReadCount(Index* index, const char* desc) WABT_WARN_UNUSED;
+ Result ReadField(TypeMut* out_value) WABT_WARN_UNUSED;
+
+ bool IsConcreteType(Type);
+ bool IsBlockType(Type);
+
+ Index NumTotalFuncs();
+
+ Result ReadInitExpr(Index index, Type required = Type::Any) WABT_WARN_UNUSED;
+ Result ReadTable(Type* out_elem_type,
+ Limits* out_elem_limits) WABT_WARN_UNUSED;
+ Result ReadMemory(Limits* out_page_limits) WABT_WARN_UNUSED;
+ Result ReadGlobalHeader(Type* out_type, bool* out_mutable) WABT_WARN_UNUSED;
+ Result ReadTagType(Index* out_sig_index) WABT_WARN_UNUSED;
+ Result ReadAddress(Address* out_value,
+ Index memory,
+ const char* desc) WABT_WARN_UNUSED;
+ Result ReadFunctionBody(Offset end_offset) WABT_WARN_UNUSED;
+ Result ReadNameSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadRelocSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadDylinkSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadDylink0Section(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadLinkingSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadCustomSection(Index section_index,
+ Offset section_size) WABT_WARN_UNUSED;
+ Result ReadTypeSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadImportSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadFunctionSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadTableSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadMemorySection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadGlobalSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadExportSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadStartSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadElemSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadCodeSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadDataSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadDataCountSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadTagSection(Offset section_size) WABT_WARN_UNUSED;
+ Result ReadSections() WABT_WARN_UNUSED;
+ Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr);
+
+ size_t read_end_ = 0; // Either the section end or data_size.
+ BinaryReaderDelegate::State state_;
+ BinaryReaderLogging logging_delegate_;
+ BinaryReaderDelegate* delegate_ = nullptr;
+ TypeVector param_types_;
+ TypeVector result_types_;
+ TypeMutVector fields_;
+ std::vector<Index> target_depths_;
+ const ReadBinaryOptions& options_;
+ BinarySection last_known_section_ = BinarySection::Invalid;
+ bool did_read_names_section_ = false;
+ bool reading_custom_section_ = false;
+ Index num_func_imports_ = 0;
+ Index num_table_imports_ = 0;
+ Index num_memory_imports_ = 0;
+ Index num_global_imports_ = 0;
+ Index num_tag_imports_ = 0;
+ Index num_function_signatures_ = 0;
+ Index num_function_bodies_ = 0;
+ Index data_count_ = kInvalidIndex;
+ std::vector<Limits> memories;
+
+ using ReadEndRestoreGuard =
+ ValueRestoreGuard<size_t, &BinaryReader::read_end_>;
+};
+
+BinaryReader::BinaryReader(const void* data,
+ size_t size,
+ BinaryReaderDelegate* delegate,
+ const ReadBinaryOptions& options)
+ : read_end_(size),
+ state_(static_cast<const uint8_t*>(data), size),
+ logging_delegate_(options.log_stream, delegate),
+ delegate_(options.log_stream ? &logging_delegate_ : delegate),
+ options_(options),
+ last_known_section_(BinarySection::Invalid) {
+ delegate->OnSetState(&state_);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format,
+ ...) {
+ ErrorLevel error_level =
+ reading_custom_section_ && !options_.fail_on_custom_section_error
+ ? ErrorLevel::Warning
+ : ErrorLevel::Error;
+
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ Error error(error_level, Location(state_.offset), buffer);
+ bool handled = delegate_->OnError(error);
+
+ if (!handled) {
+ // Not great to just print, but we don't want to eat the error either.
+ fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset,
+ GetErrorLevelName(error_level), buffer);
+ }
+}
+
+Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) {
+ std::string message = "unexpected opcode";
+ if (where) {
+ message += ' ';
+ message += where;
+ }
+
+ message += ":";
+
+ std::vector<uint8_t> bytes = opcode.GetBytes();
+ assert(bytes.size() > 0);
+
+ for (uint8_t byte : bytes) {
+ message += StringPrintf(" 0x%x", byte);
+ }
+
+ PrintError("%s", message.c_str());
+ return Result::Error;
+}
+
+Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) {
+ uint8_t value = 0;
+ CHECK_RESULT(ReadU8(&value, desc));
+
+ if (Opcode::IsPrefixByte(value)) {
+ uint32_t code;
+ CHECK_RESULT(ReadU32Leb128(&code, desc));
+ *out_value = Opcode::FromCode(value, code);
+ } else {
+ *out_value = Opcode::FromCode(value);
+ }
+ return Result::Ok;
+}
+
+template <typename T>
+Result BinaryReader::ReadT(T* out_value,
+ const char* type_name,
+ const char* desc) {
+ if (state_.offset + sizeof(T) > read_end_) {
+ PrintError("unable to read %s: %s", type_name, desc);
+ return Result::Error;
+ }
+#if WABT_BIG_ENDIAN
+ uint8_t tmp[sizeof(T)];
+ memcpy(tmp, state_.data + state_.offset, sizeof(tmp));
+ SwapBytesSized(tmp, sizeof(tmp));
+ memcpy(out_value, tmp, sizeof(T));
+#else
+ memcpy(out_value, state_.data + state_.offset, sizeof(T));
+#endif
+ state_.offset += sizeof(T);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) {
+ return ReadT(out_value, "uint8_t", desc);
+}
+
+Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) {
+ return ReadT(out_value, "uint32_t", desc);
+}
+
+Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) {
+ return ReadT(out_value, "float", desc);
+}
+
+Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) {
+ return ReadT(out_value, "double", desc);
+}
+
+Result BinaryReader::ReadV128(v128* out_value, const char* desc) {
+ return ReadT(out_value, "v128", desc);
+}
+
+Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) {
+ const uint8_t* p = state_.data + state_.offset;
+ const uint8_t* end = state_.data + read_end_;
+ size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value);
+ ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc);
+ state_.offset += bytes_read;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadType(Type* out_value, const char* desc) {
+ uint32_t type = 0;
+ CHECK_RESULT(ReadS32Leb128(&type, desc));
+ *out_value = static_cast<Type>(type);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadRefType(Type* out_value, const char* desc) {
+ uint32_t type = 0;
+ CHECK_RESULT(ReadS32Leb128(&type, desc));
+ *out_value = static_cast<Type>(type);
+ ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadExternalKind(ExternalKind* out_value,
+ const char* desc) {
+ uint8_t value = 0;
+ CHECK_RESULT(ReadU8(&value, desc));
+ ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d",
+ value);
+ *out_value = static_cast<ExternalKind>(value);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadStr(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 = string_view(
+ reinterpret_cast<const char*>(state_.data) + state_.offset, str_len);
+ state_.offset += str_len;
+
+ ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()),
+ "invalid utf-8 encoding: %s", desc);
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadBytes(const void** out_data,
+ Address* out_data_size,
+ const char* desc) {
+ uint32_t data_size = 0;
+ CHECK_RESULT(ReadU32Leb128(&data_size, "data size"));
+
+ ERROR_UNLESS(state_.offset + data_size <= read_end_,
+ "unable to read data: %s", desc);
+
+ *out_data = static_cast<const uint8_t*>(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 >= 32) {
+ PrintError("invalid %s: %u", desc, value);
+ return Result::Error;
+ }
+ *alignment_log2 = value;
+ 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();
+
+ 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, Type required) {
+ Opcode opcode;
+ CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
+ ERROR_UNLESS_OPCODE_ENABLED(opcode);
+
+ switch (opcode) {
+ case Opcode::I32Const: {
+ uint32_t value = 0;
+ CHECK_RESULT(ReadS32Leb128(&value, "init_expr i32.const value"));
+ CALLBACK(OnInitExprI32ConstExpr, index, value);
+ break;
+ }
+
+ case Opcode::I64Const: {
+ uint64_t value = 0;
+ CHECK_RESULT(ReadS64Leb128(&value, "init_expr i64.const value"));
+ CALLBACK(OnInitExprI64ConstExpr, index, value);
+ break;
+ }
+
+ case Opcode::F32Const: {
+ uint32_t value_bits = 0;
+ CHECK_RESULT(ReadF32(&value_bits, "init_expr f32.const value"));
+ CALLBACK(OnInitExprF32ConstExpr, index, value_bits);
+ break;
+ }
+
+ case Opcode::F64Const: {
+ uint64_t value_bits = 0;
+ CHECK_RESULT(ReadF64(&value_bits, "init_expr f64.const value"));
+ CALLBACK(OnInitExprF64ConstExpr, index, value_bits);
+ break;
+ }
+
+ case Opcode::V128Const: {
+ v128 value_bits;
+ ZeroMemory(value_bits);
+ CHECK_RESULT(ReadV128(&value_bits, "init_expr v128.const value"));
+ CALLBACK(OnInitExprV128ConstExpr, index, value_bits);
+ break;
+ }
+
+ case Opcode::GlobalGet: {
+ Index global_index;
+ CHECK_RESULT(ReadIndex(&global_index, "init_expr global.get index"));
+ CALLBACK(OnInitExprGlobalGetExpr, index, global_index);
+ break;
+ }
+
+ case Opcode::RefNull: {
+ Type type;
+ CHECK_RESULT(ReadRefType(&type, "ref.null type"));
+ CALLBACK(OnInitExprRefNull, index, type);
+ break;
+ }
+
+ case Opcode::RefFunc: {
+ Index func_index;
+ CHECK_RESULT(ReadIndex(&func_index, "init_expr ref.func index"));
+ CALLBACK(OnInitExprRefFunc, index, func_index);
+ break;
+ }
+
+ case Opcode::End:
+ return Result::Ok;
+
+ default:
+ return ReportUnexpectedOpcode(opcode, "in initializer expression");
+ }
+
+ if (required == Type::I32 && opcode != Opcode::I32Const &&
+ opcode != Opcode::GlobalGet) {
+ PrintError("expected i32 init_expr");
+ return Result::Error;
+ }
+ if (required == Type::I64 && opcode != Opcode::I64Const &&
+ opcode != Opcode::GlobalGet) {
+ PrintError("expected i64 init_expr");
+ return Result::Error;
+ }
+
+ CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
+ ERROR_UNLESS(opcode == Opcode::End,
+ "expected END opcode after initializer expression");
+ 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<int>(global_type));
+
+ CHECK_RESULT(ReadU8(&mutable_, "global mutability"));
+ ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1");
+
+ *out_type = global_type;
+ *out_mutable = mutable_;
+ return Result::Ok;
+}
+
+Result BinaryReader::ReadAddress(Address* out_value,
+ Index memory,
+ const char* desc) {
+ 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) {
+ bool seen_end_opcode = false;
+ while (state_.offset < end_offset) {
+ Opcode opcode;
+ CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
+ CALLBACK(OnOpcode, opcode);
+ ERROR_UNLESS_OPCODE_ENABLED(opcode);
+
+ switch (opcode) {
+ case Opcode::Unreachable:
+ CALLBACK0(OnUnreachableExpr);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::Block: {
+ 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;
+ }
+
+ Type* result_types = num_results ? result_types_.data() : nullptr;
+ CALLBACK(OnSelectExpr, num_results, result_types);
+ 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:
+ if (state_.offset == end_offset) {
+ seen_end_opcode = true;
+ CALLBACK0(OnEndFunc);
+ } else {
+ CALLBACK0(OnEndExpr);
+ }
+ 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+ CALLBACK(OnLoadExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ break;
+ }
+
+ case Opcode::I32Store8:
+ case Opcode::I32Store16:
+ case Opcode::I64Store8:
+ case Opcode::I64Store16:
+ case Opcode::I64Store32:
+ case Opcode::I32Store:
+ case Opcode::I64Store:
+ case Opcode::F32Store:
+ case Opcode::F64Store:
+ case Opcode::V128Store: {
+ Address alignment_log2;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "store offset"));
+
+ CALLBACK(OnStoreExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ break;
+ }
+
+ case Opcode::MemorySize: {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
+ CALLBACK0(OnMemorySizeExpr);
+ CALLBACK(OnOpcodeUint32, reserved);
+ break;
+ }
+
+ case Opcode::MemoryGrow: {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
+ ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
+ CALLBACK0(OnMemoryGrowExpr);
+ CALLBACK(OnOpcodeUint32, reserved);
+ break;
+ }
+
+ case Opcode::I32Add:
+ case Opcode::I32Sub:
+ case Opcode::I32Mul:
+ case Opcode::I32DivS:
+ case Opcode::I32DivU:
+ case Opcode::I32RemS:
+ case Opcode::I32RemU:
+ case Opcode::I32And:
+ case Opcode::I32Or:
+ case Opcode::I32Xor:
+ case Opcode::I32Shl:
+ case Opcode::I32ShrU:
+ case Opcode::I32ShrS:
+ case Opcode::I32Rotr:
+ case Opcode::I32Rotl:
+ case Opcode::I64Add:
+ case Opcode::I64Sub:
+ case Opcode::I64Mul:
+ case Opcode::I64DivS:
+ case Opcode::I64DivU:
+ case Opcode::I64RemS:
+ case Opcode::I64RemU:
+ case Opcode::I64And:
+ case Opcode::I64Or:
+ case Opcode::I64Xor:
+ case Opcode::I64Shl:
+ case Opcode::I64ShrU:
+ case Opcode::I64ShrS:
+ case Opcode::I64Rotr:
+ case Opcode::I64Rotl:
+ case Opcode::F32Add:
+ case Opcode::F32Sub:
+ case Opcode::F32Mul:
+ case Opcode::F32Div:
+ case Opcode::F32Min:
+ case Opcode::F32Max:
+ case Opcode::F32Copysign:
+ case Opcode::F64Add:
+ case Opcode::F64Sub:
+ case Opcode::F64Mul:
+ case Opcode::F64Div:
+ case Opcode::F64Min:
+ case Opcode::F64Max:
+ case Opcode::F64Copysign:
+ 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:
+ 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:
+ CALLBACK(OnUnaryExpr, opcode);
+ CALLBACK0(OnOpcodeBare);
+ break;
+
+ case Opcode::V128BitSelect:
+ 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+
+ CALLBACK(OnLoadSplatExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ break;
+ }
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Load64Lane: {
+ Address alignment_log2;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+ uint8_t lane_val;
+ CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
+
+ CALLBACK(OnSimdLoadLaneExpr, opcode, alignment_log2, offset, lane_val);
+ CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val);
+ break;
+ }
+ case Opcode::V128Store8Lane:
+ case Opcode::V128Store16Lane:
+ case Opcode::V128Store32Lane:
+ case Opcode::V128Store64Lane: {
+ Address alignment_log2;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+ uint8_t lane_val;
+ CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
+
+ CALLBACK(OnSimdStoreLaneExpr, opcode, alignment_log2, offset, lane_val);
+ CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val);
+ break;
+ }
+ case Opcode::V128Load32Zero:
+ case Opcode::V128Load64Zero: {
+ Address alignment_log2;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+
+ CALLBACK(OnLoadZeroExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+
+ CALLBACK(OnAtomicNotifyExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset);
+ break;
+ }
+
+ case Opcode::MemoryAtomicWait32:
+ case Opcode::MemoryAtomicWait64: {
+ Address alignment_log2;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+
+ CALLBACK(OnAtomicWaitExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "load offset"));
+
+ CALLBACK(OnAtomicLoadExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "store offset"));
+
+ CALLBACK(OnAtomicStoreExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "memory offset"));
+
+ CALLBACK(OnAtomicRmwExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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;
+ CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment"));
+ Address offset;
+ CHECK_RESULT(ReadAddress(&offset, 0, "memory offset"));
+
+ CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, alignment_log2, offset);
+ CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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"));
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ CALLBACK(OnMemoryInitExpr, segment);
+ CALLBACK(OnOpcodeUint32Uint32, segment, reserved);
+ 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: {
+ uint8_t reserved;
+ CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
+ ERROR_UNLESS(reserved == 0, "reserved value must be 0");
+ CALLBACK(OnMemoryFillExpr);
+ CALLBACK(OnOpcodeUint32, reserved);
+ break;
+ }
+ case Opcode::MemoryCopy: {
+ 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");
+ CALLBACK(OnMemoryCopyExpr);
+ CALLBACK(OnOpcodeUint32Uint32, reserved, reserved);
+ 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);
+ }
+ }
+ ERROR_UNLESS(state_.offset == end_offset,
+ "function body longer than given size");
+ ERROR_UNLESS(seen_end_opcode, "function body must end with END 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<NameSectionSubsection>(name_type);
+ if (type <= NameSectionSubsection::Last) {
+ CALLBACK(OnNameSubsection, i, type, subsection_size);
+ }
+
+ switch (type) {
+ case NameSectionSubsection::Module:
+ CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size);
+ if (subsection_size) {
+ 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;
+ 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;
+ 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:
+ 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;
+ 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(&section_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<RelocType>(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;
+
+ switch (static_cast<DylinkEntryType>(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: {
+ uint32_t count;
+ CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
+ CALLBACK(OnDylinkNeededCount, count);
+ while (count--) {
+ string_view so_name;
+ CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
+ CALLBACK(OnDylinkNeeded, so_name);
+ }
+ 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--) {
+ string_view so_name;
+ CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
+ CALLBACK(OnDylinkNeeded, so_name);
+ }
+
+ CALLBACK0(EndDylinkSection);
+ 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<LinkingEntryType>(linking_type)) {
+ case LinkingEntryType::SymbolTable:
+ CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
+ CALLBACK(OnSymbolCount, count);
+ for (Index i = 0; i < count; ++i) {
+ 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<SymbolType>(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++) {
+ 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 func;
+ CHECK_RESULT(ReadU32Leb128(&priority, "priority"));
+ CHECK_RESULT(ReadU32Leb128(&func, "function index"));
+ CALLBACK(OnInitFunction, priority, func);
+ }
+ break;
+ case LinkingEntryType::ComdatInfo:
+ CHECK_RESULT(ReadU32Leb128(&count, "count"));
+ CALLBACK(OnComdatCount, count);
+ while (count--) {
+ uint32_t flags;
+ uint32_t entry_count;
+ 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<ComdatType>(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::ReadCustomSection(Index section_index,
+ Offset section_size) {
+ string_view section_name;
+ CHECK_RESULT(ReadStr(&section_name, "section name"));
+ CALLBACK(BeginCustomSection, section_index, section_size, section_name);
+ ValueRestoreGuard<bool, &BinaryReader::reading_custom_section_> 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_LINKING) {
+ CHECK_RESULT(ReadLinkingSection(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 " PRItypecode ")",
+ WABT_PRINTF_TYPE_CODE(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(&param_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) {
+ string_view module_name;
+ CHECK_RESULT(ReadStr(&module_name, "import module name"));
+ 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<ExternalKind>(kind), module_name,
+ field_name);
+ switch (static_cast<ExternalKind>(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) {
+ 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<ExternalKind>(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, Type::I32));
+ 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());
+ 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 < 0x10000000");
+ 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);
+ }
+
+ 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)) {
+ CALLBACK(BeginDataSegmentInitExpr, i);
+ CHECK_RESULT(ReadInitExpr(i, memories[0].IndexType()));
+ 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() {
+ Result result = Result::Ok;
+ Index section_index = 0;
+ bool seen_section_code[static_cast<int>(BinarySection::Last) + 1] = {false};
+
+ for (; state_.offset < state_.size; ++section_index) {
+ uint8_t section_code;
+ Offset section_size;
+ CHECK_RESULT(ReadU8(&section_code, "section code"));
+ CHECK_RESULT(ReadOffset(&section_size, "section size"));
+ ReadEndRestoreGuard guard(this);
+ read_end_ = state_.offset + section_size;
+ if (section_code >= kBinarySectionCount) {
+ PrintError("invalid section code: %u", section_code);
+ return Result::Error;
+ }
+
+ BinarySection section = static_cast<BinarySection>(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<unsigned int>(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<unsigned int>(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() {
+ 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());
+ // 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();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary-reader.h b/third_party/wasm2c/src/binary-reader.h
new file mode 100644
index 0000000000..2871d80fd6
--- /dev/null
+++ b/third_party/wasm2c/src/binary-reader.h
@@ -0,0 +1,479 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_READER_H_
+#define WABT_BINARY_READER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "src/binary.h"
+#include "src/common.h"
+#include "src/error.h"
+#include "src/feature.h"
+#include "src/opcode.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+class Stream;
+
+struct ReadBinaryOptions {
+ ReadBinaryOptions() = default;
+ ReadBinaryOptions(const Features& features,
+ Stream* log_stream,
+ bool read_debug_names,
+ bool stop_on_first_error,
+ bool fail_on_custom_section_error)
+ : features(features),
+ log_stream(log_stream),
+ read_debug_names(read_debug_names),
+ stop_on_first_error(stop_on_first_error),
+ fail_on_custom_section_error(fail_on_custom_section_error) {}
+
+ Features features;
+ Stream* log_stream = nullptr;
+ bool read_debug_names = false;
+ bool stop_on_first_error = true;
+ bool fail_on_custom_section_error = true;
+};
+
+// TODO: Move somewhere else?
+struct TypeMut {
+ Type type;
+ bool mutable_;
+};
+using TypeMutVector = std::vector<TypeMut>;
+
+class BinaryReaderDelegate {
+ public:
+ struct State {
+ State(const uint8_t* data, Offset size)
+ : data(data), size(size), offset(0) {}
+
+ const uint8_t* data;
+ Offset size;
+ Offset offset;
+ };
+
+ virtual ~BinaryReaderDelegate() {}
+
+ virtual bool OnError(const Error&) = 0;
+ virtual void OnSetState(const State* s) { state = s; }
+
+ /* Module */
+ virtual Result BeginModule(uint32_t version) = 0;
+ virtual Result EndModule() = 0;
+
+ virtual Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) = 0;
+
+ /* Custom section */
+ virtual Result BeginCustomSection(Index section_index,
+ Offset size,
+ string_view section_name) = 0;
+ virtual Result EndCustomSection() = 0;
+
+ /* Type section */
+ virtual Result BeginTypeSection(Offset size) = 0;
+ virtual Result OnTypeCount(Index count) = 0;
+ virtual Result OnFuncType(Index index,
+ Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types) = 0;
+ virtual Result OnStructType(Index index,
+ Index field_count,
+ TypeMut* fields) = 0;
+ virtual Result OnArrayType(Index index, TypeMut field) = 0;
+ virtual Result EndTypeSection() = 0;
+
+ /* Import section */
+ virtual Result BeginImportSection(Offset size) = 0;
+ virtual Result OnImportCount(Index count) = 0;
+ virtual Result OnImport(Index index,
+ ExternalKind kind,
+ string_view module_name,
+ string_view field_name) = 0;
+ virtual Result OnImportFunc(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index func_index,
+ Index sig_index) = 0;
+ virtual Result OnImportTable(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index table_index,
+ Type elem_type,
+ const Limits* elem_limits) = 0;
+ virtual Result OnImportMemory(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index memory_index,
+ const Limits* page_limits) = 0;
+ virtual Result OnImportGlobal(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index global_index,
+ Type type,
+ bool mutable_) = 0;
+ virtual Result OnImportTag(Index import_index,
+ string_view module_name,
+ string_view field_name,
+ Index tag_index,
+ Index sig_index) = 0;
+ virtual Result EndImportSection() = 0;
+
+ /* Function section */
+ virtual Result BeginFunctionSection(Offset size) = 0;
+ virtual Result OnFunctionCount(Index count) = 0;
+ virtual Result OnFunction(Index index, Index sig_index) = 0;
+ virtual Result EndFunctionSection() = 0;
+
+ /* Table section */
+ virtual Result BeginTableSection(Offset size) = 0;
+ virtual Result OnTableCount(Index count) = 0;
+ virtual Result OnTable(Index index,
+ Type elem_type,
+ const Limits* elem_limits) = 0;
+ virtual Result EndTableSection() = 0;
+
+ /* Memory section */
+ virtual Result BeginMemorySection(Offset size) = 0;
+ virtual Result OnMemoryCount(Index count) = 0;
+ virtual Result OnMemory(Index index, const Limits* limits) = 0;
+ virtual Result EndMemorySection() = 0;
+
+ /* Global section */
+ virtual Result BeginGlobalSection(Offset size) = 0;
+ virtual Result OnGlobalCount(Index count) = 0;
+ virtual Result BeginGlobal(Index index, Type type, bool mutable_) = 0;
+ virtual Result BeginGlobalInitExpr(Index index) = 0;
+ virtual Result EndGlobalInitExpr(Index index) = 0;
+ virtual Result EndGlobal(Index index) = 0;
+ virtual Result EndGlobalSection() = 0;
+
+ /* Exports section */
+ virtual Result BeginExportSection(Offset size) = 0;
+ virtual Result OnExportCount(Index count) = 0;
+ virtual Result OnExport(Index index,
+ ExternalKind kind,
+ Index item_index,
+ string_view name) = 0;
+ virtual Result EndExportSection() = 0;
+
+ /* Start section */
+ virtual Result BeginStartSection(Offset size) = 0;
+ virtual Result OnStartFunction(Index func_index) = 0;
+ virtual Result EndStartSection() = 0;
+
+ /* Code section */
+ virtual Result BeginCodeSection(Offset size) = 0;
+ virtual Result OnFunctionBodyCount(Index count) = 0;
+ virtual Result BeginFunctionBody(Index index, Offset size) = 0;
+ virtual Result OnLocalDeclCount(Index count) = 0;
+ virtual Result OnLocalDecl(Index decl_index, Index count, Type type) = 0;
+
+ /* Function expressions; called between BeginFunctionBody and
+ EndFunctionBody */
+ virtual Result OnOpcode(Opcode Opcode) = 0;
+ virtual Result OnOpcodeBare() = 0;
+ virtual Result OnOpcodeUint32(uint32_t value) = 0;
+ virtual Result OnOpcodeIndex(Index value) = 0;
+ virtual Result OnOpcodeIndexIndex(Index value, Index value2) = 0;
+ virtual Result OnOpcodeUint32Uint32(uint32_t value, uint32_t value2) = 0;
+ virtual Result OnOpcodeUint32Uint32Uint32(uint32_t value,
+ uint32_t value2,
+ uint32_t value3) = 0;
+ virtual Result OnOpcodeUint64(uint64_t value) = 0;
+ virtual Result OnOpcodeF32(uint32_t value) = 0;
+ virtual Result OnOpcodeF64(uint64_t value) = 0;
+ virtual Result OnOpcodeV128(v128 value) = 0;
+ virtual Result OnOpcodeBlockSig(Type sig_type) = 0;
+ virtual Result OnOpcodeType(Type type) = 0;
+ virtual Result OnAtomicLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnAtomicStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnAtomicRmwExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnAtomicRmwCmpxchgExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnAtomicWaitExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnAtomicFenceExpr(uint32_t consistency_model) = 0;
+ virtual Result OnAtomicNotifyExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnBinaryExpr(Opcode opcode) = 0;
+ virtual Result OnBlockExpr(Type sig_type) = 0;
+ virtual Result OnBrExpr(Index depth) = 0;
+ virtual Result OnBrIfExpr(Index depth) = 0;
+ virtual Result OnBrTableExpr(Index num_targets,
+ Index* target_depths,
+ Index default_target_depth) = 0;
+ virtual Result OnCallExpr(Index func_index) = 0;
+ virtual Result OnCallIndirectExpr(Index sig_index, Index table_index) = 0;
+ virtual Result OnCallRefExpr() = 0;
+ virtual Result OnCatchExpr(Index tag_index) = 0;
+ virtual Result OnCatchAllExpr() = 0;
+ virtual Result OnCompareExpr(Opcode opcode) = 0;
+ virtual Result OnConvertExpr(Opcode opcode) = 0;
+ virtual Result OnDelegateExpr(Index depth) = 0;
+ virtual Result OnDropExpr() = 0;
+ virtual Result OnElseExpr() = 0;
+ virtual Result OnEndExpr() = 0;
+ virtual Result OnEndFunc() = 0;
+ virtual Result OnF32ConstExpr(uint32_t value_bits) = 0;
+ virtual Result OnF64ConstExpr(uint64_t value_bits) = 0;
+ virtual Result OnV128ConstExpr(v128 value_bits) = 0;
+ virtual Result OnGlobalGetExpr(Index global_index) = 0;
+ virtual Result OnGlobalSetExpr(Index global_index) = 0;
+ virtual Result OnI32ConstExpr(uint32_t value) = 0;
+ virtual Result OnI64ConstExpr(uint64_t value) = 0;
+ virtual Result OnIfExpr(Type sig_type) = 0;
+ virtual Result OnLoadExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnLocalGetExpr(Index local_index) = 0;
+ virtual Result OnLocalSetExpr(Index local_index) = 0;
+ virtual Result OnLocalTeeExpr(Index local_index) = 0;
+ virtual Result OnLoopExpr(Type sig_type) = 0;
+ virtual Result OnMemoryCopyExpr() = 0;
+ virtual Result OnDataDropExpr(Index segment_index) = 0;
+ virtual Result OnMemoryFillExpr() = 0;
+ virtual Result OnMemoryGrowExpr() = 0;
+ virtual Result OnMemoryInitExpr(Index segment_index) = 0;
+ virtual Result OnMemorySizeExpr() = 0;
+ virtual Result OnTableCopyExpr(Index dst_index, Index src_index) = 0;
+ virtual Result OnElemDropExpr(Index segment_index) = 0;
+ virtual Result OnTableInitExpr(Index segment_index, Index table_index) = 0;
+ virtual Result OnTableGetExpr(Index table_index) = 0;
+ virtual Result OnTableSetExpr(Index table_index) = 0;
+ virtual Result OnTableGrowExpr(Index table_index) = 0;
+ virtual Result OnTableSizeExpr(Index table_index) = 0;
+ virtual Result OnTableFillExpr(Index table_index) = 0;
+ virtual Result OnRefFuncExpr(Index func_index) = 0;
+ virtual Result OnRefNullExpr(Type type) = 0;
+ virtual Result OnRefIsNullExpr() = 0;
+ virtual Result OnNopExpr() = 0;
+ virtual Result OnRethrowExpr(Index depth) = 0;
+ virtual Result OnReturnExpr() = 0;
+ virtual Result OnReturnCallExpr(Index func_index) = 0;
+ virtual Result OnReturnCallIndirectExpr(Index sig_index,
+ Index table_index) = 0;
+ virtual Result OnSelectExpr(Index result_count, Type* result_types) = 0;
+ virtual Result OnStoreExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnThrowExpr(Index tag_index) = 0;
+ virtual Result OnTryExpr(Type sig_type) = 0;
+
+ virtual Result OnUnaryExpr(Opcode opcode) = 0;
+ virtual Result OnTernaryExpr(Opcode opcode) = 0;
+ virtual Result OnUnreachableExpr() = 0;
+ virtual Result EndFunctionBody(Index index) = 0;
+ virtual Result EndCodeSection() = 0;
+
+ /* Simd instructions with Lane Imm operand*/
+ virtual Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) = 0;
+ virtual Result OnSimdShuffleOpExpr(Opcode opcode, v128 value) = 0;
+ virtual Result OnSimdLoadLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) = 0;
+ virtual Result OnSimdStoreLaneExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset,
+ uint64_t value) = 0;
+
+ virtual Result OnLoadSplatExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+ virtual Result OnLoadZeroExpr(Opcode opcode,
+ Address alignment_log2,
+ Address offset) = 0;
+
+ /* Elem section */
+ virtual Result BeginElemSection(Offset size) = 0;
+ virtual Result OnElemSegmentCount(Index count) = 0;
+ virtual Result BeginElemSegment(Index index,
+ Index table_index,
+ uint8_t flags) = 0;
+ virtual Result BeginElemSegmentInitExpr(Index index) = 0;
+ virtual Result EndElemSegmentInitExpr(Index index) = 0;
+ virtual Result OnElemSegmentElemType(Index index, Type elem_type) = 0;
+ virtual Result OnElemSegmentElemExprCount(Index index, Index count) = 0;
+ virtual Result OnElemSegmentElemExpr_RefNull(Index segment_index,
+ Type type) = 0;
+ virtual Result OnElemSegmentElemExpr_RefFunc(Index segment_index,
+ Index func_index) = 0;
+ virtual Result EndElemSegment(Index index) = 0;
+ virtual Result EndElemSection() = 0;
+
+ /* Data section */
+ virtual Result BeginDataSection(Offset size) = 0;
+ virtual Result OnDataSegmentCount(Index count) = 0;
+ virtual Result BeginDataSegment(Index index,
+ Index memory_index,
+ uint8_t flags) = 0;
+ virtual Result BeginDataSegmentInitExpr(Index index) = 0;
+ virtual Result EndDataSegmentInitExpr(Index index) = 0;
+ virtual Result OnDataSegmentData(Index index,
+ const void* data,
+ Address size) = 0;
+ virtual Result EndDataSegment(Index index) = 0;
+ virtual Result EndDataSection() = 0;
+
+ /* DataCount section */
+ virtual Result BeginDataCountSection(Offset size) = 0;
+ virtual Result OnDataCount(Index count) = 0;
+ virtual Result EndDataCountSection() = 0;
+
+ /* Names section */
+ virtual Result BeginNamesSection(Offset size) = 0;
+ virtual Result OnModuleNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) = 0;
+ virtual Result OnModuleName(string_view name) = 0;
+ virtual Result OnFunctionNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) = 0;
+ virtual Result OnFunctionNamesCount(Index num_functions) = 0;
+ virtual Result OnFunctionName(Index function_index,
+ string_view function_name) = 0;
+ virtual Result OnLocalNameSubsection(Index index,
+ uint32_t name_type,
+ Offset subsection_size) = 0;
+ virtual Result OnLocalNameFunctionCount(Index num_functions) = 0;
+ virtual Result OnLocalNameLocalCount(Index function_index,
+ Index num_locals) = 0;
+ virtual Result OnLocalName(Index function_index,
+ Index local_index,
+ string_view local_name) = 0;
+ virtual Result OnNameSubsection(Index index,
+ NameSectionSubsection subsection_type,
+ Offset subsection_size) = 0;
+ virtual Result OnNameCount(Index num_names) = 0;
+ virtual Result OnNameEntry(NameSectionSubsection type,
+ Index index,
+ string_view name) = 0;
+ virtual Result EndNamesSection() = 0;
+
+ /* Reloc section */
+ virtual Result BeginRelocSection(Offset size) = 0;
+ virtual Result OnRelocCount(Index count,
+ Index section_index) = 0;
+ virtual Result OnReloc(RelocType type,
+ Offset offset,
+ Index index,
+ uint32_t addend) = 0;
+ virtual Result EndRelocSection() = 0;
+
+ /* Dylink section */
+ virtual Result BeginDylinkSection(Offset size) = 0;
+ virtual Result OnDylinkInfo(uint32_t mem_size,
+ uint32_t mem_align_log2,
+ uint32_t table_size,
+ uint32_t table_align_log2) = 0;
+ virtual Result OnDylinkNeededCount(Index count) = 0;
+ virtual Result OnDylinkNeeded(string_view so_name) = 0;
+ virtual Result EndDylinkSection() = 0;
+
+ /* Linking section */
+ virtual Result BeginLinkingSection(Offset size) = 0;
+ virtual Result OnSymbolCount(Index count) = 0;
+ virtual Result OnDataSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index segment,
+ uint32_t offset,
+ uint32_t size) = 0;
+ virtual Result OnFunctionSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index function_index) = 0;
+ virtual Result OnGlobalSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index global_index) = 0;
+ virtual Result OnSectionSymbol(Index index,
+ uint32_t flags,
+ Index section_index) = 0;
+ virtual Result OnTagSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index tag_index) = 0;
+ virtual Result OnTableSymbol(Index index,
+ uint32_t flags,
+ string_view name,
+ Index table_index) = 0;
+ virtual Result OnSegmentInfoCount(Index count) = 0;
+ virtual Result OnSegmentInfo(Index index,
+ string_view name,
+ Address alignment_log2,
+ uint32_t flags) = 0;
+ virtual Result OnInitFunctionCount(Index count) = 0;
+ virtual Result OnInitFunction(uint32_t priority, Index function_index) = 0;
+ virtual Result OnComdatCount(Index count) = 0;
+ virtual Result OnComdatBegin(string_view name,
+ uint32_t flags,
+ Index count) = 0;
+ virtual Result OnComdatEntry(ComdatType kind, Index index) = 0;
+ virtual Result EndLinkingSection() = 0;
+
+ /* Tag section */
+ virtual Result BeginTagSection(Offset size) = 0;
+ virtual Result OnTagCount(Index count) = 0;
+ virtual Result OnTagType(Index index, Index sig_index) = 0;
+ virtual Result EndTagSection() = 0;
+
+ /* InitExpr - used by elem, data and global sections; these functions are
+ * only called between calls to Begin*InitExpr and End*InitExpr */
+ virtual Result OnInitExprF32ConstExpr(Index index, uint32_t value) = 0;
+ virtual Result OnInitExprF64ConstExpr(Index index, uint64_t value) = 0;
+ virtual Result OnInitExprV128ConstExpr(Index index, v128 value) = 0;
+ virtual Result OnInitExprGlobalGetExpr(Index index, Index global_index) = 0;
+ virtual Result OnInitExprI32ConstExpr(Index index, uint32_t value) = 0;
+ virtual Result OnInitExprI64ConstExpr(Index index, uint64_t value) = 0;
+ virtual Result OnInitExprRefNull(Index index, Type type) = 0;
+ virtual Result OnInitExprRefFunc(Index index, Index func_index) = 0;
+
+ const State* state = nullptr;
+};
+
+Result ReadBinary(const void* data,
+ size_t size,
+ BinaryReaderDelegate* reader,
+ const ReadBinaryOptions& options);
+
+size_t ReadU32Leb128(const uint8_t* ptr,
+ const uint8_t* end,
+ uint32_t* out_value);
+
+size_t ReadI32Leb128(const uint8_t* ptr,
+ const uint8_t* end,
+ uint32_t* out_value);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_READER_H_ */
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..13e71122e5
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer-spec.cc
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-writer-spec.h"
+
+#include <cassert>
+#include <cinttypes>
+
+#include "config.h"
+
+#include "src/binary-writer.h"
+#include "src/binary.h"
+#include "src/cast.h"
+#include "src/filenames.h"
+#include "src/ir.h"
+#include "src/literal.h"
+#include "src/stream.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+namespace {
+
+class BinaryWriterSpec {
+ public:
+ BinaryWriterSpec(Stream* json_stream,
+ WriteBinarySpecStreamFactory module_stream_factory,
+ string_view source_filename,
+ 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(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(string_view filename, const Module& module);
+ void WriteScriptModule(string_view filename,
+ const ScriptModule& script_module);
+ void WriteInvalidModule(const ScriptModule& module, 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,
+ string_view source_filename,
+ 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(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",
+ "action",
+ "register",
+ "assert_malformed",
+ "assert_invalid",
+ "assert_unlinkable",
+ "assert_uninstantiable",
+ "assert_return",
+ "assert_trap",
+ "assert_exhaustion",
+ };
+ WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount);
+
+ WriteKey("type");
+ assert(s_command_names[static_cast<size_t>(command.type)]);
+ WriteString(s_command_names[static_cast<size_t>(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());
+ 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());
+ 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<uint8_t>(lane));
+ break;
+
+ case Type::I16:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint16_t>(lane));
+ break;
+
+ case Type::I32:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint32_t>(lane));
+ break;
+
+ case Type::I64:
+ json_stream_->Writef("\"%" PRIu64 "\"",
+ const_.v128_lane<uint64_t>(lane));
+ break;
+
+ case Type::F32:
+ WriteF32(const_.v128_lane<uint32_t>(lane),
+ const_.expected_nan(lane));
+ break;
+
+ case Type::F64:
+ WriteF64(const_.v128_lane<uint64_t>(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<InvokeAction>(&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(string_view filename, const Module& module) {
+ result_ |=
+ WriteBinaryModule(module_stream_factory_(filename), &module, options_);
+}
+
+void BinaryWriterSpec::WriteScriptModule(string_view filename,
+ const ScriptModule& script_module) {
+ switch (script_module.type()) {
+ case ScriptModuleType::Text:
+ WriteModule(filename, cast<TextScriptModule>(&script_module)->module);
+ break;
+
+ case ScriptModuleType::Binary:
+ module_stream_factory_(filename)->WriteData(
+ cast<BinaryScriptModule>(&script_module)->data, "");
+ break;
+
+ case ScriptModuleType::Quoted:
+ module_stream_factory_(filename)->WriteData(
+ cast<QuotedScriptModule>(&script_module)->data, "");
+ break;
+ }
+}
+
+void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module,
+ 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<ModuleCommand>(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::Action: {
+ const Action& action = *cast<ActionCommand>(command)->action;
+ WriteLocation(action.loc);
+ WriteSeparator();
+ WriteAction(action);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteActionResultType(action);
+ break;
+ }
+
+ case CommandType::Register: {
+ auto* register_command = cast<RegisterCommand>(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<AssertMalformedCommand>(command);
+ WriteInvalidModule(*assert_malformed_command->module,
+ assert_malformed_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertInvalid: {
+ auto* assert_invalid_command = cast<AssertInvalidCommand>(command);
+ WriteInvalidModule(*assert_invalid_command->module,
+ assert_invalid_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertUnlinkable: {
+ auto* assert_unlinkable_command =
+ cast<AssertUnlinkableCommand>(command);
+ WriteInvalidModule(*assert_unlinkable_command->module,
+ assert_unlinkable_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertUninstantiable: {
+ auto* assert_uninstantiable_command =
+ cast<AssertUninstantiableCommand>(command);
+ WriteInvalidModule(*assert_uninstantiable_command->module,
+ assert_uninstantiable_command->text);
+ num_modules_++;
+ break;
+ }
+
+ case CommandType::AssertReturn: {
+ auto* assert_return_command = cast<AssertReturnCommand>(command);
+ WriteLocation(assert_return_command->action->loc);
+ WriteSeparator();
+ WriteAction(*assert_return_command->action);
+ WriteSeparator();
+ WriteKey("expected");
+ WriteConstVector(assert_return_command->expected);
+ break;
+ }
+
+ case CommandType::AssertTrap: {
+ auto* assert_trap_command = cast<AssertTrapCommand>(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<AssertExhaustionCommand>(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;
+ }
+ }
+
+ 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,
+ string_view source_filename,
+ 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,
+ string_view source_filename,
+ string_view module_filename_noext,
+ const WriteBinaryOptions& options,
+ std::vector<FilenameMemoryStreamPair>* out_module_streams,
+ Stream* log_stream) {
+ WriteBinarySpecStreamFactory module_stream_factory =
+ [&](string_view filename) {
+ out_module_streams->emplace_back(filename,
+ MakeUnique<MemoryStream>(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-spec.h b/third_party/wasm2c/src/binary-writer-spec.h
new file mode 100644
index 0000000000..197d6f2c3a
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer-spec.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_WRITER_SPEC_H_
+#define WABT_BINARY_WRITER_SPEC_H_
+
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include "src/binary-writer.h"
+#include "src/common.h"
+#include "src/ir.h"
+
+namespace wabt {
+
+struct FilenameMemoryStreamPair {
+ FilenameMemoryStreamPair(string_view filename,
+ std::unique_ptr<MemoryStream> stream)
+ : filename(filename), stream(std::move(stream)) {}
+ std::string filename;
+ std::unique_ptr<MemoryStream> stream;
+};
+
+typedef std::function<Stream*(string_view filename)>
+ WriteBinarySpecStreamFactory;
+
+Result WriteBinarySpecScript(Stream* json_stream,
+ WriteBinarySpecStreamFactory module_stream_factory,
+ Script*,
+ string_view source_filename,
+ string_view module_filename_noext,
+ const WriteBinaryOptions&);
+
+// Convenience function for producing MemoryStream outputs all modules.
+Result WriteBinarySpecScript(
+ Stream* json_stream,
+ Script*,
+ string_view source_filename,
+ string_view module_filename_noext,
+ const WriteBinaryOptions&,
+ std::vector<FilenameMemoryStreamPair>* out_module_streams,
+ Stream* log_stream = nullptr);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_WRITER_SPEC_H_ */
diff --git a/third_party/wasm2c/src/binary-writer.cc b/third_party/wasm2c/src/binary-writer.cc
new file mode 100644
index 0000000000..aae2b4bf5e
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer.cc
@@ -0,0 +1,1675 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary-writer.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <set>
+#include <vector>
+
+#include "config.h"
+
+#include "src/binary.h"
+#include "src/cast.h"
+#include "src/ir.h"
+#include "src/leb128.h"
+#include "src/stream.h"
+#include "src/string-view.h"
+
+#define PRINT_HEADER_NO_INDEX -1
+#define MAX_U32_LEB128_BYTES 5
+
+namespace wabt {
+
+void WriteStr(Stream* stream,
+ 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());
+}
+
+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, string_view name, const char* desc) {
+ 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. */
+static const 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<Reloc> 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_;
+ string_view name_;
+ uint8_t flags_;
+ union {
+ Function function_;
+ Data data_;
+ Global global_;
+ Section section_;
+ Tag tag_;
+ Table table_;
+ };
+
+ public:
+ Symbol(const string_view& name, uint8_t flags, const Function& f)
+ : type_(Function::type), name_(name), flags_(flags), function_(f) {}
+ Symbol(const string_view& name, uint8_t flags, const Data& d)
+ : type_(Data::type), name_(name), flags_(flags), data_(d) {}
+ Symbol(const string_view& name, uint8_t flags, const Global& g)
+ : type_(Global::type), name_(name), flags_(flags), global_(g) {}
+ Symbol(const string_view& name, uint8_t flags, const Section& s)
+ : type_(Section::type), name_(name), flags_(flags), section_(s) {}
+ Symbol(const string_view& name, uint8_t flags, const Tag& e)
+ : type_(Tag::type), name_(name), flags_(flags), tag_(e) {}
+ Symbol(const string_view& name, uint8_t flags, const Table& t)
+ : type_(Table::type), name_(name), flags_(flags), table_(t) {}
+
+ SymbolType type() const { return type_; }
+ const string_view& name() const { return name_; }
+ uint8_t flags() const { return flags_; }
+
+ SymbolVisibility visibility() const {
+ return static_cast<SymbolVisibility>(flags() & WABT_SYMBOL_MASK_VISIBILITY);
+ }
+ SymbolBinding binding() const {
+ return static_cast<SymbolBinding>(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<Symbol> symbols_;
+
+ std::vector<Index> functions_;
+ std::vector<Index> tables_;
+ std::vector<Index> globals_;
+
+ std::set<string_view> seen_names_;
+
+ Result EnsureUnique(const 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 <typename T>
+ Result AddSymbol(std::vector<Index>* map, 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 = 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<Index>& 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<Index> exported_funcs;
+ std::set<Index> exported_globals;
+ std::set<Index> exported_tags;
+ std::set<Index> 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<Symbol>& 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);
+ }
+};
+
+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 <typename T>
+ void WriteLoadStoreExpr(const Func* func, const Expr* expr, const char* desc);
+ template <typename T>
+ 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 <typename T>
+ void WriteNames(const std::vector<T*>& elems, NameSectionSubsection type);
+
+ Stream* stream_;
+ const WriteBinaryOptions& options_;
+ const Module* module_;
+
+ SymbolTable symtab_;
+ std::vector<RelocSection> 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;
+};
+
+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<unsigned>(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 <typename T>
+void BinaryWriter::WriteLoadStoreExpr(const Func* func,
+ const Expr* expr,
+ const char* desc) {
+ auto* typed_expr = cast<T>(expr);
+ WriteOpcode(stream_, typed_expr->opcode);
+ Address align = typed_expr->opcode.GetAlignment(typed_expr->align);
+ stream_->WriteU8(log2_u32(align), "alignment");
+ WriteU32Leb128(stream_, typed_expr->offset, desc);
+}
+
+template <typename T>
+void BinaryWriter::WriteSimdLoadStoreLaneExpr(const Func* func,
+ const Expr* expr,
+ const char* desc) {
+ auto* typed_expr = cast<T>(expr);
+ WriteOpcode(stream_, typed_expr->opcode);
+ Address align = typed_expr->opcode.GetAlignment(typed_expr->align);
+ stream_->WriteU8(log2_u32(align), "alignment");
+ WriteU32Leb128(stream_, typed_expr->offset, desc);
+ stream_->WriteU8(static_cast<uint8_t>(typed_expr->val), "Simd Lane literal");
+}
+
+void BinaryWriter::WriteExpr(const Func* func, const Expr* expr) {
+ switch (expr->type()) {
+ case ExprType::AtomicLoad:
+ WriteLoadStoreExpr<AtomicLoadExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicRmw:
+ WriteLoadStoreExpr<AtomicRmwExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicRmwCmpxchg:
+ WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicStore:
+ WriteLoadStoreExpr<AtomicStoreExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicWait:
+ WriteLoadStoreExpr<AtomicWaitExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::AtomicFence: {
+ auto* fence_expr = cast<AtomicFenceExpr>(expr);
+ WriteOpcode(stream_, Opcode::AtomicFence);
+ WriteU32Leb128(stream_, fence_expr->consistency_model,
+ "consistency model");
+ break;
+ }
+ case ExprType::AtomicNotify:
+ WriteLoadStoreExpr<AtomicNotifyExpr>(func, expr, "memory offset");
+ break;
+ case ExprType::Binary:
+ WriteOpcode(stream_, cast<BinaryExpr>(expr)->opcode);
+ break;
+ case ExprType::Block:
+ WriteOpcode(stream_, Opcode::Block);
+ WriteBlockDecl(cast<BlockExpr>(expr)->block.decl);
+ WriteExprList(func, cast<BlockExpr>(expr)->block.exprs);
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ case ExprType::Br:
+ WriteOpcode(stream_, Opcode::Br);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrExpr>(expr)->var),
+ "break depth");
+ break;
+ case ExprType::BrIf:
+ WriteOpcode(stream_, Opcode::BrIf);
+ WriteU32Leb128(stream_, GetLabelVarDepth(&cast<BrIfExpr>(expr)->var),
+ "break depth");
+ break;
+ case ExprType::BrTable: {
+ auto* br_table_expr = cast<BrTableExpr>(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<CallExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::Call);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::ReturnCall: {
+ Index index = module_->GetFuncIndex(cast<ReturnCallExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::ReturnCall);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::CallIndirect:{
+ Index sig_index =
+ module_->GetFuncTypeIndex(cast<CallIndirectExpr>(expr)->decl);
+ Index table_index =
+ module_->GetTableIndex(cast<CallIndirectExpr>(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<ReturnCallIndirectExpr>(expr)->decl);
+ Index table_index =
+ module_->GetTableIndex(cast<ReturnCallIndirectExpr>(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<CompareExpr>(expr)->opcode);
+ break;
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(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<ConvertExpr>(expr)->opcode);
+ break;
+ case ExprType::Drop:
+ WriteOpcode(stream_, Opcode::Drop);
+ break;
+ case ExprType::GlobalGet: {
+ Index index = module_->GetGlobalIndex(cast<GlobalGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::GlobalGet);
+ WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
+ break;
+ }
+ case ExprType::GlobalSet: {
+ Index index = module_->GetGlobalIndex(cast<GlobalSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::GlobalSet);
+ WriteU32Leb128WithReloc(index, "global index", RelocType::GlobalIndexLEB);
+ break;
+ }
+ case ExprType::If: {
+ auto* if_expr = cast<IfExpr>(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<LoadExpr>(func, expr, "load offset");
+ break;
+ case ExprType::LocalGet: {
+ Index index = GetLocalIndex(func, cast<LocalGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalGet);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::LocalSet: {
+ Index index = GetLocalIndex(func, cast<LocalSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalSet);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::LocalTee: {
+ Index index = GetLocalIndex(func, cast<LocalTeeExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::LocalTee);
+ WriteU32Leb128(stream_, index, "local index");
+ break;
+ }
+ case ExprType::Loop:
+ WriteOpcode(stream_, Opcode::Loop);
+ WriteBlockDecl(cast<LoopExpr>(expr)->block.decl);
+ WriteExprList(func, cast<LoopExpr>(expr)->block.exprs);
+ WriteOpcode(stream_, Opcode::End);
+ break;
+ case ExprType::MemoryCopy:
+ WriteOpcode(stream_, Opcode::MemoryCopy);
+ WriteU32Leb128(stream_, 0, "memory.copy reserved");
+ WriteU32Leb128(stream_, 0, "memory.copy reserved");
+ break;
+ case ExprType::DataDrop: {
+ Index index =
+ module_->GetDataSegmentIndex(cast<DataDropExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::DataDrop);
+ WriteU32Leb128(stream_, index, "data.drop segment");
+ has_data_segment_instruction_ = true;
+ break;
+ }
+ case ExprType::MemoryFill:
+ WriteOpcode(stream_, Opcode::MemoryFill);
+ WriteU32Leb128(stream_, 0, "memory.fill reserved");
+ break;
+ case ExprType::MemoryGrow:
+ WriteOpcode(stream_, Opcode::MemoryGrow);
+ WriteU32Leb128(stream_, 0, "memory.grow reserved");
+ break;
+ case ExprType::MemoryInit: {
+ Index index =
+ module_->GetDataSegmentIndex(cast<MemoryInitExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::MemoryInit);
+ WriteU32Leb128(stream_, index, "memory.init segment");
+ WriteU32Leb128(stream_, 0, "memory.init reserved");
+ has_data_segment_instruction_ = true;
+ break;
+ }
+ case ExprType::MemorySize:
+ WriteOpcode(stream_, Opcode::MemorySize);
+ WriteU32Leb128(stream_, 0, "memory.size reserved");
+ break;
+ case ExprType::TableCopy: {
+ auto* copy_expr = cast<TableCopyExpr>(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<ElemDropExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::ElemDrop);
+ WriteU32Leb128(stream_, index, "elem.drop segment");
+ break;
+ }
+ case ExprType::TableInit: {
+ auto* init_expr = cast<TableInitExpr>(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<TableGetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableGet);
+ WriteTableNumberWithReloc(index, "table.get table index");
+ break;
+ }
+ case ExprType::TableSet: {
+ Index index =
+ module_->GetTableIndex(cast<TableSetExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableSet);
+ WriteTableNumberWithReloc(index, "table.set table index");
+ break;
+ }
+ case ExprType::TableGrow: {
+ Index index =
+ module_->GetTableIndex(cast<TableGrowExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableGrow);
+ WriteTableNumberWithReloc(index, "table.grow table index");
+ break;
+ }
+ case ExprType::TableSize: {
+ Index index =
+ module_->GetTableIndex(cast<TableSizeExpr>(expr)->var);
+ WriteOpcode(stream_, Opcode::TableSize);
+ WriteTableNumberWithReloc(index, "table.size table index");
+ break;
+ }
+ case ExprType::TableFill: {
+ Index index =
+ module_->GetTableIndex(cast<TableFillExpr>(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<RefFuncExpr>(expr)->var);
+ WriteU32Leb128WithReloc(index, "function index", RelocType::FuncIndexLEB);
+ break;
+ }
+ case ExprType::RefNull: {
+ WriteOpcode(stream_, Opcode::RefNull);
+ WriteType(stream_, cast<RefNullExpr>(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<RethrowExpr>(expr)->var),
+ "rethrow depth");
+ break;
+ case ExprType::Return:
+ WriteOpcode(stream_, Opcode::Return);
+ break;
+ case ExprType::Select: {
+ auto* select_expr = cast<SelectExpr>(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<StoreExpr>(func, expr, "store offset");
+ break;
+ case ExprType::Throw:
+ WriteOpcode(stream_, Opcode::Throw);
+ WriteU32Leb128(stream_, GetTagVarDepth(&cast<ThrowExpr>(expr)->var),
+ "throw tag");
+ break;
+ case ExprType::Try: {
+ auto* try_expr = cast<TryExpr>(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<UnaryExpr>(expr)->opcode);
+ break;
+ case ExprType::Ternary:
+ WriteOpcode(stream_, cast<TernaryExpr>(expr)->opcode);
+ break;
+ case ExprType::SimdLaneOp: {
+ const Opcode opcode = cast<SimdLaneOpExpr>(expr)->opcode;
+ WriteOpcode(stream_, opcode);
+ stream_->WriteU8(static_cast<uint8_t>(cast<SimdLaneOpExpr>(expr)->val),
+ "Simd Lane literal");
+ break;
+ }
+ case ExprType::SimdLoadLane: {
+ WriteSimdLoadStoreLaneExpr<SimdLoadLaneExpr>(func, expr, "load offset");
+ break;
+ }
+ case ExprType::SimdStoreLane: {
+ WriteSimdLoadStoreLaneExpr<SimdStoreLaneExpr>(func, expr, "store offset");
+ break;
+ }
+ case ExprType::SimdShuffleOp: {
+ const Opcode opcode = cast<SimdShuffleOpExpr>(expr)->opcode;
+ WriteOpcode(stream_, opcode);
+ stream_->WriteU128(cast<SimdShuffleOpExpr>(expr)->val,
+ "Simd Lane[16] literal");
+ break;
+ }
+ case ExprType::LoadSplat:
+ WriteLoadStoreExpr<LoadSplatExpr>(func, expr, "load offset");
+ break;
+ case ExprType::LoadZero:
+ WriteLoadStoreExpr<LoadZeroExpr>(func, expr, "load offset");
+ break;
+ case ExprType::Unreachable:
+ WriteOpcode(stream_, Opcode::Unreachable);
+ 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<Reloc>& 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<Symbol>& 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 <typename T>
+void BinaryWriter::WriteNames(const std::vector<T*>& 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<FuncType>(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<StructType>(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<ArrayType>(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<FuncImport>(import)->func.decl),
+ "import signature index");
+ break;
+
+ case ExternalKind::Table:
+ WriteTable(&cast<TableImport>(import)->table);
+ break;
+
+ case ExternalKind::Memory:
+ WriteMemory(&cast<MemoryImport>(import)->memory);
+ break;
+
+ case ExternalKind::Global:
+ WriteGlobalHeader(&cast<GlobalImport>(import)->global);
+ break;
+
+ case ExternalKind::Tag:
+ WriteTagType(&cast<TagImport>(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 ElemExpr& elem_expr : segment->elem_exprs) {
+ switch (elem_expr.kind) {
+ case ElemExprKind::RefNull:
+ WriteOpcode(stream_, Opcode::RefNull);
+ WriteType(stream_, elem_expr.type, "elem expr ref.null type");
+ break;
+
+ case ElemExprKind::RefFunc:
+ WriteOpcode(stream_, Opcode::RefFunc);
+ WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem expr function index");
+ break;
+ }
+ WriteOpcode(stream_, Opcode::End);
+ }
+ } else {
+ for (const ElemExpr& elem_expr : segment->elem_exprs) {
+ assert(elem_expr.kind == ElemExprKind::RefFunc);
+ WriteU32Leb128(stream_, module_->GetFuncIndex(elem_expr.var), "elem function index");
+ }
+ }
+ }
+ EndSection();
+ }
+
+ if (options_.features.bulk_memory_enabled()) {
+ // 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) {
+ WriteHeader("function body", i);
+ const Func* func = module_->funcs[i + module_->num_func_imports];
+
+ /* 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)");
+ 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() &&
+ !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);
+ }
+ 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;
+ }
+ }
+ }
+
+ 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)) {
+ 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<std::string> 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<Func>(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();
+
+ WriteU32Leb128(stream_, i, "function index");
+ WriteU32Leb128(stream_, num_params_and_locals, "num locals");
+
+ MakeTypeBindingReverseMapping(num_params_and_locals, func->bindings,
+ &index_to_name);
+ for (size_t j = 0; j < num_params_and_locals; ++j) {
+ const std::string& name = index_to_name[j];
+ wabt_snprintf(desc, sizeof(desc), "local name %" PRIzd, j);
+ WriteU32Leb128(stream_, j, "local index");
+ WriteDebugName(stream_, name, desc);
+ }
+ }
+ EndSubsection();
+
+ WriteNames<TypeEntry>(module_->types, NameSectionSubsection::Type);
+ WriteNames<Table>(module_->tables, NameSectionSubsection::Table);
+ WriteNames<Memory>(module_->memories, NameSectionSubsection::Memory);
+ WriteNames<Global>(module_->globals, NameSectionSubsection::Global);
+ WriteNames<ElemSegment>(module_->elem_segments,
+ NameSectionSubsection::ElemSegment);
+ WriteNames<DataSegment>(module_->data_segments,
+ NameSectionSubsection::DataSegment);
+
+ EndSection();
+ }
+
+ if (options_.relocatable) {
+ WriteLinkingSection();
+ for (RelocSection& section : reloc_sections_) {
+ WriteRelocSection(&section);
+ }
+ }
+
+ return stream_->result();
+}
+
+} // 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-writer.h b/third_party/wasm2c/src/binary-writer.h
new file mode 100644
index 0000000000..4304a0a70e
--- /dev/null
+++ b/third_party/wasm2c/src/binary-writer.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_WRITER_H_
+#define WABT_BINARY_WRITER_H_
+
+#include "src/common.h"
+#include "src/feature.h"
+#include "src/opcode.h"
+#include "src/stream.h"
+
+namespace wabt {
+
+struct Module;
+struct Script;
+
+struct WriteBinaryOptions {
+ WriteBinaryOptions() = default;
+ WriteBinaryOptions(const Features& features,
+ bool canonicalize_lebs,
+ bool relocatable,
+ bool write_debug_names)
+ : features(features),
+ canonicalize_lebs(canonicalize_lebs),
+ relocatable(relocatable),
+ write_debug_names(write_debug_names) {}
+
+ Features features;
+ bool canonicalize_lebs = true;
+ bool relocatable = false;
+ bool write_debug_names = false;
+};
+
+Result WriteBinaryModule(Stream*, const Module*, const WriteBinaryOptions&);
+
+void WriteType(Stream* stream, Type type, const char* desc = nullptr);
+
+void WriteStr(Stream* stream,
+ string_view s,
+ const char* desc,
+ PrintChars print_chars = PrintChars::No);
+
+void WriteOpcode(Stream* stream, Opcode opcode);
+
+void WriteLimits(Stream* stream, const Limits* limits);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_WRITER_H_ */
diff --git a/third_party/wasm2c/src/binary.cc b/third_party/wasm2c/src/binary.cc
new file mode 100644
index 0000000000..60b20cbb6a
--- /dev/null
+++ b/third_party/wasm2c/src/binary.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/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;
+ }
+}
+
+const char* NameSubsectionName[] = {
+ "module",
+ "function",
+ "local",
+ "label",
+ "type",
+ "table",
+ "memory",
+ "global",
+ "elemseg",
+ "dataseg",
+};
+
+const char* GetNameSectionSubsectionName(NameSectionSubsection subsec) {
+ return NameSubsectionName[size_t(subsec)];
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/binary.h b/third_party/wasm2c/src/binary.h
new file mode 100644
index 0000000000..c6757820b7
--- /dev/null
+++ b/third_party/wasm2c/src/binary.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINARY_H_
+#define WABT_BINARY_H_
+
+#include "src/common.h"
+
+#define WABT_BINARY_MAGIC 0x6d736100
+#define WABT_BINARY_VERSION 1
+#define WABT_BINARY_LIMITS_HAS_MAX_FLAG 0x1
+#define WABT_BINARY_LIMITS_IS_SHARED_FLAG 0x2
+#define WABT_BINARY_LIMITS_IS_64_FLAG 0x4
+#define WABT_BINARY_LIMITS_ALL_FLAGS \
+ (WABT_BINARY_LIMITS_HAS_MAX_FLAG | WABT_BINARY_LIMITS_IS_SHARED_FLAG | \
+ WABT_BINARY_LIMITS_IS_64_FLAG)
+
+#define WABT_BINARY_SECTION_NAME "name"
+#define WABT_BINARY_SECTION_RELOC "reloc"
+#define WABT_BINARY_SECTION_LINKING "linking"
+#define WABT_BINARY_SECTION_DYLINK "dylink"
+#define WABT_BINARY_SECTION_DYLINK0 "dylink.0"
+
+#define WABT_FOREACH_BINARY_SECTION(V) \
+ V(Custom, custom, 0) \
+ V(Type, type, 1) \
+ V(Import, import, 2) \
+ V(Function, function, 3) \
+ V(Table, table, 4) \
+ V(Memory, memory, 5) \
+ V(Tag, tag, 13) \
+ V(Global, global, 6) \
+ V(Export, export, 7) \
+ V(Start, start, 8) \
+ V(Elem, elem, 9) \
+ V(DataCount, data_count, 12) \
+ V(Code, code, 10) \
+ V(Data, data, 11)
+
+namespace wabt {
+
+/* clang-format off */
+enum class BinarySection {
+#define V(Name, name, code) Name = code,
+ WABT_FOREACH_BINARY_SECTION(V)
+#undef V
+ Invalid = ~0,
+
+ First = Custom,
+ Last = Tag,
+};
+/* clang-format on */
+static const int kBinarySectionCount = WABT_ENUM_COUNT(BinarySection);
+
+enum class BinarySectionOrder {
+#define V(Name, name, code) Name,
+ WABT_FOREACH_BINARY_SECTION(V)
+#undef V
+};
+
+BinarySectionOrder GetSectionOrder(BinarySection);
+const char* GetSectionName(BinarySection);
+
+enum class NameSectionSubsection {
+ Module = 0,
+ Function = 1,
+ Local = 2,
+ Label = 3,
+ Type = 4,
+ Table = 5,
+ Memory = 6,
+ Global = 7,
+ ElemSegment = 8,
+ DataSegment = 9,
+ Last = DataSegment,
+};
+const char* GetNameSectionSubsectionName(NameSectionSubsection subsec);
+
+} // namespace wabt
+
+#endif /* WABT_BINARY_H_ */
diff --git a/third_party/wasm2c/src/binding-hash.cc b/third_party/wasm2c/src/binding-hash.cc
new file mode 100644
index 0000000000..f52682d7fe
--- /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 "src/binding-hash.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "src/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/binding-hash.h b/third_party/wasm2c/src/binding-hash.h
new file mode 100644
index 0000000000..077952700d
--- /dev/null
+++ b/third_party/wasm2c/src/binding-hash.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_BINDING_HASH_H_
+#define WABT_BINDING_HASH_H_
+
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "src/common.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+struct Var;
+
+struct Binding {
+ explicit Binding(Index index) : index(index) {}
+ Binding(const Location& loc, Index index) : loc(loc), index(index) {}
+
+ Location loc;
+ Index index;
+};
+
+// This class derives from a C++ container, which is usually not advisable
+// because they don't have virtual destructors. So don't delete a BindingHash
+// object through a pointer to std::unordered_multimap.
+class BindingHash : public std::unordered_multimap<std::string, Binding> {
+ public:
+ typedef std::function<void(const value_type&, const value_type&)>
+ DuplicateCallback;
+
+ void FindDuplicates(DuplicateCallback callback) const;
+
+ Index FindIndex(const Var&) const;
+
+ Index FindIndex(const std::string& name) const {
+ auto iter = find(name);
+ return iter != end() ? iter->second.index : kInvalidIndex;
+ }
+
+ Index FindIndex(string_view name) const {
+ return FindIndex(name.to_string());
+ }
+
+ private:
+ typedef std::vector<const value_type*> ValueTypeVector;
+
+ void CreateDuplicatesVector(ValueTypeVector* out_duplicates) const;
+ void SortDuplicatesVectorByLocation(ValueTypeVector* duplicates) const;
+ void CallCallbacks(const ValueTypeVector& duplicates,
+ DuplicateCallback callback) const;
+};
+
+} // namespace wabt
+
+#endif /* WABT_BINDING_HASH_H_ */
diff --git a/third_party/wasm2c/src/c-writer.cc b/third_party/wasm2c/src/c-writer.cc
new file mode 100644
index 0000000000..c008474aeb
--- /dev/null
+++ b/third_party/wasm2c/src/c-writer.cc
@@ -0,0 +1,2595 @@
+/*
+ * 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 "src/c-writer.h"
+
+#include <cctype>
+#include <cinttypes>
+#include <map>
+#include <set>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/ir.h"
+#include "src/literal.h"
+#include "src/stream.h"
+#include "src/string-view.h"
+
+#define INDENT_SIZE 2
+
+#define UNIMPLEMENTED(x) printf("unimplemented: %s\n", (x)), abort()
+
+namespace wabt {
+
+namespace {
+
+struct Label {
+ Label(LabelType label_type,
+ const std::string& name,
+ const TypeVector& sig,
+ size_t type_stack_size,
+ bool used = false)
+ : label_type(label_type),
+ name(name),
+ sig(sig),
+ type_stack_size(type_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;
+ bool used = false;
+};
+
+template <int>
+struct Name {
+ explicit Name(const std::string& name) : name(name) {}
+ const std::string& name;
+};
+
+typedef Name<0> LocalName;
+typedef Name<1> GlobalName;
+typedef Name<2> ExternalPtr;
+typedef Name<3> ExternalRef;
+
+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 GlobalVar {
+ explicit GlobalVar(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 Newline {};
+struct OpenBrace {};
+struct CloseBrace {};
+
+int GetShiftMask(Type type) {
+ switch (type) {
+ case Type::I32: return 31;
+ case Type::I64: return 63;
+ default: WABT_UNREACHABLE; return 0;
+ }
+}
+
+class CWriter {
+ public:
+ CWriter(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const WriteCOptions& options)
+ : options_(options),
+ c_stream_(c_stream),
+ h_stream_(h_stream),
+ header_name_(header_name) {}
+
+ Result WriteModule(const Module&);
+
+ private:
+ typedef std::set<std::string> SymbolSet;
+ typedef std::map<std::string, std::string> SymbolMap;
+ typedef std::pair<Index, Type> StackTypePair;
+ typedef std::map<StackTypePair, std::string> StackVarSymbolMap;
+
+ void UseStream(Stream*);
+
+ 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 IsTopLabelUsed() const;
+ void PopLabel();
+
+ static std::string AddressOf(const std::string&);
+
+ static char MangleType(Type);
+ static std::string MangleTypes(const TypeVector&);
+ static std::string MangleMultivalueTypes(const TypeVector&);
+ static std::string MangleName(string_view);
+ static std::string MangleFuncName(string_view,
+ const TypeVector& param_types,
+ const TypeVector& result_types);
+ static std::string MangleGlobalName(string_view, Type);
+ static std::string LegalizeName(string_view);
+ std::string DefineName(SymbolSet*, string_view);
+ std::string DefineImportName(const std::string& name,
+ string_view module_name,
+ string_view mangled_field_name);
+ std::string DefineGlobalScopeName(const std::string&);
+ std::string DefineLocalScopeName(const std::string&);
+ std::string DefineGlobalVarName(const std::string& name);
+ std::string DefineStackVarName(Index, Type, string_view);
+
+ void Indent(int size = INDENT_SIZE);
+ void Dedent(int size = INDENT_SIZE);
+ void WriteIndent();
+ void WriteData(const void* src, size_t size);
+ void Writef(const char* format, ...);
+
+ template <typename T, typename U, typename... Args>
+ void Write(T&& t, U&& u, Args&&... args) {
+ Write(std::forward<T>(t));
+ Write(std::forward<U>(u));
+ Write(std::forward<Args>(args)...);
+ }
+
+ std::string GetGlobalName(const std::string&) const;
+ std::string GetGlobalVarName(const std::string& name) const;
+
+ void Write() {}
+ void Write(Newline);
+ void Write(OpenBrace);
+ void Write(CloseBrace);
+ void Write(Index);
+ void Write(string_view);
+ void Write(const LocalName&);
+ void Write(const GlobalName&);
+ void Write(const ExternalPtr&);
+ void Write(const ExternalRef&);
+ void Write(Type);
+ void Write(SignedType);
+ void Write(TypeEnum);
+ void Write(const Var&);
+ void Write(const GotoLabel&);
+ void Write(const LabelDecl&);
+ void Write(const GlobalVar&);
+ void Write(const StackVar&);
+ void Write(const ResultType&);
+ void Write(const Const&);
+ void WriteInitExpr(const ExprList&);
+ std::string GenerateHeaderGuard() const;
+ void WriteSourceTop();
+ void WriteMultivalueTypes();
+ void WriteSandboxStruct();
+ void WriteFuncTypes();
+ void WriteImports();
+ bool IsFuncStatic(std::string name);
+ std::string GetFuncStaticOrExport(std::string);
+ void WriteFuncDeclarations(bool for_header);
+ void WriteFuncDeclaration(const FuncDeclaration&, const std::string&, bool add_storage_class);
+ void WriteEntryFuncs();
+ void WriteEntryFunc(const FuncDeclaration&, const std::string&, bool add_storage_class);
+ void WriteImportFuncDeclaration(const FuncDeclaration&, const std::string&);
+ std::string GetMainMemoryName();
+ void WriteGlobalInitializers();
+ void WriteGlobals();
+ void WriteGlobalsExport();
+ void WriteGlobal(const Global&, const std::string&);
+ void WriteMemories();
+ void WriteMemoriesExport();
+ void WriteMemory(const std::string&);
+ void WriteTables();
+ void WriteTablesExport();
+ void WriteTable(const std::string&);
+ void WriteDataInitializers();
+ void WriteElemInitializers();
+ void WriteExportLookup();
+ void WriteCallbackAddRemove();
+ void WriteInit();
+ void WriteFuncs();
+ void Write(const Func&);
+ void WriteParamsAndLocals();
+ void WriteParams(const std::vector<std::string>& index_to_name);
+ void WriteLocals(const std::vector<std::string>& 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&);
+
+ const WriteCOptions& options_;
+ const Module* module_ = nullptr;
+ const Func* func_ = nullptr;
+ Stream* stream_ = nullptr;
+ MemoryStream func_stream_;
+ Stream* c_stream_ = nullptr;
+ Stream* h_stream_ = nullptr;
+ std::string header_name_;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ bool should_write_indent_next_ = false;
+
+ SymbolMap global_sym_map_;
+ SymbolMap local_sym_map_;
+ SymbolMap globalvars_sym_map_;
+ StackVarSymbolMap stack_var_sym_map_;
+ SymbolSet global_syms_;
+ SymbolSet local_syms_;
+ SymbolSet globalvars_syms_;
+ SymbolSet import_syms_;
+ TypeVector type_stack_;
+ std::vector<Label> label_stack_;
+};
+
+static const char kImplicitFuncLabel[] = "$Bfunc";
+
+#define SECTION_NAME(x) s_header_##x
+#include "src/prebuilt/wasm2c.include.h"
+#undef SECTION_NAME
+
+#define SECTION_NAME(x) s_source_##x
+#include "src/prebuilt/wasm2c.include.c"
+#undef SECTION_NAME
+
+size_t CWriter::MarkTypeStack() const {
+ return type_stack_.size();
+}
+
+void CWriter::ResetTypeStack(size_t mark) {
+ assert(mark <= type_stack_.size());
+ type_stack_.erase(type_stack_.begin() + mark, type_stack_.end());
+}
+
+Type CWriter::StackType(Index index) const {
+ assert(index < type_stack_.size());
+ return *(type_stack_.rbegin() + index);
+}
+
+void CWriter::PushType(Type type) {
+ type_stack_.push_back(type);
+}
+
+void CWriter::PushTypes(const TypeVector& types) {
+ type_stack_.insert(type_stack_.end(), types.begin(), types.end());
+}
+
+void CWriter::DropTypes(size_t count) {
+ assert(count <= type_stack_.size());
+ type_stack_.erase(type_stack_.end() - count, type_stack_.end());
+}
+
+void CWriter::PushLabel(LabelType label_type,
+ const std::string& name,
+ const FuncSignature& sig,
+ bool used) {
+ if (label_type == LabelType::Loop)
+ label_stack_.emplace_back(label_type, name, sig.param_types,
+ type_stack_.size(), used);
+ else
+ label_stack_.emplace_back(label_type, name, sig.result_types,
+ type_stack_.size(), used);
+}
+
+const Label* CWriter::FindLabel(const Var& var) {
+ Label* label = nullptr;
+
+ if (var.is_index()) {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ assert(var.index() + 1 == label_stack_.size());
+ label = &label_stack_[0];
+ } else {
+ assert(var.is_name());
+ for (Index i = label_stack_.size(); i > 0; --i) {
+ label = &label_stack_[i - 1];
+ if (label->name == var.name())
+ break;
+ }
+ }
+
+ assert(label);
+ label->used = true;
+ return label;
+}
+
+bool CWriter::IsTopLabelUsed() const {
+ assert(!label_stack_.empty());
+ return label_stack_.back().used;
+}
+
+void CWriter::PopLabel() {
+ label_stack_.pop_back();
+}
+
+// static
+std::string CWriter::AddressOf(const std::string& s) {
+ return "(&" + s + ")";
+}
+
+// static
+char CWriter::MangleType(Type type) {
+ switch (type) {
+ case Type::I32: return 'i';
+ case Type::I64: return 'j';
+ case Type::F32: return 'f';
+ case Type::F64: return 'd';
+ default: WABT_UNREACHABLE;
+ }
+}
+
+// static
+std::string CWriter::MangleTypes(const TypeVector& types) {
+ if (types.empty())
+ return std::string("v");
+
+ std::string result;
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+// static
+std::string CWriter::MangleMultivalueTypes(const TypeVector& types) {
+ assert(types.size() >= 2);
+ std::string result = "wasm_multi_";
+ for (auto type : types) {
+ result += MangleType(type);
+ }
+ return result;
+}
+
+// static
+std::string CWriter::MangleName(string_view name) {
+ const char kPrefix = 'Z';
+ std::string result = "Z_";
+
+ if (!name.empty()) {
+ for (char c : name) {
+ if ((isalnum(c) && c != kPrefix) || c == '_') {
+ result += c;
+ } else {
+ result += kPrefix;
+ result += StringPrintf("%02X", static_cast<uint8_t>(c));
+ }
+ }
+ }
+
+ return result;
+}
+
+// static
+std::string CWriter::MangleFuncName(string_view name,
+ const TypeVector& param_types,
+ const TypeVector& result_types) {
+ std::string sig = MangleTypes(result_types) + MangleTypes(param_types);
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::MangleGlobalName(string_view name, Type type) {
+ std::string sig(1, MangleType(type));
+ return MangleName(name) + MangleName(sig);
+}
+
+// static
+std::string CWriter::LegalizeName(string_view name) {
+ if (name.empty())
+ return "_";
+
+ std::string result;
+ result = isalpha(name[0]) ? name[0] : '_';
+ for (size_t i = 1; i < name.size(); ++i)
+ result += isalnum(name[i]) ? name[i] : '_';
+
+ // In addition to containing valid characters for C, we must also avoid
+ // colliding with things C cares about, such as reserved words (e.g. "void")
+ // or a function name like main() (which a compiler will complain about if we
+ // define it with another type). To avoid such problems, prefix.
+ result = "w2c_" + result;
+
+ return result;
+}
+
+std::string CWriter::DefineName(SymbolSet* set, string_view name) {
+ std::string legal = LegalizeName(name);
+ if (set->find(legal) != set->end()) {
+ std::string base = legal + "_";
+ size_t count = 0;
+ do {
+ legal = base + std::to_string(count++);
+ } while (set->find(legal) != set->end());
+ }
+ set->insert(legal);
+ return legal;
+}
+
+string_view StripLeadingDollar(string_view name) {
+ if (!name.empty() && name[0] == '$') {
+ name.remove_prefix(1);
+ }
+ return name;
+}
+
+std::string CWriter::DefineImportName(const std::string& name,
+ string_view module,
+ string_view mangled_field_name) {
+ std::string mangled = MangleName(module) + mangled_field_name.to_string();
+ import_syms_.insert(name);
+ global_syms_.insert(mangled);
+ global_sym_map_.insert(SymbolMap::value_type(name, mangled));
+ return mangled;
+}
+
+std::string CWriter::DefineGlobalScopeName(const std::string& name) {
+ std::string unique = DefineName(&global_syms_, StripLeadingDollar(name));
+ global_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineLocalScopeName(const std::string& name) {
+ std::string unique = DefineName(&local_syms_, StripLeadingDollar(name));
+ local_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineGlobalVarName(const std::string& name) {
+ std::string unique = DefineName(&globalvars_syms_, StripLeadingDollar(name));
+ globalvars_sym_map_.insert(SymbolMap::value_type(name, unique));
+ return unique;
+}
+
+std::string CWriter::DefineStackVarName(Index index,
+ Type type,
+ string_view name) {
+ std::string unique = DefineName(&local_syms_, name);
+ StackTypePair stp = {index, type};
+ stack_var_sym_map_.insert(StackVarSymbolMap::value_type(stp, unique));
+ return unique;
+}
+
+void CWriter::Indent(int size) {
+ indent_ += size;
+}
+
+void CWriter::Dedent(int size) {
+ indent_ -= size;
+ assert(indent_ >= 0);
+}
+
+void CWriter::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t to_write = indent_;
+ while (to_write >= s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ to_write -= s_indent_len;
+ }
+ if (to_write > 0) {
+ stream_->WriteData(s_indent, to_write);
+ }
+}
+
+void CWriter::WriteData(const void* src, size_t size) {
+ if (should_write_indent_next_) {
+ WriteIndent();
+ should_write_indent_next_ = false;
+ }
+ stream_->WriteData(src, size);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) CWriter::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ WriteData(buffer, length);
+}
+
+void CWriter::Write(Newline) {
+ Write("\n");
+ should_write_indent_next_ = true;
+}
+
+void CWriter::Write(OpenBrace) {
+ Write("{");
+ Indent();
+ Write(Newline());
+}
+
+void CWriter::Write(CloseBrace) {
+ Dedent();
+ Write("}");
+}
+
+void CWriter::Write(Index index) {
+ Writef("%" PRIindex, index);
+}
+
+void CWriter::Write(string_view s) {
+ WriteData(s.data(), s.size());
+}
+
+void CWriter::Write(const LocalName& name) {
+ assert(local_sym_map_.count(name.name) == 1);
+ Write(local_sym_map_[name.name]);
+}
+
+std::string CWriter::GetGlobalName(const std::string& name) const {
+ assert(global_sym_map_.count(name) == 1);
+ auto iter = global_sym_map_.find(name);
+ assert(iter != global_sym_map_.end());
+ return iter->second;
+}
+
+std::string CWriter::GetGlobalVarName(const std::string& name) const {
+ assert(globalvars_sym_map_.count(name) == 1);
+ auto iter = globalvars_sym_map_.find(name);
+ assert(iter != globalvars_sym_map_.end());
+ return iter->second;
+}
+
+void CWriter::Write(const GlobalName& name) {
+ Write(GetGlobalName(name.name));
+}
+
+void CWriter::Write(const ExternalPtr& name) {
+ bool is_import = import_syms_.count(name.name) != 0;
+ if (is_import) {
+ Write(GetGlobalName(name.name));
+ } else {
+ Write(AddressOf(GetGlobalName(name.name)));
+ }
+}
+
+void CWriter::Write(const ExternalRef& name) {
+ Write(GetGlobalName(name.name));
+}
+
+void CWriter::Write(const Var& var) {
+ assert(var.is_name());
+ Write(LocalName(var.name()));
+}
+
+void CWriter::Write(const GotoLabel& goto_label) {
+ const Label* label = FindLabel(goto_label.var);
+ if (label->HasValue()) {
+ size_t amount = label->sig.size();
+ assert(type_stack_.size() >= label->type_stack_size);
+ assert(type_stack_.size() >= amount);
+ assert(type_stack_.size() - amount >= label->type_stack_size);
+ Index offset = type_stack_.size() - label->type_stack_size - amount;
+ if (offset != 0) {
+ for (Index i = 0; i < amount; ++i) {
+ Write(StackVar(amount - i - 1 + offset, label->sig[i]), " = ", StackVar(amount - i - 1), "; ");
+ }
+ }
+ }
+
+ if (goto_label.var.is_name()) {
+ Write("goto ", goto_label.var, ";");
+ } else {
+ // We've generated names for all labels, so we should only be using an
+ // index when branching to the implicit function label, which can't be
+ // named.
+ Write("goto ", Var(kImplicitFuncLabel), ";");
+ }
+}
+
+void CWriter::Write(const LabelDecl& label) {
+ if (IsTopLabelUsed())
+ Write(label.name, ":;", Newline());
+}
+
+void CWriter::Write(const GlobalVar& var) {
+ assert(var.var.is_name());
+ Write(GetGlobalVarName(var.var.name()));
+}
+
+void CWriter::Write(const StackVar& sv) {
+ Index index = type_stack_.size() - 1 - sv.index;
+ Type type = sv.type;
+ if (type == Type::Any) {
+ assert(index < type_stack_.size());
+ type = type_stack_[index];
+ }
+
+ StackTypePair stp = {index, type};
+ auto iter = stack_var_sym_map_.find(stp);
+ if (iter == stack_var_sym_map_.end()) {
+ std::string name = MangleType(type) + std::to_string(index);
+ Write(DefineStackVarName(index, type, name));
+ } else {
+ Write(iter->second);
+ }
+}
+
+void CWriter::Write(Type type) {
+ switch (type) {
+ case Type::I32: Write("u32"); break;
+ case Type::I64: Write("u64"); break;
+ case Type::F32: Write("f32"); break;
+ case Type::F64: Write("f64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(TypeEnum type) {
+ switch (type.type) {
+ case Type::I32: Write("WASM_RT_I32"); break;
+ case Type::I64: Write("WASM_RT_I64"); break;
+ case Type::F32: Write("WASM_RT_F32"); break;
+ case Type::F64: Write("WASM_RT_F64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(SignedType type) {
+ switch (type.type) {
+ case Type::I32: Write("s32"); break;
+ case Type::I64: Write("s64"); break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ResultType& rt) {
+ if (rt.types.empty()) {
+ Write("void");
+ } else if (rt.types.size() == 1) {
+ Write(rt.types[0]);
+ } else {
+ Write("struct ", MangleMultivalueTypes(rt.types));
+ }
+}
+
+void CWriter::Write(const Const& const_) {
+ switch (const_.type()) {
+ case Type::I32:
+ Writef("%uu", static_cast<int32_t>(const_.u32()));
+ break;
+
+ case Type::I64:
+ Writef("%" PRIu64 "ull", static_cast<int64_t>(const_.u64()));
+ break;
+
+ case Type::F32: {
+ uint32_t f32_bits = const_.f32_bits();
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((f32_bits & 0x7f800000u) == 0x7f800000u) {
+ const char* sign = (f32_bits & 0x80000000) ? "-" : "";
+ uint32_t significand = f32_bits & 0x7fffffu;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f32_reinterpret_i32(0x%08x) /* %snan:0x%06x */", f32_bits,
+ sign, significand);
+ }
+ } else if (f32_bits == 0x80000000) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.f");
+ } else {
+ Writef("%.9g", Bitcast<float>(f32_bits));
+ }
+ break;
+ }
+
+ case Type::F64: {
+ uint64_t f64_bits = const_.f64_bits();
+ // TODO(binji): Share with similar float info in interp.cc and literal.cc
+ if ((f64_bits & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
+ const char* sign = (f64_bits & 0x8000000000000000ull) ? "-" : "";
+ uint64_t significand = f64_bits & 0xfffffffffffffull;
+ if (significand == 0) {
+ // Infinity.
+ Writef("%sINFINITY", sign);
+ } else {
+ // Nan.
+ Writef("f64_reinterpret_i64(0x%016" PRIx64 ") /* %snan:0x%013" PRIx64
+ " */",
+ f64_bits, sign, significand);
+ }
+ } else if (f64_bits == 0x8000000000000000ull) {
+ // Negative zero. Special-cased so it isn't written as -0 below.
+ Writef("-0.0");
+ } else {
+ Writef("%.17g", Bitcast<double>(f64_bits));
+ }
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::WriteInitExpr(const ExprList& expr_list) {
+ if (expr_list.empty())
+ return;
+
+ assert(expr_list.size() == 1);
+ const Expr* expr = &expr_list.front();
+ switch (expr_list.front().type()) {
+ case ExprType::Const:
+ Write(cast<ConstExpr>(expr)->const_);
+ break;
+
+ case ExprType::GlobalGet:
+ Write(GlobalVar(cast<GlobalGetExpr>(expr)->var));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+std::string CWriter::GenerateHeaderGuard() const {
+ std::string result;
+ for (char c : header_name_) {
+ if (isalnum(c) || c == '_') {
+ result += toupper(c);
+ } else {
+ result += '_';
+ }
+ }
+ result += "_GENERATED_";
+ return result;
+}
+
+void CWriter::WriteSourceTop() {
+ Write(s_source_includes);
+ Write(Newline(), "#include \"", header_name_, "\"", Newline());
+ Write(s_source_declarations);
+}
+
+void CWriter::WriteMultivalueTypes() {
+ for (TypeEntry* type : module_->types) {
+ FuncType* func_type = cast<FuncType>(type);
+ Index num_results = func_type->GetNumResults();
+ if (num_results <= 1) {
+ continue;
+ }
+ std::string name = MangleMultivalueTypes(func_type->sig.result_types);
+ // these ifndefs are actually to support importing multiple modules
+ // incidentally they also mean we don't have to bother with deduplication
+ Write("#ifndef ", name, Newline());
+ Write("#define ", name, " ", name, Newline());
+ Write("struct ", name, " {", Newline());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func_type->GetResultType(i);
+ Write(" ", type);
+ Writef(" %c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write("};", Newline(), "#endif /* ", name, " */", Newline());
+ }
+}
+
+void CWriter::WriteSandboxStruct() {
+ Write("struct wasm2c_sandbox_t {", Newline());
+ Indent(2);
+
+ Write("wasm_sandbox_wasi_data wasi_data;", Newline());
+
+ WriteMemories();
+ WriteTables();
+
+ {
+ Write("wasm_func_type_t* func_type_structs;");
+ Write(Newline());
+ Write("u32 func_type_count;");
+ Write(Newline());
+ Writef("u32 func_types[%" PRIzd "];", module_->types.size());
+ Write(Newline());
+ }
+
+ WriteGlobals();
+
+ Dedent(2);
+ Write("};", Newline(), Newline());
+}
+
+void CWriter::WriteFuncTypes() {
+ Write(Newline());
+ Write("static void init_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Index func_type_index = 0;
+ for (TypeEntry* type : module_->types) {
+ FuncType* func_type = cast<FuncType>(type);
+ Index num_params = func_type->GetNumParams();
+ Index num_results = func_type->GetNumResults();
+ Write(OpenBrace());
+ Write("wasm_rt_type_t param_ret_types[] = { ");
+ bool first = true;
+ if (num_results == 0 && num_params == 0) {
+ // Make sure array has at least one element
+ Write("/* void(*)(void) */ WASM_RT_I32 ");
+ } else {
+ for (Index i = 0; i < num_params; ++i) {
+ if (!first) {
+ Write(", ");
+ }
+ Write(TypeEnum(func_type->GetParamType(i)));
+ first = false;
+ }
+ for (Index i = 0; i < num_results; ++i) {
+ if (!first) {
+ Write(", ");
+ }
+ Write(TypeEnum(func_type->GetResultType(i)));
+ first = false;
+ }
+ }
+ Write(" };", Newline());
+ Write("sbx->func_types[", func_type_index, "] = wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, ",
+ num_params, ", ", num_results, ", param_ret_types");
+ Write(");", Newline());
+ Write(CloseBrace(), Newline());
+ ++func_type_index;
+ }
+ Write(CloseBrace(), Newline());
+
+ Write("static void cleanup_func_types(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ {
+ Write("wasm_rt_cleanup_func_types(&sbx->func_type_structs, &sbx->func_type_count);", Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteImports() {
+ if (module_->imports.empty())
+ return;
+
+ Write(Newline());
+
+ // TODO(binji): Write imports ordered by type.
+ for (const Import* import : module_->imports) {
+ Write("/* import: '", import->module_name, "' '", import->field_name,
+ "' */", Newline());
+ Write("extern ");
+ switch (import->kind()) {
+ case ExternalKind::Func: {
+ const Func& func = cast<FuncImport>(import)->func;
+ WriteImportFuncDeclaration(
+ func.decl,
+ DefineImportName(
+ func.name, import->module_name,
+ MangleFuncName(import->field_name, func.decl.sig.param_types,
+ func.decl.sig.result_types)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Global: {
+ const Global& global = cast<GlobalImport>(import)->global;
+ WriteGlobal(global,
+ DefineImportName(
+ global.name, import->module_name,
+ MangleGlobalName(import->field_name, global.type)));
+ Write(";");
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ const Memory& memory = cast<MemoryImport>(import)->memory;
+ WriteMemory(DefineImportName(memory.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ case ExternalKind::Table: {
+ const Table& table = cast<TableImport>(import)->table;
+ WriteTable(DefineImportName(table.name, import->module_name,
+ MangleName(import->field_name)));
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ Write(Newline());
+ }
+}
+
+void CWriter::WriteFuncDeclarations(bool for_header) {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ std::string global_func_name;
+ if (for_header) {
+ global_func_name = DefineGlobalScopeName(func->name);
+ } else {
+ global_func_name = GetGlobalName(func->name);
+ }
+ bool is_import = func_index < module_->num_func_imports;
+ bool static_exclude = for_header && IsFuncStatic(global_func_name);
+ if (!is_import && !static_exclude) {
+ WriteFuncDeclaration(func->decl, global_func_name, true /* add_storage_class */);
+ Write(";", Newline());
+ }
+ ++func_index;
+ }
+}
+
+
+void CWriter::WriteEntryFuncs() {
+ if (module_->funcs.size() == module_->num_func_imports)
+ return;
+
+ Write(Newline());
+ Write("#if defined(ENTRY_PROLOGUE) || defined(ENTRY_EPILOGUE)", Newline());
+
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import) {
+ WriteEntryFunc(func->decl, GetGlobalName(func->name), true /* add_storage_class */);
+ Write(Newline());
+ }
+ ++func_index;
+ }
+
+ Write("#endif", Newline());
+}
+
+
+bool CWriter::IsFuncStatic(std::string name) {
+ // static functions starts with prefix __
+ return name.rfind("w2c___", 0) == 0 || name == "w2c_main";
+}
+
+std::string CWriter::GetFuncStaticOrExport(std::string name) {
+ std::string static_export_string = IsFuncStatic(name)? "static " : "FUNC_EXPORT ";
+ return static_export_string;
+}
+
+void CWriter::WriteFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name,
+ bool add_storage_class) {
+ // LLVM adds some extra function calls to all wasm objects prefixed with "__".
+ // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules
+ // Additionally windows dlls have to export functions explicitly
+ if (add_storage_class) {
+ Write(GetFuncStaticOrExport(name));
+ }
+ Write(ResultType(decl.sig.result_types), " ", name, "(wasm2c_sandbox_t* const");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i));
+ }
+ Write(")");
+}
+
+void CWriter::WriteEntryFunc(const FuncDeclaration& decl,
+ const std::string& name,
+ bool add_storage_class) {
+ // LLVM adds some extra function calls to all wasm objects prefixed with "__".
+ // Keep this static (private), else we cause symbol collisions when linking multiple wasm modules
+ // Additionally windows dlls have to export functions explicitly
+ if (add_storage_class) {
+ Write(GetFuncStaticOrExport(name));
+ }
+ Write(ResultType(decl.sig.result_types), " w2centry_", name, "(wasm2c_sandbox_t* const sbx");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i), " p", std::to_string(i));
+ }
+ Write(") ", OpenBrace());
+ {
+ Write("ENTRY_PROLOGUE;", Newline());
+ if (!decl.sig.result_types.empty()) {
+ Write(ResultType(decl.sig.result_types), " ret = ");
+ }
+ Write(name, "(sbx");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", p", std::to_string(i));
+ }
+ Write(");", Newline());
+ Write("ENTRY_EPILOGUE;", Newline());
+ if (!decl.sig.result_types.empty()) {
+ Write("return ret;", Newline());
+ }
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteImportFuncDeclaration(const FuncDeclaration& decl,
+ const std::string& name) {
+ Write(ResultType(decl.sig.result_types), " ", name, "(void*");
+ for (Index i = 0; i < decl.GetNumParams(); ++i) {
+ Write(", ", decl.GetParamType(i));
+ }
+ Write(")");
+}
+
+void CWriter::WriteGlobals() {
+ Index global_index = 0;
+ if (module_->globals.size() != module_->num_global_imports) {
+
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ WriteGlobal(*global, DefineGlobalVarName(global->name));
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ }
+}
+
+void CWriter::WriteGlobalsExport() {
+ Index global_index = 0;
+ if (module_->globals.size() != module_->num_global_imports) {
+
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ std::string curr_global_name = GetGlobalVarName(global->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_global_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_global_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++global_index;
+ }
+ }
+}
+
+std::string CWriter::GetMainMemoryName() {
+ assert (!(module_->memories.size() == module_->num_memory_imports));
+ assert(module_->memories.size() <= 1);
+
+ std::string ret = GetGlobalName(module_->memories[0]->name);
+ return ret;
+}
+
+void CWriter::WriteGlobalInitializers() {
+
+ Write(Newline(), "static void init_globals(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+
+ {
+ Index global_index = 0;
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ assert(!global->init_expr.empty());
+ Write("sbx->", GetGlobalVarName(global->name), " = ");
+ WriteInitExpr(global->init_expr);
+ Write(";", Newline());
+ }
+ ++global_index;
+ }
+ }
+
+ {
+ Index global_index = 0;
+ std::string memory_name = GetMainMemoryName();
+ for (const Global* global : module_->globals) {
+ bool is_import = global_index < module_->num_global_imports;
+ if (!is_import) {
+ std::string global_name = GetGlobalVarName(global->name);
+ std::string global_name_expr = "sbx->" + global_name;
+ Write("WASM2C_SHADOW_MEMORY_RESERVE(&(sbx->", memory_name ,"), ", global_name_expr, ", sizeof(", global_name_expr, "));", Newline());
+ if (global_name == "w2c___heap_base") {
+ Write("WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(&(sbx->", memory_name, "), ", global_name_expr, ");", Newline());
+ }
+ }
+
+ ++global_index;
+ }
+ }
+
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteGlobal(const Global& global, const std::string& name) {
+ Write(global.type, " ", name);
+}
+
+void CWriter::WriteMemories() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ assert(module_->memories.size() <= 1);
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ WriteMemory(DefineGlobalScopeName(memory->name));
+ Write(Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemoriesExport() {
+ if (module_->memories.size() == module_->num_memory_imports)
+ return;
+
+ assert(module_->memories.size() <= 1);
+ Index memory_index = 0;
+ for (const Memory* memory : module_->memories) {
+ bool is_import = memory_index < module_->num_memory_imports;
+ if (!is_import) {
+ std::string curr_memory_name = GetGlobalName(memory->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_memory_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_memory_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++memory_index;
+ }
+}
+
+void CWriter::WriteMemory(const std::string& name) {
+ Write("wasm_rt_memory_t ", name, ";");
+}
+
+void CWriter::WriteTables() {
+ if (module_->tables.size() == module_->num_table_imports)
+ return;
+
+ assert(module_->tables.size() <= 1);
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ std::string curr_table_name = DefineGlobalScopeName(table->name);
+ Writef("u32 %s_current_index;", curr_table_name.c_str());
+ Write(Newline());
+ WriteTable(curr_table_name);
+ Write(Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTablesExport() {
+ if (module_->tables.size() == module_->num_table_imports)
+ return;
+
+ assert(module_->tables.size() <= 1);
+ Index table_index = 0;
+ for (const Table* table : module_->tables) {
+ bool is_import = table_index < module_->num_table_imports;
+ if (!is_import) {
+ std::string curr_table_name = GetGlobalName(table->name);
+ Writef("if (strcmp(\"%s\", name) == 0)", curr_table_name.c_str());
+ Write(OpenBrace());
+ Write("return &(sbx->", curr_table_name, ");", Newline());
+ Write(CloseBrace(), Newline());
+ }
+ ++table_index;
+ }
+}
+
+void CWriter::WriteTable(const std::string& name) {
+ Write("wasm_rt_table_t ", name, ";");
+}
+
+void CWriter::WriteDataInitializers() {
+ const Memory* memory = nullptr;
+ Index data_segment_index = 0;
+
+ if (!module_->memories.empty()) {
+ if (module_->data_segments.empty()) {
+ Write(Newline());
+ } else {
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write(Newline(), "static const u8 data_segment_data_",
+ data_segment_index, "[] = ", OpenBrace());
+ size_t i = 0;
+ for (uint8_t x : data_segment->data) {
+ Writef("0x%02x, ", x);
+ if ((++i % 12) == 0)
+ Write(Newline());
+ }
+ if (i > 0)
+ Write(Newline());
+ Write(CloseBrace(), ";", Newline());
+ ++data_segment_index;
+ }
+ }
+
+ memory = module_->memories[0];
+ }
+
+ Write(Newline(), "static bool init_memory(wasm2c_sandbox_t* const sbx, uint32_t max_wasm_pages_from_rt) ", OpenBrace());
+ if (memory && module_->num_memory_imports == 0) {
+ Write("const uint32_t max_pages_specified_in_module = ", memory->page_limits.has_max ? memory->page_limits.max : 0, ";", Newline());
+ Write("const uint32_t max_pages = max_wasm_pages_from_rt == 0? max_pages_specified_in_module : max_wasm_pages_from_rt;", Newline());
+ Write("const bool success = wasm_rt_allocate_memory(&(sbx->", ExternalRef(memory->name), "), ",
+ memory->page_limits.initial, ", max_pages);", Newline());
+ Write("if (!success) { return false; }", Newline(), Newline());
+ }
+ data_segment_index = 0;
+ for (const DataSegment* data_segment : module_->data_segments) {
+ Write("LOAD_DATA(sbx->", ExternalRef(memory->name), ", ");
+ WriteInitExpr(data_segment->offset);
+ Write(", data_segment_data_", data_segment_index, ", ",
+ data_segment->data.size(), ");", Newline());
+ ++data_segment_index;
+ }
+
+ Write("sbx->wasi_data.heap_memory = &(sbx->", ExternalRef(memory->name), ");", Newline());
+ Write("return true;", Newline());
+ Write(CloseBrace(), Newline());
+
+ Write(Newline(), "static void cleanup_memory(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Write("wasm_rt_deallocate_memory(&(sbx->", ExternalRef(memory->name), "));", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteElemInitializers() {
+ const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
+
+ Write(Newline(), "static void init_table(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ Write("uint32_t offset = 0;", Newline());
+ if (table && module_->num_table_imports == 0) {
+ uint32_t max =
+ table->elem_limits.has_max ? table->elem_limits.max : UINT32_MAX;
+ Write("wasm_rt_allocate_table(&(sbx->", ExternalRef(table->name), "), ",
+ table->elem_limits.initial, ", ", max, ");", Newline());
+ }
+ Index elem_segment_index = 0;
+ size_t first_unused_elem = 0;
+ for (const ElemSegment* elem_segment : module_->elem_segments) {
+ Write("offset = ");
+ WriteInitExpr(elem_segment->offset);
+ Write(";", Newline());
+
+ size_t i = 0;
+ for (const ElemExpr& elem_expr : elem_segment->elem_exprs) {
+ // We don't support the bulk-memory proposal here, so we know that we
+ // don't have any passive segments (where ref.null can be used).
+ assert(elem_expr.kind == ElemExprKind::RefFunc);
+ const Func* func = module_->GetFunc(elem_expr.var);
+ Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var);
+
+ Write("sbx->",ExternalRef(table->name), ".data[offset + ", i,
+ "] = (wasm_rt_elem_t){ WASM_RT_INTERNAL_FUNCTION, sbx->func_types[", func_type_index,
+ "], (wasm_rt_anyfunc_t)", ExternalPtr(func->name), " };", Newline());
+ if (i >= first_unused_elem) {
+ first_unused_elem = i+1;
+ }
+ ++i;
+ }
+ ++elem_segment_index;
+ }
+ Write("sbx->", ExternalRef(table->name), "_current_index = offset + ", std::to_string(first_unused_elem), ";", Newline());
+ Write(CloseBrace(), Newline());
+
+ Write(Newline(), "static void cleanup_table(wasm2c_sandbox_t* const sbx) ", OpenBrace());
+ if (table && module_->num_table_imports == 0) {
+ Write("wasm_rt_deallocate_table(&(sbx->", ExternalRef(table->name), "));", Newline());
+ }
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteExportLookup() {
+ Write(Newline(), "static void* lookup_wasm2c_nonfunc_export(void* sbx_ptr, const char* name) ", OpenBrace());
+ Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline());
+
+ WriteMemoriesExport();
+ WriteTablesExport();
+ WriteGlobalsExport();
+
+ Write("return 0;", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteCallbackAddRemove() {
+ const Table* table = module_->tables.empty() ? nullptr : module_->tables[0];
+
+ Write(Newline(), "static wasm_rt_table_t* get_wasm2c_callback_table(void* sbx_ptr)", OpenBrace());
+ Write("wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;", Newline());
+ Write("return &(sbx->", ExternalRef(table->name), ");", Newline());
+ Write(CloseBrace(), Newline());
+}
+
+void CWriter::WriteInit() {
+ Write(Newline(), "static void init_module_starts(void) ", OpenBrace());
+ for (Var* var : module_->starts) {
+ Write(ExternalRef(module_->GetFunc(*var)->name), "();", Newline());
+ }
+ Write(CloseBrace(), Newline());
+
+ Write(s_source_sandboxapis);
+}
+
+void CWriter::WriteFuncs() {
+ Index func_index = 0;
+ for (const Func* func : module_->funcs) {
+ bool is_import = func_index < module_->num_func_imports;
+ if (!is_import)
+ Write(Newline(), *func, Newline());
+ ++func_index;
+ }
+}
+
+void CWriter::Write(const Func& func) {
+ func_ = &func;
+ // Copy symbols from global symbol table so we don't shadow them.
+ local_syms_ = global_syms_;
+ local_sym_map_.clear();
+ stack_var_sym_map_.clear();
+
+ std::string func_name_suffix;
+ auto out_func_name = GetGlobalName(func.name);
+
+ if (out_func_name == "w2c_dlmalloc" || out_func_name == "w2c_dlfree")
+ {
+ func_name_suffix = "_wrapped";
+ }
+
+ Write(GetFuncStaticOrExport(out_func_name), ResultType(func.decl.sig.result_types), " ",
+ out_func_name + func_name_suffix, "(");
+ WriteParamsAndLocals();
+ Write("FUNC_PROLOGUE;", Newline());
+
+ stream_ = &func_stream_;
+ stream_->ClearOffset();
+
+ std::string label = DefineLocalScopeName(kImplicitFuncLabel);
+ ResetTypeStack(0);
+ std::string empty; // Must not be temporary, since address is taken by Label.
+ PushLabel(LabelType::Func, empty, func.decl.sig);
+ Write(func.exprs, LabelDecl(label));
+ PopLabel();
+ ResetTypeStack(0);
+ PushTypes(func.decl.sig.result_types);
+ Write("FUNC_EPILOGUE;", Newline());
+
+ // Return the top of the stack implicitly.
+ Index num_results = func.GetNumResults();
+ if (num_results == 1) {
+ Write("return ", StackVar(0), ";", Newline());
+ } else if (num_results >= 2) {
+ Write(OpenBrace());
+ Write(ResultType(func.decl.sig.result_types), " tmp;", Newline());
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ Writef("tmp.%c%d = ", MangleType(type), i);
+ Write(StackVar(num_results - i - 1), ";", Newline());
+ }
+ Write("return tmp;", Newline());
+ Write(CloseBrace(), Newline());
+ }
+
+ stream_ = c_stream_;
+
+ WriteStackVarDeclarations();
+
+ std::unique_ptr<OutputBuffer> buf = func_stream_.ReleaseOutputBuffer();
+ stream_->WriteData(buf->data.data(), buf->data.size());
+
+ Write(CloseBrace());
+
+ std::string memory_name = GetMainMemoryName();
+ if (out_func_name == "w2c_dlmalloc") {
+ Write(Newline(), Newline());
+ Write(GetFuncStaticOrExport(out_func_name), "u32 w2c_dlmalloc(wasm2c_sandbox_t* const sbx, u32 ptr_size) ", OpenBrace());
+ Write("u32 ret = w2c_dlmalloc_wrapped(sbx, ptr_size);", Newline());
+ Write("WASM2C_SHADOW_MEMORY_DLMALLOC(&(sbx->", memory_name, "), ret, ptr_size);", Newline());
+ Write("WASM2C_MALLOC_FAIL_CHECK(ret, ptr_size);", Newline());
+ Write("return ret;", Newline());
+ Write(CloseBrace());
+ } else if (out_func_name == "w2c_dlfree") {
+ Write(Newline(), Newline());
+ Write(GetFuncStaticOrExport(out_func_name), "void w2c_dlfree(wasm2c_sandbox_t* const sbx, u32 ptr) ", OpenBrace());
+ Write("WASM2C_SHADOW_MEMORY_DLFREE(&(sbx->", memory_name, "), ptr);", Newline());
+ Write("w2c_dlfree_wrapped(sbx, ptr);", Newline());
+ Write(CloseBrace());
+ }
+
+ func_stream_.Clear();
+ func_ = nullptr;
+}
+
+void CWriter::WriteParamsAndLocals() {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func_->GetNumParamsAndLocals(), func_->bindings,
+ &index_to_name);
+ WriteParams(index_to_name);
+ WriteLocals(index_to_name);
+}
+
+void CWriter::WriteParams(const std::vector<std::string>& index_to_name) {
+ Indent(4);
+ Write("wasm2c_sandbox_t* const sbx");
+ for (Index i = 0; i < func_->GetNumParams(); ++i) {
+ Write(", ");
+ if (i != 0) {
+ if ((i % 8) == 0)
+ Write(Newline());
+ }
+ Write(func_->GetParamType(i), " ",
+ DefineLocalScopeName(index_to_name[i]));
+ }
+ Dedent(4);
+ Write(") ", OpenBrace());
+}
+
+void CWriter::WriteLocals(const std::vector<std::string>& index_to_name) {
+ Index num_params = func_->GetNumParams();
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ Index local_index = 0;
+ size_t count = 0;
+ for (Type local_type : func_->local_types) {
+ if (local_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(DefineLocalScopeName(index_to_name[num_params + local_index]),
+ " = 0");
+ ++count;
+ }
+ ++local_index;
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::WriteStackVarDeclarations() {
+ for (Type type : {Type::I32, Type::I64, Type::F32, Type::F64}) {
+ size_t count = 0;
+ for (const auto& pair : stack_var_sym_map_) {
+ Type stp_type = pair.first.second;
+ const std::string& name = pair.second;
+
+ if (stp_type == type) {
+ if (count == 0) {
+ Write(type, " ");
+ Indent(4);
+ } else {
+ Write(", ");
+ if ((count % 8) == 0)
+ Write(Newline());
+ }
+
+ Write(name);
+ ++count;
+ }
+ }
+ if (count != 0) {
+ Dedent(4);
+ Write(";", Newline());
+ }
+ }
+}
+
+void CWriter::Write(const ExprList& exprs) {
+ for (const Expr& expr : exprs) {
+ switch (expr.type()) {
+ case ExprType::Binary:
+ Write(*cast<BinaryExpr>(&expr));
+ break;
+
+ case ExprType::Block: {
+ const Block& block = cast<BlockExpr>(&expr)->block;
+ std::string label = DefineLocalScopeName(block.label);
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Block, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(block.exprs, LabelDecl(label));
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Br:
+ Write(GotoLabel(cast<BrExpr>(&expr)->var), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::BrIf:
+ Write("if (", StackVar(0), ") {");
+ DropTypes(1);
+ Write(GotoLabel(cast<BrIfExpr>(&expr)->var), "}", Newline());
+ break;
+
+ case ExprType::BrTable: {
+ const auto* bt_expr = cast<BrTableExpr>(&expr);
+ Write("switch (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ Index i = 0;
+ for (const Var& var : bt_expr->targets) {
+ Write("case ", i++, ": ", GotoLabel(var), Newline());
+ }
+ Write("default: ");
+ Write(GotoLabel(bt_expr->default_target), Newline(), CloseBrace(),
+ Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+ }
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ const Func& func = *module_->GetFunc(var);
+ Index num_params = func.GetNumParams();
+ Index num_results = func.GetNumResults();
+ assert(type_stack_.size() >= num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(func.decl.sig.result_types));
+ Write(" tmp = ");
+ } else if (num_results == 1) {
+ Write(StackVar(num_params - 1, func.GetResultType(0)), " = ");
+ }
+
+ Write(GlobalName(var.name()), "(sbx");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i - 1));
+ }
+ Write(");", Newline());
+ DropTypes(num_params);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = func.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(func.decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::CallIndirect: {
+ const FuncDeclaration& decl = cast<CallIndirectExpr>(&expr)->decl;
+ Index num_params = decl.GetNumParams();
+ Index num_results = decl.GetNumResults();
+ assert(type_stack_.size() > num_params);
+ if (num_results > 1) {
+ Write(OpenBrace());
+ Write("struct ", MangleMultivalueTypes(decl.sig.result_types)," tmp;");
+ }
+
+ assert(module_->tables.size() == 1);
+ const Table* table = module_->tables[0];
+
+ assert(decl.has_func_type);
+ Index func_type_index = module_->GetFuncTypeIndex(decl.type_var);
+
+ if (num_results > 0) {
+ assert(num_results == 1);
+ Write("CALL_INDIRECT_RES(");
+ if (num_results > 1) {
+ Write("tmp, ");
+ } else {
+ Write(StackVar(num_params, decl.GetResultType(0)), ", ");
+ }
+ } else {
+ Write("CALL_INDIRECT_VOID(");
+ }
+
+ Write("sbx->", ExternalRef(table->name), ", ");
+ WriteFuncDeclaration(decl, "(*)", false /* add_storage_class*/);
+ Write(", ", func_type_index, ", ", StackVar(0));
+ Write(", sbx->func_types, sbx");
+ for (Index i = 0; i < num_params; ++i) {
+ Write(", ", StackVar(num_params - i));
+ }
+ Write(");", Newline());
+ DropTypes(num_params + 1);
+ if (num_results > 1) {
+ for (Index i = 0; i < num_results; ++i) {
+ Type type = decl.GetResultType(i);
+ PushType(type);
+ Write(StackVar(0));
+ Writef(" = tmp.%c%d;", MangleType(type), i);
+ Write(Newline());
+ }
+ Write(CloseBrace(), Newline());
+ } else {
+ PushTypes(decl.sig.result_types);
+ }
+ break;
+ }
+
+ case ExprType::Compare:
+ Write(*cast<CompareExpr>(&expr));
+ break;
+
+ case ExprType::Const: {
+ const Const& const_ = cast<ConstExpr>(&expr)->const_;
+ PushType(const_.type());
+ Write(StackVar(0), " = ", const_, ";", Newline());
+ break;
+ }
+
+ case ExprType::Convert:
+ Write(*cast<ConvertExpr>(&expr));
+ break;
+
+ case ExprType::Drop:
+ DropTypes(1);
+ break;
+
+ case ExprType::GlobalGet: {
+ const Var& var = cast<GlobalGetExpr>(&expr)->var;
+ PushType(module_->GetGlobal(var)->type);
+ Write(StackVar(0), " = ", "sbx->", GlobalVar(var), ";", Newline());
+ break;
+ }
+
+ case ExprType::GlobalSet: {
+ const Var& var = cast<GlobalSetExpr>(&expr)->var;
+ Write("sbx->", GlobalVar(var), " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::If: {
+ const IfExpr& if_ = *cast<IfExpr>(&expr);
+ Write("if (", StackVar(0), ") ", OpenBrace());
+ DropTypes(1);
+ std::string label = DefineLocalScopeName(if_.true_.label);
+ DropTypes(if_.true_.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::If, if_.true_.label, if_.true_.decl.sig);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(if_.true_.exprs, CloseBrace());
+ if (!if_.false_.empty()) {
+ ResetTypeStack(mark);
+ PushTypes(if_.true_.decl.sig.param_types);
+ Write(" else ", OpenBrace(), if_.false_, CloseBrace());
+ }
+ ResetTypeStack(mark);
+ Write(Newline(), LabelDecl(label));
+ PopLabel();
+ PushTypes(if_.true_.decl.sig.result_types);
+ break;
+ }
+
+ case ExprType::Load:
+ Write(*cast<LoadExpr>(&expr));
+ break;
+
+ case ExprType::LocalGet: {
+ const Var& var = cast<LocalGetExpr>(&expr)->var;
+ PushType(func_->GetLocalType(var));
+ Write(StackVar(0), " = ", var, ";", Newline());
+ break;
+ }
+
+ case ExprType::LocalSet: {
+ const Var& var = cast<LocalSetExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case ExprType::LocalTee: {
+ const Var& var = cast<LocalTeeExpr>(&expr)->var;
+ Write(var, " = ", StackVar(0), ";", Newline());
+ break;
+ }
+
+ case ExprType::Loop: {
+ const Block& block = cast<LoopExpr>(&expr)->block;
+ if (!block.exprs.empty()) {
+ Write(DefineLocalScopeName(block.label), ": ");
+ Indent();
+ DropTypes(block.decl.GetNumParams());
+ size_t mark = MarkTypeStack();
+ PushLabel(LabelType::Loop, block.label, block.decl.sig);
+ PushTypes(block.decl.sig.param_types);
+ Write(Newline(), block.exprs);
+ ResetTypeStack(mark);
+ PopLabel();
+ PushTypes(block.decl.sig.result_types);
+ Dedent();
+ }
+ break;
+ }
+
+ case ExprType::MemoryCopy:
+ case ExprType::DataDrop:
+ case ExprType::MemoryInit:
+ case ExprType::MemoryFill:
+ case ExprType::TableCopy:
+ case ExprType::ElemDrop:
+ case ExprType::TableInit:
+ case ExprType::TableGet:
+ case ExprType::TableSet:
+ case ExprType::TableGrow:
+ case ExprType::TableSize:
+ case ExprType::TableFill:
+ case ExprType::RefFunc:
+ case ExprType::RefNull:
+ case ExprType::RefIsNull:
+ UNIMPLEMENTED("...");
+ break;
+
+ case ExprType::MemoryGrow: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(StackVar(0), " = wasm_rt_grow_memory((&sbx->", ExternalRef(memory->name),
+ "), ", StackVar(0), ");", Newline());
+ break;
+ }
+
+ case ExprType::MemorySize: {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ PushType(Type::I32);
+ Write(StackVar(0), " = sbx->", ExternalRef(memory->name), ".pages;",
+ Newline());
+ break;
+ }
+
+ case ExprType::Nop:
+ break;
+
+ case ExprType::Return:
+ // Goto the function label instead; this way we can do shared function
+ // cleanup code in one place.
+ Write(GotoLabel(Var(label_stack_.size() - 1)), Newline());
+ // Stop processing this ExprList, since the following are unreachable.
+ return;
+
+ case ExprType::Select: {
+ Type type = StackType(1);
+ Write(StackVar(2), " = ", StackVar(0), " ? ", StackVar(2), " : ",
+ StackVar(1), ";", Newline());
+ DropTypes(3);
+ PushType(type);
+ break;
+ }
+
+ case ExprType::Store:
+ Write(*cast<StoreExpr>(&expr));
+ break;
+
+ case ExprType::Unary:
+ Write(*cast<UnaryExpr>(&expr));
+ break;
+
+ case ExprType::Ternary:
+ Write(*cast<TernaryExpr>(&expr));
+ break;
+
+ case ExprType::SimdLaneOp: {
+ Write(*cast<SimdLaneOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdLoadLane: {
+ Write(*cast<SimdLoadLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdStoreLane: {
+ Write(*cast<SimdStoreLaneExpr>(&expr));
+ break;
+ }
+
+ case ExprType::SimdShuffleOp: {
+ Write(*cast<SimdShuffleOpExpr>(&expr));
+ break;
+ }
+
+ case ExprType::LoadSplat:
+ Write(*cast<LoadSplatExpr>(&expr));
+ break;
+
+ case ExprType::LoadZero:
+ Write(*cast<LoadZeroExpr>(&expr));
+ break;
+
+ case ExprType::Unreachable:
+ Write("UNREACHABLE;", Newline());
+ return;
+
+ case ExprType::AtomicLoad:
+ case ExprType::AtomicRmw:
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicStore:
+ case ExprType::AtomicWait:
+ case ExprType::AtomicFence:
+ case ExprType::AtomicNotify:
+ case ExprType::Rethrow:
+ case ExprType::ReturnCall:
+ case ExprType::ReturnCallIndirect:
+ case ExprType::Throw:
+ case ExprType::Try:
+ case ExprType::CallRef:
+ UNIMPLEMENTED("...");
+ break;
+ }
+ }
+}
+
+void CWriter::WriteSimpleUnaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", op, "(", StackVar(0), ");", Newline());
+ DropTypes(1);
+ PushType(opcode.GetResultType());
+}
+
+void CWriter::WriteInfixBinaryExpr(Opcode opcode,
+ const char* op,
+ AssignOp assign_op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type));
+ if (assign_op == AssignOp::Allowed) {
+ Write(" ", op, "= ", StackVar(0));
+ } else {
+ Write(" = ", StackVar(1), " ", op, " ", StackVar(0));
+ }
+ Write(";", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WritePrefixBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", op, "(", StackVar(1), ", ",
+ StackVar(0), ");", Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::WriteSignedBinaryExpr(Opcode opcode, const char* op) {
+ Type result_type = opcode.GetResultType();
+ Type type = opcode.GetParamType1();
+ assert(opcode.GetParamType2() == type);
+ Write(StackVar(1, result_type), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " ", op, " (", SignedType(type), ")", StackVar(0), ");",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const BinaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Add:
+ case Opcode::I64Add:
+ case Opcode::F32Add:
+ case Opcode::F64Add:
+ WriteInfixBinaryExpr(expr.opcode, "+");
+ break;
+
+ case Opcode::I32Sub:
+ case Opcode::I64Sub:
+ case Opcode::F32Sub:
+ case Opcode::F64Sub:
+ WriteInfixBinaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::I32Mul:
+ case Opcode::I64Mul:
+ case Opcode::F32Mul:
+ case Opcode::F64Mul:
+ WriteInfixBinaryExpr(expr.opcode, "*");
+ break;
+
+ case Opcode::I32DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_DIV_S");
+ break;
+
+ case Opcode::I64DivS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_DIV_S");
+ break;
+
+ case Opcode::I32DivU:
+ case Opcode::I64DivU:
+ WritePrefixBinaryExpr(expr.opcode, "DIV_U");
+ break;
+
+ case Opcode::F32Div:
+ case Opcode::F64Div:
+ WriteInfixBinaryExpr(expr.opcode, "/");
+ break;
+
+ case Opcode::I32RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I32_REM_S");
+ break;
+
+ case Opcode::I64RemS:
+ WritePrefixBinaryExpr(expr.opcode, "I64_REM_S");
+ break;
+
+ case Opcode::I32RemU:
+ case Opcode::I64RemU:
+ WritePrefixBinaryExpr(expr.opcode, "REM_U");
+ break;
+
+ case Opcode::I32And:
+ case Opcode::I64And:
+ WriteInfixBinaryExpr(expr.opcode, "&");
+ break;
+
+ case Opcode::I32Or:
+ case Opcode::I64Or:
+ WriteInfixBinaryExpr(expr.opcode, "|");
+ break;
+
+ case Opcode::I32Xor:
+ case Opcode::I64Xor:
+ WriteInfixBinaryExpr(expr.opcode, "^");
+ break;
+
+ case Opcode::I32Shl:
+ case Opcode::I64Shl:
+ Write(StackVar(1), " <<= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32ShrS:
+ case Opcode::I64ShrS: {
+ Type type = expr.opcode.GetResultType();
+ Write(StackVar(1), " = (", type, ")((", SignedType(type), ")",
+ StackVar(1), " >> (", StackVar(0), " & ", GetShiftMask(type), "));",
+ Newline());
+ DropTypes(1);
+ break;
+ }
+
+ case Opcode::I32ShrU:
+ case Opcode::I64ShrU:
+ Write(StackVar(1), " >>= (", StackVar(0), " & ",
+ GetShiftMask(expr.opcode.GetResultType()), ");", Newline());
+ DropTypes(1);
+ break;
+
+ case Opcode::I32Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTL");
+ break;
+
+ case Opcode::I64Rotl:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTL");
+ break;
+
+ case Opcode::I32Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I32_ROTR");
+ break;
+
+ case Opcode::I64Rotr:
+ WritePrefixBinaryExpr(expr.opcode, "I64_ROTR");
+ break;
+
+ case Opcode::F32Min:
+ case Opcode::F64Min:
+ WritePrefixBinaryExpr(expr.opcode, "FMIN");
+ break;
+
+ case Opcode::F32Max:
+ case Opcode::F64Max:
+ WritePrefixBinaryExpr(expr.opcode, "FMAX");
+ break;
+
+ case Opcode::F32Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysignf");
+ break;
+
+ case Opcode::F64Copysign:
+ WritePrefixBinaryExpr(expr.opcode, "copysign");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const CompareExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eq:
+ case Opcode::I64Eq:
+ case Opcode::F32Eq:
+ case Opcode::F64Eq:
+ WriteInfixBinaryExpr(expr.opcode, "==", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32Ne:
+ case Opcode::I64Ne:
+ case Opcode::F32Ne:
+ case Opcode::F64Ne:
+ WriteInfixBinaryExpr(expr.opcode, "!=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LtS:
+ case Opcode::I64LtS:
+ WriteSignedBinaryExpr(expr.opcode, "<");
+ break;
+
+ case Opcode::I32LtU:
+ case Opcode::I64LtU:
+ case Opcode::F32Lt:
+ case Opcode::F64Lt:
+ WriteInfixBinaryExpr(expr.opcode, "<", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32LeS:
+ case Opcode::I64LeS:
+ WriteSignedBinaryExpr(expr.opcode, "<=");
+ break;
+
+ case Opcode::I32LeU:
+ case Opcode::I64LeU:
+ case Opcode::F32Le:
+ case Opcode::F64Le:
+ WriteInfixBinaryExpr(expr.opcode, "<=", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GtS:
+ case Opcode::I64GtS:
+ WriteSignedBinaryExpr(expr.opcode, ">");
+ break;
+
+ case Opcode::I32GtU:
+ case Opcode::I64GtU:
+ case Opcode::F32Gt:
+ case Opcode::F64Gt:
+ WriteInfixBinaryExpr(expr.opcode, ">", AssignOp::Disallowed);
+ break;
+
+ case Opcode::I32GeS:
+ case Opcode::I64GeS:
+ WriteSignedBinaryExpr(expr.opcode, ">=");
+ break;
+
+ case Opcode::I32GeU:
+ case Opcode::I64GeU:
+ case Opcode::F32Ge:
+ case Opcode::F64Ge:
+ WriteInfixBinaryExpr(expr.opcode, ">=", AssignOp::Disallowed);
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const ConvertExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Eqz:
+ case Opcode::I64Eqz:
+ WriteSimpleUnaryExpr(expr.opcode, "!");
+ break;
+
+ case Opcode::I64ExtendI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)");
+ break;
+
+ case Opcode::I64ExtendI32U:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)");
+ break;
+
+ case Opcode::I32WrapI64:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)");
+ break;
+
+ case Opcode::I32TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F32");
+ break;
+
+ case Opcode::I64TruncF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F32");
+ break;
+
+ case Opcode::I32TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_S_F64");
+ break;
+
+ case Opcode::I64TruncF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_S_F64");
+ break;
+
+ case Opcode::I32TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F32");
+ break;
+
+ case Opcode::I64TruncF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F32");
+ break;
+
+ case Opcode::I32TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_U_F64");
+ break;
+
+ case Opcode::I64TruncF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_U_F64");
+ break;
+
+ case Opcode::I32TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I64TruncSatF32S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F32");
+ break;
+
+ case Opcode::I32TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I64TruncSatF64S:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_S_F64");
+ break;
+
+ case Opcode::I32TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I64TruncSatF32U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F32");
+ break;
+
+ case Opcode::I32TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::I64TruncSatF64U:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_TRUNC_SAT_U_F64");
+ break;
+
+ case Opcode::F32ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s32)");
+ break;
+
+ case Opcode::F32ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)(s64)");
+ break;
+
+ case Opcode::F32ConvertI32U:
+ case Opcode::F32DemoteF64:
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F32ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_float).
+ WriteSimpleUnaryExpr(expr.opcode, "(f32)");
+ break;
+
+ case Opcode::F64ConvertI32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s32)");
+ break;
+
+ case Opcode::F64ConvertI64S:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)(s64)");
+ break;
+
+ case Opcode::F64ConvertI32U:
+ case Opcode::F64PromoteF32:
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F64ConvertI64U:
+ // TODO(binji): This needs to be handled specially (see
+ // wabt_convert_uint64_to_double).
+ WriteSimpleUnaryExpr(expr.opcode, "(f64)");
+ break;
+
+ case Opcode::F32ReinterpretI32:
+ WriteSimpleUnaryExpr(expr.opcode, "f32_reinterpret_i32");
+ break;
+
+ case Opcode::I32ReinterpretF32:
+ WriteSimpleUnaryExpr(expr.opcode, "i32_reinterpret_f32");
+ break;
+
+ case Opcode::F64ReinterpretI64:
+ WriteSimpleUnaryExpr(expr.opcode, "f64_reinterpret_i64");
+ break;
+
+ case Opcode::I64ReinterpretF64:
+ WriteSimpleUnaryExpr(expr.opcode, "i64_reinterpret_f64");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const LoadExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Load: func = "i32_load"; break;
+ case Opcode::I64Load: func = "i64_load"; break;
+ case Opcode::F32Load: func = "f32_load"; break;
+ case Opcode::F64Load: func = "f64_load"; break;
+ case Opcode::I32Load8S: func = "i32_load8_s"; break;
+ case Opcode::I64Load8S: func = "i64_load8_s"; break;
+ case Opcode::I32Load8U: func = "i32_load8_u"; break;
+ case Opcode::I64Load8U: func = "i64_load8_u"; break;
+ case Opcode::I32Load16S: func = "i32_load16_s"; break;
+ case Opcode::I64Load16S: func = "i64_load16_s"; break;
+ case Opcode::I32Load16U: func = "i32_load16_u"; break;
+ case Opcode::I64Load16U: func = "i64_load16_u"; break;
+ case Opcode::I64Load32S: func = "i64_load32_s"; break;
+ case Opcode::I64Load32U: func = "i64_load32_u"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", func, "(&(sbx->", ExternalRef(memory->name),
+ "), (u64)(", StackVar(0), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset, "u");
+ Write(", \"", GetGlobalName(func_->name), "\"");
+ Write(");", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const StoreExpr& expr) {
+ const char* func = nullptr;
+ switch (expr.opcode) {
+ case Opcode::I32Store: func = "i32_store"; break;
+ case Opcode::I64Store: func = "i64_store"; break;
+ case Opcode::F32Store: func = "f32_store"; break;
+ case Opcode::F64Store: func = "f64_store"; break;
+ case Opcode::I32Store8: func = "i32_store8"; break;
+ case Opcode::I64Store8: func = "i64_store8"; break;
+ case Opcode::I32Store16: func = "i32_store16"; break;
+ case Opcode::I64Store16: func = "i64_store16"; break;
+ case Opcode::I64Store32: func = "i64_store32"; break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Write(func, "(&(sbx->", ExternalRef(memory->name), "), (u64)(", StackVar(1), ")");
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write(", ", StackVar(0));
+ Write(", \"", GetGlobalName(func_->name), "\"");
+ Write(");", Newline());
+ DropTypes(2);
+}
+
+void CWriter::Write(const UnaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::I32Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CLZ");
+ break;
+
+ case Opcode::I64Clz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CLZ");
+ break;
+
+ case Opcode::I32Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_CTZ");
+ break;
+
+ case Opcode::I64Ctz:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_CTZ");
+ break;
+
+ case Opcode::I32Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I32_POPCNT");
+ break;
+
+ case Opcode::I64Popcnt:
+ WriteSimpleUnaryExpr(expr.opcode, "I64_POPCNT");
+ break;
+
+ case Opcode::F32Neg:
+ case Opcode::F64Neg:
+ WriteSimpleUnaryExpr(expr.opcode, "-");
+ break;
+
+ case Opcode::F32Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabsf");
+ break;
+
+ case Opcode::F64Abs:
+ WriteSimpleUnaryExpr(expr.opcode, "fabs");
+ break;
+
+ case Opcode::F32Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrtf");
+ break;
+
+ case Opcode::F64Sqrt:
+ WriteSimpleUnaryExpr(expr.opcode, "sqrt");
+ break;
+
+ case Opcode::F32Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceilf");
+ break;
+
+ case Opcode::F64Ceil:
+ WriteSimpleUnaryExpr(expr.opcode, "ceil");
+ break;
+
+ case Opcode::F32Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floorf");
+ break;
+
+ case Opcode::F64Floor:
+ WriteSimpleUnaryExpr(expr.opcode, "floor");
+ break;
+
+ case Opcode::F32Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "truncf");
+ break;
+
+ case Opcode::F64Trunc:
+ WriteSimpleUnaryExpr(expr.opcode, "trunc");
+ break;
+
+ case Opcode::F32Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyintf");
+ break;
+
+ case Opcode::F64Nearest:
+ WriteSimpleUnaryExpr(expr.opcode, "nearbyint");
+ break;
+
+ case Opcode::I32Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s8)(u8)");
+ break;
+
+ case Opcode::I32Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u32)(s32)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend8S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s8)(u8)");
+ break;
+
+ case Opcode::I64Extend16S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s16)(u16)");
+ break;
+
+ case Opcode::I64Extend32S:
+ WriteSimpleUnaryExpr(expr.opcode, "(u64)(s64)(s32)(u32)");
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const TernaryExpr& expr) {
+ switch (expr.opcode) {
+ case Opcode::V128BitSelect: {
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(2, result_type), " = ", "v128.bitselect", "(", StackVar(0),
+ ", ", StackVar(1), ", ", StackVar(2), ");", Newline());
+ DropTypes(3);
+ PushType(result_type);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+void CWriter::Write(const SimdLaneOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+
+ switch (expr.opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane: {
+ Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(0), ", lane Imm: ", expr.val, ");", Newline());
+ DropTypes(1);
+ break;
+ }
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane: {
+ Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(0), ", ", StackVar(1), ", lane Imm: ", expr.val, ");",
+ Newline());
+ DropTypes(2);
+ break;
+ }
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ PushType(result_type);
+}
+
+void CWriter::Write(const SimdLoadLaneExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::Write(const SimdStoreLaneExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::Write(const SimdShuffleOpExpr& expr) {
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(1, result_type), " = ", expr.opcode.GetName(), "(",
+ StackVar(1), " ", StackVar(0), ", lane Imm: $0x%08x %08x %08x %08x",
+ expr.val.u32(0), expr.val.u32(1), expr.val.u32(2), expr.val.u32(3), ")",
+ Newline());
+ DropTypes(2);
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadSplatExpr& expr) {
+ assert(module_->memories.size() == 1);
+ Memory* memory = module_->memories[0];
+
+ Type result_type = expr.opcode.GetResultType();
+ Write(StackVar(0, result_type), " = ", expr.opcode.GetName(), "(",
+ ExternalPtr(memory->name), ", (u64)(", StackVar(0));
+ if (expr.offset != 0)
+ Write(" + ", expr.offset);
+ Write("));", Newline());
+ DropTypes(1);
+ PushType(result_type);
+}
+
+void CWriter::Write(const LoadZeroExpr& expr) {
+ UNIMPLEMENTED("SIMD support");
+}
+
+void CWriter::WriteCHeader() {
+ stream_ = h_stream_;
+ std::string guard = GenerateHeaderGuard();
+ Write("#ifndef ", guard, Newline());
+ Write("#define ", guard, Newline(), Newline());
+ Write("#define WASM_CURR_MODULE_PREFIX ", options_.mod_name, Newline());
+ Write(s_header_top);
+ WriteMultivalueTypes();
+ WriteImports();
+ WriteFuncDeclarations(true /* for_header */);
+ Write(s_header_bottom, Newline());
+ Write("#endif /* ", guard, " */", Newline());
+}
+
+void CWriter::WriteCSource() {
+ stream_ = c_stream_;
+ WriteSourceTop();
+ WriteSandboxStruct();
+ WriteFuncTypes();
+ WriteFuncDeclarations(false /* for_header */);
+ WriteEntryFuncs();
+ WriteGlobalInitializers();
+ WriteFuncs();
+ WriteDataInitializers();
+ WriteElemInitializers();
+ WriteExportLookup();
+ WriteCallbackAddRemove();
+ WriteInit();
+}
+
+Result CWriter::WriteModule(const Module& module) {
+ WABT_USE(options_);
+ module_ = &module;
+ WriteCHeader();
+ WriteCSource();
+ return result_;
+}
+
+} // end anonymous namespace
+
+Result WriteC(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const Module* module,
+ const WriteCOptions& options) {
+ CWriter c_writer(c_stream, h_stream, header_name, options);
+ return c_writer.WriteModule(*module);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/c-writer.h b/third_party/wasm2c/src/c-writer.h
new file mode 100644
index 0000000000..8aa13b2e2b
--- /dev/null
+++ b/third_party/wasm2c/src/c-writer.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_C_WRITER_H_
+#define WABT_C_WRITER_H_
+
+#include "src/common.h"
+
+#include <string>
+
+namespace wabt {
+
+struct Module;
+class Stream;
+
+struct WriteCOptions {
+ std::string mod_name;
+};
+
+Result WriteC(Stream* c_stream,
+ Stream* h_stream,
+ const char* header_name,
+ const Module*,
+ const WriteCOptions&);
+
+} // namespace wabt
+
+#endif /* WABT_C_WRITER_H_ */
diff --git a/third_party/wasm2c/src/cast.h b/third_party/wasm2c/src/cast.h
new file mode 100644
index 0000000000..7275415027
--- /dev/null
+++ b/third_party/wasm2c/src/cast.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_CAST_H_
+#define WABT_CAST_H_
+
+#include <memory>
+#include <type_traits>
+
+#include "src/common.h"
+
+// Modeled after LLVM's dynamic casts:
+// http://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates
+//
+// Use isa<T>(foo) to check whether foo is a T*:
+//
+// if (isa<Minivan>(car)) {
+// ...
+// }
+//
+// Use cast<T>(foo) when you know that foo is a T* -- it will assert that the
+// type matches:
+//
+// switch (car.type) {
+// case CarType::Minivan: {
+// auto minivan = cast<Minivan>(car);
+// ...
+// }
+// }
+//
+// Use dyn_cast<T>(foo) as a combination if isa and cast, it will return
+// nullptr if the type doesn't match:
+//
+// if (auto minivan = dyn_cast<Minivan>(car)) {
+// ...
+// }
+//
+//
+// To use these classes in a type hierarchy, you must implement classof:
+//
+// enum CarType { Minivan, ... };
+// struct Car { CarType type; ... };
+// struct Minivan : Car {
+// static bool classof(const Car* car) { return car->type == Minivan; }
+// ...
+// };
+//
+
+namespace wabt {
+
+template <typename Derived, typename Base>
+bool isa(const Base* base) {
+ WABT_STATIC_ASSERT((std::is_base_of<Base, Derived>::value));
+ return Derived::classof(base);
+}
+
+template <typename Derived, typename Base>
+const Derived* cast(const Base* base) {
+ assert(isa<Derived>(base));
+ return static_cast<const Derived*>(base);
+};
+
+template <typename Derived, typename Base>
+Derived* cast(Base* base) {
+ assert(isa<Derived>(base));
+ return static_cast<Derived*>(base);
+};
+
+template <typename Derived, typename Base>
+const Derived* dyn_cast(const Base* base) {
+ return isa<Derived>(base) ? static_cast<const Derived*>(base) : nullptr;
+};
+
+template <typename Derived, typename Base>
+Derived* dyn_cast(Base* base) {
+ return isa<Derived>(base) ? static_cast<Derived*>(base) : nullptr;
+};
+
+// Cast functionality for unique_ptr. isa and dyn_cast are not included because
+// they won't always pass ownership back to the caller.
+
+template <typename Derived, typename Base>
+std::unique_ptr<const Derived> cast(std::unique_ptr<const Base>&& base) {
+ assert(isa<Derived>(base.get()));
+ return std::unique_ptr<Derived>(static_cast<const Derived*>(base.release()));
+};
+
+template <typename Derived, typename Base>
+std::unique_ptr<Derived> cast(std::unique_ptr<Base>&& base) {
+ assert(isa<Derived>(base.get()));
+ return std::unique_ptr<Derived>(static_cast<Derived*>(base.release()));
+};
+
+} // namespace wabt
+
+#endif // WABT_CAST_H_
diff --git a/third_party/wasm2c/src/circular-array.h b/third_party/wasm2c/src/circular-array.h
new file mode 100644
index 0000000000..afdead77de
--- /dev/null
+++ b/third_party/wasm2c/src/circular-array.h
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_CIRCULAR_ARRAY_H_
+#define WABT_CIRCULAR_ARRAY_H_
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <type_traits>
+
+namespace wabt {
+
+// TODO(karlschimpf) Complete the API
+// Note: Capacity must be a power of 2.
+template <class T, size_t kCapacity>
+class CircularArray {
+ public:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ CircularArray() {
+ static_assert(kCapacity && ((kCapacity & (kCapacity - 1)) == 0),
+ "Capacity must be a power of 2.");
+ }
+
+ CircularArray(const CircularArray&) = default;
+ CircularArray& operator=(const CircularArray&) = default;
+
+ CircularArray(CircularArray&&) = default;
+ CircularArray& operator=(CircularArray&&) = default;
+
+ ~CircularArray() { clear(); }
+
+ reference at(size_type index) {
+ assert(index < size_);
+ return (*this)[index];
+ }
+
+ const_reference at(size_type index) const {
+ assert(index < size_);
+ return (*this)[index];
+ }
+
+ reference operator[](size_type index) { return contents_[position(index)]; }
+
+ const_reference operator[](size_type index) const {
+ return contents_[position(index)];
+ }
+
+ reference back() { return at(size_ - 1); }
+
+ const_reference back() const { return at(size_ - 1); }
+
+ bool empty() const { return size_ == 0; }
+
+ reference front() { return at(0); }
+
+ const_reference front() const { return at(0); }
+
+ size_type max_size() const { return kCapacity; }
+
+ void pop_back() {
+ assert(size_ > 0);
+ SetElement(back());
+ --size_;
+ }
+
+ void pop_front() {
+ assert(size_ > 0);
+ SetElement(front());
+ front_ = (front_ + 1) & kMask;
+ --size_;
+ }
+
+ void push_back(const value_type& value) {
+ assert(size_ < kCapacity);
+ SetElement(at(size_++), value);
+ }
+
+ size_type size() const { return size_; }
+
+ void clear() {
+ while (!empty()) {
+ pop_back();
+ }
+ }
+
+ private:
+ static const size_type kMask = kCapacity - 1;
+
+ size_t position(size_t index) const { return (front_ + index) & kMask; }
+
+ template <typename... Args>
+ void SetElement(reference element, Args&&... args) {
+ element.~T();
+ new (&element) T(std::forward<Args>(args)...);
+ }
+
+ std::array<T, kCapacity> contents_;
+ size_type size_ = 0;
+ size_type front_ = 0;
+};
+
+} // namespace wabt
+
+#endif // WABT_CIRCULAR_ARRAY_H_
diff --git a/third_party/wasm2c/src/color.cc b/third_party/wasm2c/src/color.cc
new file mode 100644
index 0000000000..8fdc5d00f8
--- /dev/null
+++ b/third_party/wasm2c/src/color.cc
@@ -0,0 +1,84 @@
+/*
+ * 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 "src/color.h"
+
+#include <cstdlib>
+
+#include "src/common.h"
+
+#if _WIN32
+#include <io.h>
+#include <windows.h>
+#elif HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace wabt {
+
+Color::Color(FILE* file, bool enabled) : file_(file) {
+ enabled_ = enabled && SupportsColor(file_);
+}
+
+// static
+bool Color::SupportsColor(FILE* file) {
+ char* force = getenv("FORCE_COLOR");
+ if (force) {
+ return atoi(force) != 0;
+ }
+
+#if _WIN32
+
+ {
+#if HAVE_WIN32_VT100
+ HANDLE handle;
+ if (file == stdout) {
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ } else if (file == stderr) {
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ } else {
+ return false;
+ }
+ DWORD mode;
+ if (!_isatty(_fileno(file)) || !GetConsoleMode(handle, &mode) ||
+ !SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+ return false;
+ }
+ return true;
+#else
+ // TODO(binji): Support older Windows by using SetConsoleTextAttribute?
+ return false;
+#endif
+ }
+
+#elif HAVE_UNISTD_H
+
+ return isatty(fileno(file));
+
+#else
+
+ return false;
+
+#endif
+}
+
+void Color::WriteCode(const char* code) const {
+ if (enabled_) {
+ fputs(code, file_);
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/color.h b/third_party/wasm2c/src/color.h
new file mode 100644
index 0000000000..8af57f33f7
--- /dev/null
+++ b/third_party/wasm2c/src/color.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_COLOR_H_
+#define WABT_COLOR_H_
+
+#include <cstdio>
+
+namespace wabt {
+
+#define WABT_FOREACH_COLOR_CODE(V) \
+ V(Default, "\x1b[0m") \
+ V(Bold, "\x1b[1m") \
+ V(NoBold, "\x1b[22m") \
+ V(Black, "\x1b[30m") \
+ V(Red, "\x1b[31m") \
+ V(Green, "\x1b[32m") \
+ V(Yellow, "\x1b[33m") \
+ V(Blue, "\x1b[34m") \
+ V(Magenta, "\x1b[35m") \
+ V(Cyan, "\x1b[36m") \
+ V(White, "\x1b[37m")
+
+class Color {
+ public:
+ Color() : file_(nullptr), enabled_(false) {}
+ Color(FILE*, bool enabled = true);
+
+// Write the given color to the file, if enabled.
+#define WABT_COLOR(Name, code) \
+ void Name() const { WriteCode(Name##Code()); }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+// Get the color code as a string, if enabled.
+#define WABT_COLOR(Name, code) \
+ const char* Maybe##Name##Code() const { return enabled_ ? Name##Code() : ""; }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+// Get the color code as a string.
+#define WABT_COLOR(Name, code) \
+ static const char* Name##Code() { return code; }
+ WABT_FOREACH_COLOR_CODE(WABT_COLOR)
+#undef WABT_COLOR
+
+ private:
+ static bool SupportsColor(FILE*);
+ void WriteCode(const char*) const;
+
+ FILE* file_;
+ bool enabled_;
+};
+
+#undef WABT_FOREACH_COLOR_CODE
+
+} // namespace wabt
+
+#endif // WABT_COLOR_H_
diff --git a/third_party/wasm2c/src/common.cc b/third_party/wasm2c/src/common.cc
new file mode 100644
index 0000000000..6fe754a2b2
--- /dev/null
+++ b/third_party/wasm2c/src/common.cc
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/common.h"
+
+#include <cassert>
+#include <climits>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if COMPILER_IS_MSVC
+#include <fcntl.h>
+#include <io.h>
+#include <stdlib.h>
+#define PATH_MAX _MAX_PATH
+#define stat _stat
+#define S_IFREG _S_IFREG
+#endif
+
+namespace wabt {
+
+Reloc::Reloc(RelocType type, Offset offset, Index index, int32_t addend)
+ : type(type), offset(offset), index(index), addend(addend) {}
+
+const char* g_kind_name[] = {"func", "table", "memory", "global", "tag"};
+WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_kind_name) == kExternalKindCount);
+
+const char* g_reloc_type_name[] = {
+ "R_WASM_FUNCTION_INDEX_LEB", "R_WASM_TABLE_INDEX_SLEB",
+ "R_WASM_TABLE_INDEX_I32", "R_WASM_MEMORY_ADDR_LEB",
+ "R_WASM_MEMORY_ADDR_SLEB", "R_WASM_MEMORY_ADDR_I32",
+ "R_WASM_TYPE_INDEX_LEB", "R_WASM_GLOBAL_INDEX_LEB",
+ "R_WASM_FUNCTION_OFFSET_I32", "R_WASM_SECTION_OFFSET_I32",
+ "R_WASM_TAG_INDEX_LEB", "R_WASM_MEMORY_ADDR_REL_SLEB",
+ "R_WASM_TABLE_INDEX_REL_SLEB", "R_WASM_GLOBAL_INDEX_I32",
+ "R_WASM_MEMORY_ADDR_LEB64", "R_WASM_MEMORY_ADDR_SLEB64",
+ "R_WASM_MEMORY_ADDR_I64", "R_WASM_MEMORY_ADDR_REL_SLEB64",
+ "R_WASM_TABLE_INDEX_SLEB64", "R_WASM_TABLE_INDEX_I64",
+ "R_WASM_TABLE_NUMBER_LEB", "R_WASM_MEMORY_ADDR_TLS_SLEB",
+ "R_WASM_MEMORY_ADDR_TLS_I32",
+};
+WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(g_reloc_type_name) == kRelocTypeCount);
+
+static Result ReadStdin(std::vector<uint8_t>* out_data) {
+ out_data->resize(0);
+ uint8_t buffer[4096];
+ while (true) {
+ size_t bytes_read = fread(buffer, 1, sizeof(buffer), stdin);
+ if (bytes_read == 0) {
+ if (ferror(stdin)) {
+ fprintf(stderr, "error reading from stdin: %s\n", strerror(errno));
+ return Result::Error;
+ }
+ return Result::Ok;
+ }
+ size_t old_size = out_data->size();
+ out_data->resize(old_size + bytes_read);
+ memcpy(out_data->data() + old_size, buffer, bytes_read);
+ }
+}
+
+Result ReadFile(string_view filename, std::vector<uint8_t>* out_data) {
+ std::string filename_str = filename.to_string();
+ const char* filename_cstr = filename_str.c_str();
+
+ if (filename == "-") {
+ return ReadStdin(out_data);
+ }
+
+ struct stat statbuf;
+ if (stat(filename_cstr, &statbuf) < 0) {
+ fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno));
+ return Result::Error;
+ }
+
+ if (!(statbuf.st_mode & S_IFREG)) {
+ fprintf(stderr, "%s: not a regular file\n", filename_cstr);
+ return Result::Error;
+ }
+
+ FILE* infile = fopen(filename_cstr, "rb");
+ if (!infile) {
+ fprintf(stderr, "%s: %s\n", filename_cstr, strerror(errno));
+ return Result::Error;
+ }
+
+ if (fseek(infile, 0, SEEK_END) < 0) {
+ perror("fseek to end failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ long size = ftell(infile);
+ if (size < 0) {
+ perror("ftell failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ if (fseek(infile, 0, SEEK_SET) < 0) {
+ perror("fseek to beginning failed");
+ fclose(infile);
+ return Result::Error;
+ }
+
+ out_data->resize(size);
+ if (size != 0 && fread(out_data->data(), size, 1, infile) != 1) {
+ fprintf(stderr, "%s: fread failed: %s\n", filename_cstr, strerror(errno));
+ fclose(infile);
+ return Result::Error;
+ }
+
+ fclose(infile);
+ return Result::Ok;
+}
+
+void InitStdio() {
+#if COMPILER_IS_MSVC
+ int result = _setmode(_fileno(stdout), _O_BINARY);
+ if (result == -1) {
+ perror("Cannot set mode binary to stdout");
+ }
+ result = _setmode(_fileno(stderr), _O_BINARY);
+ if (result == -1) {
+ perror("Cannot set mode binary to stderr");
+ }
+#endif
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/common.h b/third_party/wasm2c/src/common.h
new file mode 100644
index 0000000000..de5e44af36
--- /dev/null
+++ b/third_party/wasm2c/src/common.h
@@ -0,0 +1,490 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_COMMON_H_
+#define WABT_COMMON_H_
+
+#include <algorithm>
+#include <cassert>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "config.h"
+
+#include "src/make-unique.h"
+#include "src/result.h"
+#include "src/string-view.h"
+#include "src/type.h"
+
+#define WABT_FATAL(...) fprintf(stderr, __VA_ARGS__), exit(1)
+#define WABT_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+#define WABT_USE(x) static_cast<void>(x)
+
+#define WABT_PAGE_SIZE 0x10000 /* 64k */
+#define WABT_MAX_PAGES32 0x10000 /* # of pages that fit in 32-bit address \
+ space */
+#define WABT_MAX_PAGES64 0x1000000000000 /* # of pages that fit in 64-bit \
+ address space */
+#define WABT_BYTES_TO_PAGES(x) ((x) >> 16)
+#define WABT_ALIGN_UP_TO_PAGE(x) \
+ (((x) + WABT_PAGE_SIZE - 1) & ~(WABT_PAGE_SIZE - 1))
+
+#define PRIstringview "%.*s"
+#define WABT_PRINTF_STRING_VIEW_ARG(x) \
+ static_cast<int>((x).length()), (x).data()
+
+#define PRItypecode "%s%#x"
+#define WABT_PRINTF_TYPE_CODE(x) \
+ (static_cast<int32_t>(x) < 0 ? "-" : ""), std::abs(static_cast<int32_t>(x))
+
+#define WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE 128
+#define WABT_SNPRINTF_ALLOCA(buffer, len, format) \
+ va_list args; \
+ va_list args_copy; \
+ va_start(args, format); \
+ va_copy(args_copy, args); \
+ char fixed_buf[WABT_DEFAULT_SNPRINTF_ALLOCA_BUFSIZE]; \
+ char* buffer = fixed_buf; \
+ size_t len = wabt_vsnprintf(fixed_buf, sizeof(fixed_buf), format, args); \
+ va_end(args); \
+ if (len + 1 > sizeof(fixed_buf)) { \
+ buffer = static_cast<char*>(alloca(len + 1)); \
+ len = wabt_vsnprintf(buffer, len + 1, format, args_copy); \
+ } \
+ va_end(args_copy)
+
+#define WABT_ENUM_COUNT(name) \
+ (static_cast<int>(name::Last) - static_cast<int>(name::First) + 1)
+
+#define WABT_DISALLOW_COPY_AND_ASSIGN(type) \
+ type(const type&) = delete; \
+ type& operator=(const type&) = delete;
+
+#if WITH_EXCEPTIONS
+#define WABT_TRY try {
+#define WABT_CATCH_BAD_ALLOC \
+ } \
+ catch (std::bad_alloc&) { \
+ }
+#define WABT_CATCH_BAD_ALLOC_AND_EXIT \
+ } \
+ catch (std::bad_alloc&) { \
+ WABT_FATAL("Memory allocation failure.\n"); \
+ }
+#else
+#define WABT_TRY
+#define WABT_CATCH_BAD_ALLOC
+#define WABT_CATCH_BAD_ALLOC_AND_EXIT
+#endif
+
+#define PRIindex "u"
+#define PRIaddress PRIu64
+#define PRIoffset PRIzx
+
+namespace wabt {
+#if WABT_BIG_ENDIAN
+ inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) {
+ memcpy(static_cast<char*>(dst) + (dsize) - (len) - (doff),
+ static_cast<const char*>(src) + (ssize) - (len) - (soff),
+ (len));
+ }
+#else
+ inline void MemcpyEndianAware(void *dst, const void *src, size_t dsize, size_t ssize, size_t doff, size_t soff, size_t len) {
+ memcpy(static_cast<char*>(dst) + (doff),
+ static_cast<const char*>(src) + (soff),
+ (len));
+ }
+#endif
+}
+
+struct v128 {
+ v128() = default;
+ v128(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) {
+ set_u32(0, x0);
+ set_u32(1, x1);
+ set_u32(2, x2);
+ set_u32(3, x3);
+ }
+
+ bool operator==(const v128& other) const {
+ return std::equal(std::begin(v), std::end(v), std::begin(other.v));
+ }
+ bool operator!=(const v128& other) const { return !(*this == other); }
+
+ uint8_t u8(int lane) const { return To<uint8_t>(lane); }
+ uint16_t u16(int lane) const { return To<uint16_t>(lane); }
+ uint32_t u32(int lane) const { return To<uint32_t>(lane); }
+ uint64_t u64(int lane) const { return To<uint64_t>(lane); }
+ uint32_t f32_bits(int lane) const { return To<uint32_t>(lane); }
+ uint64_t f64_bits(int lane) const { return To<uint64_t>(lane); }
+
+ void set_u8(int lane, uint8_t x) { return From<uint8_t>(lane, x); }
+ void set_u16(int lane, uint16_t x) { return From<uint16_t>(lane, x); }
+ void set_u32(int lane, uint32_t x) { return From<uint32_t>(lane, x); }
+ void set_u64(int lane, uint64_t x) { return From<uint64_t>(lane, x); }
+ void set_f32_bits(int lane, uint32_t x) { return From<uint32_t>(lane, x); }
+ void set_f64_bits(int lane, uint64_t x) { return From<uint64_t>(lane, x); }
+
+ bool is_zero() const {
+ return std::all_of(std::begin(v), std::end(v),
+ [](uint8_t x) { return x == 0; });
+ }
+ void set_zero() { std::fill(std::begin(v), std::end(v), 0); }
+
+ template <typename T>
+ T To(int lane) const {
+ static_assert(sizeof(T) <= sizeof(v), "Invalid cast!");
+ assert((lane + 1) * sizeof(T) <= sizeof(v));
+ T result;
+ wabt::MemcpyEndianAware(&result, v, sizeof(result), sizeof(v), 0, lane * sizeof(T), sizeof(result));
+ return result;
+ }
+
+ template <typename T>
+ void From(int lane, T data) {
+ static_assert(sizeof(T) <= sizeof(v), "Invalid cast!");
+ assert((lane + 1) * sizeof(T) <= sizeof(v));
+ wabt::MemcpyEndianAware(v, &data, sizeof(v), sizeof(data), lane * sizeof(T), 0, sizeof(data));
+ }
+
+ uint8_t v[16];
+};
+
+namespace wabt {
+
+typedef uint32_t Index; // An index into one of the many index spaces.
+typedef uint64_t Address; // An address or size in linear memory.
+typedef size_t Offset; // An offset into a host's file or memory buffer.
+
+static const Address kInvalidAddress = ~0;
+static const Index kInvalidIndex = ~0;
+static const Offset kInvalidOffset = ~0;
+
+template <typename Dst, typename Src>
+Dst WABT_VECTORCALL Bitcast(Src&& value) {
+ static_assert(sizeof(Src) == sizeof(Dst), "Bitcast sizes must match.");
+ Dst result;
+ memcpy(&result, &value, sizeof(result));
+ return result;
+}
+
+template <typename T>
+void ZeroMemory(T& v) {
+ WABT_STATIC_ASSERT(std::is_pod<T>::value);
+ memset(&v, 0, sizeof(v));
+}
+
+// Placement construct
+template <typename T, typename... Args>
+void Construct(T& placement, Args&&... args) {
+ new (&placement) T(std::forward<Args>(args)...);
+}
+
+// Placement destruct
+template <typename T>
+void Destruct(T& placement) {
+ placement.~T();
+}
+
+inline std::string WABT_PRINTF_FORMAT(1, 2)
+ StringPrintf(const char* format, ...) {
+ va_list args;
+ va_list args_copy;
+ va_start(args, format);
+ va_copy(args_copy, args);
+ size_t len = wabt_vsnprintf(nullptr, 0, format, args) + 1; // For \0.
+ std::vector<char> buffer(len);
+ va_end(args);
+ wabt_vsnprintf(buffer.data(), len, format, args_copy);
+ va_end(args_copy);
+ return std::string(buffer.data(), len - 1);
+}
+
+enum class LabelType {
+ Func,
+ Block,
+ Loop,
+ If,
+ Else,
+ Try,
+ Catch,
+
+ First = Func,
+ Last = Catch,
+};
+static const int kLabelTypeCount = WABT_ENUM_COUNT(LabelType);
+
+struct Location {
+ enum class Type {
+ Text,
+ Binary,
+ };
+
+ Location() : line(0), first_column(0), last_column(0) {}
+ Location(string_view filename, int line, int first_column, int last_column)
+ : filename(filename),
+ line(line),
+ first_column(first_column),
+ last_column(last_column) {}
+ explicit Location(size_t offset) : offset(offset) {}
+
+ string_view filename;
+ union {
+ // For text files.
+ struct {
+ int line;
+ int first_column;
+ int last_column;
+ };
+ // For binary files.
+ struct {
+ size_t offset;
+ };
+ };
+};
+
+enum class SegmentKind {
+ Active,
+ Passive,
+ Declared,
+};
+
+// Used in test asserts for special expected values "nan:canonical" and
+// "nan:arithmetic"
+enum class ExpectedNan {
+ None,
+ Canonical,
+ Arithmetic,
+};
+
+// Matches binary format, do not change.
+enum SegmentFlags : uint8_t {
+ SegFlagsNone = 0,
+ SegPassive = 1, // bit 0: Is passive
+ SegExplicitIndex = 2, // bit 1: Has explict index (Implies table 0 if absent)
+ SegDeclared = 3, // Only used for declared segments
+ SegUseElemExprs = 4, // bit 2: Is elemexpr (Or else index sequence)
+
+ SegFlagMax = (SegUseElemExprs << 1) - 1, // All bits set.
+};
+
+enum class RelocType {
+ FuncIndexLEB = 0, // e.g. Immediate of call instruction
+ TableIndexSLEB = 1, // e.g. Loading address of function
+ TableIndexI32 = 2, // e.g. Function address in DATA
+ MemoryAddressLEB = 3, // e.g. Memory address in load/store offset immediate
+ MemoryAddressSLEB = 4, // e.g. Memory address in i32.const
+ MemoryAddressI32 = 5, // e.g. Memory address in DATA
+ TypeIndexLEB = 6, // e.g. Immediate type in call_indirect
+ GlobalIndexLEB = 7, // e.g. Immediate of get_global inst
+ FunctionOffsetI32 = 8, // e.g. Code offset in DWARF metadata
+ SectionOffsetI32 = 9, // e.g. Section offset in DWARF metadata
+ TagIndexLEB = 10, // Used in throw instructions
+ MemoryAddressRelSLEB = 11, // In PIC code, addr relative to __memory_base
+ TableIndexRelSLEB = 12, // In PIC code, table index relative to __table_base
+ GlobalIndexI32 = 13, // e.g. Global index in data (e.g. DWARF)
+ MemoryAddressLEB64 = 14, // Memory64: Like MemoryAddressLEB
+ MemoryAddressSLEB64 = 15, // Memory64: Like MemoryAddressSLEB
+ MemoryAddressI64 = 16, // Memory64: Like MemoryAddressI32
+ MemoryAddressRelSLEB64 = 17, // Memory64: Like MemoryAddressRelSLEB
+ TableIndexSLEB64 = 18, // Memory64: Like TableIndexSLEB
+ TableIndexI64 = 19, // Memory64: Like TableIndexI32
+ TableNumberLEB = 20, // e.g. Immediate of table.get
+ MemoryAddressTLSSLEB = 21, // Address relative to __tls_base
+ MemoryAddressTLSI32 = 22, // Address relative to __tls_base
+
+ First = FuncIndexLEB,
+ Last = MemoryAddressTLSI32,
+};
+static const int kRelocTypeCount = WABT_ENUM_COUNT(RelocType);
+
+struct Reloc {
+ Reloc(RelocType, size_t offset, Index index, int32_t addend = 0);
+
+ RelocType type;
+ size_t offset;
+ Index index;
+ int32_t addend;
+};
+
+enum class LinkingEntryType {
+ SegmentInfo = 5,
+ InitFunctions = 6,
+ ComdatInfo = 7,
+ SymbolTable = 8,
+};
+
+enum class DylinkEntryType {
+ MemInfo = 1,
+ Needed = 2,
+};
+
+enum class SymbolType {
+ Function = 0,
+ Data = 1,
+ Global = 2,
+ Section = 3,
+ Tag = 4,
+ Table = 5,
+};
+
+enum class ComdatType {
+ Data = 0x0,
+ Function = 0x1,
+};
+
+#define WABT_SYMBOL_MASK_VISIBILITY 0x4
+#define WABT_SYMBOL_MASK_BINDING 0x3
+#define WABT_SYMBOL_FLAG_UNDEFINED 0x10
+#define WABT_SYMBOL_FLAG_EXPORTED 0x20
+#define WABT_SYMBOL_FLAG_EXPLICIT_NAME 0x40
+#define WABT_SYMBOL_FLAG_NO_STRIP 0x80
+#define WABT_SYMBOL_FLAG_TLS 0x100
+#define WABT_SYMBOL_FLAG_MAX 0x1ff
+
+#define WABT_SEGMENT_FLAG_STRINGS 0x1
+#define WABT_SEGMENT_FLAG_TLS 0x2
+#define WABT_SEGMENT_FLAG_MAX 0xff
+
+enum class SymbolVisibility {
+ Default = 0,
+ Hidden = 4,
+};
+
+enum class SymbolBinding {
+ Global = 0,
+ Weak = 1,
+ Local = 2,
+};
+
+/* matches binary format, do not change */
+enum class ExternalKind {
+ Func = 0,
+ Table = 1,
+ Memory = 2,
+ Global = 3,
+ Tag = 4,
+
+ First = Func,
+ Last = Tag,
+};
+static const int kExternalKindCount = WABT_ENUM_COUNT(ExternalKind);
+
+struct Limits {
+ Limits() = default;
+ explicit Limits(uint64_t initial) : initial(initial) {}
+ Limits(uint64_t initial, uint64_t max)
+ : initial(initial), max(max), has_max(true) {}
+ Limits(uint64_t initial, uint64_t max, bool is_shared)
+ : initial(initial), max(max), has_max(true), is_shared(is_shared) {}
+ Limits(uint64_t initial, uint64_t max, bool is_shared, bool is_64)
+ : initial(initial),
+ max(max),
+ has_max(true),
+ is_shared(is_shared),
+ is_64(is_64) {}
+ Type IndexType() const { return is_64 ? Type::I64 : Type::I32; }
+
+ uint64_t initial = 0;
+ uint64_t max = 0;
+ bool has_max = false;
+ bool is_shared = false;
+ bool is_64 = false;
+};
+
+enum { WABT_USE_NATURAL_ALIGNMENT = 0xFFFFFFFFFFFFFFFF };
+
+Result ReadFile(string_view filename, std::vector<uint8_t>* out_data);
+
+void InitStdio();
+
+/* external kind */
+
+extern const char* g_kind_name[];
+
+static WABT_INLINE const char* GetKindName(ExternalKind kind) {
+ return static_cast<size_t>(kind) < kExternalKindCount
+ ? g_kind_name[static_cast<size_t>(kind)]
+ : "<error_kind>";
+}
+
+/* reloc */
+
+extern const char* g_reloc_type_name[];
+
+static WABT_INLINE const char* GetRelocTypeName(RelocType reloc) {
+ return static_cast<size_t>(reloc) < kRelocTypeCount
+ ? g_reloc_type_name[static_cast<size_t>(reloc)]
+ : "<error_reloc_type>";
+}
+
+/* symbol */
+
+static WABT_INLINE const char* GetSymbolTypeName(SymbolType type) {
+ switch (type) {
+ case SymbolType::Function:
+ return "func";
+ case SymbolType::Global:
+ return "global";
+ case SymbolType::Data:
+ return "data";
+ case SymbolType::Section:
+ return "section";
+ case SymbolType::Tag:
+ return "tag";
+ case SymbolType::Table:
+ return "table";
+ default:
+ return "<error_symbol_type>";
+ }
+}
+
+template <typename T>
+void ConvertBackslashToSlash(T begin, T end) {
+ std::replace(begin, end, '\\', '/');
+}
+
+inline void ConvertBackslashToSlash(char* s, size_t length) {
+ ConvertBackslashToSlash(s, s + length);
+}
+
+inline void ConvertBackslashToSlash(char* s) {
+ ConvertBackslashToSlash(s, strlen(s));
+}
+
+inline void ConvertBackslashToSlash(std::string* s) {
+ ConvertBackslashToSlash(s->begin(), s->end());
+}
+
+inline void SwapBytesSized(void *addr, size_t size) {
+ auto bytes = static_cast<uint8_t*>(addr);
+ for (size_t i = 0; i < size / 2; i++) {
+ std::swap(bytes[i], bytes[size-1-i]);
+ }
+}
+
+} // namespace wabt
+
+#endif // WABT_COMMON_H_
diff --git a/third_party/wasm2c/src/config.cc b/third_party/wasm2c/src/config.cc
new file mode 100644
index 0000000000..9347c860a9
--- /dev/null
+++ b/third_party/wasm2c/src/config.cc
@@ -0,0 +1,162 @@
+/*
+ * 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 "config.h"
+
+#include <cstdarg>
+#include <cstdio>
+
+#if COMPILER_IS_MSVC && _M_X64
+#include <emmintrin.h>
+#elif COMPILER_IS_MSVC && _M_IX86
+#include <float.h>
+#endif
+
+/* c99-style vsnprintf for MSVC < 2015. See http://stackoverflow.com/a/8712996
+ using _snprintf or vsnprintf will not-properly null-terminate, and will return
+ -1 instead of the number of characters needed on overflow. */
+#if COMPILER_IS_MSVC
+int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
+ int result = -1;
+ if (size != 0) {
+ result = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
+ }
+ if (result == -1) {
+ result = _vscprintf(format, ap);
+ }
+ return result;
+}
+
+#if !HAVE_SNPRINTF
+int wabt_snprintf(char* str, size_t size, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = wabt_vsnprintf(str, size, format, args);
+ va_end(args);
+ return result;
+}
+#endif
+#endif
+
+#if COMPILER_IS_MSVC && _M_IX86
+// Allow the following functions to change the floating-point environment (e.g.
+// update to 64-bit precision in the mantissa). This is only needed for x87
+// floats, which are only used on MSVC 32-bit.
+#pragma fenv_access (on)
+namespace {
+
+typedef unsigned int FPControl;
+
+FPControl Set64BitPrecisionControl() {
+ FPControl old_ctrl = _control87(0, 0);
+ _control87(_PC_64, _MCW_PC);
+ return old_ctrl;
+}
+
+void ResetPrecisionControl(FPControl old_ctrl) {
+ _control87(old_ctrl, _MCW_PC);
+}
+
+} // end of anonymous namespace
+#endif
+
+double wabt_convert_uint64_to_double(uint64_t x) {
+#if COMPILER_IS_MSVC && _M_X64
+ // MSVC on x64 generates uint64 -> float conversions but doesn't do
+ // round-to-nearest-ties-to-even, which is required by WebAssembly.
+ __m128d result = _mm_setzero_pd();
+ if (x & 0x8000000000000000ULL) {
+ result = _mm_cvtsi64_sd(result, (x >> 1) | (x & 1));
+ result = _mm_add_sd(result, result);
+ } else {
+ result = _mm_cvtsi64_sd(result, x);
+ }
+ return _mm_cvtsd_f64(result);
+#elif COMPILER_IS_MSVC && _M_IX86
+ // MSVC on x86 converts from i64 -> double -> float, which causes incorrect
+ // rounding. Using the x87 float stack instead preserves the correct
+ // rounding.
+ FPControl old_ctrl = Set64BitPrecisionControl();
+ static const double c = 18446744073709551616.0;
+ double result;
+ __asm fild x;
+ if (x & 0x8000000000000000ULL) {
+ __asm fadd c;
+ }
+ __asm fstp result;
+ ResetPrecisionControl(old_ctrl);
+ return result;
+#else
+ return static_cast<double>(x);
+#endif
+}
+
+float wabt_convert_uint64_to_float(uint64_t x) {
+#if COMPILER_IS_MSVC && _M_X64
+ // MSVC on x64 generates uint64 -> float conversions but doesn't do
+ // round-to-nearest-ties-to-even, which is required by WebAssembly.
+ __m128 result = _mm_setzero_ps();
+ if (x & 0x8000000000000000ULL) {
+ result = _mm_cvtsi64_ss(result, (x >> 1) | (x & 1));
+ result = _mm_add_ss(result, result);
+ } else {
+ result = _mm_cvtsi64_ss(result, x);
+ }
+ return _mm_cvtss_f32(result);
+#elif COMPILER_IS_MSVC && _M_IX86
+ // MSVC on x86 converts from i64 -> double -> float, which causes incorrect
+ // rounding. Using the x87 float stack instead preserves the correct
+ // rounding.
+ FPControl old_ctrl = Set64BitPrecisionControl();
+ static const float c = 18446744073709551616.0f;
+ float result;
+ __asm fild x;
+ if (x & 0x8000000000000000ULL) {
+ __asm fadd c;
+ }
+ __asm fstp result;
+ ResetPrecisionControl(old_ctrl);
+ return result;
+#else
+ return static_cast<float>(x);
+#endif
+}
+
+double wabt_convert_int64_to_double(int64_t x) {
+#if COMPILER_IS_MSVC && _M_IX86
+ double result;
+ __asm fild x;
+ __asm fstp result;
+ return result;
+#else
+ return static_cast<double>(x);
+#endif
+}
+
+float wabt_convert_int64_to_float(int64_t x) {
+#if COMPILER_IS_MSVC && _M_IX86
+ float result;
+ __asm fild x;
+ __asm fstp result;
+ return result;
+#else
+ return static_cast<float>(x);
+#endif
+}
+
+#if COMPILER_IS_MSVC && _M_IX86
+#pragma fenv_access (off)
+#endif
diff --git a/third_party/wasm2c/src/config.h.in b/third_party/wasm2c/src/config.h.in
new file mode 100644
index 0000000000..5960310084
--- /dev/null
+++ b/third_party/wasm2c/src/config.h.in
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_CONFIG_H_
+#define WABT_CONFIG_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#cmakedefine CMAKE_PROJECT_VERSION "${CMAKE_PROJECT_VERSION}"
+
+/* TODO(binji): nice way to define these with WABT_ prefix? */
+
+/* Whether <alloca.h> is available */
+#cmakedefine01 HAVE_ALLOCA_H
+
+/* Whether <unistd.h> is available */
+#cmakedefine01 HAVE_UNISTD_H
+
+/* Whether snprintf is defined by stdio.h */
+#cmakedefine01 HAVE_SNPRINTF
+
+/* Whether ssize_t is defined by stddef.h */
+#cmakedefine01 HAVE_SSIZE_T
+
+/* Whether strcasecmp is defined by strings.h */
+#cmakedefine01 HAVE_STRCASECMP
+
+/* Whether ENABLE_VIRTUAL_TERMINAL_PROCESSING is defined by windows.h */
+#cmakedefine01 HAVE_WIN32_VT100
+
+#cmakedefine01 COMPILER_IS_CLANG
+#cmakedefine01 COMPILER_IS_GNU
+#cmakedefine01 COMPILER_IS_MSVC
+
+#cmakedefine01 WITH_EXCEPTIONS
+
+#define SIZEOF_SIZE_T @SIZEOF_SIZE_T@
+
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#elif COMPILER_IS_MSVC
+#include <malloc.h>
+#define alloca _alloca
+#elif defined(__MINGW32__)
+#include <malloc.h>
+#endif
+
+#if COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+#define WABT_UNUSED __attribute__((unused))
+#define WABT_WARN_UNUSED __attribute__((warn_unused_result))
+#define WABT_INLINE inline
+#define WABT_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define WABT_LIKELY(x) __builtin_expect(!!(x), 1)
+
+#define WABT_VECTORCALL
+
+#if __MINGW32__
+// mingw defaults to printf format specifier being ms_printf (which doesn't
+// understand 'llu', etc.) We always want gnu_printf, and force mingw to always
+// use mingw_printf, mingw_vprintf, etc.
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+ __attribute__((format(gnu_printf, (format_arg), (first_arg))))
+#else
+#define WABT_PRINTF_FORMAT(format_arg, first_arg) \
+ __attribute__((format(printf, (format_arg), (first_arg))))
+#endif
+
+#ifdef __cplusplus
+#if __cplusplus >= 201103L
+#define WABT_STATIC_ASSERT(x) static_assert((x), #x)
+#else
+#define WABT_STATIC_ASSERT__(x, c) \
+ static int static_assert_##c[(x ? 0 : -1)] WABT_UNUSED
+#define WABT_STATIC_ASSERT_(x, c) WABT_STATIC_ASSERT__(x, c)
+#define WABT_STATIC_ASSERT(x) WABT_STATIC_ASSERT_(x, __COUNTER__)
+#endif
+#else
+#define WABT_STATIC_ASSERT(x) _Static_assert((x), #x)
+#endif
+
+#elif COMPILER_IS_MSVC
+
+#include <intrin.h>
+#include <string.h>
+
+#define WABT_UNUSED
+#define WABT_WARN_UNUSED
+#define WABT_INLINE __inline
+#define WABT_STATIC_ASSERT(x) _STATIC_ASSERT(x)
+#define WABT_UNLIKELY(x) (x)
+#define WABT_LIKELY(x) (x)
+#define WABT_PRINTF_FORMAT(format_arg, first_arg)
+
+#define WABT_VECTORCALL __vectorcall
+
+#else
+
+#error unknown compiler
+
+#endif
+
+#define WABT_UNREACHABLE abort()
+
+#ifdef __cplusplus
+
+namespace wabt {
+
+#if COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+inline int Clz(unsigned x) { return x ? __builtin_clz(x) : sizeof(x) * 8; }
+inline int Clz(unsigned long x) { return x ? __builtin_clzl(x) : sizeof(x) * 8; }
+inline int Clz(unsigned long long x) { return x ? __builtin_clzll(x) : sizeof(x) * 8; }
+
+inline int Ctz(unsigned x) { return x ? __builtin_ctz(x) : sizeof(x) * 8; }
+inline int Ctz(unsigned long x) { return x ? __builtin_ctzl(x) : sizeof(x) * 8; }
+inline int Ctz(unsigned long long x) { return x ? __builtin_ctzll(x) : sizeof(x) * 8; }
+
+inline int Popcount(uint8_t x) { return __builtin_popcount(x); }
+inline int Popcount(unsigned x) { return __builtin_popcount(x); }
+inline int Popcount(unsigned long x) { return __builtin_popcountl(x); }
+inline int Popcount(unsigned long long x) { return __builtin_popcountll(x); }
+
+#elif COMPILER_IS_MSVC
+
+#if _M_IX86
+inline unsigned long LowDword(unsigned __int64 value) {
+ return (unsigned long)value;
+}
+
+inline unsigned long HighDword(unsigned __int64 value) {
+ unsigned long high;
+ memcpy(&high, (unsigned char*)&value + sizeof(high), sizeof(high));
+ return high;
+}
+#endif
+
+inline int Clz(unsigned long mask) {
+ if (mask == 0)
+ return 32;
+
+ unsigned long index;
+ _BitScanReverse(&index, mask);
+ return sizeof(unsigned long) * 8 - (index + 1);
+}
+
+inline int Clz(unsigned int mask) {
+ return Clz((unsigned long)mask);
+}
+
+inline int Clz(unsigned __int64 mask) {
+#if _M_X64
+ if (mask == 0)
+ return 64;
+
+ unsigned long index;
+ _BitScanReverse64(&index, mask);
+ return sizeof(unsigned __int64) * 8 - (index + 1);
+#elif _M_IX86
+ int result = Clz(HighDword(mask));
+ if (result == 32)
+ result += Clz(LowDword(mask));
+
+ return result;
+#else
+#error unexpected architecture
+#endif
+}
+
+inline int Ctz(unsigned long mask) {
+ if (mask == 0)
+ return 32;
+
+ unsigned long index;
+ _BitScanForward(&index, mask);
+ return index;
+}
+
+inline int Ctz(unsigned int mask) {
+ return Ctz((unsigned long)mask);
+}
+
+inline int Ctz(unsigned __int64 mask) {
+#if _M_X64
+ if (mask == 0)
+ return 64;
+
+ unsigned long index;
+ _BitScanForward64(&index, mask);
+ return index;
+#elif _M_IX86
+ int result = Ctz(LowDword(mask));
+ if (result == 32)
+ result += Ctz(HighDword(mask));
+
+ return result;
+#else
+#error unexpected architecture
+#endif
+}
+
+inline int Popcount(uint8_t value) {
+ return __popcnt(value);
+}
+
+inline int Popcount(unsigned long value) {
+ return __popcnt(value);
+}
+
+inline int Popcount(unsigned int value) {
+ return Popcount((unsigned long)value);
+}
+
+inline int Popcount(unsigned __int64 value) {
+#if _M_X64
+ return __popcnt64(value);
+#elif _M_IX86
+ return Popcount(HighDword(value)) + Popcount(LowDword(value));
+#else
+#error unexpected architecture
+#endif
+}
+
+#else
+
+#error unknown compiler
+
+#endif
+
+} // namespace wabt
+
+#if COMPILER_IS_MSVC
+
+/* print format specifier for size_t */
+#if SIZEOF_SIZE_T == 4
+#define PRIzd "d"
+#define PRIzx "x"
+#elif SIZEOF_SIZE_T == 8
+#define PRIzd "I64d"
+#define PRIzx "I64x"
+#else
+#error "weird sizeof size_t"
+#endif
+
+#elif COMPILER_IS_CLANG || COMPILER_IS_GNU
+
+/* print format specifier for size_t */
+#define PRIzd "zd"
+#define PRIzx "zx"
+
+#else
+
+#error unknown compiler
+
+#endif
+
+#if HAVE_SNPRINTF
+#define wabt_snprintf snprintf
+#elif COMPILER_IS_MSVC
+/* can't just use _snprintf because it doesn't always null terminate */
+#include <cstdarg>
+int wabt_snprintf(char* str, size_t size, const char* format, ...);
+#else
+#error no snprintf
+#endif
+
+#if COMPILER_IS_MSVC
+/* can't just use vsnprintf because it doesn't always null terminate */
+int wabt_vsnprintf(char* str, size_t size, const char* format, va_list ap);
+#else
+#define wabt_vsnprintf vsnprintf
+#endif
+
+#if !HAVE_SSIZE_T
+#if COMPILER_IS_MSVC
+/* define ssize_t identically to how LLVM does, to avoid conflicts if including both */
+#if defined(_WIN64)
+typedef signed __int64 ssize_t;
+#else
+typedef signed int ssize_t;
+#endif /* _WIN64 */
+#else
+typedef long ssize_t;
+#endif
+#endif
+
+#if !HAVE_STRCASECMP
+#if COMPILER_IS_MSVC
+#define strcasecmp _stricmp
+#else
+#error no strcasecmp
+#endif
+#endif
+
+double wabt_convert_uint64_to_double(uint64_t x);
+float wabt_convert_uint64_to_float(uint64_t x);
+double wabt_convert_int64_to_double(int64_t x);
+float wabt_convert_int64_to_float(int64_t x);
+
+#endif // __cplusplus
+
+#endif /* WABT_CONFIG_H_ */
diff --git a/third_party/wasm2c/src/decompiler-ast.h b/third_party/wasm2c/src/decompiler-ast.h
new file mode 100644
index 0000000000..ec5b3880ca
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler-ast.h
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_DECOMPILER_AST_H_
+#define WABT_DECOMPILER_AST_H_
+
+#include "src/cast.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/ir-util.h"
+
+#include <map>
+
+namespace wabt {
+
+enum class NodeType {
+ Uninitialized,
+ FlushToVars,
+ FlushedVar,
+ Statements,
+ EndReturn,
+ Decl,
+ DeclInit,
+ Expr
+};
+
+// The AST we're going to convert the standard IR into.
+struct Node {
+ NodeType ntype;
+ ExprType etype; // Only if ntype == Expr.
+ const Expr* e;
+ std::vector<Node> children;
+ // Node specific annotations.
+ union {
+ struct { Index var_start, var_count; }; // FlushedVar/FlushToVars
+ const Var* var; // Decl/DeclInit.
+ LabelType lt; // br/br_if target.
+ } u;
+
+ Node() : ntype(NodeType::Uninitialized), etype(ExprType::Nop), e(nullptr) {
+ }
+ Node(NodeType ntype, ExprType etype, const Expr* e, const Var* v)
+ : ntype(ntype), etype(etype), e(e) {
+ u.var = v;
+ }
+
+ // This value should really never be copied, only moved.
+ Node(const Node& rhs) = delete;
+ Node& operator=(const Node& rhs) = delete;
+
+ Node(Node&& rhs) { *this = std::move(rhs); }
+ Node& operator=(Node&& rhs) {
+ ntype = rhs.ntype;
+ // Reset ntype to avoid moved from values still being used.
+ rhs.ntype = NodeType::Uninitialized;
+ etype = rhs.etype;
+ rhs.etype = ExprType::Nop;
+ e = rhs.e;
+ std::swap(children, rhs.children);
+ u = rhs.u;
+ return *this;
+ }
+};
+
+struct AST {
+ AST(ModuleContext& mc, const Func *f) : mc(mc), f(f) {
+ if (f) {
+ mc.BeginFunc(*f);
+ for (Index i = 0; i < f->GetNumParams(); i++) {
+ auto name = "$" + IndexToAlphaName(i);
+ vars_defined.insert({ name, { 0, false }});
+ }
+ }
+ }
+
+ ~AST() {
+ if (f) mc.EndFunc();
+ }
+
+ // Create a new node, take nargs existing nodes on the exp stack as children.
+ Node& InsertNode(NodeType ntype, ExprType etype, const Expr* e, Index nargs) {
+ assert(exp_stack.size() >= nargs);
+ Node n { ntype, etype, e, nullptr };
+ n.children.reserve(nargs);
+ std::move(exp_stack.end() - nargs, exp_stack.end(),
+ std::back_inserter(n.children));
+ exp_stack.erase(exp_stack.end() - nargs, exp_stack.end());
+ exp_stack.push_back(std::move(n));
+ return exp_stack.back();
+ }
+
+ template<ExprType T> void PreDecl(const VarExpr<T>& ve) {
+ // FIXME: this is slow, and would be better to avoid in callers.
+ // See https://github.com/WebAssembly/wabt/issues/1565
+ // And https://github.com/WebAssembly/wabt/issues/1665
+ for (auto& n : predecls) {
+ if (n.u.var->name() == ve.var.name()) {
+ return;
+ }
+ }
+ predecls.emplace_back(NodeType::Decl, ExprType::Nop, nullptr, &ve.var);
+ }
+
+ template<ExprType T> void Get(const VarExpr<T>& ve, bool local) {
+ if (local) {
+ auto ret = vars_defined.insert({ ve.var.name(), { cur_block_id, false }});
+ if (ret.second) {
+ // Use before def, may happen since locals are guaranteed 0.
+ PreDecl(ve);
+ } else if (blocks_closed[ret.first->second.block_id]) {
+ // This is a use of a variable that was defined in a block that has
+ // already ended. This happens rarely, but we should cater for this
+ // case by lifting it to the top scope.
+ PreDecl(ve);
+ }
+ }
+ InsertNode(NodeType::Expr, T, &ve, 0);
+ }
+
+ template<ExprType T> void Set(const VarExpr<T>& ve, bool local) {
+ // Seen this var before?
+ if (local &&
+ vars_defined.insert({ ve.var.name(), { cur_block_id, false }}).second) {
+ if (value_stack_depth == 1) {
+ // Top level, declare it here.
+ InsertNode(NodeType::DeclInit, ExprType::Nop, nullptr, 1).u.var =
+ &ve.var;
+ return;
+ } else {
+ // Inside exp, better leave it as assignment exp and lift the decl out.
+ PreDecl(ve);
+ }
+ }
+ InsertNode(NodeType::Expr, T, &ve, 1);
+ }
+
+ template<ExprType T> void Block(const BlockExprBase<T>& be, LabelType label) {
+ mc.BeginBlock(label, be.block);
+ Construct(be.block.exprs, be.block.decl.GetNumResults(), be.block.decl.GetNumParams(), false);
+ mc.EndBlock();
+ InsertNode(NodeType::Expr, T, &be, 1);
+ }
+
+ void Construct(const Expr& e) {
+ auto arity = mc.GetExprArity(e);
+ switch (e.type()) {
+ case ExprType::LocalGet: {
+ Get(*cast<LocalGetExpr>(&e), true);
+ return;
+ }
+ case ExprType::GlobalGet: {
+ Get(*cast<GlobalGetExpr>(&e), false);
+ return;
+ }
+ case ExprType::LocalSet: {
+ Set(*cast<LocalSetExpr>(&e), true);
+ return;
+ }
+ case ExprType::GlobalSet: {
+ Set(*cast<GlobalSetExpr>(&e), false);
+ return;
+ }
+ case ExprType::LocalTee: {
+ auto& lt = *cast<LocalTeeExpr>(&e);
+ Set(lt, true);
+ if (value_stack_depth == 1) { // Tee is the only thing on there.
+ Get(lt, true); // Now Set + Get instead.
+ } else {
+ // Things are above us on the stack so the Tee can't be eliminated.
+ // The Set makes this work as a Tee when consumed by a parent.
+ }
+ return;
+ }
+ case ExprType::If: {
+ auto ife = cast<IfExpr>(&e);
+ value_stack_depth--; // Condition.
+ mc.BeginBlock(LabelType::Block, ife->true_);
+ Construct(ife->true_.exprs, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false);
+ if (!ife->false_.empty()) {
+ Construct(ife->false_, ife->true_.decl.GetNumResults(), ife->true_.decl.GetNumParams(), false);
+ }
+ mc.EndBlock();
+ value_stack_depth++; // Put Condition back.
+ InsertNode(NodeType::Expr, ExprType::If, &e, ife->false_.empty() ? 2 : 3);
+ return;
+ }
+ case ExprType::Block: {
+ Block(*cast<BlockExpr>(&e), LabelType::Block);
+ return;
+ }
+ case ExprType::Loop: {
+ Block(*cast<LoopExpr>(&e), LabelType::Loop);
+ return;
+ }
+ case ExprType::Br: {
+ InsertNode(NodeType::Expr, ExprType::Br, &e, 0).u.lt =
+ mc.GetLabel(cast<BrExpr>(&e)->var)->label_type;
+ return;
+ }
+ case ExprType::BrIf: {
+ InsertNode(NodeType::Expr, ExprType::BrIf, &e, 1).u.lt =
+ mc.GetLabel(cast<BrIfExpr>(&e)->var)->label_type;
+ return;
+ }
+ default: {
+ InsertNode(NodeType::Expr, e.type(), &e, arity.nargs);
+ return;
+ }
+ }
+ }
+
+ void Construct(const ExprList& es, Index nresults, Index nparams, bool is_function_body) {
+ block_stack.push_back(cur_block_id);
+ cur_block_id = blocks_closed.size();
+ blocks_closed.push_back(false);
+ auto start = exp_stack.size();
+ int value_stack_depth_start = value_stack_depth - nparams;
+ auto value_stack_in_variables = value_stack_depth;
+ bool unreachable = false;
+ for (auto& e : es) {
+ Construct(e);
+ auto arity = mc.GetExprArity(e);
+ value_stack_depth -= arity.nargs;
+ value_stack_in_variables = std::min(value_stack_in_variables,
+ value_stack_depth);
+ unreachable = unreachable || arity.unreachable;
+ assert(unreachable || value_stack_depth >= value_stack_depth_start);
+ value_stack_depth += arity.nreturns;
+ // We maintain the invariant that a value_stack_depth of N is represented
+ // by the last N exp_stack items (each of which returning exactly 1
+ // value), all exp_stack items before it return void ("statements").
+ // In particular for the wasm stack:
+ // - The values between 0 and value_stack_depth_start are part of the
+ // parent block, and not touched here.
+ // - The values from there up to value_stack_in_variables are variables
+ // to be used, representing previous statements that flushed the
+ // stack into variables.
+ // - Values on top of that up to value_stack_depth are exps returning
+ // a single value.
+ // The code below maintains the above invariants. With this in place
+ // code "falls into place" the way you expect it.
+ if (arity.nreturns != 1) {
+ auto num_vars = value_stack_in_variables - value_stack_depth_start;
+ auto num_vals = value_stack_depth - value_stack_in_variables;
+ auto GenFlushVars = [&](int nargs) {
+ auto& ftv = InsertNode(NodeType::FlushToVars, ExprType::Nop, nullptr,
+ nargs);
+ ftv.u.var_start = flushed_vars;
+ ftv.u.var_count = num_vals;
+ };
+ auto MoveStatementsBelowVars = [&](size_t amount) {
+ std::rotate(exp_stack.end() - num_vars - amount,
+ exp_stack.end() - amount,
+ exp_stack.end());
+ };
+ auto GenFlushedVars = [&]() {
+ // Re-generate these values as vars.
+ for (int i = 0; i < num_vals; i++) {
+ auto& fv = InsertNode(NodeType::FlushedVar, ExprType::Nop, nullptr,
+ 0);
+ fv.u.var_start = flushed_vars++;
+ fv.u.var_count = 1;
+ }
+ };
+ if (arity.nreturns == 0 &&
+ value_stack_depth > value_stack_depth_start) {
+ // We have a void item on top of the exp_stack, so we must "lift" the
+ // previous values around it.
+ // We track what part of the stack is in variables to avoid doing
+ // this unnecessarily.
+ if (num_vals > 0) {
+ // We have actual new values that need lifting.
+ // This puts the part of the stack that wasn't already a variable
+ // (before the current void exp) into a FlushToVars.
+ auto void_exp = std::move(exp_stack.back());
+ exp_stack.pop_back();
+ GenFlushVars(num_vals);
+ exp_stack.push_back(std::move(void_exp));
+ // Put this flush statement + void statement before any
+ // existing variables.
+ MoveStatementsBelowVars(2);
+ // Now re-generate these values after the void exp as vars.
+ GenFlushedVars();
+ } else {
+ // We have existing variables that need lifting, but no need to
+ // create them anew.
+ std::rotate(exp_stack.end() - num_vars - 1, exp_stack.end() - 1,
+ exp_stack.end());
+ }
+ value_stack_in_variables = value_stack_depth;
+ } else if (arity.nreturns > 1) {
+ // Multivalue: we flush the stack also.
+ // Number of other non-variable values that may be present:
+ assert(num_vals >= static_cast<int>(arity.nreturns));
+ // Flush multi-value exp + others.
+ GenFlushVars(num_vals - arity.nreturns + 1);
+ // Put this flush statement before any existing variables.
+ MoveStatementsBelowVars(1);
+ GenFlushedVars();
+ value_stack_in_variables = value_stack_depth;
+ }
+ } else {
+ // Special optimisation: for constant instructions, we can mark these
+ // as if they were variables, so they can be re-ordered for free with
+ // the above code, without needing new variables!
+ // TODO: this would be nice to also do for get_local and maybe others,
+ // though that needs a way to ensure there's no set_local in between
+ // when they get lifted, so complicates matters a bit.
+ if (e.type() == ExprType::Const &&
+ value_stack_in_variables == value_stack_depth - 1) {
+ value_stack_in_variables++;
+ }
+ }
+ }
+ assert(unreachable ||
+ value_stack_depth - value_stack_depth_start ==
+ static_cast<int>(nresults));
+ // Undo any changes to value_stack_depth, since parent takes care of arity
+ // changes.
+ value_stack_depth = value_stack_depth_start;
+ auto end = exp_stack.size();
+ assert(end >= start);
+ if (is_function_body) {
+ if (!exp_stack.empty()) {
+ if (exp_stack.back().etype == ExprType::Return) {
+ if (exp_stack.back().children.empty()) {
+ // Return statement at the end of a void function.
+ exp_stack.pop_back();
+ }
+ } else if (nresults) {
+ // Combine nresults into a return statement, for when this is used as
+ // a function body.
+ // TODO: if this is some other kind of block and >1 value is being
+ // returned, probably need some kind of syntax to make that clearer.
+ InsertNode(NodeType::EndReturn, ExprType::Nop, nullptr, nresults);
+ }
+ }
+ // TODO: these predecls are always at top level, but in the case of
+ // use inside an exp, it be nice to do it in the current block. Can't
+ // do that for predecls that are "out if scope" however.
+ std::move(predecls.begin(), predecls.end(),
+ std::back_inserter(exp_stack));
+ std::rotate(exp_stack.begin(), exp_stack.end() - predecls.size(),
+ exp_stack.end());
+ predecls.clear();
+ }
+ end = exp_stack.size();
+ assert(end >= start);
+ auto size = end - start;
+ if (size != 1) {
+ InsertNode(NodeType::Statements, ExprType::Nop, nullptr, size);
+ }
+ blocks_closed[cur_block_id] = true;
+ cur_block_id = block_stack.back();
+ block_stack.pop_back();
+ }
+
+ ModuleContext& mc;
+ std::vector<Node> exp_stack;
+ std::vector<Node> predecls;
+ const Func *f;
+ int value_stack_depth = 0;
+ struct Variable { size_t block_id; bool defined; };
+ std::map<std::string, Variable> vars_defined;
+ Index flushed_vars = 0;
+ size_t cur_block_id = 0;
+ std::vector<size_t> block_stack;
+ std::vector<bool> blocks_closed;
+};
+
+} // namespace wabt
+
+#endif // WABT_DECOMPILER_AST_H_
diff --git a/third_party/wasm2c/src/decompiler-ls.h b/third_party/wasm2c/src/decompiler-ls.h
new file mode 100644
index 0000000000..f622e3fd9f
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler-ls.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#ifndef WABT_DECOMPILER_LS_H_
+#define WABT_DECOMPILER_LS_H_
+
+#include "src/decompiler-ast.h"
+
+#include <map>
+
+namespace wabt {
+
+// Names starting with "u" are unsigned, the rest are "signed or doesn't matter"
+inline const char *GetDecompTypeName(Type t) {
+ switch (t) {
+ case Type::I8: return "byte";
+ case Type::I8U: return "ubyte";
+ case Type::I16: return "short";
+ case Type::I16U: return "ushort";
+ case Type::I32: return "int";
+ case Type::I32U: return "uint";
+ case Type::I64: return "long";
+ case Type::F32: return "float";
+ case Type::F64: return "double";
+ case Type::V128: return "simd";
+ case Type::Func: return "func";
+ case Type::FuncRef: return "funcref";
+ case Type::ExternRef: return "externref";
+ case Type::Void: return "void";
+ default: return "ILLEGAL";
+ }
+}
+
+inline Type GetMemoryType(Type operand_type, Opcode opc) {
+ // TODO: something something SIMD.
+ // TODO: this loses information of the type it is read into.
+ // That may well not be the biggest deal since that is usually obvious
+ // from context, if not, we should probably represent that as a cast around
+ // the access, since it should not be part of the field type.
+ if (operand_type == Type::I32 || operand_type == Type::I64) {
+ auto name = string_view(opc.GetName());
+ // FIXME: change into a new column in opcode.def instead?
+ auto is_unsigned = name.substr(name.size() - 2) == "_u";
+ switch (opc.GetMemorySize()) {
+ case 1: return is_unsigned ? Type::I8U : Type::I8;
+ case 2: return is_unsigned ? Type::I16U : Type::I16;
+ case 4: return is_unsigned ? Type::I32U : Type::I32;
+ }
+ }
+ return operand_type;
+}
+
+// Track all loads and stores inside a single function, to be able to detect
+// struct layouts we can use to annotate variables with, to make code more
+// readable.
+struct LoadStoreTracking {
+ struct LSAccess {
+ Address byte_size = 0;
+ Type type = Type::Any;
+ Address align = 0;
+ uint32_t idx = 0;
+ bool is_uniform = true;
+ };
+
+ struct LSVar {
+ std::map<uint64_t, LSAccess> accesses;
+ bool struct_layout = true;
+ Type same_type = Type::Any;
+ Address same_align = kInvalidAddress;
+ Opcode last_opc;
+ };
+
+ void Track(const Node& n) {
+ for (auto& c : n.children) Track(c);
+ switch (n.etype) {
+ case ExprType::Load: {
+ auto& le = *cast<LoadExpr>(n.e);
+ LoadStore(le.offset, le.opcode, le.opcode.GetResultType(),
+ le.align, n.children[0]);
+ break;
+ }
+ case ExprType::Store: {
+ auto& se = *cast<StoreExpr>(n.e);
+ LoadStore(se.offset, se.opcode, se.opcode.GetParamType2(),
+ se.align, n.children[0]);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ const std::string AddrExpName(const Node& addr_exp) const {
+ // TODO: expand this to more kinds of address expressions.
+ switch (addr_exp.etype) {
+ case ExprType::LocalGet:
+ return cast<LocalGetExpr>(addr_exp.e)->var.name();
+ break;
+ case ExprType::LocalTee:
+ return cast<LocalTeeExpr>(addr_exp.e)->var.name();
+ break;
+ default:
+ return "";
+ }
+ }
+
+ void LoadStore(uint64_t offset, Opcode opc, Type type, Address align,
+ const Node& addr_exp) {
+ auto byte_size = opc.GetMemorySize();
+ type = GetMemoryType(type, opc);
+ // We want to associate memory ops of a certain offset & size as being
+ // relative to a uniquely identifiable pointer, such as a local.
+ auto name = AddrExpName(addr_exp);
+ if (name.empty()) {
+ return;
+ }
+ auto& var = vars[name];
+ auto& access = var.accesses[offset];
+ // Check if previous access at this offset (if any) is of same size
+ // and type (see Checklayouts below).
+ if (access.byte_size &&
+ ((access.byte_size != byte_size) ||
+ (access.type != type) ||
+ (access.align != align)))
+ access.is_uniform = false;
+ // Also exclude weird alignment accesses from structs.
+ if (!opc.IsNaturallyAligned(align))
+ access.is_uniform = false;
+ access.byte_size = byte_size;
+ access.type = type;
+ access.align = align;
+ // Additionally, check if all accesses are to the same type, so
+ // if layout check fails, we can at least declare it as pointer to
+ // a type.
+ if ((var.same_type == type || var.same_type == Type::Any) &&
+ (var.same_align == align || var.same_align == kInvalidAddress)) {
+ var.same_type = type;
+ var.same_align = align;
+ var.last_opc = opc;
+ } else {
+ var.same_type = Type::Void;
+ var.same_align = kInvalidAddress;
+ }
+ }
+
+ void CheckLayouts() {
+ // Here we check if the set of accesses we have collected form a sequence
+ // we could declare as a struct, meaning they are properly aligned,
+ // contiguous, and have no overlaps between different types and sizes.
+ // We do this because an int access of size 2 at offset 0 followed by
+ // a float access of size 4 at offset 4 can compactly represented as a
+ // struct { short, float }, whereas something that reads from overlapping
+ // or discontinuous offsets would need a more complicated syntax that
+ // involves explicit offsets.
+ // We assume that the bulk of memory accesses are of this very regular kind,
+ // so we choose not to even emit struct layouts for irregular ones,
+ // given that they are rare and confusing, and thus do not benefit from
+ // being represented as if they were structs.
+ for (auto& var : vars) {
+ if (var.second.accesses.size() == 1) {
+ // If we have just one access, this is better represented as a pointer
+ // than a struct.
+ var.second.struct_layout = false;
+ continue;
+ }
+ uint64_t cur_offset = 0;
+ uint32_t idx = 0;
+ for (auto& access : var.second.accesses) {
+ access.second.idx = idx++;
+ if (!access.second.is_uniform) {
+ var.second.struct_layout = false;
+ break;
+ }
+ // Align to next access: all elements are expected to be aligned to
+ // a memory address thats a multiple of their own size.
+ auto mask = static_cast<uint64_t>(access.second.byte_size - 1);
+ cur_offset = (cur_offset + mask) & ~mask;
+ if (cur_offset != access.first) {
+ var.second.struct_layout = false;
+ break;
+ }
+ cur_offset += access.second.byte_size;
+ }
+ }
+ }
+
+ std::string IdxToName(uint32_t idx) const {
+ return IndexToAlphaName(idx); // TODO: more descriptive names?
+ }
+
+ std::string GenAlign(Address align, Opcode opc) const {
+ return opc.IsNaturallyAligned(align)
+ ? ""
+ : cat("@", std::to_string(align));
+ }
+
+ std::string GenTypeDecl(const std::string& name) const {
+ auto it = vars.find(name);
+ if (it == vars.end()) {
+ return "";
+ }
+ if (it->second.struct_layout) {
+ std::string s = "{ ";
+ for (auto& access : it->second.accesses) {
+ if (access.second.idx) s += ", ";
+ s += IdxToName(access.second.idx);
+ s += ':';
+ s += GetDecompTypeName(access.second.type);
+ }
+ s += " }";
+ return s;
+ }
+ // We don't have a struct layout, or the struct has just one field,
+ // so maybe we can just declare it as a pointer to one type?
+ if (it->second.same_type != Type::Void) {
+ return cat(GetDecompTypeName(it->second.same_type), "_ptr",
+ GenAlign(it->second.same_align, it->second.last_opc));
+ }
+ return "";
+ }
+
+ std::string GenAccess(uint64_t offset, const Node& addr_exp) const {
+ auto name = AddrExpName(addr_exp);
+ if (name.empty()) {
+ return "";
+ }
+ auto it = vars.find(name);
+ if (it == vars.end()) {
+ return "";
+ }
+ if (it->second.struct_layout) {
+ auto ait = it->second.accesses.find(offset);
+ assert (ait != it->second.accesses.end());
+ return IdxToName(ait->second.idx);
+ }
+ // Not a struct, see if it is a typed pointer.
+ if (it->second.same_type != Type::Void) {
+ return "*";
+ }
+ return "";
+ }
+
+ void Clear() {
+ vars.clear();
+ }
+
+ std::map<std::string, LSVar> vars;
+};
+
+} // namespace wabt
+
+#endif // WABT_DECOMPILER_LS_H_
diff --git a/third_party/wasm2c/src/decompiler-naming.h b/third_party/wasm2c/src/decompiler-naming.h
new file mode 100644
index 0000000000..6f34831702
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler-naming.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#ifndef WABT_DECOMPILER_NAMING_H_
+#define WABT_DECOMPILER_NAMING_H_
+
+#include "src/decompiler-ast.h"
+
+#include <set>
+
+namespace wabt {
+
+inline void RenameToIdentifier(std::string& name, Index i,
+ BindingHash& bh,
+ const std::set<string_view>* filter) {
+ // Filter out non-identifier characters, and try to reduce the size of
+ // gigantic C++ signature names.
+ std::string s;
+ size_t nesting = 0;
+ size_t read = 0;
+ size_t word_start = 0;
+ for (auto c : name) {
+ read++;
+ // We most certainly don't want to parse the entirety of C++ signatures,
+ // but these names are sometimes several lines long, so would be great
+ // to trim down. One quick way to do that is to remove anything between
+ // nested (), which usually means the parameter list.
+ if (c == '(') {
+ nesting++;
+ }
+ if (c == ')') {
+ nesting--;
+ }
+ if (nesting) {
+ continue;
+ }
+ if (!isalnum(static_cast<unsigned char>(c))) {
+ c = '_';
+ }
+ if (c == '_') {
+ if (s.empty()) {
+ continue; // Skip leading.
+ }
+ if (s.back() == '_') {
+ continue; // Consecutive.
+ }
+ }
+ s += c;
+ if (filter && (c == '_' || read == name.size())) {
+ // We found a "word" inside a snake_case identifier.
+ auto word_end = s.size();
+ if (c == '_') {
+ word_end--;
+ }
+ assert(word_end > word_start);
+ auto word = string_view(s.c_str() + word_start, word_end - word_start);
+ if (filter->find(word) != filter->end()) {
+ s.resize(word_start);
+ }
+ word_start = s.size();
+ }
+ }
+ if (!s.empty() && s.back() == '_') {
+ s.pop_back(); // Trailing.
+ }
+ // If after all this culling, we're still gigantic (STL identifier can
+ // easily be hundreds of chars in size), just cut the identifier
+ // down, it will be disambiguated below, if needed.
+ const size_t max_identifier_length = 100;
+ if (s.size() > max_identifier_length) {
+ s.resize(max_identifier_length);
+ }
+ // Remove original binding first, such that it doesn't match with our
+ // new name.
+ bh.erase(name);
+ // Find a unique name.
+ Index disambiguator = 0;
+ auto base_len = s.size();
+ for (;;) {
+ if (bh.count(s) == 0) {
+ break;
+ }
+ disambiguator++;
+ s.resize(base_len);
+ s += '_';
+ s += std::to_string(disambiguator);
+ }
+ // Replace name in bindings.
+ name = s;
+ bh.emplace(s, Binding(i));
+}
+
+template<typename T>
+void RenameToIdentifiers(std::vector<T*>& things, BindingHash& bh,
+ const std::set<string_view>* filter) {
+ Index i = 0;
+ for (auto thing : things) {
+ RenameToIdentifier(thing->name, i++, bh, filter);
+ }
+}
+
+enum {
+ // This a bit arbitrary, change at will.
+ min_content_identifier_size = 7,
+ max_content_identifier_size = 30
+};
+
+void RenameToContents(std::vector<DataSegment*>& segs, BindingHash& bh) {
+ std::string s;
+ for (auto seg : segs) {
+ if (seg->name.substr(0, 2) != "d_") {
+ // This segment was named explicitly by a symbol.
+ // FIXME: this is not a great check, a symbol could start with d_.
+ continue;
+ }
+ s = "d_";
+ for (auto c : seg->data) {
+ if (isalnum(c) || c == '_') {
+ s += static_cast<char>(c);
+ }
+ if (s.size() >= max_content_identifier_size) {
+ // We truncate any very long names, since those make for hard to
+ // format output. They can be somewhat long though, since data segment
+ // references tend to not occur that often.
+ break;
+ }
+ }
+ if (s.size() < min_content_identifier_size) {
+ // It is useful to have a minimum, since if there few printable characters
+ // in a data section, that is probably a sign of binary, and those few
+ // characters are not going to be very significant.
+ continue;
+ }
+ // We could do the same disambiguition as RenameToIdentifier and
+ // GenerateNames do, but if we come up with a clashing name here it is
+ // likely a sign of not very meaningful binary data, so it is easier to
+ // just keep the original generated name in that case.
+ if (bh.count(s) != 0) {
+ continue;
+ }
+ // Remove original entry.
+ bh.erase(seg->name);
+ seg->name = s;
+ bh.emplace(s, Binding(static_cast<Index>(&seg - &segs[0])));
+ }
+}
+
+// Function names may contain arbitrary C++ syntax, so we want to
+// filter those to look like identifiers. A function name may be set
+// by a name section (applied in ReadBinaryIr, called before this function)
+// or by an export (applied by GenerateNames, called before this function),
+// to both the Func and func_bindings.
+// Those names then further perculate down the IR in ApplyNames (called after
+// this function).
+// To not have to add too many decompiler-specific code into those systems
+// (using a callback??) we instead rename everything here.
+// Also do data section renaming here.
+void RenameAll(Module& module) {
+ // We also filter common C++ keywords/STL idents that make for huge
+ // identifiers.
+ // FIXME: this can obviously give bad results if the input is not C++..
+ std::set<string_view> filter = {
+ { "const" },
+ { "std" },
+ { "allocator" },
+ { "char" },
+ { "basic" },
+ { "traits" },
+ { "wchar" },
+ { "t" },
+ { "void" },
+ { "int" },
+ { "unsigned" },
+ { "2" },
+ { "cxxabiv1" },
+ { "short" },
+ { "4096ul" },
+ };
+ RenameToIdentifiers(module.funcs, module.func_bindings, &filter);
+ // Also do this for some other kinds of names, but without the keyword
+ // substitution.
+ RenameToIdentifiers(module.globals, module.global_bindings, nullptr);
+ RenameToIdentifiers(module.tables, module.table_bindings, nullptr);
+ RenameToIdentifiers(module.tags, module.tag_bindings, nullptr);
+ RenameToIdentifiers(module.exports, module.export_bindings, nullptr);
+ RenameToIdentifiers(module.types, module.type_bindings, nullptr);
+ RenameToIdentifiers(module.memories, module.memory_bindings, nullptr);
+ RenameToIdentifiers(module.data_segments, module.data_segment_bindings,
+ nullptr);
+ RenameToIdentifiers(module.elem_segments, module.elem_segment_bindings,
+ nullptr);
+ // Special purpose naming for data segments.
+ RenameToContents(module.data_segments, module.data_segment_bindings);
+}
+
+} // namespace wabt
+
+#endif // WABT_DECOMPILER_NAMING_H_
diff --git a/third_party/wasm2c/src/decompiler.cc b/third_party/wasm2c/src/decompiler.cc
new file mode 100644
index 0000000000..25fd75a1bb
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler.cc
@@ -0,0 +1,832 @@
+/*
+ * Copyright 2019 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/decompiler.h"
+
+#include "src/decompiler-ast.h"
+#include "src/decompiler-ls.h"
+#include "src/decompiler-naming.h"
+
+#include "src/stream.h"
+
+#define WABT_TRACING 0
+#include "src/tracing.h"
+
+#include <inttypes.h>
+
+namespace wabt {
+
+struct Decompiler {
+ Decompiler(const Module& module, const DecompileOptions& options)
+ : mc(module), options(options) {}
+
+ // Sorted such that "greater precedence" is also the bigger enum val.
+ enum Precedence {
+ // Low precedence.
+ None, // precedence doesn't matter, since never nested.
+ Assign, // =
+ OtherBin, // min
+ Bit, // & |
+ Equal, // == != < > >= <=
+ Shift, // << >>
+ Add, // + -
+ Multiply, // * / %
+ If, // if{}
+ Indexing, // []
+ Atomic, // (), a, 1, a()
+ // High precedence.
+ };
+
+ // Anything besides these will get parentheses if used with equal precedence,
+ // for clarity.
+ bool Associative(Precedence p) {
+ return p == Precedence::Add || p == Precedence::Multiply;
+ }
+
+ struct Value {
+ std::vector<std::string> v;
+ // Lazily add bracketing only if the parent requires it.
+ // This is the precedence level of this value, for example, if this
+ // precedence is Add, and the parent is Multiply, bracketing is needed,
+ // but not if it is the reverse.
+ Precedence precedence;
+
+ Value(std::vector<std::string>&& v, Precedence p)
+ : v(v), precedence(p) {}
+
+ size_t width() {
+ size_t w = 0;
+ for (auto &s : v) {
+ w = std::max(w, s.size());
+ }
+ return w;
+ }
+
+ // This value should really never be copied, only moved.
+ Value(Value&& rhs) = default;
+ Value(const Value& rhs) = delete;
+ Value& operator=(Value&& rhs) = default;
+ Value& operator=(const Value& rhs) = delete;
+ };
+
+ std::string to_string(double d) {
+ auto s = std::to_string(d);
+ // Remove redundant trailing '0's that to_string produces.
+ while (s.size() > 2 && s.back() == '0' && s[s.size() - 2] != '.')
+ s.pop_back();
+ return s;
+ }
+
+ std::string Indent(size_t amount) {
+ return std::string(amount, ' ');
+ }
+
+ std::string OpcodeToToken(Opcode opcode) {
+ std::string s = opcode.GetDecomp();
+ std::replace(s.begin(), s.end(), '.', '_');
+ return s;
+ }
+
+ void IndentValue(Value &val, size_t amount, string_view first_indent) {
+ auto indent = Indent(amount);
+ for (auto& stat : val.v) {
+ auto is = (&stat != &val.v[0] || first_indent.empty())
+ ? string_view(indent)
+ : first_indent;
+ stat.insert(0, is.data(), is.size());
+ }
+ }
+
+ Value WrapChild(Value &child, string_view prefix, string_view postfix,
+ Precedence precedence) {
+ auto width = prefix.size() + postfix.size() + child.width();
+ auto &v = child.v;
+ if (width < target_exp_width ||
+ (prefix.size() <= indent_amount && postfix.size() <= indent_amount)) {
+ if (v.size() == 1) {
+ // Fits in a single line.
+ v[0].insert(0, prefix.data(), prefix.size());
+ v[0].append(postfix.data(), postfix.size());
+ } else {
+ // Multiline, but with prefix on same line.
+ IndentValue(child, prefix.size(), prefix);
+ v.back().append(postfix.data(), postfix.size());
+ }
+ } else {
+ // Multiline with prefix on its own line.
+ IndentValue(child, indent_amount, {});
+ v.insert(v.begin(), std::string(prefix));
+ v.back().append(postfix.data(), postfix.size());
+ }
+ child.precedence = precedence;
+ return std::move(child);
+ }
+
+ void BracketIfNeeded(Value &val, Precedence parent_precedence) {
+ if (parent_precedence < val.precedence ||
+ (parent_precedence == val.precedence &&
+ Associative(parent_precedence))) return;
+ val = WrapChild(val, "(", ")", Precedence::Atomic);
+ }
+
+ Value WrapBinary(std::vector<Value>& args, string_view infix,
+ bool indent_right, Precedence precedence) {
+ assert(args.size() == 2);
+ auto& left = args[0];
+ auto& right = args[1];
+ BracketIfNeeded(left, precedence);
+ BracketIfNeeded(right, precedence);
+ auto width = infix.size() + left.width() + right.width() + 2;
+ if (width < target_exp_width && left.v.size() == 1 && right.v.size() == 1) {
+ return Value{{cat(left.v[0], " ", infix, " ", right.v[0])}, precedence};
+ } else {
+ Value bin { {}, precedence };
+ std::move(left.v.begin(), left.v.end(), std::back_inserter(bin.v));
+ bin.v.back().append(" ", 1);
+ bin.v.back().append(infix.data(), infix.size());
+ if (indent_right) IndentValue(right, indent_amount, {});
+ std::move(right.v.begin(), right.v.end(), std::back_inserter(bin.v));
+ return bin;
+ }
+ }
+
+ Value WrapNAry(std::vector<Value>& args, string_view prefix,
+ string_view postfix, Precedence precedence) {
+ size_t total_width = 0;
+ size_t max_width = 0;
+ bool multiline = false;
+ for (auto& child : args) {
+ auto w = child.width();
+ max_width = std::max(max_width, w);
+ total_width += w;
+ multiline = multiline || child.v.size() > 1;
+ }
+ if (!multiline &&
+ (total_width + prefix.size() + postfix.size() < target_exp_width ||
+ args.empty())) {
+ // Single line.
+ auto s = std::string(prefix);
+ for (auto& child : args) {
+ if (&child != &args[0])
+ s += ", ";
+ s += child.v[0];
+ }
+ s += postfix;
+ return Value{{std::move(s)}, precedence};
+ } else {
+ // Multi-line.
+ Value ml { {}, precedence };
+ auto ident_with_name = max_width + prefix.size() < target_exp_width;
+ size_t i = 0;
+ for (auto& child : args) {
+ IndentValue(child, ident_with_name ? prefix.size() : indent_amount,
+ !i && ident_with_name ? prefix : string_view {});
+ if (i < args.size() - 1) child.v.back() += ",";
+ std::move(child.v.begin(), child.v.end(),
+ std::back_inserter(ml.v));
+ i++;
+ }
+ if (!ident_with_name) ml.v.insert(ml.v.begin(), std::string(prefix));
+ ml.v.back() += postfix;
+ return ml;
+ }
+ }
+
+ string_view VarName(string_view name) {
+ assert(!name.empty());
+ return name[0] == '$' ? name.substr(1) : name;
+ }
+
+ template<ExprType T> Value Get(const VarExpr<T>& ve) {
+ return Value{{std::string(VarName(ve.var.name()))}, Precedence::Atomic};
+ }
+
+ template<ExprType T> Value Set(Value& child, const VarExpr<T>& ve) {
+ return WrapChild(child, VarName(ve.var.name()) + " = ", "", Precedence::Assign);
+ }
+
+ std::string TempVarName(Index n) {
+ // FIXME: this needs much better variable naming. Problem is, the code
+ // in generate-names.cc has allready run, its dictionaries deleted, so it
+ // is not easy to integrate with it.
+ return "t" + std::to_string(n);
+ }
+
+ std::string LocalDecl(const std::string& name, Type t) {
+ auto struc = lst.GenTypeDecl(name);
+ return cat(VarName(name), ":",
+ struc.empty() ? GetDecompTypeName(t) : struc);
+ }
+
+ bool ConstIntVal(const Expr* e, uint64_t &dest) {
+ dest = 0;
+ if (!e || e->type() != ExprType::Const) return false;
+ auto& c = cast<ConstExpr>(e)->const_;
+ if (c.type() != Type::I32 && c.type() != Type::I64) return false;
+ dest = c.type() == Type::I32 ? c.u32() : c.u64();
+ return true;
+ }
+
+ void LoadStore(Value &val, const Node& addr_exp, uint64_t offset,
+ Opcode opc, Address align, Type op_type) {
+ bool append_type = true;
+ auto access = lst.GenAccess(offset, addr_exp);
+ if (!access.empty()) {
+ if (access == "*") {
+ // The variable was declared as a typed pointer, so this access
+ // doesn't need a type.
+ append_type = false;
+ } else {
+ // We can do this load/store as a struct access.
+ BracketIfNeeded(val, Precedence::Indexing);
+ val.v.back() += "." + access;
+ return;
+ }
+ }
+ // Detect absolute addressing, which we try to turn into references to the
+ // data section when possible.
+ uint64_t abs_base;
+ if (ConstIntVal(addr_exp.e, abs_base)) {
+ // We don't care what part of the absolute address was stored where,
+ // 1[0] and 0[1] are the same.
+ abs_base += offset;
+ // FIXME: make this less expensive with a binary search or whatever.
+ for (auto dat : mc.module.data_segments) {
+ uint64_t dat_base;
+ if (dat->offset.size() == 1 &&
+ ConstIntVal(&dat->offset.front(), dat_base) &&
+ abs_base >= dat_base &&
+ abs_base < dat_base + dat->data.size()) {
+ // We are inside the range of this data segment!
+ // Turn expression into data_name[index]
+ val = Value { { dat->name }, Precedence::Atomic };
+ // The new offset is from the start of the data segment, instead of
+ // whatever it was.. this may be a different value from both the
+ // original const and offset!
+ offset = abs_base - dat_base;
+ }
+ }
+ }
+ // Do the load/store as a generalized indexing operation.
+ // The offset is divisible by the alignment in 99.99% of
+ // cases, but the spec doesn't guarantee it, so we must
+ // have a backup syntax.
+ auto index = offset % align == 0
+ ? std::to_string(offset / align)
+ : cat(std::to_string(offset), "@", std::to_string(align));
+ // Detect the very common case of (base + (index << 2))[0]:int etc.
+ // so we can instead do base[index]:int
+ // TODO: (index << 2) on the left of + occurs also.
+ // TODO: sadly this does not address cases where the shift amount > align.
+ // (which happens for arrays of structs or arrays of long (with align=4)).
+ // TODO: also very common is (v = base + (index << 2))[0]:int
+ if (addr_exp.etype == ExprType::Binary) {
+ auto& pe = *cast<BinaryExpr>(addr_exp.e);
+ auto& shift_exp = addr_exp.children[1];
+ if (pe.opcode == Opcode::I32Add &&
+ shift_exp.etype == ExprType::Binary) {
+ auto& se = *cast<BinaryExpr>(shift_exp.e);
+ auto& const_exp = shift_exp.children[1];
+ if (se.opcode == Opcode::I32Shl &&
+ const_exp.etype == ExprType::Const) {
+ auto& ce = *cast<ConstExpr>(const_exp.e);
+ if (ce.const_.type() == Type::I32 &&
+ (1ULL << ce.const_.u32()) == align) {
+ // Pfew, case detected :( Lets re-write this in Haskell.
+ // TODO: we're decompiling these twice.
+ // The thing to the left of << is going to be part of the index.
+ auto ival = DecompileExpr(shift_exp.children[0], &shift_exp);
+ if (ival.v.size() == 1) { // Don't bother if huge.
+ if (offset == 0) {
+ index = ival.v[0];
+ } else {
+ BracketIfNeeded(ival, Precedence::Add);
+ index = cat(ival.v[0], " + ", index);
+ }
+ // We're going to use the thing to the left of + as the new
+ // base address:
+ val = DecompileExpr(addr_exp.children[0], &addr_exp);
+ }
+ }
+ }
+ }
+ }
+ BracketIfNeeded(val, Precedence::Indexing);
+ val.v.back() += cat("[", index, "]");
+ if (append_type) {
+ val.v.back() +=
+ cat(":", GetDecompTypeName(GetMemoryType(op_type, opc)),
+ lst.GenAlign(align, opc));
+ }
+ val.precedence = Precedence::Indexing;
+ }
+
+ Value DecompileExpr(const Node& n, const Node* parent) {
+ std::vector<Value> args;
+ for (auto& c : n.children) {
+ args.push_back(DecompileExpr(c, &n));
+ }
+ // First deal with the specialized node types.
+ switch (n.ntype) {
+ case NodeType::FlushToVars: {
+ std::string decls = "let ";
+ for (Index i = 0; i < n.u.var_count; i++) {
+ if (i) decls += ", ";
+ decls += TempVarName(n.u.var_start + i);
+ }
+ decls += " = ";
+ return WrapNAry(args, decls, "", Precedence::Assign);
+ }
+ case NodeType::FlushedVar: {
+ return Value { { TempVarName(n.u.var_start) }, Precedence::Atomic };
+ }
+ case NodeType::Statements: {
+ Value stats { {}, Precedence::None };
+ for (size_t i = 0; i < n.children.size(); i++) {
+ auto& s = args[i].v.back();
+ if (s.back() != '}' && s.back() != ':') s += ';';
+ std::move(args[i].v.begin(), args[i].v.end(),
+ std::back_inserter(stats.v));
+ }
+ return stats;
+ }
+ case NodeType::EndReturn: {
+ return WrapNAry(args, "return ", "", Precedence::None);
+ }
+ case NodeType::Decl: {
+ cur_ast->vars_defined[n.u.var->name()].defined = true;
+ return Value{
+ {"var " + LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var))},
+ Precedence::None};
+ }
+ case NodeType::DeclInit: {
+ if (cur_ast->vars_defined[n.u.var->name()].defined) {
+ // This has already been pre-declared, output as assign.
+ return WrapChild(args[0], cat(VarName(n.u.var->name()), " = "), "",
+ Precedence::None);
+ } else {
+ return WrapChild(
+ args[0],
+ cat("var ",
+ LocalDecl(std::string(n.u.var->name()),
+ cur_func->GetLocalType(*n.u.var)),
+ " = "),
+ "", Precedence::None);
+ }
+ }
+ case NodeType::Expr:
+ // We're going to fall thru to the second switch to deal with ExprType.
+ break;
+ case NodeType::Uninitialized:
+ assert(false);
+ break;
+ }
+ // Existing ExprTypes.
+ switch (n.etype) {
+ case ExprType::Const: {
+ auto& c = cast<ConstExpr>(n.e)->const_;
+ switch (c.type()) {
+ case Type::I32:
+ return Value{{std::to_string(static_cast<int32_t>(c.u32()))},
+ Precedence::Atomic};
+ case Type::I64:
+ return Value{{std::to_string(static_cast<int64_t>(c.u64())) + "L"},
+ Precedence::Atomic};
+ case Type::F32: {
+ float f = Bitcast<float>(c.f32_bits());
+ return Value{{to_string(f) + "f"}, Precedence::Atomic};
+ }
+ case Type::F64: {
+ double d = Bitcast<double>(c.f64_bits());
+ return Value{{to_string(d)}, Precedence::Atomic};
+ }
+ case Type::V128:
+ return Value{{"V128"}, Precedence::Atomic}; // FIXME
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ case ExprType::LocalGet: {
+ return Get(*cast<LocalGetExpr>(n.e));
+ }
+ case ExprType::GlobalGet: {
+ return Get(*cast<GlobalGetExpr>(n.e));
+ }
+ case ExprType::LocalSet: {
+ return Set(args[0], *cast<LocalSetExpr>(n.e));
+ }
+ case ExprType::GlobalSet: {
+ return Set(args[0], *cast<GlobalSetExpr>(n.e));
+ }
+ case ExprType::LocalTee: {
+ auto& te = *cast<LocalTeeExpr>(n.e);
+ return args.empty() ? Get(te) : Set(args[0], te);
+ }
+ case ExprType::Binary: {
+ auto& be = *cast<BinaryExpr>(n.e);
+ auto opcs = OpcodeToToken(be.opcode);
+ // TODO: Is this selection better done on Opcode values directly?
+ // What if new values get added and OtherBin doesn't make sense?
+ auto prec = Precedence::OtherBin;
+ if (opcs == "*" || opcs == "/" || opcs == "%") {
+ prec = Precedence::Multiply;
+ } else if (opcs == "+" || opcs == "-") {
+ prec = Precedence::Add;
+ } else if (opcs == "&" || opcs == "|" || opcs == "^") {
+ prec = Precedence::Bit;
+ } else if (opcs == "<<" || opcs == ">>") {
+ prec = Precedence::Shift;
+ }
+ return WrapBinary(args, opcs, false, prec);
+ }
+ case ExprType::Compare: {
+ auto& ce = *cast<CompareExpr>(n.e);
+ return WrapBinary(args, OpcodeToToken(ce.opcode), false,
+ Precedence::Equal);
+ }
+ case ExprType::Unary: {
+ auto& ue = *cast<UnaryExpr>(n.e);
+ //BracketIfNeeded(stack.back());
+ // TODO: also version without () depending on precedence.
+ return WrapChild(args[0], OpcodeToToken(ue.opcode) + "(", ")",
+ Precedence::Atomic);
+ }
+ case ExprType::Load: {
+ auto& le = *cast<LoadExpr>(n.e);
+ LoadStore(args[0], n.children[0], le.offset, le.opcode, le.align,
+ le.opcode.GetResultType());
+ return std::move(args[0]);
+ }
+ case ExprType::Store: {
+ auto& se = *cast<StoreExpr>(n.e);
+ LoadStore(args[0], n.children[0], se.offset, se.opcode, se.align,
+ se.opcode.GetParamType2());
+ return WrapBinary(args, "=", true, Precedence::Assign);
+ }
+ case ExprType::If: {
+ auto ife = cast<IfExpr>(n.e);
+ Value *elsep = nullptr;
+ if (!ife->false_.empty()) {
+ elsep = &args[2];
+ }
+ auto& thenp = args[1];
+ auto& ifs = args[0];
+ bool multiline = ifs.v.size() > 1 || thenp.v.size() > 1;
+ size_t width = ifs.width() + thenp.width();
+ if (elsep) {
+ width += elsep->width();
+ multiline = multiline || elsep->v.size() > 1;
+ }
+ multiline = multiline || width > target_exp_width;
+ if (multiline) {
+ auto if_start = string_view("if (");
+ IndentValue(ifs, if_start.size(), if_start);
+ ifs.v.back() += ") {";
+ IndentValue(thenp, indent_amount, {});
+ std::move(thenp.v.begin(), thenp.v.end(), std::back_inserter(ifs.v));
+ if (elsep) {
+ ifs.v.push_back("} else {");
+ IndentValue(*elsep, indent_amount, {});
+ std::move(elsep->v.begin(), elsep->v.end(), std::back_inserter(ifs.v));
+ }
+ ifs.v.push_back("}");
+ ifs.precedence = Precedence::If;
+ return std::move(ifs);
+ } else {
+ auto s = cat("if (", ifs.v[0], ") { ", thenp.v[0], " }");
+ if (elsep)
+ s += cat(" else { ", elsep->v[0], " }");
+ return Value{{std::move(s)}, Precedence::If};
+ }
+ }
+ case ExprType::Block: {
+ auto& val = args[0];
+ val.v.push_back(
+ cat("label ", VarName(cast<BlockExpr>(n.e)->block.label), ":"));
+ // If this block is part of a larger statement scope, it doesn't
+ // need its own indenting, but if its part of an exp we wrap it in {}.
+ if (parent && parent->ntype != NodeType::Statements
+ && parent->etype != ExprType::Block
+ && parent->etype != ExprType::Loop
+ && (parent->etype != ExprType::If ||
+ &parent->children[0] == &n)) {
+ IndentValue(val, indent_amount, {});
+ val.v.insert(val.v.begin(), "{");
+ val.v.push_back("}");
+ }
+ val.precedence = Precedence::Atomic;
+ return std::move(val);
+ }
+ case ExprType::Loop: {
+ auto& val = args[0];
+ auto& block = cast<LoopExpr>(n.e)->block;
+ IndentValue(val, indent_amount, {});
+ val.v.insert(val.v.begin(), cat("loop ", VarName(block.label), " {"));
+ val.v.push_back("}");
+ val.precedence = Precedence::Atomic;
+ return std::move(val);
+ }
+ case ExprType::Br: {
+ auto be = cast<BrExpr>(n.e);
+ return Value{{(n.u.lt == LabelType::Loop ? "continue " : "goto ") +
+ VarName(be->var.name())},
+ Precedence::None};
+ }
+ case ExprType::BrIf: {
+ auto bie = cast<BrIfExpr>(n.e);
+ auto jmp = n.u.lt == LabelType::Loop ? "continue" : "goto";
+ return WrapChild(args[0], "if (", cat(") ", jmp, " ",
+ VarName(bie->var.name())),
+ Precedence::None);
+ }
+ case ExprType::Return: {
+ return WrapNAry(args, "return ", "", Precedence::None);
+ }
+ case ExprType::Rethrow: {
+ return WrapNAry(args, "rethrow ", "", Precedence::None);
+ }
+ case ExprType::Drop: {
+ // Silent dropping of return values is very common, so currently
+ // don't output this.
+ return std::move(args[0]);
+ }
+ case ExprType::Nop: {
+ return Value{{"nop"}, Precedence::None};
+ }
+ case ExprType::Unreachable: {
+ return Value{{"unreachable"}, Precedence::None};
+ }
+ case ExprType::RefNull: {
+ return Value{{"null"}, Precedence::Atomic};
+ }
+ case ExprType::BrTable: {
+ auto bte = cast<BrTableExpr>(n.e);
+ std::string ts = "br_table[";
+ for (auto &v : bte->targets) {
+ ts += VarName(v.name());
+ ts += ", ";
+ }
+ ts += "..";
+ ts += VarName(bte->default_target.name());
+ ts += "](";
+ return WrapChild(args[0], ts, ")", Precedence::Atomic);
+ }
+ default: {
+ // Everything that looks like a function call.
+ std::string name;
+ auto precedence = Precedence::Atomic;
+ switch (n.etype) {
+ case ExprType::Call:
+ name = cast<CallExpr>(n.e)->var.name();
+ break;
+ case ExprType::ReturnCall:
+ name = "return_call " + cast<ReturnCallExpr>(n.e)->var.name();
+ precedence = Precedence::None;
+ break;
+ case ExprType::Convert:
+ name = std::string(OpcodeToToken(cast<ConvertExpr>(n.e)->opcode));
+ break;
+ case ExprType::Ternary:
+ name = std::string(OpcodeToToken(cast<TernaryExpr>(n.e)->opcode));
+ break;
+ case ExprType::Select:
+ // This one looks like it could be translated to "?:" style ternary,
+ // but the arguments are NOT lazy, and side effects definitely do
+ // occur in the branches. So it has no clear equivalent in C-syntax.
+ // To emphasize that all args are being evaluated in order, we
+ // leave it as a function call.
+ name = "select_if";
+ break;
+ case ExprType::MemoryGrow:
+ name = "memory_grow";
+ break;
+ case ExprType::MemorySize:
+ name = "memory_size";
+ break;
+ case ExprType::MemoryCopy:
+ name = "memory_copy";
+ break;
+ case ExprType::MemoryFill:
+ name = "memory_fill";
+ break;
+ case ExprType::RefIsNull:
+ name = "is_null";
+ break;
+ case ExprType::CallIndirect:
+ name = "call_indirect";
+ break;
+ case ExprType::ReturnCallIndirect:
+ name = "return_call call_indirect";
+ break;
+ default:
+ name = GetExprTypeName(n.etype);
+ break;
+ }
+ return WrapNAry(args, name + "(", ")", precedence);
+ }
+ }
+ }
+
+ bool CheckImportExport(std::string& s,
+ ExternalKind kind,
+ Index index,
+ string_view name) {
+ // Figure out if this thing is imported, exported, or neither.
+ auto is_import = mc.module.IsImport(kind, Var(index));
+ // TODO: is this the best way to check for export?
+ // FIXME: this doesn't work for functions that get renamed in some way,
+ // as the export has the original name..
+ auto xport = mc.module.GetExport(name);
+ auto is_export = xport && xport->kind == kind;
+ if (is_export)
+ s += "export ";
+ if (is_import)
+ s += "import ";
+ return is_import;
+ }
+
+ std::string InitExp(const ExprList &el) {
+ assert(!el.empty());
+ AST ast(mc, nullptr);
+ ast.Construct(el, 1, 0, false);
+ auto val = DecompileExpr(ast.exp_stack[0], nullptr);
+ assert(ast.exp_stack.size() == 1 && val.v.size() == 1);
+ return std::move(val.v[0]);
+ }
+
+ // FIXME: Merge with WatWriter::WriteQuotedData somehow.
+ std::string BinaryToString(const std::vector<uint8_t> &in) {
+ std::string s = "\"";
+ size_t line_start = 0;
+ static const char s_hexdigits[] = "0123456789abcdef";
+ for (auto c : in) {
+ if (c >= ' ' && c <= '~') {
+ s += c;
+ } else {
+ s += '\\';
+ s += s_hexdigits[c >> 4];
+ s += s_hexdigits[c & 0xf];
+ }
+ if (s.size() - line_start > target_exp_width) {
+ if (line_start == 0) {
+ s = " " + s;
+ }
+ s += "\"\n ";
+ line_start = s.size();
+ s += "\"";
+ }
+ }
+ s += '\"';
+ return s;
+ }
+
+ std::string Decompile() {
+ std::string s;
+ // Memories.
+ Index memory_index = 0;
+ for (auto m : mc.module.memories) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Memory, memory_index, m->name);
+ s += cat("memory ", m->name);
+ if (!is_import) {
+ s += cat("(initial: ", std::to_string(m->page_limits.initial),
+ ", max: ", std::to_string(m->page_limits.max), ")");
+ }
+ s += ";\n";
+ memory_index++;
+ }
+ if (!mc.module.memories.empty())
+ s += "\n";
+
+ // Globals.
+ Index global_index = 0;
+ for (auto g : mc.module.globals) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Global, global_index, g->name);
+ s += cat("global ", g->name, ":", GetDecompTypeName(g->type));
+ if (!is_import) {
+ s += cat(" = ", InitExp(g->init_expr));
+ }
+ s += ";\n";
+ global_index++;
+ }
+ if (!mc.module.globals.empty())
+ s += "\n";
+
+ // Tables.
+ Index table_index = 0;
+ for (auto tab : mc.module.tables) {
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Table, table_index, tab->name);
+ s += cat("table ", tab->name, ":", GetDecompTypeName(tab->elem_type));
+ if (!is_import) {
+ s += cat("(min: ", std::to_string(tab->elem_limits.initial),
+ ", max: ", std::to_string(tab->elem_limits.max), ")");
+ }
+ s += ";\n";
+ table_index++;
+ }
+ if (!mc.module.tables.empty())
+ s += "\n";
+
+ // Data.
+ for (auto dat : mc.module.data_segments) {
+ s += cat("data ", dat->name, "(offset: ", InitExp(dat->offset), ") =");
+ auto ds = BinaryToString(dat->data);
+ if (ds.size() > target_exp_width / 2) {
+ s += "\n";
+ } else {
+ s += " ";
+ }
+ s += ds;
+ s += ";\n";
+ }
+ if (!mc.module.data_segments.empty())
+ s += "\n";
+
+ // Code.
+ Index func_index = 0;
+ for (auto f : mc.module.funcs) {
+ cur_func = f;
+ auto is_import =
+ CheckImportExport(s, ExternalKind::Func, func_index, f->name);
+ AST ast(mc, f);
+ cur_ast = &ast;
+ if (!is_import) {
+ ast.Construct(f->exprs, f->GetNumResults(), 0, true);
+ lst.Track(ast.exp_stack[0]);
+ lst.CheckLayouts();
+ }
+ s += cat("function ", f->name, "(");
+ for (Index i = 0; i < f->GetNumParams(); i++) {
+ if (i)
+ s += ", ";
+ auto t = f->GetParamType(i);
+ auto name = "$" + IndexToAlphaName(i);
+ s += LocalDecl(name, t);
+ }
+ s += ")";
+ if (f->GetNumResults()) {
+ if (f->GetNumResults() == 1) {
+ s += cat(":", GetDecompTypeName(f->GetResultType(0)));
+ } else {
+ s += ":(";
+ for (Index i = 0; i < f->GetNumResults(); i++) {
+ if (i)
+ s += ", ";
+ s += GetDecompTypeName(f->GetResultType(i));
+ }
+ s += ")";
+ }
+ }
+ if (is_import) {
+ s += ";";
+ } else {
+ s += " {\n";
+ auto val = DecompileExpr(ast.exp_stack[0], nullptr);
+ IndentValue(val, indent_amount, {});
+ for (auto& stat : val.v) {
+ s += stat;
+ s += "\n";
+ }
+ s += "}";
+ }
+ s += "\n\n";
+ mc.EndFunc();
+ lst.Clear();
+ func_index++;
+ cur_ast = nullptr;
+ cur_func = nullptr;
+ }
+ return s;
+ }
+
+ ModuleContext mc;
+ const DecompileOptions& options;
+ size_t indent_amount = 2;
+ size_t target_exp_width = 70;
+ const Func* cur_func = nullptr;
+ AST* cur_ast = nullptr;
+ LoadStoreTracking lst;
+};
+
+std::string Decompile(const Module& module, const DecompileOptions& options) {
+ Decompiler decompiler(module, options);
+ return decompiler.Decompile();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/decompiler.h b/third_party/wasm2c/src/decompiler.h
new file mode 100644
index 0000000000..89bfe78d15
--- /dev/null
+++ b/third_party/wasm2c/src/decompiler.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_DECOMPILER_H_
+#define WABT_DECOMPILER_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+struct Module;
+class Stream;
+
+struct DecompileOptions {
+};
+
+void RenameAll(Module&);
+
+std::string Decompile(const Module&, const DecompileOptions&);
+
+} // namespace wabt
+
+#endif /* WABT_DECOMPILER_H_ */
diff --git a/third_party/wasm2c/src/emscripten-exported.json b/third_party/wasm2c/src/emscripten-exported.json
new file mode 100644
index 0000000000..a35a276ced
--- /dev/null
+++ b/third_party/wasm2c/src/emscripten-exported.json
@@ -0,0 +1,59 @@
+[
+"_free",
+"_malloc",
+"_wabt_apply_names_module",
+"_wabt_bulk_memory_enabled",
+"_wabt_destroy_errors",
+"_wabt_destroy_features",
+"_wabt_destroy_module",
+"_wabt_destroy_output_buffer",
+"_wabt_destroy_parse_wat_result",
+"_wabt_destroy_read_binary_result",
+"_wabt_destroy_wast_lexer",
+"_wabt_destroy_write_module_result",
+"_wabt_exceptions_enabled",
+"_wabt_format_binary_errors",
+"_wabt_format_text_errors",
+"_wabt_generate_names_module",
+"_wabt_multi_value_enabled",
+"_wabt_mutable_globals_enabled",
+"_wabt_new_errors",
+"_wabt_new_features",
+"_wabt_new_wast_buffer_lexer",
+"_wabt_output_buffer_get_data",
+"_wabt_output_buffer_get_size",
+"_wabt_parse_wast",
+"_wabt_parse_wast_result_get_result",
+"_wabt_parse_wast_result_release_module",
+"_wabt_parse_wat",
+"_wabt_parse_wat_result_get_result",
+"_wabt_parse_wat_result_release_module",
+"_wabt_read_binary",
+"_wabt_read_binary_result_get_result",
+"_wabt_read_binary_result_release_module",
+"_wabt_reference_types_enabled",
+"_wabt_sat_float_to_int_enabled",
+"_wabt_set_bulk_memory_enabled",
+"_wabt_set_exceptions_enabled",
+"_wabt_set_multi_value_enabled",
+"_wabt_set_mutable_globals_enabled",
+"_wabt_set_reference_types_enabled",
+"_wabt_set_sat_float_to_int_enabled",
+"_wabt_set_sign_extension_enabled",
+"_wabt_set_simd_enabled",
+"_wabt_set_tail_call_enabled",
+"_wabt_set_threads_enabled",
+"_wabt_sign_extension_enabled",
+"_wabt_simd_enabled",
+"_wabt_tail_call_enabled",
+"_wabt_threads_enabled",
+"_wabt_validate_module",
+"_wabt_validate_script",
+"_wabt_write_binary_module",
+"_wabt_write_binary_spec_script",
+"_wabt_write_module_result_get_result",
+"_wabt_write_module_result_release_log_output_buffer",
+"_wabt_write_module_result_release_output_buffer",
+"_wabt_write_text_module",
+"_dummy_workaround_for_emscripten_issue_7073"
+]
diff --git a/third_party/wasm2c/src/emscripten-helpers.cc b/third_party/wasm2c/src/emscripten-helpers.cc
new file mode 100644
index 0000000000..f7c3b4597d
--- /dev/null
+++ b/third_party/wasm2c/src/emscripten-helpers.cc
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_EMSCRIPTEN_HELPERS_H_
+#define WABT_EMSCRIPTEN_HELPERS_H_
+
+#include <cstddef>
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "src/apply-names.h"
+#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
+#include "src/binary-writer-spec.h"
+#include "src/binary-writer.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/filenames.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/wast-parser.h"
+#include "src/wat-writer.h"
+
+typedef std::unique_ptr<wabt::OutputBuffer> WabtOutputBufferPtr;
+typedef std::pair<std::string, WabtOutputBufferPtr>
+ WabtFilenameOutputBufferPair;
+
+struct WabtParseWatResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Module> module;
+};
+
+struct WabtReadBinaryResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Module> module;
+};
+
+struct WabtWriteModuleResult {
+ wabt::Result result;
+ WabtOutputBufferPtr buffer;
+ WabtOutputBufferPtr log_buffer;
+};
+
+struct WabtWriteScriptResult {
+ wabt::Result result;
+ WabtOutputBufferPtr json_buffer;
+ WabtOutputBufferPtr log_buffer;
+ std::vector<WabtFilenameOutputBufferPair> module_buffers;
+};
+
+struct WabtParseWastResult {
+ wabt::Result result;
+ std::unique_ptr<wabt::Script> script;
+};
+
+extern "C" {
+
+wabt::Features* wabt_new_features(void) {
+ return new wabt::Features();
+}
+
+void wabt_destroy_features(wabt::Features* f) {
+ delete f;
+}
+
+#define WABT_FEATURE(variable, flag, default_, help) \
+ bool wabt_##variable##_enabled(wabt::Features* f) { \
+ return f->variable##_enabled(); \
+ } \
+ void wabt_set_##variable##_enabled(wabt::Features* f, int enabled) { \
+ f->set_##variable##_enabled(enabled); \
+ }
+#include "src/feature.def"
+#undef WABT_FEATURE
+
+wabt::WastLexer* wabt_new_wast_buffer_lexer(const char* filename,
+ const void* data,
+ size_t size) {
+ std::unique_ptr<wabt::WastLexer> lexer =
+ wabt::WastLexer::CreateBufferLexer(filename, data, size);
+ return lexer.release();
+}
+
+WabtParseWatResult* wabt_parse_wat(wabt::WastLexer* lexer,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::WastParseOptions options(*features);
+ WabtParseWatResult* result = new WabtParseWatResult();
+ std::unique_ptr<wabt::Module> module;
+ result->result = wabt::ParseWatModule(lexer, &module, errors, &options);
+ result->module = std::move(module);
+ return result;
+}
+
+WabtParseWastResult* wabt_parse_wast(wabt::WastLexer* lexer,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::WastParseOptions options(*features);
+ WabtParseWastResult* result = new WabtParseWastResult();
+ std::unique_ptr<wabt::Script> script;
+ result->result = wabt::ParseWastScript(lexer, &script, errors, &options);
+ result->script = std::move(script);
+ return result;
+}
+
+WabtReadBinaryResult* wabt_read_binary(const void* data,
+ size_t size,
+ int read_debug_names,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ReadBinaryOptions options;
+ options.features = *features;
+ options.read_debug_names = read_debug_names;
+
+ WabtReadBinaryResult* result = new WabtReadBinaryResult();
+ wabt::Module* module = new wabt::Module();
+ // TODO(binji): Pass through from wabt_read_binary parameter.
+ const char* filename = "<binary>";
+ result->result =
+ wabt::ReadBinaryIr(filename, data, size, options, errors, module);
+ result->module.reset(module);
+ return result;
+}
+
+wabt::Result::Enum wabt_validate_module(wabt::Module* module,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ValidateOptions options;
+ options.features = *features;
+ return ValidateModule(module, errors, options);
+}
+
+wabt::Result::Enum wabt_validate_script(wabt::Script* script,
+ wabt::Features* features,
+ wabt::Errors* errors) {
+ wabt::ValidateOptions options;
+ options.features = *features;
+ return ValidateScript(script, errors, options);
+}
+
+WabtWriteScriptResult* wabt_write_binary_spec_script(
+ wabt::Script* script,
+ const char* source_filename,
+ const char* out_filename,
+ int log,
+ int canonicalize_lebs,
+ int relocatable,
+ int write_debug_names) {
+ wabt::MemoryStream log_stream;
+ wabt::MemoryStream* log_stream_p = log ? &log_stream : nullptr;
+
+ wabt::WriteBinaryOptions options;
+ options.canonicalize_lebs = canonicalize_lebs;
+ options.relocatable = relocatable;
+ options.write_debug_names = write_debug_names;
+
+ std::vector<wabt::FilenameMemoryStreamPair> module_streams;
+ wabt::MemoryStream json_stream(log_stream_p);
+
+ std::string module_filename_noext =
+ wabt::StripExtension(out_filename ? out_filename : source_filename)
+ .to_string();
+
+ WabtWriteScriptResult* result = new WabtWriteScriptResult();
+ result->result = WriteBinarySpecScript(&json_stream, script, source_filename,
+ module_filename_noext, options,
+ &module_streams, log_stream_p);
+
+ if (result->result == wabt::Result::Ok) {
+ result->json_buffer = json_stream.ReleaseOutputBuffer();
+ result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr;
+ std::transform(module_streams.begin(), module_streams.end(),
+ std::back_inserter(result->module_buffers),
+ [](wabt::FilenameMemoryStreamPair& pair) {
+ return WabtFilenameOutputBufferPair(
+ pair.filename, pair.stream->ReleaseOutputBuffer());
+ });
+ }
+ return result;
+}
+
+wabt::Result::Enum wabt_apply_names_module(wabt::Module* module) {
+ return ApplyNames(module);
+}
+
+wabt::Result::Enum wabt_generate_names_module(wabt::Module* module) {
+ return GenerateNames(module);
+}
+
+WabtWriteModuleResult* wabt_write_binary_module(wabt::Module* module,
+ int log,
+ int canonicalize_lebs,
+ int relocatable,
+ int write_debug_names) {
+ wabt::MemoryStream log_stream;
+ wabt::WriteBinaryOptions options;
+ options.canonicalize_lebs = canonicalize_lebs;
+ options.relocatable = relocatable;
+ options.write_debug_names = write_debug_names;
+
+ wabt::MemoryStream stream(log ? &log_stream : nullptr);
+ WabtWriteModuleResult* result = new WabtWriteModuleResult();
+ result->result = WriteBinaryModule(&stream, module, options);
+ if (result->result == wabt::Result::Ok) {
+ result->buffer = stream.ReleaseOutputBuffer();
+ result->log_buffer = log ? log_stream.ReleaseOutputBuffer() : nullptr;
+ }
+ return result;
+}
+
+WabtWriteModuleResult* wabt_write_text_module(wabt::Module* module,
+ int fold_exprs,
+ int inline_export) {
+ wabt::WriteWatOptions options;
+ options.fold_exprs = fold_exprs;
+ options.inline_export = inline_export;
+
+ wabt::MemoryStream stream;
+ WabtWriteModuleResult* result = new WabtWriteModuleResult();
+ result->result = WriteWat(&stream, module, options);
+ if (result->result == wabt::Result::Ok) {
+ result->buffer = stream.ReleaseOutputBuffer();
+ }
+ return result;
+}
+
+void wabt_destroy_module(wabt::Module* module) {
+ delete module;
+}
+
+void wabt_destroy_wast_lexer(wabt::WastLexer* lexer) {
+ delete lexer;
+}
+
+// Errors
+wabt::Errors* wabt_new_errors(void) {
+ return new wabt::Errors();
+}
+
+wabt::OutputBuffer* wabt_format_text_errors(wabt::Errors* errors,
+ wabt::WastLexer* lexer) {
+ auto line_finder = lexer->MakeLineFinder();
+ std::string string_result = FormatErrorsToString(
+ *errors, wabt::Location::Type::Text, line_finder.get());
+
+ wabt::OutputBuffer* result = new wabt::OutputBuffer();
+ std::copy(string_result.begin(), string_result.end(),
+ std::back_inserter(result->data));
+ return result;
+}
+
+wabt::OutputBuffer* wabt_format_binary_errors(wabt::Errors* errors) {
+ std::string string_result =
+ FormatErrorsToString(*errors, wabt::Location::Type::Binary);
+
+ wabt::OutputBuffer* result = new wabt::OutputBuffer();
+ std::copy(string_result.begin(), string_result.end(),
+ std::back_inserter(result->data));
+ return result;
+}
+
+void wabt_destroy_errors(wabt::Errors* errors) {
+ delete errors;
+}
+
+// WabtParseWatResult
+wabt::Result::Enum wabt_parse_wat_result_get_result(
+ WabtParseWatResult* result) {
+ return result->result;
+}
+
+wabt::Module* wabt_parse_wat_result_release_module(WabtParseWatResult* result) {
+ return result->module.release();
+}
+
+void wabt_destroy_parse_wat_result(WabtParseWatResult* result) {
+ delete result;
+}
+
+// WabtParseWastResult
+wabt::Result::Enum wabt_parse_wast_result_get_result(
+ WabtParseWastResult* result) {
+ return result->result;
+}
+
+wabt::Script* wabt_parse_wast_result_release_module(
+ WabtParseWastResult* result) {
+ return result->script.release();
+}
+
+void wabt_destroy_parse_wast_result(WabtParseWastResult* result) {
+ delete result;
+}
+
+// WabtReadBinaryResult
+wabt::Result::Enum wabt_read_binary_result_get_result(
+ WabtReadBinaryResult* result) {
+ return result->result;
+}
+
+wabt::Module* wabt_read_binary_result_release_module(
+ WabtReadBinaryResult* result) {
+ return result->module.release();
+}
+
+void wabt_destroy_read_binary_result(WabtReadBinaryResult* result) {
+ delete result;
+}
+
+// WabtWriteModuleResult
+wabt::Result::Enum wabt_write_module_result_get_result(
+ WabtWriteModuleResult* result) {
+ return result->result;
+}
+
+wabt::OutputBuffer* wabt_write_module_result_release_output_buffer(
+ WabtWriteModuleResult* result) {
+ return result->buffer.release();
+}
+
+wabt::OutputBuffer* wabt_write_module_result_release_log_output_buffer(
+ WabtWriteModuleResult* result) {
+ return result->log_buffer.release();
+}
+
+void wabt_destroy_write_module_result(WabtWriteModuleResult* result) {
+ delete result;
+}
+
+// WabtWriteScriptResult
+wabt::Result::Enum wabt_write_script_result_get_result(
+ WabtWriteScriptResult* result) {
+ return result->result;
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_json_output_buffer(
+ WabtWriteScriptResult* result) {
+ return result->json_buffer.release();
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_log_output_buffer(
+ WabtWriteScriptResult* result) {
+ return result->log_buffer.release();
+}
+
+size_t wabt_write_script_result_get_module_count(
+ WabtWriteScriptResult* result) {
+ return result->module_buffers.size();
+}
+
+const char* wabt_write_script_result_get_module_filename(
+ WabtWriteScriptResult* result,
+ size_t index) {
+ return result->module_buffers[index].first.c_str();
+}
+
+wabt::OutputBuffer* wabt_write_script_result_release_module_output_buffer(
+ WabtWriteScriptResult* result,
+ size_t index) {
+ return result->module_buffers[index].second.release();
+}
+
+void wabt_destroy_write_script_result(WabtWriteScriptResult* result) {
+ delete result;
+}
+
+// wabt::OutputBuffer*
+const void* wabt_output_buffer_get_data(wabt::OutputBuffer* output_buffer) {
+ return output_buffer->data.data();
+}
+
+size_t wabt_output_buffer_get_size(wabt::OutputBuffer* output_buffer) {
+ return output_buffer->data.size();
+}
+
+void wabt_destroy_output_buffer(wabt::OutputBuffer* output_buffer) {
+ delete output_buffer;
+}
+
+// See https://github.com/kripken/emscripten/issues/7073.
+void dummy_workaround_for_emscripten_issue_7073(void) {}
+
+} // extern "C"
+
+#endif /* WABT_EMSCRIPTEN_HELPERS_H_ */
diff --git a/third_party/wasm2c/src/error-formatter.cc b/third_party/wasm2c/src/error-formatter.cc
new file mode 100644
index 0000000000..14c5d92185
--- /dev/null
+++ b/third_party/wasm2c/src/error-formatter.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/error-formatter.h"
+
+namespace wabt {
+
+namespace {
+
+std::string FormatError(const Error& error,
+ Location::Type location_type,
+ const Color& color,
+ LexerSourceLineFinder* line_finder,
+ int source_line_max_length,
+ int indent) {
+ std::string indent_str(indent, ' ');
+ std::string result = indent_str;
+
+ result += color.MaybeBoldCode();
+
+ const Location& loc = error.loc;
+ if (!loc.filename.empty()) {
+ result += loc.filename.to_string();
+ result += ":";
+ }
+
+ if (location_type == Location::Type::Text) {
+ result += StringPrintf("%d:%d: ", loc.line, loc.first_column);
+ } else if (loc.offset != kInvalidOffset) {
+ result += StringPrintf("%07" PRIzx ": ", loc.offset);
+ }
+
+ result += color.MaybeRedCode();
+ result += GetErrorLevelName(error.error_level);
+ result += ": ";
+ result += color.MaybeDefaultCode();
+
+ result += error.message;
+ result += '\n';
+
+ LexerSourceLineFinder::SourceLine source_line;
+ if (line_finder) {
+ line_finder->GetSourceLine(loc, source_line_max_length, &source_line);
+ }
+
+ if (!source_line.line.empty()) {
+ result += indent_str;
+ result += source_line.line;
+ result += '\n';
+ result += indent_str;
+
+ size_t num_spaces = (loc.first_column - 1) - source_line.column_offset;
+ size_t num_carets = loc.last_column - loc.first_column;
+ num_carets = std::min(num_carets, source_line.line.size() - num_spaces);
+ num_carets = std::max<size_t>(num_carets, 1);
+ result.append(num_spaces, ' ');
+ result += color.MaybeBoldCode();
+ result += color.MaybeGreenCode();
+ result.append(num_carets, '^');
+ result += color.MaybeDefaultCode();
+ result += '\n';
+ }
+
+ return result;
+}
+
+} // End of anonymous namespace
+
+std::string FormatErrorsToString(const Errors& errors,
+ Location::Type location_type,
+ LexerSourceLineFinder* line_finder,
+ const Color& color,
+ const std::string& header,
+ PrintHeader print_header,
+ int source_line_max_length) {
+ std::string result;
+ for (const auto& error : errors) {
+ if (!header.empty()) {
+ switch (print_header) {
+ case PrintHeader::Never:
+ break;
+ case PrintHeader::Once:
+ print_header = PrintHeader::Never;
+ // Fallthrough.
+ case PrintHeader::Always:
+ result += header;
+ result += ":\n";
+ break;
+ }
+ }
+
+ int indent = header.empty() ? 0 : 2;
+
+ result += FormatError(error, location_type, color, line_finder,
+ source_line_max_length, indent);
+ }
+ return result;
+}
+
+void FormatErrorsToFile(const Errors& errors,
+ Location::Type location_type,
+ LexerSourceLineFinder* line_finder,
+ FILE* file,
+ const std::string& header,
+ PrintHeader print_header,
+ int source_line_max_length) {
+ Color color(file);
+ std::string s =
+ FormatErrorsToString(errors, location_type, line_finder, color, header,
+ print_header, source_line_max_length);
+ fwrite(s.data(), 1, s.size(), file);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/error-formatter.h b/third_party/wasm2c/src/error-formatter.h
new file mode 100644
index 0000000000..cd2ed91261
--- /dev/null
+++ b/third_party/wasm2c/src/error-formatter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef WABT_ERROR_FORMATTER_H_
+#define WABT_ERROR_FORMATTER_H_
+
+#include <cstdio>
+#include <string>
+#include <memory>
+
+#include "src/color.h"
+#include "src/error.h"
+#include "src/lexer-source-line-finder.h"
+
+namespace wabt {
+
+enum class PrintHeader {
+ Never,
+ Once,
+ Always,
+};
+
+std::string FormatErrorsToString(const Errors&,
+ Location::Type,
+ LexerSourceLineFinder* = nullptr,
+ const Color& color = Color(nullptr, false),
+ const std::string& header = {},
+ PrintHeader print_header = PrintHeader::Never,
+ int source_line_max_length = 80);
+
+void FormatErrorsToFile(const Errors&,
+ Location::Type,
+ LexerSourceLineFinder* = nullptr,
+ FILE* = stderr,
+ const std::string& header = {},
+ PrintHeader print_header = PrintHeader::Never,
+ int source_line_max_length = 80);
+
+} // namespace wabt
+
+#endif // WABT_ERROR_FORMATTER_H_
diff --git a/third_party/wasm2c/src/error.h b/third_party/wasm2c/src/error.h
new file mode 100644
index 0000000000..cffca1e5dd
--- /dev/null
+++ b/third_party/wasm2c/src/error.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef WABT_ERROR_H_
+#define WABT_ERROR_H_
+
+#include <string>
+#include <vector>
+
+#include "src/common.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+enum class ErrorLevel {
+ Warning,
+ Error,
+};
+
+static WABT_INLINE const char* GetErrorLevelName(ErrorLevel error_level) {
+ switch (error_level) {
+ case ErrorLevel::Warning:
+ return "warning";
+ case ErrorLevel::Error:
+ return "error";
+ }
+ WABT_UNREACHABLE;
+}
+
+class Error {
+ public:
+ Error() : error_level(ErrorLevel::Error) {}
+ Error(ErrorLevel error_level, Location loc, string_view message)
+ : error_level(error_level), loc(loc), message(message.to_string()) {}
+
+ ErrorLevel error_level;
+ Location loc;
+ std::string message;
+};
+
+using Errors = std::vector<Error>;
+
+} // namespace wabt
+
+#endif // WABT_ERROR_H_
diff --git a/third_party/wasm2c/src/expr-visitor.cc b/third_party/wasm2c/src/expr-visitor.cc
new file mode 100644
index 0000000000..b50400c4bd
--- /dev/null
+++ b/third_party/wasm2c/src/expr-visitor.cc
@@ -0,0 +1,469 @@
+/*
+ * 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 "src/expr-visitor.h"
+
+#include "src/cast.h"
+#include "src/ir.h"
+
+namespace wabt {
+
+ExprVisitor::ExprVisitor(Delegate* delegate) : delegate_(delegate) {}
+
+Result ExprVisitor::VisitExpr(Expr* root_expr) {
+ state_stack_.clear();
+ expr_stack_.clear();
+ expr_iter_stack_.clear();
+ catch_index_stack_.clear();
+
+ PushDefault(root_expr);
+
+ while (!state_stack_.empty()) {
+ State state = state_stack_.back();
+ auto* expr = expr_stack_.back();
+
+ switch (state) {
+ case State::Default:
+ PopDefault();
+ CHECK_RESULT(HandleDefaultState(expr));
+ break;
+
+ case State::Block: {
+ auto block_expr = cast<BlockExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != block_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndBlockExpr(block_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::IfTrue: {
+ auto if_expr = cast<IfExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != if_expr->true_.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->AfterIfTrueExpr(if_expr));
+ PopExprlist();
+ PushExprlist(State::IfFalse, expr, if_expr->false_);
+ }
+ break;
+ }
+
+ case State::IfFalse: {
+ auto if_expr = cast<IfExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != if_expr->false_.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndIfExpr(if_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::Loop: {
+ auto loop_expr = cast<LoopExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != loop_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ CHECK_RESULT(delegate_->EndLoopExpr(loop_expr));
+ PopExprlist();
+ }
+ break;
+ }
+
+ case State::Try: {
+ auto try_expr = cast<TryExpr>(expr);
+ auto& iter = expr_iter_stack_.back();
+ if (iter != try_expr->block.exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ PopExprlist();
+ switch (try_expr->kind) {
+ case TryKind::Catch:
+ if (!try_expr->catches.empty()) {
+ Catch& catch_ = try_expr->catches[0];
+ CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_));
+ PushCatch(expr, 0, catch_.exprs);
+ } else {
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ }
+ break;
+ case TryKind::Delegate:
+ CHECK_RESULT(delegate_->OnDelegateExpr(try_expr));
+ break;
+ case TryKind::Plain:
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ break;
+ }
+ }
+ break;
+ }
+
+ case State::Catch: {
+ auto try_expr = cast<TryExpr>(expr);
+ Index catch_index = catch_index_stack_.back();
+ auto& iter = expr_iter_stack_.back();
+ if (iter != try_expr->catches[catch_index].exprs.end()) {
+ PushDefault(&*iter++);
+ } else {
+ PopCatch();
+ catch_index++;
+ if (catch_index < try_expr->catches.size()) {
+ Catch& catch_ = try_expr->catches[catch_index];
+ CHECK_RESULT(delegate_->OnCatchExpr(try_expr, &catch_));
+ PushCatch(expr, catch_index, catch_.exprs);
+ } else {
+ CHECK_RESULT(delegate_->EndTryExpr(try_expr));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result ExprVisitor::VisitExprList(ExprList& exprs) {
+ for (Expr& expr : exprs)
+ CHECK_RESULT(VisitExpr(&expr));
+ return Result::Ok;
+}
+
+Result ExprVisitor::VisitFunc(Func* func) {
+ return VisitExprList(func->exprs);
+}
+
+Result ExprVisitor::HandleDefaultState(Expr* expr) {
+ switch (expr->type()) {
+ case ExprType::AtomicLoad:
+ CHECK_RESULT(delegate_->OnAtomicLoadExpr(cast<AtomicLoadExpr>(expr)));
+ break;
+
+ case ExprType::AtomicStore:
+ CHECK_RESULT(delegate_->OnAtomicStoreExpr(cast<AtomicStoreExpr>(expr)));
+ break;
+
+ case ExprType::AtomicRmw:
+ CHECK_RESULT(delegate_->OnAtomicRmwExpr(cast<AtomicRmwExpr>(expr)));
+ break;
+
+ case ExprType::AtomicRmwCmpxchg:
+ CHECK_RESULT(
+ delegate_->OnAtomicRmwCmpxchgExpr(cast<AtomicRmwCmpxchgExpr>(expr)));
+ break;
+
+ case ExprType::AtomicWait:
+ CHECK_RESULT(delegate_->OnAtomicWaitExpr(cast<AtomicWaitExpr>(expr)));
+ break;
+
+ case ExprType::AtomicFence:
+ CHECK_RESULT(delegate_->OnAtomicFenceExpr(cast<AtomicFenceExpr>(expr)));
+ break;
+
+ case ExprType::AtomicNotify:
+ CHECK_RESULT(delegate_->OnAtomicNotifyExpr(cast<AtomicNotifyExpr>(expr)));
+ break;
+
+ case ExprType::Binary:
+ CHECK_RESULT(delegate_->OnBinaryExpr(cast<BinaryExpr>(expr)));
+ break;
+
+ case ExprType::Block: {
+ auto block_expr = cast<BlockExpr>(expr);
+ CHECK_RESULT(delegate_->BeginBlockExpr(block_expr));
+ PushExprlist(State::Block, expr, block_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::Br:
+ CHECK_RESULT(delegate_->OnBrExpr(cast<BrExpr>(expr)));
+ break;
+
+ case ExprType::BrIf:
+ CHECK_RESULT(delegate_->OnBrIfExpr(cast<BrIfExpr>(expr)));
+ break;
+
+ case ExprType::BrTable:
+ CHECK_RESULT(delegate_->OnBrTableExpr(cast<BrTableExpr>(expr)));
+ break;
+
+ case ExprType::Call:
+ CHECK_RESULT(delegate_->OnCallExpr(cast<CallExpr>(expr)));
+ break;
+
+ case ExprType::CallIndirect:
+ CHECK_RESULT(delegate_->OnCallIndirectExpr(cast<CallIndirectExpr>(expr)));
+ break;
+
+ case ExprType::CallRef:
+ CHECK_RESULT(delegate_->OnCallRefExpr(cast<CallRefExpr>(expr)));
+ break;
+
+ case ExprType::Compare:
+ CHECK_RESULT(delegate_->OnCompareExpr(cast<CompareExpr>(expr)));
+ break;
+
+ case ExprType::Const:
+ CHECK_RESULT(delegate_->OnConstExpr(cast<ConstExpr>(expr)));
+ break;
+
+ case ExprType::Convert:
+ CHECK_RESULT(delegate_->OnConvertExpr(cast<ConvertExpr>(expr)));
+ break;
+
+ case ExprType::Drop:
+ CHECK_RESULT(delegate_->OnDropExpr(cast<DropExpr>(expr)));
+ break;
+
+ case ExprType::GlobalGet:
+ CHECK_RESULT(delegate_->OnGlobalGetExpr(cast<GlobalGetExpr>(expr)));
+ break;
+
+ case ExprType::GlobalSet:
+ CHECK_RESULT(delegate_->OnGlobalSetExpr(cast<GlobalSetExpr>(expr)));
+ break;
+
+ case ExprType::If: {
+ auto if_expr = cast<IfExpr>(expr);
+ CHECK_RESULT(delegate_->BeginIfExpr(if_expr));
+ PushExprlist(State::IfTrue, expr, if_expr->true_.exprs);
+ break;
+ }
+
+ case ExprType::Load:
+ CHECK_RESULT(delegate_->OnLoadExpr(cast<LoadExpr>(expr)));
+ break;
+
+ case ExprType::LoadSplat:
+ CHECK_RESULT(delegate_->OnLoadSplatExpr(cast<LoadSplatExpr>(expr)));
+ break;
+
+ case ExprType::LoadZero:
+ CHECK_RESULT(delegate_->OnLoadZeroExpr(cast<LoadZeroExpr>(expr)));
+ break;
+
+ case ExprType::LocalGet:
+ CHECK_RESULT(delegate_->OnLocalGetExpr(cast<LocalGetExpr>(expr)));
+ break;
+
+ case ExprType::LocalSet:
+ CHECK_RESULT(delegate_->OnLocalSetExpr(cast<LocalSetExpr>(expr)));
+ break;
+
+ case ExprType::LocalTee:
+ CHECK_RESULT(delegate_->OnLocalTeeExpr(cast<LocalTeeExpr>(expr)));
+ break;
+
+ case ExprType::Loop: {
+ auto loop_expr = cast<LoopExpr>(expr);
+ CHECK_RESULT(delegate_->BeginLoopExpr(loop_expr));
+ PushExprlist(State::Loop, expr, loop_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::MemoryCopy:
+ CHECK_RESULT(delegate_->OnMemoryCopyExpr(cast<MemoryCopyExpr>(expr)));
+ break;
+
+ case ExprType::DataDrop:
+ CHECK_RESULT(delegate_->OnDataDropExpr(cast<DataDropExpr>(expr)));
+ break;
+
+ case ExprType::MemoryFill:
+ CHECK_RESULT(delegate_->OnMemoryFillExpr(cast<MemoryFillExpr>(expr)));
+ break;
+
+ case ExprType::MemoryGrow:
+ CHECK_RESULT(delegate_->OnMemoryGrowExpr(cast<MemoryGrowExpr>(expr)));
+ break;
+
+ case ExprType::MemoryInit:
+ CHECK_RESULT(delegate_->OnMemoryInitExpr(cast<MemoryInitExpr>(expr)));
+ break;
+
+ case ExprType::MemorySize:
+ CHECK_RESULT(delegate_->OnMemorySizeExpr(cast<MemorySizeExpr>(expr)));
+ break;
+
+ case ExprType::TableCopy:
+ CHECK_RESULT(delegate_->OnTableCopyExpr(cast<TableCopyExpr>(expr)));
+ break;
+
+ case ExprType::ElemDrop:
+ CHECK_RESULT(delegate_->OnElemDropExpr(cast<ElemDropExpr>(expr)));
+ break;
+
+ case ExprType::TableInit:
+ CHECK_RESULT(delegate_->OnTableInitExpr(cast<TableInitExpr>(expr)));
+ break;
+
+ case ExprType::TableGet:
+ CHECK_RESULT(delegate_->OnTableGetExpr(cast<TableGetExpr>(expr)));
+ break;
+
+ case ExprType::TableSet:
+ CHECK_RESULT(delegate_->OnTableSetExpr(cast<TableSetExpr>(expr)));
+ break;
+
+ case ExprType::TableGrow:
+ CHECK_RESULT(delegate_->OnTableGrowExpr(cast<TableGrowExpr>(expr)));
+ break;
+
+ case ExprType::TableSize:
+ CHECK_RESULT(delegate_->OnTableSizeExpr(cast<TableSizeExpr>(expr)));
+ break;
+
+ case ExprType::TableFill:
+ CHECK_RESULT(delegate_->OnTableFillExpr(cast<TableFillExpr>(expr)));
+ break;
+
+ case ExprType::RefFunc:
+ CHECK_RESULT(delegate_->OnRefFuncExpr(cast<RefFuncExpr>(expr)));
+ break;
+
+ case ExprType::RefNull:
+ CHECK_RESULT(delegate_->OnRefNullExpr(cast<RefNullExpr>(expr)));
+ break;
+
+ case ExprType::RefIsNull:
+ CHECK_RESULT(delegate_->OnRefIsNullExpr(cast<RefIsNullExpr>(expr)));
+ break;
+
+ case ExprType::Nop:
+ CHECK_RESULT(delegate_->OnNopExpr(cast<NopExpr>(expr)));
+ break;
+
+ case ExprType::Rethrow:
+ CHECK_RESULT(delegate_->OnRethrowExpr(cast<RethrowExpr>(expr)));
+ break;
+
+ case ExprType::Return:
+ CHECK_RESULT(delegate_->OnReturnExpr(cast<ReturnExpr>(expr)));
+ break;
+
+ case ExprType::ReturnCall:
+ CHECK_RESULT(delegate_->OnReturnCallExpr(cast<ReturnCallExpr>(expr)));
+ break;
+
+ case ExprType::ReturnCallIndirect:
+ CHECK_RESULT(delegate_->OnReturnCallIndirectExpr(
+ cast<ReturnCallIndirectExpr>(expr)));
+ break;
+
+ case ExprType::Select:
+ CHECK_RESULT(delegate_->OnSelectExpr(cast<SelectExpr>(expr)));
+ break;
+
+ case ExprType::Store:
+ CHECK_RESULT(delegate_->OnStoreExpr(cast<StoreExpr>(expr)));
+ break;
+
+
+ case ExprType::Throw:
+ CHECK_RESULT(delegate_->OnThrowExpr(cast<ThrowExpr>(expr)));
+ break;
+
+ case ExprType::Try: {
+ auto try_expr = cast<TryExpr>(expr);
+ CHECK_RESULT(delegate_->BeginTryExpr(try_expr));
+ PushExprlist(State::Try, expr, try_expr->block.exprs);
+ break;
+ }
+
+ case ExprType::Unary:
+ CHECK_RESULT(delegate_->OnUnaryExpr(cast<UnaryExpr>(expr)));
+ break;
+
+ case ExprType::Ternary:
+ CHECK_RESULT(delegate_->OnTernaryExpr(cast<TernaryExpr>(expr)));
+ break;
+
+ case ExprType::SimdLaneOp: {
+ CHECK_RESULT(delegate_->OnSimdLaneOpExpr(cast<SimdLaneOpExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdLoadLane: {
+ CHECK_RESULT(delegate_->OnSimdLoadLaneExpr(cast<SimdLoadLaneExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdStoreLane: {
+ CHECK_RESULT(
+ delegate_->OnSimdStoreLaneExpr(cast<SimdStoreLaneExpr>(expr)));
+ break;
+ }
+
+ case ExprType::SimdShuffleOp: {
+ CHECK_RESULT(
+ delegate_->OnSimdShuffleOpExpr(cast<SimdShuffleOpExpr>(expr)));
+ break;
+ }
+
+ case ExprType::Unreachable:
+ CHECK_RESULT(delegate_->OnUnreachableExpr(cast<UnreachableExpr>(expr)));
+ break;
+ }
+
+ return Result::Ok;
+}
+
+void ExprVisitor::PushDefault(Expr* expr) {
+ state_stack_.emplace_back(State::Default);
+ expr_stack_.emplace_back(expr);
+}
+
+void ExprVisitor::PopDefault() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+}
+
+void ExprVisitor::PushExprlist(State state, Expr* expr, ExprList& expr_list) {
+ state_stack_.emplace_back(state);
+ expr_stack_.emplace_back(expr);
+ expr_iter_stack_.emplace_back(expr_list.begin());
+}
+
+void ExprVisitor::PopExprlist() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+ expr_iter_stack_.pop_back();
+}
+
+void ExprVisitor::PushCatch(Expr* expr,
+ Index catch_index,
+ ExprList& expr_list) {
+ state_stack_.emplace_back(State::Catch);
+ expr_stack_.emplace_back(expr);
+ expr_iter_stack_.emplace_back(expr_list.begin());
+ catch_index_stack_.emplace_back(catch_index);
+}
+
+void ExprVisitor::PopCatch() {
+ state_stack_.pop_back();
+ expr_stack_.pop_back();
+ expr_iter_stack_.pop_back();
+ catch_index_stack_.pop_back();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/expr-visitor.h b/third_party/wasm2c/src/expr-visitor.h
new file mode 100644
index 0000000000..ab7dc9e418
--- /dev/null
+++ b/third_party/wasm2c/src/expr-visitor.h
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_EXPR_VISITOR_H_
+#define WABT_EXPR_VISITOR_H_
+
+#include "src/common.h"
+#include "src/ir.h"
+
+namespace wabt {
+
+class ExprVisitor {
+ public:
+ class Delegate;
+ class DelegateNop;
+
+ explicit ExprVisitor(Delegate* delegate);
+
+ Result VisitExpr(Expr*);
+ Result VisitExprList(ExprList&);
+ Result VisitFunc(Func*);
+
+ private:
+ enum class State {
+ Default,
+ Block,
+ IfTrue,
+ IfFalse,
+ Loop,
+ Try,
+ Catch,
+ };
+
+ Result HandleDefaultState(Expr*);
+ void PushDefault(Expr*);
+ void PopDefault();
+ void PushExprlist(State state, Expr*, ExprList&);
+ void PopExprlist();
+ void PushCatch(Expr*, Index catch_index, ExprList&);
+ void PopCatch();
+
+ Delegate* delegate_;
+
+ // Use parallel arrays instead of array of structs so we can avoid allocating
+ // unneeded objects. ExprList::iterator has no default constructor, so it
+ // must only be allocated for states that use it.
+ std::vector<State> state_stack_;
+ std::vector<Expr*> expr_stack_;
+ std::vector<ExprList::iterator> expr_iter_stack_;
+ std::vector<Index> catch_index_stack_;
+};
+
+class ExprVisitor::Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ virtual Result OnBinaryExpr(BinaryExpr*) = 0;
+ virtual Result BeginBlockExpr(BlockExpr*) = 0;
+ virtual Result EndBlockExpr(BlockExpr*) = 0;
+ virtual Result OnBrExpr(BrExpr*) = 0;
+ virtual Result OnBrIfExpr(BrIfExpr*) = 0;
+ virtual Result OnBrTableExpr(BrTableExpr*) = 0;
+ virtual Result OnCallExpr(CallExpr*) = 0;
+ virtual Result OnCallIndirectExpr(CallIndirectExpr*) = 0;
+ virtual Result OnCallRefExpr(CallRefExpr*) = 0;
+ virtual Result OnCompareExpr(CompareExpr*) = 0;
+ virtual Result OnConstExpr(ConstExpr*) = 0;
+ virtual Result OnConvertExpr(ConvertExpr*) = 0;
+ virtual Result OnDropExpr(DropExpr*) = 0;
+ virtual Result OnGlobalGetExpr(GlobalGetExpr*) = 0;
+ virtual Result OnGlobalSetExpr(GlobalSetExpr*) = 0;
+ virtual Result BeginIfExpr(IfExpr*) = 0;
+ virtual Result AfterIfTrueExpr(IfExpr*) = 0;
+ virtual Result EndIfExpr(IfExpr*) = 0;
+ virtual Result OnLoadExpr(LoadExpr*) = 0;
+ virtual Result OnLocalGetExpr(LocalGetExpr*) = 0;
+ virtual Result OnLocalSetExpr(LocalSetExpr*) = 0;
+ virtual Result OnLocalTeeExpr(LocalTeeExpr*) = 0;
+ virtual Result BeginLoopExpr(LoopExpr*) = 0;
+ virtual Result EndLoopExpr(LoopExpr*) = 0;
+ virtual Result OnMemoryCopyExpr(MemoryCopyExpr*) = 0;
+ virtual Result OnDataDropExpr(DataDropExpr*) = 0;
+ virtual Result OnMemoryFillExpr(MemoryFillExpr*) = 0;
+ virtual Result OnMemoryGrowExpr(MemoryGrowExpr*) = 0;
+ virtual Result OnMemoryInitExpr(MemoryInitExpr*) = 0;
+ virtual Result OnMemorySizeExpr(MemorySizeExpr*) = 0;
+ virtual Result OnTableCopyExpr(TableCopyExpr*) = 0;
+ virtual Result OnElemDropExpr(ElemDropExpr*) = 0;
+ virtual Result OnTableInitExpr(TableInitExpr*) = 0;
+ virtual Result OnTableGetExpr(TableGetExpr*) = 0;
+ virtual Result OnTableSetExpr(TableSetExpr*) = 0;
+ virtual Result OnTableGrowExpr(TableGrowExpr*) = 0;
+ virtual Result OnTableSizeExpr(TableSizeExpr*) = 0;
+ virtual Result OnTableFillExpr(TableFillExpr*) = 0;
+ virtual Result OnRefFuncExpr(RefFuncExpr*) = 0;
+ virtual Result OnRefNullExpr(RefNullExpr*) = 0;
+ virtual Result OnRefIsNullExpr(RefIsNullExpr*) = 0;
+ virtual Result OnNopExpr(NopExpr*) = 0;
+ virtual Result OnReturnExpr(ReturnExpr*) = 0;
+ virtual Result OnReturnCallExpr(ReturnCallExpr*) = 0;
+ virtual Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) = 0;
+ virtual Result OnSelectExpr(SelectExpr*) = 0;
+ virtual Result OnStoreExpr(StoreExpr*) = 0;
+ virtual Result OnUnaryExpr(UnaryExpr*) = 0;
+ virtual Result OnUnreachableExpr(UnreachableExpr*) = 0;
+ virtual Result BeginTryExpr(TryExpr*) = 0;
+ virtual Result OnCatchExpr(TryExpr*, Catch*) = 0;
+ virtual Result OnDelegateExpr(TryExpr*) = 0;
+ virtual Result EndTryExpr(TryExpr*) = 0;
+ virtual Result OnThrowExpr(ThrowExpr*) = 0;
+ virtual Result OnRethrowExpr(RethrowExpr*) = 0;
+ virtual Result OnAtomicWaitExpr(AtomicWaitExpr*) = 0;
+ virtual Result OnAtomicFenceExpr(AtomicFenceExpr*) = 0;
+ virtual Result OnAtomicNotifyExpr(AtomicNotifyExpr*) = 0;
+ virtual Result OnAtomicLoadExpr(AtomicLoadExpr*) = 0;
+ virtual Result OnAtomicStoreExpr(AtomicStoreExpr*) = 0;
+ virtual Result OnAtomicRmwExpr(AtomicRmwExpr*) = 0;
+ virtual Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) = 0;
+ virtual Result OnTernaryExpr(TernaryExpr*) = 0;
+ virtual Result OnSimdLaneOpExpr(SimdLaneOpExpr*) = 0;
+ virtual Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) = 0;
+ virtual Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) = 0;
+ virtual Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) = 0;
+ virtual Result OnLoadSplatExpr(LoadSplatExpr*) = 0;
+ virtual Result OnLoadZeroExpr(LoadZeroExpr*) = 0;
+};
+
+class ExprVisitor::DelegateNop : public ExprVisitor::Delegate {
+ public:
+ Result OnBinaryExpr(BinaryExpr*) override { return Result::Ok; }
+ Result BeginBlockExpr(BlockExpr*) override { return Result::Ok; }
+ Result EndBlockExpr(BlockExpr*) override { return Result::Ok; }
+ Result OnBrExpr(BrExpr*) override { return Result::Ok; }
+ Result OnBrIfExpr(BrIfExpr*) override { return Result::Ok; }
+ Result OnBrTableExpr(BrTableExpr*) override { return Result::Ok; }
+ Result OnCallExpr(CallExpr*) override { return Result::Ok; }
+ Result OnCallIndirectExpr(CallIndirectExpr*) override { return Result::Ok; }
+ Result OnCallRefExpr(CallRefExpr*) override { return Result::Ok; }
+ Result OnCompareExpr(CompareExpr*) override { return Result::Ok; }
+ Result OnConstExpr(ConstExpr*) override { return Result::Ok; }
+ Result OnConvertExpr(ConvertExpr*) override { return Result::Ok; }
+ Result OnDropExpr(DropExpr*) override { return Result::Ok; }
+ Result OnGlobalGetExpr(GlobalGetExpr*) override { return Result::Ok; }
+ Result OnGlobalSetExpr(GlobalSetExpr*) override { return Result::Ok; }
+ Result BeginIfExpr(IfExpr*) override { return Result::Ok; }
+ Result AfterIfTrueExpr(IfExpr*) override { return Result::Ok; }
+ Result EndIfExpr(IfExpr*) override { return Result::Ok; }
+ Result OnLoadExpr(LoadExpr*) override { return Result::Ok; }
+ Result OnLocalGetExpr(LocalGetExpr*) override { return Result::Ok; }
+ Result OnLocalSetExpr(LocalSetExpr*) override { return Result::Ok; }
+ Result OnLocalTeeExpr(LocalTeeExpr*) override { return Result::Ok; }
+ Result BeginLoopExpr(LoopExpr*) override { return Result::Ok; }
+ Result EndLoopExpr(LoopExpr*) override { return Result::Ok; }
+ Result OnMemoryCopyExpr(MemoryCopyExpr*) override { return Result::Ok; }
+ Result OnDataDropExpr(DataDropExpr*) override { return Result::Ok; }
+ Result OnMemoryFillExpr(MemoryFillExpr*) override { return Result::Ok; }
+ Result OnMemoryGrowExpr(MemoryGrowExpr*) override { return Result::Ok; }
+ Result OnMemoryInitExpr(MemoryInitExpr*) override { return Result::Ok; }
+ Result OnMemorySizeExpr(MemorySizeExpr*) override { return Result::Ok; }
+ Result OnTableCopyExpr(TableCopyExpr*) override { return Result::Ok; }
+ Result OnElemDropExpr(ElemDropExpr*) override { return Result::Ok; }
+ Result OnTableInitExpr(TableInitExpr*) override { return Result::Ok; }
+ Result OnTableGetExpr(TableGetExpr*) override { return Result::Ok; }
+ Result OnTableSetExpr(TableSetExpr*) override { return Result::Ok; }
+ Result OnTableGrowExpr(TableGrowExpr*) override { return Result::Ok; }
+ Result OnTableSizeExpr(TableSizeExpr*) override { return Result::Ok; }
+ Result OnTableFillExpr(TableFillExpr*) override { return Result::Ok; }
+ Result OnRefFuncExpr(RefFuncExpr*) override { return Result::Ok; }
+ Result OnRefNullExpr(RefNullExpr*) override { return Result::Ok; }
+ Result OnRefIsNullExpr(RefIsNullExpr*) override { return Result::Ok; }
+ Result OnNopExpr(NopExpr*) override { return Result::Ok; }
+ Result OnReturnExpr(ReturnExpr*) override { return Result::Ok; }
+ Result OnReturnCallExpr(ReturnCallExpr*) override { return Result::Ok; }
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override {
+ return Result::Ok;
+ }
+ Result OnSelectExpr(SelectExpr*) override { return Result::Ok; }
+ Result OnStoreExpr(StoreExpr*) override { return Result::Ok; }
+ Result OnUnaryExpr(UnaryExpr*) override { return Result::Ok; }
+ Result OnUnreachableExpr(UnreachableExpr*) override { return Result::Ok; }
+ Result BeginTryExpr(TryExpr*) override { return Result::Ok; }
+ Result OnCatchExpr(TryExpr*, Catch*) override { return Result::Ok; }
+ Result OnDelegateExpr(TryExpr*) override { return Result::Ok; }
+ Result EndTryExpr(TryExpr*) override { return Result::Ok; }
+ Result OnThrowExpr(ThrowExpr*) override { return Result::Ok; }
+ Result OnRethrowExpr(RethrowExpr*) override { return Result::Ok; }
+ Result OnAtomicWaitExpr(AtomicWaitExpr*) override { return Result::Ok; }
+ Result OnAtomicFenceExpr(AtomicFenceExpr*) override { return Result::Ok; }
+ Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override { return Result::Ok; }
+ Result OnAtomicLoadExpr(AtomicLoadExpr*) override { return Result::Ok; }
+ Result OnAtomicStoreExpr(AtomicStoreExpr*) override { return Result::Ok; }
+ Result OnAtomicRmwExpr(AtomicRmwExpr*) override { return Result::Ok; }
+ Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override {
+ return Result::Ok;
+ }
+ Result OnTernaryExpr(TernaryExpr*) override { return Result::Ok; }
+ Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override { return Result::Ok; }
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override { return Result::Ok; }
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override { return Result::Ok; }
+ Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override { return Result::Ok; }
+ Result OnLoadSplatExpr(LoadSplatExpr*) override { return Result::Ok; }
+ Result OnLoadZeroExpr(LoadZeroExpr*) override { return Result::Ok; }
+};
+
+} // namespace wabt
+
+#endif // WABT_EXPR_VISITOR_H_
diff --git a/third_party/wasm2c/src/feature.cc b/third_party/wasm2c/src/feature.cc
new file mode 100644
index 0000000000..9d7878e18b
--- /dev/null
+++ b/third_party/wasm2c/src/feature.cc
@@ -0,0 +1,56 @@
+/*
+ * 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 "src/feature.h"
+
+#include "src/option-parser.h"
+
+namespace wabt {
+
+void Features::AddOptions(OptionParser* parser) {
+#define WABT_FEATURE(variable, flag, default_, help) \
+ if (default_ == true) { \
+ parser->AddOption("disable-" flag, "Disable " help, \
+ [this]() { disable_##variable(); }); \
+ } else { \
+ parser->AddOption("enable-" flag, "Enable " help, \
+ [this]() { enable_##variable(); }); \
+ }
+
+#include "src/feature.def"
+#undef WABT_FEATURE
+ parser->AddOption("enable-all", "Enable all features",
+ [this]() { EnableAll(); });
+}
+
+void Features::UpdateDependencies() {
+ // Exception handling requires reference types.
+ if (exceptions_enabled_) {
+ reference_types_enabled_ = true;
+ }
+
+ // Function references require reference types.
+ if (function_references_enabled_) {
+ reference_types_enabled_ = true;
+ }
+
+ // Reference types requires bulk memory.
+ if (reference_types_enabled_) {
+ bulk_memory_enabled_ = true;
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/feature.def b/third_party/wasm2c/src/feature.def
new file mode 100644
index 0000000000..f6f90f614d
--- /dev/null
+++ b/third_party/wasm2c/src/feature.def
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_FEATURE
+#error "You must define WABT_FEATURE before including this file."
+#endif
+
+/*
+ * variable flag default help
+ * ========================================================================= */
+
+WABT_FEATURE(exceptions, "exceptions", false, "Experimental exception handling")
+WABT_FEATURE(mutable_globals, "mutable-globals", true, "Import/export mutable globals")
+WABT_FEATURE(sat_float_to_int, "saturating-float-to-int", true, "Saturating float-to-int operators")
+WABT_FEATURE(sign_extension, "sign-extension", true, "Sign-extension operators")
+WABT_FEATURE(simd, "simd", true, "SIMD support")
+WABT_FEATURE(threads, "threads", false, "Threading support")
+WABT_FEATURE(function_references, "function-references", false, "Typed function references")
+WABT_FEATURE(multi_value, "multi-value", true, "Multi-value")
+WABT_FEATURE(tail_call, "tail-call", false, "Tail-call support")
+WABT_FEATURE(bulk_memory, "bulk-memory", false, "Bulk-memory operations")
+WABT_FEATURE(reference_types, "reference-types", false, "Reference types (externref)")
+WABT_FEATURE(annotations, "annotations", false, "Custom annotation syntax")
+WABT_FEATURE(gc, "gc", false, "Garbage collection")
+WABT_FEATURE(memory64, "memory64", false, "64-bit memory")
diff --git a/third_party/wasm2c/src/feature.h b/third_party/wasm2c/src/feature.h
new file mode 100644
index 0000000000..7ebc9ef85b
--- /dev/null
+++ b/third_party/wasm2c/src/feature.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_FEATURE_H_
+#define WABT_FEATURE_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+class OptionParser;
+
+class Features {
+ public:
+ void AddOptions(OptionParser*);
+
+ void EnableAll() {
+#define WABT_FEATURE(variable, flag, default_, help) enable_##variable();
+#include "src/feature.def"
+#undef WABT_FEATURE
+ }
+
+#define WABT_FEATURE(variable, flag, default_, help) \
+ bool variable##_enabled() const { return variable##_enabled_; } \
+ void enable_##variable() { set_##variable##_enabled(true); } \
+ void disable_##variable() { set_##variable##_enabled(false); } \
+ void set_##variable##_enabled(bool value) { \
+ variable##_enabled_ = value; \
+ UpdateDependencies(); \
+ }
+#include "src/feature.def"
+#undef WABT_FEATURE
+
+ private:
+ void UpdateDependencies();
+
+#define WABT_FEATURE(variable, flag, default_, help) \
+ bool variable##_enabled_ = default_;
+#include "src/feature.def"
+#undef WABT_FEATURE
+};
+
+} // namespace wabt
+
+#endif // WABT_FEATURE_H_
diff --git a/third_party/wasm2c/src/filenames.cc b/third_party/wasm2c/src/filenames.cc
new file mode 100644
index 0000000000..989453c7dd
--- /dev/null
+++ b/third_party/wasm2c/src/filenames.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/filenames.h"
+
+namespace wabt {
+
+const char* kWasmExtension = ".wasm";
+
+const char* kWatExtension = ".wat";
+
+string_view StripExtension(string_view filename) {
+ return filename.substr(0, filename.find_last_of('.'));
+}
+
+string_view GetBasename(string_view filename) {
+ size_t last_slash = filename.find_last_of('/');
+ size_t last_backslash = filename.find_last_of('\\');
+ if (last_slash == string_view::npos && last_backslash == string_view::npos) {
+ return filename;
+ }
+
+ if (last_slash == string_view::npos) {
+ if (last_backslash == string_view::npos) {
+ return filename;
+ }
+ last_slash = last_backslash;
+ } else if (last_backslash != string_view::npos) {
+ last_slash = std::max(last_slash, last_backslash);
+ }
+
+ return filename.substr(last_slash + 1);
+}
+
+string_view GetExtension(string_view filename) {
+ size_t pos = filename.find_last_of('.');
+ if (pos == string_view::npos) {
+ return "";
+ }
+ return filename.substr(pos);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/filenames.h b/third_party/wasm2c/src/filenames.h
new file mode 100644
index 0000000000..87529aa29c
--- /dev/null
+++ b/third_party/wasm2c/src/filenames.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_FILENAMES_H_
+#define WABT_FILENAMES_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+extern const char* kWasmExtension;
+extern const char* kWatExtension;
+
+// Return only the file extension, e.g.:
+//
+// "foo.txt", => ".txt"
+// "foo" => ""
+// "/foo/bar/foo.wasm" => ".wasm"
+string_view GetExtension(string_view filename);
+
+// Strip extension, e.g.:
+//
+// "foo", => "foo"
+// "foo.bar" => "foo"
+// "/path/to/foo.bar" => "/path/to/foo"
+// "\\path\\to\\foo.bar" => "\\path\\to\\foo"
+string_view StripExtension(string_view s);
+
+// Strip everything up to and including the last slash, e.g.:
+//
+// "/foo/bar/baz", => "baz"
+// "/usr/local/include/stdio.h", => "stdio.h"
+// "foo.bar", => "foo.bar"
+string_view GetBasename(string_view filename);
+
+} // namespace wabt
+
+#endif /* WABT_FILENAMES_H_ */
diff --git a/third_party/wasm2c/src/generate-names.cc b/third_party/wasm2c/src/generate-names.cc
new file mode 100644
index 0000000000..5f99f0e84c
--- /dev/null
+++ b/third_party/wasm2c/src/generate-names.cc
@@ -0,0 +1,430 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/generate-names.h"
+
+#include <cassert>
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "src/cast.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+
+namespace wabt {
+
+namespace {
+
+class NameGenerator : public ExprVisitor::DelegateNop {
+ public:
+ NameGenerator(NameOpts opts);
+
+ Result VisitModule(Module* module);
+
+ // Implementation of ExprVisitor::DelegateNop.
+ Result BeginBlockExpr(BlockExpr* expr) override;
+ Result BeginLoopExpr(LoopExpr* expr) override;
+ Result BeginIfExpr(IfExpr* expr) override;
+
+ private:
+ static bool HasName(const std::string& str);
+
+ // Generate a name with the given prefix, followed by the index and
+ // optionally a disambiguating number. If index == kInvalidIndex, the index
+ // is not appended.
+ void GenerateName(const char* prefix,
+ Index index,
+ unsigned disambiguator,
+ std::string* out_str);
+
+ // Like GenerateName, but only generates a name if |out_str| is empty.
+ void MaybeGenerateName(const char* prefix,
+ Index index,
+ std::string* out_str);
+
+ // Generate a name via GenerateName and bind it to the given binding hash. If
+ // the name already exists, the name will be disambiguated until it can be
+ // added.
+ void GenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* out_str);
+
+ // Like GenerateAndBindName, but only generates a name if |out_str| is empty.
+ void MaybeGenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* out_str);
+
+ // Like MaybeGenerateAndBindName but uses the name directly, without
+ // appending the index. If the name already exists, a disambiguating suffix
+ // is added.
+ void MaybeUseAndBindName(BindingHash* bindings,
+ const char* name,
+ Index index,
+ std::string* out_str);
+
+ void GenerateAndBindLocalNames(Func* func);
+
+ template <typename T>
+ Result VisitAll(const std::vector<T*>& items,
+ Result (NameGenerator::*func)(Index, T*));
+
+ Result VisitFunc(Index func_index, Func* func);
+ Result VisitGlobal(Index global_index, Global* global);
+ Result VisitType(Index func_type_index, TypeEntry* type);
+ Result VisitTable(Index table_index, Table* table);
+ Result VisitMemory(Index memory_index, Memory* memory);
+ Result VisitTag(Index tag_index, Tag* tag);
+ Result VisitDataSegment(Index data_segment_index, DataSegment* data_segment);
+ Result VisitElemSegment(Index elem_segment_index, ElemSegment* elem_segment);
+ Result VisitImport(Import* import);
+ Result VisitExport(Export* export_);
+
+ Module* module_ = nullptr;
+ ExprVisitor visitor_;
+ Index label_count_ = 0;
+
+ 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;
+
+ NameOpts opts_;
+};
+
+NameGenerator::NameGenerator(NameOpts opts)
+ : visitor_(this), opts_(opts) {}
+
+// static
+bool NameGenerator::HasName(const std::string& str) {
+ return !str.empty();
+}
+
+void NameGenerator::GenerateName(const char* prefix,
+ Index index,
+ unsigned disambiguator,
+ std::string* str) {
+ *str = "$";
+ *str += prefix;
+ if (index != kInvalidIndex) {
+ if (opts_ & NameOpts::AlphaNames) {
+ // For params and locals, do not use a prefix char.
+ if (!strcmp(prefix, "p") || !strcmp(prefix, "l")) {
+ str->pop_back();
+ } else {
+ *str += '_';
+ }
+ *str += IndexToAlphaName(index);
+ } else {
+ *str += std::to_string(index);
+ }
+ }
+ if (disambiguator != 0) {
+ *str += '_' + std::to_string(disambiguator);
+ }
+}
+
+void NameGenerator::MaybeGenerateName(const char* prefix,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ // There's no bindings hash, so the name can't be a duplicate. Therefore it
+ // doesn't need a disambiguating number.
+ GenerateName(prefix, index, 0, str);
+ }
+}
+
+void NameGenerator::GenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* str) {
+ unsigned disambiguator = 0;
+ while (true) {
+ GenerateName(prefix, index, disambiguator, str);
+ if (bindings->find(*str) == bindings->end()) {
+ bindings->emplace(*str, Binding(index));
+ break;
+ }
+
+ disambiguator++;
+ }
+}
+
+void NameGenerator::MaybeGenerateAndBindName(BindingHash* bindings,
+ const char* prefix,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ GenerateAndBindName(bindings, prefix, index, str);
+ }
+}
+
+void NameGenerator::MaybeUseAndBindName(BindingHash* bindings,
+ const char* name,
+ Index index,
+ std::string* str) {
+ if (!HasName(*str)) {
+ unsigned disambiguator = 0;
+ while (true) {
+ GenerateName(name, kInvalidIndex, disambiguator, str);
+ if (bindings->find(*str) == bindings->end()) {
+ bindings->emplace(*str, Binding(index));
+ break;
+ }
+
+ disambiguator++;
+ }
+ }
+}
+
+void NameGenerator::GenerateAndBindLocalNames(Func* func) {
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings,
+ &index_to_name);
+ for (size_t i = 0; i < index_to_name.size(); ++i) {
+ const std::string& old_name = index_to_name[i];
+ if (!old_name.empty()) {
+ continue;
+ }
+
+ const char* prefix = i < func->GetNumParams() ? "p" : "l";
+ std::string new_name;
+ GenerateAndBindName(&func->bindings, prefix, i, &new_name);
+ index_to_name[i] = new_name;
+ }
+}
+
+Result NameGenerator::BeginBlockExpr(BlockExpr* expr) {
+ MaybeGenerateName("B", label_count_++, &expr->block.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::BeginLoopExpr(LoopExpr* expr) {
+ MaybeGenerateName("L", label_count_++, &expr->block.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::BeginIfExpr(IfExpr* expr) {
+ MaybeGenerateName("I", label_count_++, &expr->true_.label);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitFunc(Index func_index, Func* func) {
+ MaybeGenerateAndBindName(&module_->func_bindings, "f", func_index,
+ &func->name);
+ GenerateAndBindLocalNames(func);
+
+ label_count_ = 0;
+ CHECK_RESULT(visitor_.VisitFunc(func));
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitGlobal(Index global_index, Global* global) {
+ MaybeGenerateAndBindName(&module_->global_bindings, "g", global_index,
+ &global->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitType(Index type_index, TypeEntry* type) {
+ MaybeGenerateAndBindName(&module_->type_bindings, "t", type_index,
+ &type->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitTable(Index table_index, Table* table) {
+ MaybeGenerateAndBindName(&module_->table_bindings, "T", table_index,
+ &table->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitMemory(Index memory_index, Memory* memory) {
+ MaybeGenerateAndBindName(&module_->memory_bindings, "M", memory_index,
+ &memory->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitTag(Index tag_index, Tag* tag) {
+ MaybeGenerateAndBindName(&module_->tag_bindings, "e", tag_index, &tag->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitDataSegment(Index data_segment_index,
+ DataSegment* data_segment) {
+ MaybeGenerateAndBindName(&module_->data_segment_bindings, "d",
+ data_segment_index, &data_segment->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitElemSegment(Index elem_segment_index,
+ ElemSegment* elem_segment) {
+ MaybeGenerateAndBindName(&module_->elem_segment_bindings, "e",
+ elem_segment_index, &elem_segment->name);
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitImport(Import* import) {
+ BindingHash* bindings = nullptr;
+ std::string* name = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (import->kind()) {
+ case ExternalKind::Func:
+ if (auto* func_import = cast<FuncImport>(import)) {
+ bindings = &module_->func_bindings;
+ name = &func_import->func.name;
+ index = num_func_imports_++;
+ }
+ break;
+
+ case ExternalKind::Table:
+ if (auto* table_import = cast<TableImport>(import)) {
+ bindings = &module_->table_bindings;
+ name = &table_import->table.name;
+ index = num_table_imports_++;
+ }
+ break;
+
+ case ExternalKind::Memory:
+ if (auto* memory_import = cast<MemoryImport>(import)) {
+ bindings = &module_->memory_bindings;
+ name = &memory_import->memory.name;
+ index = num_memory_imports_++;
+ }
+ break;
+
+ case ExternalKind::Global:
+ if (auto* global_import = cast<GlobalImport>(import)) {
+ bindings = &module_->global_bindings;
+ name = &global_import->global.name;
+ index = num_global_imports_++;
+ }
+ break;
+
+ case ExternalKind::Tag:
+ if (auto* tag_import = cast<TagImport>(import)) {
+ bindings = &module_->tag_bindings;
+ name = &tag_import->tag.name;
+ index = num_tag_imports_++;
+ }
+ break;
+ }
+
+ if (bindings && name) {
+ assert(index != kInvalidIndex);
+ std::string new_name = import->module_name + '.' + import->field_name;
+ MaybeUseAndBindName(bindings, new_name.c_str(), index, name);
+ }
+
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitExport(Export* export_) {
+ BindingHash* bindings = nullptr;
+ std::string* name = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ if (Func* func = module_->GetFunc(export_->var)) {
+ index = module_->GetFuncIndex(export_->var);
+ bindings = &module_->func_bindings;
+ name = &func->name;
+ }
+ break;
+
+ case ExternalKind::Table:
+ if (Table* table = module_->GetTable(export_->var)) {
+ index = module_->GetTableIndex(export_->var);
+ bindings = &module_->table_bindings;
+ name = &table->name;
+ }
+ break;
+
+ case ExternalKind::Memory:
+ if (Memory* memory = module_->GetMemory(export_->var)) {
+ index = module_->GetMemoryIndex(export_->var);
+ bindings = &module_->memory_bindings;
+ name = &memory->name;
+ }
+ break;
+
+ case ExternalKind::Global:
+ if (Global* global = module_->GetGlobal(export_->var)) {
+ index = module_->GetGlobalIndex(export_->var);
+ bindings = &module_->global_bindings;
+ name = &global->name;
+ }
+ break;
+
+ case ExternalKind::Tag:
+ if (Tag* tag = module_->GetTag(export_->var)) {
+ index = module_->GetTagIndex(export_->var);
+ bindings = &module_->tag_bindings;
+ name = &tag->name;
+ }
+ break;
+ }
+
+ if (bindings && name) {
+ MaybeUseAndBindName(bindings, export_->name.c_str(), index, name);
+ }
+
+ return Result::Ok;
+}
+
+template <typename T>
+Result NameGenerator::VisitAll(const std::vector<T*>& items,
+ Result (NameGenerator::*func)(Index, T*)) {
+ for (Index i = 0; i < items.size(); ++i) {
+ CHECK_RESULT((this->*func)(i, items[i]));
+ }
+ return Result::Ok;
+}
+
+Result NameGenerator::VisitModule(Module* module) {
+ module_ = module;
+ // Visit imports and exports first to give better names, derived from the
+ // import/export name.
+ for (auto* import : module->imports) {
+ CHECK_RESULT(VisitImport(import));
+ }
+ for (auto* export_ : module->exports) {
+ CHECK_RESULT(VisitExport(export_));
+ }
+
+ VisitAll(module->globals, &NameGenerator::VisitGlobal);
+ VisitAll(module->types, &NameGenerator::VisitType);
+ VisitAll(module->funcs, &NameGenerator::VisitFunc);
+ VisitAll(module->tables, &NameGenerator::VisitTable);
+ VisitAll(module->memories, &NameGenerator::VisitMemory);
+ VisitAll(module->tags, &NameGenerator::VisitTag);
+ VisitAll(module->data_segments, &NameGenerator::VisitDataSegment);
+ VisitAll(module->elem_segments, &NameGenerator::VisitElemSegment);
+ module_ = nullptr;
+ return Result::Ok;
+}
+
+} // end anonymous namespace
+
+Result GenerateNames(Module* module, NameOpts opts) {
+ NameGenerator generator(opts);
+ return generator.VisitModule(module);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/generate-names.h b/third_party/wasm2c/src/generate-names.h
new file mode 100644
index 0000000000..9cd926e462
--- /dev/null
+++ b/third_party/wasm2c/src/generate-names.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_GENERATE_NAMES_H_
+#define WABT_GENERATE_NAMES_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+struct Module;
+
+enum NameOpts {
+ None = 0,
+ AlphaNames = 1 << 0,
+};
+
+Result GenerateNames(struct Module*, NameOpts opts = NameOpts::None);
+
+inline std::string IndexToAlphaName(Index index) {
+ std::string s;
+ do {
+ // For multiple chars, put most frequently changing char first.
+ s += 'a' + (index % 26);
+ index /= 26;
+ // Continue remaining sequence with 'a' rather than 'b'.
+ } while (index--);
+ return s;
+}
+
+} // namespace wabt
+
+#endif /* WABT_GENERATE_NAMES_H_ */
diff --git a/third_party/wasm2c/src/hash-util.cc b/third_party/wasm2c/src/hash-util.cc
new file mode 100644
index 0000000000..efb8427028
--- /dev/null
+++ b/third_party/wasm2c/src/hash-util.cc
@@ -0,0 +1,38 @@
+/*
+ * 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 "src/hash-util.h"
+
+#include "config.h"
+
+namespace wabt {
+
+// Hash combiner from:
+// http://stackoverflow.com/questions/4948780/magic-number-in-boosthash-combine
+
+hash_code HashCombine(hash_code seed, hash_code y) {
+#if SIZEOF_SIZE_T == 4
+ constexpr hash_code magic = 0x9e3779b9;
+#elif SIZEOF_SIZE_T == 8
+ constexpr hash_code magic = 0x9e3779b97f4a7c16;
+#else
+#error "weird sizeof size_t"
+#endif
+ seed ^= y + magic + (seed << 6) + (seed >> 2);
+ return seed;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/hash-util.h b/third_party/wasm2c/src/hash-util.h
new file mode 100644
index 0000000000..0a135d5e68
--- /dev/null
+++ b/third_party/wasm2c/src/hash-util.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_HASH_UTIL_H_
+#define WABT_HASH_UTIL_H_
+
+#include <cstdlib>
+#include <functional>
+
+namespace wabt {
+
+typedef std::size_t hash_code;
+
+inline hash_code HashCombine() {
+ return 0;
+}
+inline hash_code HashCombine(hash_code seed) {
+ return seed;
+}
+hash_code HashCombine(hash_code x, hash_code y);
+
+template <typename T, typename... U>
+inline hash_code HashCombine(const T& first, const U&... rest) {
+ return HashCombine(HashCombine(rest...), std::hash<T>()(first));
+}
+
+template <typename It>
+inline hash_code HashRange(It first, It last) {
+ hash_code result = 0;
+ for (auto iter = first; iter != last; ++iter) {
+ result = HashCombine(result, *iter);
+ }
+ return result;
+}
+
+} // namespace wabt
+
+#endif // WABT_HASH_UTIL_H_
diff --git a/third_party/wasm2c/src/intrusive-list.h b/third_party/wasm2c/src/intrusive-list.h
new file mode 100644
index 0000000000..7d9611a74a
--- /dev/null
+++ b/third_party/wasm2c/src/intrusive-list.h
@@ -0,0 +1,633 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_INTRUSIVE_LIST_H_
+#define WABT_INTRUSIVE_LIST_H_
+
+#include <cassert>
+#include <iterator>
+#include <memory>
+
+#include "src/make-unique.h"
+
+// This uses a similar interface as std::list, but is missing the following
+// features:
+//
+// * Add "extract_" functions that remove an element from the list and return
+// it.
+// * Only supports move-only operations
+// * No allocator support
+// * No initializer lists
+// * Asserts instead of exceptions
+// * Some functions are not implemented (merge, remove, remove_if, reverse,
+// unique, sort, non-member comparison operators)
+
+namespace wabt {
+
+template <typename T>
+class intrusive_list;
+
+template <typename T>
+class intrusive_list_base {
+ private:
+ friend class intrusive_list<T>;
+
+ mutable T* next_ = nullptr;
+ mutable T* prev_ = nullptr;
+};
+
+template <typename T>
+class intrusive_list {
+ public:
+ // types:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ class iterator;
+ class const_iterator;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ // construct/copy/destroy:
+ intrusive_list();
+ explicit intrusive_list(std::unique_ptr<T> node);
+ explicit intrusive_list(T&& node);
+ intrusive_list(const intrusive_list&) = delete;
+ intrusive_list(intrusive_list&&);
+ ~intrusive_list();
+ intrusive_list& operator=(const intrusive_list& other) = delete;
+ intrusive_list& operator=(intrusive_list&& other);
+
+ // iterators:
+ iterator begin() noexcept;
+ const_iterator begin() const noexcept;
+ iterator end() noexcept;
+ const_iterator end() const noexcept;
+
+ reverse_iterator rbegin() noexcept;
+ const_reverse_iterator rbegin() const noexcept;
+ reverse_iterator rend() noexcept;
+ const_reverse_iterator rend() const noexcept;
+
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+
+ // capacity:
+ size_type size() const noexcept;
+ bool empty() const noexcept;
+
+ // element access:
+ reference front();
+ const_reference front() const;
+ reference back();
+ const_reference back() const;
+
+ // modifiers:
+ template <class... Args>
+ void emplace_front(Args&&... args);
+ template <class... Args>
+ void emplace_back(Args&&... args);
+ void push_front(std::unique_ptr<T> node);
+ void push_front(T&& node);
+ void push_back(std::unique_ptr<T> node);
+ void push_back(T&& node);
+ void pop_front();
+ void pop_back();
+ std::unique_ptr<T> extract_front();
+ std::unique_ptr<T> extract_back();
+
+ template <class... Args>
+ iterator emplace(iterator pos, Args&&... args);
+ iterator insert(iterator pos, std::unique_ptr<T> node);
+ iterator insert(iterator pos, T&& node);
+ std::unique_ptr<T> extract(iterator it);
+
+ iterator erase(iterator pos);
+ iterator erase(iterator first, iterator last);
+ void swap(intrusive_list&);
+ void clear() noexcept;
+
+ void splice(iterator pos, intrusive_list& node);
+ void splice(iterator pos, intrusive_list&& node);
+ void splice(iterator pos, intrusive_list& node, iterator it);
+ void splice(iterator pos,
+ intrusive_list& node,
+ iterator first,
+ iterator last);
+
+ private:
+ T* first_ = nullptr;
+ T* last_ = nullptr;
+ size_t size_ = 0;
+};
+
+/// iterator
+template <typename T>
+class intrusive_list<T>::iterator {
+ public:
+ typedef std::ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ iterator(const intrusive_list<T>& list, T* node)
+ : list_(&list), node_(node) {}
+
+ reference operator*() const {
+ assert(node_);
+ return *node_;
+ }
+
+ pointer operator->() const {
+ assert(node_);
+ return node_;
+ }
+
+ iterator& operator++() {
+ assert(node_);
+ node_ = node_->next_;
+ return *this;
+ }
+
+ iterator operator++(int) {
+ iterator tmp = *this;
+ operator++();
+ return tmp;
+ }
+
+ iterator& operator--() {
+ node_ = node_ ? node_->prev_ : list_->last_;
+ return *this;
+ }
+
+ iterator operator--(int) {
+ iterator tmp = *this;
+ operator--();
+ return tmp;
+ }
+
+ bool operator==(iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ == rhs.node_;
+ }
+
+ bool operator!=(iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ != rhs.node_;
+ }
+
+ private:
+ friend class const_iterator;
+
+ const intrusive_list<T>* list_;
+ T* node_;
+};
+
+/// const_iterator
+template <typename T>
+class intrusive_list<T>::const_iterator {
+ public:
+ typedef std::ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef const T* pointer;
+ typedef const T& reference;
+
+ const_iterator(const intrusive_list<T>& list, T* node)
+ : list_(&list), node_(node) {}
+
+ const_iterator(const iterator& other)
+ : list_(other.list_), node_(other.node_) {}
+
+ reference operator*() const {
+ assert(node_);
+ return *node_;
+ }
+
+ pointer operator->() const {
+ assert(node_);
+ return node_;
+ }
+
+ const_iterator& operator++() {
+ assert(node_);
+ node_ = node_->next_;
+ return *this;
+ }
+
+ const_iterator operator++(int) {
+ const_iterator tmp = *this;
+ operator++();
+ return tmp;
+ }
+
+ const_iterator& operator--() {
+ node_ = node_ ? node_->prev_ : list_->last_;
+ return *this;
+ }
+
+ const_iterator operator--(int) {
+ const_iterator tmp = *this;
+ operator--();
+ return tmp;
+ }
+
+ bool operator==(const_iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ == rhs.node_;
+ }
+
+ bool operator!=(const_iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ != rhs.node_;
+ }
+
+ private:
+ const intrusive_list<T>* list_;
+ T* node_;
+};
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list() {}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(std::unique_ptr<T> node) {
+ push_back(std::move(node));
+}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(T&& node) {
+ push_back(std::move(node));
+}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(intrusive_list&& other)
+ : first_(other.first_), last_(other.last_), size_(other.size_) {
+ other.first_ = other.last_ = nullptr;
+ other.size_ = 0;
+}
+
+template <typename T>
+inline intrusive_list<T>::~intrusive_list() {
+ clear();
+}
+
+template <typename T>
+inline intrusive_list<T>& intrusive_list<T>::operator=(
+ intrusive_list<T>&& other) {
+ clear();
+ first_ = other.first_;
+ last_ = other.last_;
+ size_ = other.size_;
+ other.first_ = other.last_ = nullptr;
+ other.size_ = 0;
+ return *this;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator
+intrusive_list<T>::begin() noexcept {
+ return iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::begin()
+ const noexcept {
+ return const_iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::end() noexcept {
+ return iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const
+ noexcept {
+ return const_iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reverse_iterator
+intrusive_list<T>::rbegin() noexcept {
+ return reverse_iterator(iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::rbegin() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reverse_iterator
+intrusive_list<T>::rend() noexcept {
+ return reverse_iterator(iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::rend() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cbegin()
+ const noexcept {
+ return const_iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cend()
+ const noexcept {
+ return const_iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::crbegin() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::crend() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::size_type intrusive_list<T>::size() const
+ noexcept {
+ return size_;
+}
+
+template <typename T>
+inline bool intrusive_list<T>::empty() const noexcept {
+ return size_ == 0;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reference intrusive_list<T>::front() {
+ assert(!empty());
+ return *first_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reference intrusive_list<T>::front()
+ const {
+ assert(!empty());
+ return *first_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reference intrusive_list<T>::back() {
+ assert(!empty());
+ return *last_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reference intrusive_list<T>::back()
+ const {
+ assert(!empty());
+ return *last_;
+}
+
+template <typename T>
+template <class... Args>
+inline void intrusive_list<T>::emplace_front(Args&&... args) {
+ push_front(MakeUnique<T>(std::forward<Args>(args)...));
+}
+
+template <typename T>
+template <class... Args>
+inline void intrusive_list<T>::emplace_back(Args&&... args) {
+ push_back(MakeUnique<T>(std::forward<Args>(args)...));
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_front(std::unique_ptr<T> node) {
+ assert(node->prev_ == nullptr && node->next_ == nullptr);
+
+ T* node_p = node.release();
+ if (first_) {
+ node_p->next_ = first_;
+ first_->prev_ = node_p;
+ } else {
+ last_ = node_p;
+ }
+ first_ = node_p;
+ size_++;
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_front(T&& node) {
+ push_front(MakeUnique<T>(std::move(node)));
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_back(std::unique_ptr<T> node) {
+ assert(node->prev_ == nullptr && node->next_ == nullptr);
+
+ T* node_p = node.release();
+ if (last_) {
+ node_p->prev_ = last_;
+ last_->next_ = node_p;
+ } else {
+ first_ = node_p;
+ }
+ last_ = node_p;
+ size_++;
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_back(T&& node) {
+ push_back(MakeUnique<T>(std::move(node)));
+}
+
+template <typename T>
+inline void intrusive_list<T>::pop_front() {
+ extract_front();
+}
+
+template <typename T>
+inline void intrusive_list<T>::pop_back() {
+ extract_back();
+}
+
+template <typename T>
+inline std::unique_ptr<T> intrusive_list<T>::extract_front() {
+ assert(!empty());
+ T* node = first_;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ first_ = first_->next_;
+ first_->prev_ = nullptr;
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return std::unique_ptr<T>(node);
+}
+
+template <typename T>
+inline std::unique_ptr<T> intrusive_list<T>::extract_back() {
+ assert(!empty());
+ T* node = last_;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ last_ = last_->prev_;
+ last_->next_ = nullptr;
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return std::unique_ptr<T>(node);
+}
+
+template <typename T>
+template <class... Args>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::emplace(
+ iterator pos,
+ Args&&... args) {
+ return insert(pos, MakeUnique<T>(std::forward<Args>(args)...));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(
+ iterator pos,
+ std::unique_ptr<T> node) {
+ assert(node->prev_ == nullptr && node->next_ == nullptr);
+
+ T* node_p;
+ if (pos == end()) {
+ push_back(std::move(node));
+ node_p = &back();
+ } else {
+ node_p = node.release();
+ node_p->prev_ = pos->prev_;
+ node_p->next_ = &*pos;
+ if (pos->prev_) {
+ pos->prev_->next_ = node_p;
+ } else {
+ first_ = node_p;
+ }
+ pos->prev_ = node_p;
+ size_++;
+ }
+ return iterator(*this, node_p);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(
+ iterator pos,
+ T&& node) {
+ return insert(pos, MakeUnique<T>(std::move(node)));
+}
+
+template <typename T>
+inline std::unique_ptr<T> intrusive_list<T>::extract(iterator pos) {
+ assert(!empty());
+ assert(pos != end());
+ T* node = &*pos;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ if (node->prev_) {
+ node->prev_->next_ = node->next_;
+ } else {
+ first_ = node->next_;
+ }
+
+ if (node->next_) {
+ node->next_->prev_ = node->prev_;
+ } else {
+ last_ = node->prev_;
+ }
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return std::unique_ptr<T>(node);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(
+ iterator pos) {
+ iterator next = std::next(pos);
+ extract(pos);
+ return next;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(
+ iterator first,
+ iterator last) {
+ while (first != last)
+ first = erase(first);
+ return first;
+}
+
+template <typename T>
+inline void intrusive_list<T>::swap(intrusive_list& other) {
+ std::swap(first_, other.first_);
+ std::swap(last_, other.last_);
+ std::swap(size_, other.size_);
+}
+
+template <typename T>
+inline void intrusive_list<T>::clear() noexcept {
+ for (T* iter = first_; iter;) {
+ T* next = iter->next_;
+ delete iter;
+ iter = next;
+ }
+ first_ = last_ = nullptr;
+ size_ = 0;
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos, intrusive_list& other) {
+ splice(pos, other, other.begin(), other.end());
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos, intrusive_list&& other) {
+ splice(pos, other, other.begin(), other.end());
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos,
+ intrusive_list& other,
+ iterator it) {
+ insert(pos, other.extract(it));
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos,
+ intrusive_list& other,
+ iterator first,
+ iterator last) {
+ while (first != last)
+ insert(pos, other.extract(first++));
+}
+
+} // namespace wabt
+
+#endif // WABT_INTRUSIVE_LIST_H_
diff --git a/third_party/wasm2c/src/ir-util.cc b/third_party/wasm2c/src/ir-util.cc
new file mode 100644
index 0000000000..9b66461188
--- /dev/null
+++ b/third_party/wasm2c/src/ir-util.cc
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/ir-util.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/ir-util.h"
+#include "src/literal.h"
+#include "src/stream.h"
+
+#define WABT_TRACING 0
+#include "src/tracing.h"
+
+using namespace wabt;
+
+const Label* ModuleContext::GetLabel(const Var& var) const {
+ if (var.is_name()) {
+ for (Index i = GetLabelStackSize(); i > 0; --i) {
+ auto label = &label_stack_[i - 1];
+ if (label->name == var.name()) {
+ return label;
+ }
+ }
+ } else if (var.index() < GetLabelStackSize()) {
+ auto label = &label_stack_[GetLabelStackSize() - var.index() - 1];
+ return label;
+ }
+ return nullptr;
+}
+
+Index ModuleContext::GetLabelArity(const Var& var) const {
+ auto label = GetLabel(var);
+ if (!label) {
+ return 0;
+ }
+
+ return label->label_type == LabelType::Loop ? label->param_types.size()
+ : label->result_types.size();
+}
+
+Index ModuleContext::GetFuncParamCount(const Var& var) const {
+ const Func* func = module.GetFunc(var);
+ return func ? func->GetNumParams() : 0;
+}
+
+Index ModuleContext::GetFuncResultCount(const Var& var) const {
+ const Func* func = module.GetFunc(var);
+ return func ? func->GetNumResults() : 0;
+}
+
+void ModuleContext::BeginBlock(LabelType label_type, const Block& block) {
+ label_stack_.emplace_back(label_type, block.label, block.decl.sig.param_types,
+ block.decl.sig.result_types);
+}
+
+void ModuleContext::EndBlock() {
+ label_stack_.pop_back();
+}
+
+void ModuleContext::BeginFunc(const Func& func) {
+ label_stack_.clear();
+ label_stack_.emplace_back(LabelType::Func, std::string(), TypeVector(),
+ func.decl.sig.result_types);
+ current_func_ = &func;
+}
+
+void ModuleContext::EndFunc() {
+ current_func_ = nullptr;
+}
+
+ModuleContext::Arities ModuleContext::GetExprArity(const Expr& expr) const {
+ switch (expr.type()) {
+ case ExprType::AtomicNotify:
+ case ExprType::AtomicRmw:
+ case ExprType::Binary:
+ case ExprType::Compare:
+ case ExprType::TableGrow:
+ return { 2, 1 };
+
+ case ExprType::AtomicStore:
+ case ExprType::Store:
+ case ExprType::TableSet:
+ return { 2, 0 };
+
+ case ExprType::Block:
+ return { 0, cast<BlockExpr>(&expr)->block.decl.sig.GetNumResults() };
+
+ case ExprType::Br:
+ return { GetLabelArity(cast<BrExpr>(&expr)->var), 1, true };
+
+ case ExprType::BrIf: {
+ Index arity = GetLabelArity(cast<BrIfExpr>(&expr)->var);
+ return { arity + 1, arity };
+ }
+
+ case ExprType::BrTable:
+ return { GetLabelArity(cast<BrTableExpr>(&expr)->default_target) + 1, 1,
+ true };
+
+ case ExprType::Call: {
+ const Var& var = cast<CallExpr>(&expr)->var;
+ return { GetFuncParamCount(var), GetFuncResultCount(var) };
+ }
+
+ case ExprType::ReturnCall: {
+ const Var& var = cast<ReturnCallExpr>(&expr)->var;
+ return { GetFuncParamCount(var), GetFuncResultCount(var), true };
+ }
+
+ case ExprType::CallIndirect: {
+ const auto* ci_expr = cast<CallIndirectExpr>(&expr);
+ return { ci_expr->decl.GetNumParams() + 1,
+ ci_expr->decl.GetNumResults() };
+ }
+
+ case ExprType::CallRef: {
+ const Var& var = cast<CallRefExpr>(&expr)->function_type_index;
+ return { GetFuncParamCount(var) + 1, GetFuncResultCount(var) };
+ }
+
+ case ExprType::ReturnCallIndirect: {
+ const auto* rci_expr = cast<ReturnCallIndirectExpr>(&expr);
+ return { rci_expr->decl.GetNumParams() + 1,
+ rci_expr->decl.GetNumResults(), true };
+ }
+
+ case ExprType::Const:
+ case ExprType::GlobalGet:
+ case ExprType::LocalGet:
+ case ExprType::MemorySize:
+ case ExprType::TableSize:
+ case ExprType::RefNull:
+ case ExprType::RefFunc:
+ return { 0, 1 };
+
+ case ExprType::Unreachable:
+ return { 0, 1, true };
+
+ case ExprType::DataDrop:
+ case ExprType::ElemDrop:
+ case ExprType::AtomicFence:
+ return { 0, 0 };
+
+ case ExprType::MemoryInit:
+ case ExprType::TableInit:
+ case ExprType::MemoryFill:
+ case ExprType::MemoryCopy:
+ case ExprType::TableCopy:
+ case ExprType::TableFill:
+ return { 3, 0 };
+
+ case ExprType::AtomicLoad:
+ case ExprType::Convert:
+ case ExprType::Load:
+ case ExprType::LocalTee:
+ case ExprType::MemoryGrow:
+ case ExprType::Unary:
+ case ExprType::TableGet:
+ case ExprType::RefIsNull:
+ case ExprType::LoadSplat:
+ case ExprType::LoadZero:
+ return { 1, 1 };
+
+ case ExprType::Drop:
+ case ExprType::GlobalSet:
+ case ExprType::LocalSet:
+ return { 1, 0 };
+
+ case ExprType::If:
+ return { 1, cast<IfExpr>(&expr)->true_.decl.sig.GetNumResults() };
+
+ case ExprType::Loop:
+ return { 0, cast<LoopExpr>(&expr)->block.decl.sig.GetNumResults() };
+
+ case ExprType::Nop:
+ return { 0, 0 };
+
+ case ExprType::Return:
+ return
+ { static_cast<Index>(current_func_->decl.sig.result_types.size()), 1,
+ true };
+
+ case ExprType::Rethrow:
+ return { 0, 0, true };
+
+ case ExprType::AtomicRmwCmpxchg:
+ case ExprType::AtomicWait:
+ case ExprType::Select:
+ return { 3, 1 };
+
+ case ExprType::Throw: {
+ auto throw_ = cast<ThrowExpr>(&expr);
+ Index operand_count = 0;
+ if (Tag* tag = module.GetTag(throw_->var)) {
+ operand_count = tag->decl.sig.param_types.size();
+ }
+ return { operand_count, 0, true };
+ }
+
+ case ExprType::Try:
+ return { 0, cast<TryExpr>(&expr)->block.decl.sig.GetNumResults() };
+
+ case ExprType::Ternary:
+ return { 3, 1 };
+
+ case ExprType::SimdLaneOp: {
+ const Opcode opcode = cast<SimdLaneOpExpr>(&expr)->opcode;
+ switch (opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ return { 1, 1 };
+
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ return { 2, 1 };
+
+ default:
+ fprintf(stderr, "Invalid Opcode for expr type: %s\n",
+ GetExprTypeName(expr));
+ assert(0);
+ return { 0, 0 };
+ }
+ }
+
+ case ExprType::SimdLoadLane:
+ case ExprType::SimdStoreLane: {
+ return { 2, 1 };
+ }
+
+ case ExprType::SimdShuffleOp:
+ return { 2, 1 };
+ }
+
+ WABT_UNREACHABLE;
+}
diff --git a/third_party/wasm2c/src/ir-util.h b/third_party/wasm2c/src/ir-util.h
new file mode 100644
index 0000000000..226e81aad9
--- /dev/null
+++ b/third_party/wasm2c/src/ir-util.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_IR_UTIL_H_
+#define WABT_IR_UTIL_H_
+
+#include "src/common.h"
+#include "src/ir.h"
+
+namespace wabt {
+
+struct Label {
+ Label(LabelType label_type,
+ const std::string& name,
+ const TypeVector& param_types,
+ const TypeVector& result_types)
+ : name(name),
+ label_type(label_type),
+ param_types(param_types),
+ result_types(result_types) {}
+
+ std::string name;
+ LabelType label_type;
+ TypeVector param_types;
+ TypeVector result_types;
+};
+
+struct ModuleContext {
+ ModuleContext(const Module &module) : module(module) {}
+
+ Index GetLabelStackSize() const { return label_stack_.size(); }
+ const Label* GetLabel(const Var& var) const;
+ Index GetLabelArity(const Var& var) const;
+ void SetTopLabelType(LabelType label_type) {
+ label_stack_.back().label_type = label_type;
+ }
+
+ Index GetFuncParamCount(const Var& var) const;
+ Index GetFuncResultCount(const Var& var) const;
+
+ void BeginBlock(LabelType label_type, const Block& block);
+ void EndBlock();
+ void BeginFunc(const Func& func);
+ void EndFunc();
+
+ struct Arities {
+ Index nargs;
+ Index nreturns;
+ bool unreachable;
+ Arities(Index na, Index nr, bool ur = false)
+ : nargs(na), nreturns(nr), unreachable(ur) {}
+ };
+ Arities GetExprArity(const Expr& expr) const;
+
+ const Module &module;
+ private:
+ const Func* current_func_ = nullptr;
+ std::vector<Label> label_stack_;
+};
+
+} // namespace wabt
+
+#endif /* WABT_IR_UTIL_H_ */
diff --git a/third_party/wasm2c/src/ir.cc b/third_party/wasm2c/src/ir.cc
new file mode 100644
index 0000000000..ddfd490de2
--- /dev/null
+++ b/third_party/wasm2c/src/ir.cc
@@ -0,0 +1,695 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/ir.h"
+
+#include <cassert>
+#include <cstddef>
+#include <numeric>
+
+#include "src/cast.h"
+
+namespace {
+
+const char* ExprTypeName[] = {
+ "AtomicFence",
+ "AtomicLoad",
+ "AtomicRmw",
+ "AtomicRmwCmpxchg",
+ "AtomicStore",
+ "AtomicNotify",
+ "AtomicWait",
+ "Binary",
+ "Block",
+ "Br",
+ "BrIf",
+ "BrTable",
+ "Call",
+ "CallIndirect",
+ "CallRef",
+ "Compare",
+ "Const",
+ "Convert",
+ "Drop",
+ "GlobalGet",
+ "GlobalSet",
+ "If",
+ "Load",
+ "LocalGet",
+ "LocalSet",
+ "LocalTee",
+ "Loop",
+ "MemoryCopy",
+ "DataDrop",
+ "MemoryFill",
+ "MemoryGrow",
+ "MemoryInit",
+ "MemorySize",
+ "Nop",
+ "RefIsNull",
+ "RefFunc",
+ "RefNull",
+ "Rethrow",
+ "Return",
+ "ReturnCall",
+ "ReturnCallIndirect",
+ "Select",
+ "SimdLaneOp",
+ "SimdLoadLane",
+ "SimdStoreLane",
+ "SimdShuffleOp",
+ "LoadSplat",
+ "LoadZero",
+ "Store",
+ "TableCopy",
+ "ElemDrop",
+ "TableInit",
+ "TableGet",
+ "TableGrow",
+ "TableSize",
+ "TableSet",
+ "TableFill",
+ "Ternary",
+ "Throw",
+ "Try",
+ "Unary",
+ "Unreachable",
+};
+
+} // end of anonymous namespace
+
+namespace wabt {
+
+const char* GetExprTypeName(ExprType type) {
+ static_assert(WABT_ENUM_COUNT(ExprType) == WABT_ARRAY_SIZE(ExprTypeName),
+ "Malformed ExprTypeName array");
+ return ExprTypeName[size_t(type)];
+}
+
+const char* GetExprTypeName(const Expr& expr) {
+ return GetExprTypeName(expr.type());
+}
+
+bool FuncSignature::operator==(const FuncSignature& rhs) const {
+ return param_types == rhs.param_types && result_types == rhs.result_types;
+}
+
+const Export* Module::GetExport(string_view name) const {
+ Index index = export_bindings.FindIndex(name);
+ if (index >= exports.size()) {
+ return nullptr;
+ }
+ return exports[index];
+}
+
+Index Module::GetFuncIndex(const Var& var) const {
+ return func_bindings.FindIndex(var);
+}
+
+Index Module::GetGlobalIndex(const Var& var) const {
+ return global_bindings.FindIndex(var);
+}
+
+Index Module::GetTableIndex(const Var& var) const {
+ return table_bindings.FindIndex(var);
+}
+
+Index Module::GetMemoryIndex(const Var& var) const {
+ return memory_bindings.FindIndex(var);
+}
+
+Index Module::GetFuncTypeIndex(const Var& var) const {
+ return type_bindings.FindIndex(var);
+}
+
+Index Module::GetTagIndex(const Var& var) const {
+ return tag_bindings.FindIndex(var);
+}
+
+Index Module::GetDataSegmentIndex(const Var& var) const {
+ return data_segment_bindings.FindIndex(var);
+}
+
+Index Module::GetElemSegmentIndex(const Var& var) const {
+ return elem_segment_bindings.FindIndex(var);
+}
+
+bool Module::IsImport(ExternalKind kind, const Var& var) const {
+ switch (kind) {
+ case ExternalKind::Func:
+ return GetFuncIndex(var) < num_func_imports;
+
+ case ExternalKind::Global:
+ return GetGlobalIndex(var) < num_global_imports;
+
+ case ExternalKind::Memory:
+ return GetMemoryIndex(var) < num_memory_imports;
+
+ case ExternalKind::Table:
+ return GetTableIndex(var) < num_table_imports;
+
+ case ExternalKind::Tag:
+ return GetTagIndex(var) < num_tag_imports;
+
+ default:
+ return false;
+ }
+}
+
+void LocalTypes::Set(const TypeVector& types) {
+ decls_.clear();
+ if (types.empty()) {
+ return;
+ }
+
+ Type type = types[0];
+ Index count = 1;
+ for (Index i = 1; i < types.size(); ++i) {
+ if (types[i] != type) {
+ decls_.emplace_back(type, count);
+ type = types[i];
+ count = 1;
+ } else {
+ ++count;
+ }
+ }
+ decls_.emplace_back(type, count);
+}
+
+Index LocalTypes::size() const {
+ return std::accumulate(
+ decls_.begin(), decls_.end(), 0,
+ [](Index sum, const Decl& decl) { return sum + decl.second; });
+}
+
+Type LocalTypes::operator[](Index i) const {
+ Index count = 0;
+ for (auto decl: decls_) {
+ if (i < count + decl.second) {
+ return decl.first;
+ }
+ count += decl.second;
+ }
+ assert(i < count);
+ return Type::Any;
+}
+
+Type Func::GetLocalType(Index index) const {
+ Index num_params = decl.GetNumParams();
+ if (index < num_params) {
+ return GetParamType(index);
+ } else {
+ index -= num_params;
+ assert(index < local_types.size());
+ return local_types[index];
+ }
+}
+
+Type Func::GetLocalType(const Var& var) const {
+ return GetLocalType(GetLocalIndex(var));
+}
+
+Index Func::GetLocalIndex(const Var& var) const {
+ if (var.is_index()) {
+ return var.index();
+ }
+ return bindings.FindIndex(var);
+}
+
+const Func* Module::GetFunc(const Var& var) const {
+ return const_cast<Module*>(this)->GetFunc(var);
+}
+
+Func* Module::GetFunc(const Var& var) {
+ Index index = func_bindings.FindIndex(var);
+ if (index >= funcs.size()) {
+ return nullptr;
+ }
+ return funcs[index];
+}
+
+const Global* Module::GetGlobal(const Var& var) const {
+ return const_cast<Module*>(this)->GetGlobal(var);
+}
+
+Global* Module::GetGlobal(const Var& var) {
+ Index index = global_bindings.FindIndex(var);
+ if (index >= globals.size()) {
+ return nullptr;
+ }
+ return globals[index];
+}
+
+const Table* Module::GetTable(const Var& var) const {
+ return const_cast<Module*>(this)->GetTable(var);
+}
+
+Table* Module::GetTable(const Var& var) {
+ Index index = table_bindings.FindIndex(var);
+ if (index >= tables.size()) {
+ return nullptr;
+ }
+ return tables[index];
+}
+
+const Memory* Module::GetMemory(const Var& var) const {
+ return const_cast<Module*>(this)->GetMemory(var);
+}
+
+Memory* Module::GetMemory(const Var& var) {
+ Index index = memory_bindings.FindIndex(var);
+ if (index >= memories.size()) {
+ return nullptr;
+ }
+ return memories[index];
+}
+
+Tag* Module::GetTag(const Var& var) const {
+ Index index = GetTagIndex(var);
+ if (index >= tags.size()) {
+ return nullptr;
+ }
+ return tags[index];
+}
+
+const DataSegment* Module::GetDataSegment(const Var& var) const {
+ return const_cast<Module*>(this)->GetDataSegment(var);
+}
+
+DataSegment* Module::GetDataSegment(const Var& var) {
+ Index index = data_segment_bindings.FindIndex(var);
+ if (index >= data_segments.size()) {
+ return nullptr;
+ }
+ return data_segments[index];
+}
+
+const ElemSegment* Module::GetElemSegment(const Var& var) const {
+ return const_cast<Module*>(this)->GetElemSegment(var);
+}
+
+ElemSegment* Module::GetElemSegment(const Var& var) {
+ Index index = elem_segment_bindings.FindIndex(var);
+ if (index >= elem_segments.size()) {
+ return nullptr;
+ }
+ return elem_segments[index];
+}
+
+const FuncType* Module::GetFuncType(const Var& var) const {
+ return const_cast<Module*>(this)->GetFuncType(var);
+}
+
+FuncType* Module::GetFuncType(const Var& var) {
+ Index index = type_bindings.FindIndex(var);
+ if (index >= types.size()) {
+ return nullptr;
+ }
+ return dyn_cast<FuncType>(types[index]);
+}
+
+Index Module::GetFuncTypeIndex(const FuncSignature& sig) const {
+ for (size_t i = 0; i < types.size(); ++i) {
+ if (auto* func_type = dyn_cast<FuncType>(types[i])) {
+ if (func_type->sig == sig) {
+ return i;
+ }
+ }
+ }
+ return kInvalidIndex;
+}
+
+Index Module::GetFuncTypeIndex(const FuncDeclaration& decl) const {
+ if (decl.has_func_type) {
+ return GetFuncTypeIndex(decl.type_var);
+ } else {
+ return GetFuncTypeIndex(decl.sig);
+ }
+}
+
+void Module::AppendField(std::unique_ptr<DataSegmentModuleField> field) {
+ DataSegment& data_segment = field->data_segment;
+ if (!data_segment.name.empty()) {
+ data_segment_bindings.emplace(data_segment.name,
+ Binding(field->loc, data_segments.size()));
+ }
+ data_segments.push_back(&data_segment);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ElemSegmentModuleField> field) {
+ ElemSegment& elem_segment = field->elem_segment;
+ if (!elem_segment.name.empty()) {
+ elem_segment_bindings.emplace(elem_segment.name,
+ Binding(field->loc, elem_segments.size()));
+ }
+ elem_segments.push_back(&elem_segment);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TagModuleField> field) {
+ Tag& tag = field->tag;
+ if (!tag.name.empty()) {
+ tag_bindings.emplace(tag.name, Binding(field->loc, tags.size()));
+ }
+ tags.push_back(&tag);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ExportModuleField> field) {
+ // Exported names are allowed to be empty.
+ Export& export_ = field->export_;
+ export_bindings.emplace(export_.name, Binding(field->loc, exports.size()));
+ exports.push_back(&export_);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<FuncModuleField> field) {
+ Func& func = field->func;
+ if (!func.name.empty()) {
+ func_bindings.emplace(func.name, Binding(field->loc, funcs.size()));
+ }
+ funcs.push_back(&func);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TypeModuleField> field) {
+ TypeEntry& type = *field->type;
+ if (!type.name.empty()) {
+ type_bindings.emplace(type.name, Binding(field->loc, types.size()));
+ }
+ types.push_back(&type);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<GlobalModuleField> field) {
+ Global& global = field->global;
+ if (!global.name.empty()) {
+ global_bindings.emplace(global.name, Binding(field->loc, globals.size()));
+ }
+ globals.push_back(&global);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ImportModuleField> field) {
+ Import* import = field->import.get();
+ const std::string* name = nullptr;
+ BindingHash* bindings = nullptr;
+ Index index = kInvalidIndex;
+
+ switch (import->kind()) {
+ case ExternalKind::Func: {
+ Func& func = cast<FuncImport>(import)->func;
+ name = &func.name;
+ bindings = &func_bindings;
+ index = funcs.size();
+ funcs.push_back(&func);
+ ++num_func_imports;
+ break;
+ }
+
+ case ExternalKind::Table: {
+ Table& table = cast<TableImport>(import)->table;
+ name = &table.name;
+ bindings = &table_bindings;
+ index = tables.size();
+ tables.push_back(&table);
+ ++num_table_imports;
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ Memory& memory = cast<MemoryImport>(import)->memory;
+ name = &memory.name;
+ bindings = &memory_bindings;
+ index = memories.size();
+ memories.push_back(&memory);
+ ++num_memory_imports;
+ break;
+ }
+
+ case ExternalKind::Global: {
+ Global& global = cast<GlobalImport>(import)->global;
+ name = &global.name;
+ bindings = &global_bindings;
+ index = globals.size();
+ globals.push_back(&global);
+ ++num_global_imports;
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ Tag& tag = cast<TagImport>(import)->tag;
+ name = &tag.name;
+ bindings = &tag_bindings;
+ index = tags.size();
+ tags.push_back(&tag);
+ ++num_tag_imports;
+ break;
+ }
+ }
+
+ assert(name && bindings && index != kInvalidIndex);
+ if (!name->empty()) {
+ bindings->emplace(*name, Binding(field->loc, index));
+ }
+ imports.push_back(import);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<MemoryModuleField> field) {
+ Memory& memory = field->memory;
+ if (!memory.name.empty()) {
+ memory_bindings.emplace(memory.name, Binding(field->loc, memories.size()));
+ }
+ memories.push_back(&memory);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<StartModuleField> field) {
+ starts.push_back(&field->start);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<TableModuleField> field) {
+ Table& table = field->table;
+ if (!table.name.empty()) {
+ table_bindings.emplace(table.name, Binding(field->loc, tables.size()));
+ }
+ tables.push_back(&table);
+ fields.push_back(std::move(field));
+}
+
+void Module::AppendField(std::unique_ptr<ModuleField> field) {
+ switch (field->type()) {
+ case ModuleFieldType::Func:
+ AppendField(cast<FuncModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Global:
+ AppendField(cast<GlobalModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Import:
+ AppendField(cast<ImportModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Export:
+ AppendField(cast<ExportModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Type:
+ AppendField(cast<TypeModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Table:
+ AppendField(cast<TableModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::ElemSegment:
+ AppendField(cast<ElemSegmentModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Memory:
+ AppendField(cast<MemoryModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::DataSegment:
+ AppendField(cast<DataSegmentModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Start:
+ AppendField(cast<StartModuleField>(std::move(field)));
+ break;
+
+ case ModuleFieldType::Tag:
+ AppendField(cast<TagModuleField>(std::move(field)));
+ break;
+ }
+}
+
+void Module::AppendFields(ModuleFieldList* fields) {
+ while (!fields->empty())
+ AppendField(std::unique_ptr<ModuleField>(fields->extract_front()));
+}
+
+const Module* Script::GetFirstModule() const {
+ return const_cast<Script*>(this)->GetFirstModule();
+}
+
+Module* Script::GetFirstModule() {
+ for (const std::unique_ptr<Command>& command : commands) {
+ if (auto* module_command = dyn_cast<ModuleCommand>(command.get())) {
+ return &module_command->module;
+ }
+ }
+ return nullptr;
+}
+
+const Module* Script::GetModule(const Var& var) const {
+ Index index = module_bindings.FindIndex(var);
+ if (index >= commands.size()) {
+ return nullptr;
+ }
+ auto* command = cast<ModuleCommand>(commands[index].get());
+ return &command->module;
+}
+
+void MakeTypeBindingReverseMapping(
+ size_t num_types,
+ const BindingHash& bindings,
+ std::vector<std::string>* out_reverse_mapping) {
+ out_reverse_mapping->clear();
+ out_reverse_mapping->resize(num_types);
+ for (const auto& pair : bindings) {
+ assert(static_cast<size_t>(pair.second.index) <
+ out_reverse_mapping->size());
+ (*out_reverse_mapping)[pair.second.index] = pair.first;
+ }
+}
+
+Var::Var(Index index, const Location& loc)
+ : loc(loc), type_(VarType::Index), index_(index) {}
+
+Var::Var(string_view name, const Location& loc)
+ : loc(loc), type_(VarType::Name), name_(name) {}
+
+Var::Var(Var&& rhs) : Var(kInvalidIndex) {
+ *this = std::move(rhs);
+}
+
+Var::Var(const Var& rhs) : Var(kInvalidIndex) {
+ *this = rhs;
+}
+
+Var& Var::operator=(Var&& rhs) {
+ loc = rhs.loc;
+ if (rhs.is_index()) {
+ set_index(rhs.index_);
+ } else {
+ set_name(rhs.name_);
+ }
+ return *this;
+}
+
+Var& Var::operator=(const Var& rhs) {
+ loc = rhs.loc;
+ if (rhs.is_index()) {
+ set_index(rhs.index_);
+ } else {
+ set_name(rhs.name_);
+ }
+ return *this;
+}
+
+Var::~Var() {
+ Destroy();
+}
+
+void Var::set_index(Index index) {
+ Destroy();
+ type_ = VarType::Index;
+ index_ = index;
+}
+
+void Var::set_name(std::string&& name) {
+ Destroy();
+ type_ = VarType::Name;
+ Construct(name_, std::move(name));
+}
+
+void Var::set_name(string_view name) {
+ set_name(name.to_string());
+}
+
+void Var::Destroy() {
+ if (is_name()) {
+ Destruct(name_);
+ }
+}
+
+uint8_t ElemSegment::GetFlags(const Module* module) const {
+ uint8_t flags = 0;
+
+ bool all_ref_func = elem_type == Type::FuncRef;
+
+ switch (kind) {
+ case SegmentKind::Active: {
+ Index table_index = module->GetTableIndex(table_var);
+ if (table_index != 0) {
+ flags |= SegExplicitIndex;
+ }
+ break;
+ }
+
+ case SegmentKind::Passive:
+ flags |= SegPassive;
+ break;
+
+ case SegmentKind::Declared:
+ flags |= SegDeclared;
+ break;
+ }
+
+ all_ref_func = all_ref_func &&
+ std::all_of(elem_exprs.begin(), elem_exprs.end(),
+ [](const ElemExpr& elem_expr) {
+ return elem_expr.kind == ElemExprKind::RefFunc;
+ });
+ if (!all_ref_func) {
+ flags |= SegUseElemExprs;
+ }
+
+ return flags;
+}
+
+uint8_t DataSegment::GetFlags(const Module* module) const {
+ uint8_t flags = 0;
+
+ if (kind == SegmentKind::Passive) {
+ flags |= SegPassive;
+ }
+
+ Index memory_index = module->GetMemoryIndex(memory_var);
+ if (memory_index != 0) {
+ flags |= SegExplicitIndex;
+ }
+
+ return flags;
+}
+
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/ir.h b/third_party/wasm2c/src/ir.h
new file mode 100644
index 0000000000..3c63128456
--- /dev/null
+++ b/third_party/wasm2c/src/ir.h
@@ -0,0 +1,1354 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_IR_H_
+#define WABT_IR_H_
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "src/binding-hash.h"
+#include "src/common.h"
+#include "src/intrusive-list.h"
+#include "src/opcode.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+struct Module;
+
+enum class VarType {
+ Index,
+ Name,
+};
+
+struct Var {
+ explicit Var(Index index = kInvalidIndex, const Location& loc = Location());
+ explicit Var(string_view name, const Location& loc = Location());
+ Var(Var&&);
+ Var(const Var&);
+ Var& operator=(const Var&);
+ Var& operator=(Var&&);
+ ~Var();
+
+ VarType type() const { return type_; }
+ bool is_index() const { return type_ == VarType::Index; }
+ bool is_name() const { return type_ == VarType::Name; }
+
+ Index index() const { assert(is_index()); return index_; }
+ const std::string& name() const { assert(is_name()); return name_; }
+
+ void set_index(Index);
+ void set_name(std::string&&);
+ void set_name(string_view);
+
+ Location loc;
+
+ private:
+ void Destroy();
+
+ VarType type_;
+ union {
+ Index index_;
+ std::string name_;
+ };
+};
+typedef std::vector<Var> VarVector;
+
+struct Const {
+ static constexpr uintptr_t kRefNullBits = ~uintptr_t(0);
+
+ Const() : Const(Type::I32, uint32_t(0)) {}
+
+ static Const I32(uint32_t val = 0, const Location& loc = Location()) {
+ return Const(Type::I32, val, loc);
+ }
+ static Const I64(uint64_t val = 0, const Location& loc = Location()) {
+ return Const(Type::I64, val, loc);
+ }
+ static Const F32(uint32_t val = 0, const Location& loc = Location()) {
+ return Const(Type::F32, val, loc);
+ }
+ static Const F64(uint64_t val = 0, const Location& loc = Location()) {
+ return Const(Type::F64, val, loc);
+ }
+ static Const V128(v128 val, const Location& loc = Location()) {
+ return Const(Type::V128, val, loc);
+ }
+
+ Type type() const { return type_; }
+ Type lane_type() const { assert(type_ == Type::V128); return lane_type_; }
+
+ int lane_count() const {
+ switch (lane_type()) {
+ case Type::I8: return 16;
+ case Type::I16: return 8;
+ case Type::I32: return 4;
+ case Type::I64: return 2;
+ case Type::F32: return 4;
+ case Type::F64: return 2;
+ default: WABT_UNREACHABLE;
+ }
+ }
+
+ uint32_t u32() const { return data_.u32(0); }
+ uint64_t u64() const { return data_.u64(0); }
+ uint32_t f32_bits() const { return data_.f32_bits(0); }
+ uint64_t f64_bits() const { return data_.f64_bits(0); }
+ uintptr_t ref_bits() const { return data_.To<uintptr_t>(0); }
+ v128 vec128() const { return data_; }
+
+ template <typename T>
+ T v128_lane(int lane) const { return data_.To<T>(lane); }
+
+ void set_u32(uint32_t x) { From(Type::I32, x); }
+ void set_u64(uint64_t x) { From(Type::I64, x); }
+ void set_f32(uint32_t x) { From(Type::F32, x); }
+ void set_f64(uint64_t x) { From(Type::F64, x); }
+
+ void set_v128_u8(int lane, uint8_t x) { set_v128_lane(lane, Type::I8, x); }
+ void set_v128_u16(int lane, uint16_t x) { set_v128_lane(lane, Type::I16, x); }
+ void set_v128_u32(int lane, uint32_t x) { set_v128_lane(lane, Type::I32, x); }
+ void set_v128_u64(int lane, uint64_t x) { set_v128_lane(lane, Type::I64, x); }
+ void set_v128_f32(int lane, uint32_t x) { set_v128_lane(lane, Type::F32, x); }
+ void set_v128_f64(int lane, uint64_t x) { set_v128_lane(lane, Type::F64, x); }
+
+ // Only used for expectations. (e.g. wast assertions)
+ void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); }
+ void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); }
+ void set_funcref() { From<uintptr_t>(Type::FuncRef, 0); }
+ void set_externref(uintptr_t x) { From(Type::ExternRef, x); }
+ void set_null(Type type) { From<uintptr_t>(type, kRefNullBits); }
+
+ bool is_expected_nan(int lane = 0) const {
+ return expected_nan(lane) != ExpectedNan::None;
+ }
+
+ ExpectedNan expected_nan(int lane = 0) const {
+ return lane < 4 ? nan_[lane] : ExpectedNan::None;
+ }
+
+ void set_expected_nan(int lane, ExpectedNan nan) {
+ if (lane < 4) {
+ nan_[lane] = nan;
+ }
+ }
+
+ // v128 support
+ Location loc;
+
+ private:
+ template <typename T>
+ void set_v128_lane(int lane, Type lane_type, T x) {
+ lane_type_ = lane_type;
+ From(Type::V128, x, lane);
+ set_expected_nan(lane, ExpectedNan::None);
+ }
+
+ template <typename T>
+ Const(Type type, T data, const Location& loc = Location()) : loc(loc) {
+ From<T>(type, data);
+ }
+
+ template <typename T>
+ void From(Type type, T data, int lane = 0) {
+ static_assert(sizeof(T) <= sizeof(data_), "Invalid cast!");
+ assert((lane + 1) * sizeof(T) <= sizeof(data_));
+ type_ = type;
+ data_.From<T>(lane, data);
+ set_expected_nan(lane, ExpectedNan::None);
+ }
+
+ Type type_;
+ Type lane_type_; // Only valid if type_ == Type::V128.
+ v128 data_;
+ ExpectedNan nan_[4];
+};
+typedef std::vector<Const> ConstVector;
+
+struct FuncSignature {
+ TypeVector param_types;
+ TypeVector result_types;
+
+ Index GetNumParams() const { return param_types.size(); }
+ Index GetNumResults() const { return result_types.size(); }
+ Type GetParamType(Index index) const { return param_types[index]; }
+ Type GetResultType(Index index) const { return result_types[index]; }
+
+ bool operator==(const FuncSignature&) const;
+};
+
+enum class TypeEntryKind {
+ Func,
+ Struct,
+ Array,
+};
+
+class TypeEntry {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+
+ virtual ~TypeEntry() = default;
+
+ TypeEntryKind kind() const { return kind_; }
+
+ Location loc;
+ std::string name;
+
+ protected:
+ explicit TypeEntry(TypeEntryKind kind,
+ string_view name = string_view(),
+ const Location& loc = Location())
+ : loc(loc), name(name.to_string()), kind_(kind) {}
+
+ TypeEntryKind kind_;
+};
+
+class FuncType : public TypeEntry {
+ public:
+ static bool classof(const TypeEntry* entry) {
+ return entry->kind() == TypeEntryKind::Func;
+ }
+
+ explicit FuncType(string_view name = string_view())
+ : TypeEntry(TypeEntryKind::Func, name) {}
+
+ Index GetNumParams() const { return sig.GetNumParams(); }
+ Index GetNumResults() const { return sig.GetNumResults(); }
+ Type GetParamType(Index index) const { return sig.GetParamType(index); }
+ Type GetResultType(Index index) const { return sig.GetResultType(index); }
+
+ FuncSignature sig;
+};
+
+struct Field {
+ std::string name;
+ Type type = Type::Void;
+ bool mutable_ = false;
+};
+
+class StructType : public TypeEntry {
+ public:
+ static bool classof(const TypeEntry* entry) {
+ return entry->kind() == TypeEntryKind::Struct;
+ }
+
+ explicit StructType(string_view name = string_view())
+ : TypeEntry(TypeEntryKind::Struct) {}
+
+ std::vector<Field> fields;
+};
+
+class ArrayType : public TypeEntry {
+ public:
+ static bool classof(const TypeEntry* entry) {
+ return entry->kind() == TypeEntryKind::Array;
+ }
+
+ explicit ArrayType(string_view name = string_view())
+ : TypeEntry(TypeEntryKind::Array) {}
+
+ Field field;
+};
+
+struct FuncDeclaration {
+ Index GetNumParams() const { return sig.GetNumParams(); }
+ Index GetNumResults() const { return sig.GetNumResults(); }
+ Type GetParamType(Index index) const { return sig.GetParamType(index); }
+ Type GetResultType(Index index) const { return sig.GetResultType(index); }
+
+ bool has_func_type = false;
+ Var type_var;
+ FuncSignature sig;
+};
+
+enum class ExprType {
+ AtomicLoad,
+ AtomicRmw,
+ AtomicRmwCmpxchg,
+ AtomicStore,
+ AtomicNotify,
+ AtomicFence,
+ AtomicWait,
+ Binary,
+ Block,
+ Br,
+ BrIf,
+ BrTable,
+ Call,
+ CallIndirect,
+ CallRef,
+ Compare,
+ Const,
+ Convert,
+ Drop,
+ GlobalGet,
+ GlobalSet,
+ If,
+ Load,
+ LocalGet,
+ LocalSet,
+ LocalTee,
+ Loop,
+ MemoryCopy,
+ DataDrop,
+ MemoryFill,
+ MemoryGrow,
+ MemoryInit,
+ MemorySize,
+ Nop,
+ RefIsNull,
+ RefFunc,
+ RefNull,
+ Rethrow,
+ Return,
+ ReturnCall,
+ ReturnCallIndirect,
+ Select,
+ SimdLaneOp,
+ SimdLoadLane,
+ SimdStoreLane,
+ SimdShuffleOp,
+ LoadSplat,
+ LoadZero,
+ Store,
+ TableCopy,
+ ElemDrop,
+ TableInit,
+ TableGet,
+ TableGrow,
+ TableSize,
+ TableSet,
+ TableFill,
+ Ternary,
+ Throw,
+ Try,
+ Unary,
+ Unreachable,
+
+ First = AtomicLoad,
+ Last = Unreachable
+};
+
+const char* GetExprTypeName(ExprType type);
+
+class Expr;
+typedef intrusive_list<Expr> ExprList;
+
+typedef FuncDeclaration BlockDeclaration;
+
+struct Block {
+ Block() = default;
+ explicit Block(ExprList exprs) : exprs(std::move(exprs)) {}
+
+ std::string label;
+ BlockDeclaration decl;
+ ExprList exprs;
+ Location end_loc;
+};
+
+struct Catch {
+ explicit Catch(const Location& loc = Location()) : loc(loc) {}
+ explicit Catch(const Var& var, const Location& loc = Location())
+ : loc(loc), var(var) {}
+ Location loc;
+ Var var;
+ ExprList exprs;
+ bool IsCatchAll() const {
+ return var.is_index() && var.index() == kInvalidIndex;
+ }
+};
+typedef std::vector<Catch> CatchVector;
+
+enum class TryKind {
+ Plain,
+ Catch,
+ Delegate
+};
+
+class Expr : public intrusive_list_base<Expr> {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Expr);
+ Expr() = delete;
+ virtual ~Expr() = default;
+
+ ExprType type() const { return type_; }
+
+ Location loc;
+
+ protected:
+ explicit Expr(ExprType type, const Location& loc = Location())
+ : loc(loc), type_(type) {}
+
+ ExprType type_;
+};
+
+const char* GetExprTypeName(const Expr& expr);
+
+template <ExprType TypeEnum>
+class ExprMixin : public Expr {
+ public:
+ static bool classof(const Expr* expr) { return expr->type() == TypeEnum; }
+
+ explicit ExprMixin(const Location& loc = Location()) : Expr(TypeEnum, loc) {}
+};
+
+typedef ExprMixin<ExprType::Drop> DropExpr;
+typedef ExprMixin<ExprType::MemoryGrow> MemoryGrowExpr;
+typedef ExprMixin<ExprType::MemorySize> MemorySizeExpr;
+typedef ExprMixin<ExprType::MemoryCopy> MemoryCopyExpr;
+typedef ExprMixin<ExprType::MemoryFill> MemoryFillExpr;
+typedef ExprMixin<ExprType::Nop> NopExpr;
+typedef ExprMixin<ExprType::Return> ReturnExpr;
+typedef ExprMixin<ExprType::Unreachable> UnreachableExpr;
+
+template <ExprType TypeEnum>
+class RefTypeExpr : public ExprMixin<TypeEnum> {
+ public:
+ RefTypeExpr(Type type, const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc), type(type) {}
+
+ Type type;
+};
+
+typedef RefTypeExpr<ExprType::RefNull> RefNullExpr;
+typedef ExprMixin<ExprType::RefIsNull> RefIsNullExpr;
+
+template <ExprType TypeEnum>
+class OpcodeExpr : public ExprMixin<TypeEnum> {
+ public:
+ OpcodeExpr(Opcode opcode, const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc), opcode(opcode) {}
+
+ Opcode opcode;
+};
+
+typedef OpcodeExpr<ExprType::Binary> BinaryExpr;
+typedef OpcodeExpr<ExprType::Compare> CompareExpr;
+typedef OpcodeExpr<ExprType::Convert> ConvertExpr;
+typedef OpcodeExpr<ExprType::Unary> UnaryExpr;
+typedef OpcodeExpr<ExprType::Ternary> TernaryExpr;
+
+class SimdLaneOpExpr : public ExprMixin<ExprType::SimdLaneOp> {
+ public:
+ SimdLaneOpExpr(Opcode opcode, uint64_t val, const Location& loc = Location())
+ : ExprMixin<ExprType::SimdLaneOp>(loc), opcode(opcode), val(val) {}
+
+ Opcode opcode;
+ uint64_t val;
+};
+
+class SimdLoadLaneExpr : public OpcodeExpr<ExprType::SimdLoadLane> {
+ public:
+ SimdLoadLaneExpr(Opcode opcode,
+ Address align,
+ Address offset,
+ uint64_t val,
+ const Location& loc = Location())
+ : OpcodeExpr<ExprType::SimdLoadLane>(opcode, loc),
+ align(align),
+ offset(offset),
+ val(val) {}
+
+ Address align;
+ Address offset;
+ uint64_t val;
+};
+
+class SimdStoreLaneExpr : public OpcodeExpr<ExprType::SimdStoreLane> {
+ public:
+ SimdStoreLaneExpr(Opcode opcode,
+ Address align,
+ Address offset,
+ uint64_t val,
+ const Location& loc = Location())
+ : OpcodeExpr<ExprType::SimdStoreLane>(opcode, loc),
+ align(align),
+ offset(offset),
+ val(val) {}
+
+ Address align;
+ Address offset;
+ uint64_t val;
+};
+
+class SimdShuffleOpExpr : public ExprMixin<ExprType::SimdShuffleOp> {
+ public:
+ SimdShuffleOpExpr(Opcode opcode, v128 val, const Location& loc = Location())
+ : ExprMixin<ExprType::SimdShuffleOp>(loc), opcode(opcode), val(val) {}
+
+ Opcode opcode;
+ v128 val;
+};
+
+template <ExprType TypeEnum>
+class VarExpr : public ExprMixin<TypeEnum> {
+ public:
+ VarExpr(const Var& var, const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc), var(var) {}
+
+ Var var;
+};
+
+typedef VarExpr<ExprType::Br> BrExpr;
+typedef VarExpr<ExprType::BrIf> BrIfExpr;
+typedef VarExpr<ExprType::Call> CallExpr;
+typedef VarExpr<ExprType::RefFunc> RefFuncExpr;
+typedef VarExpr<ExprType::GlobalGet> GlobalGetExpr;
+typedef VarExpr<ExprType::GlobalSet> GlobalSetExpr;
+typedef VarExpr<ExprType::LocalGet> LocalGetExpr;
+typedef VarExpr<ExprType::LocalSet> LocalSetExpr;
+typedef VarExpr<ExprType::LocalTee> LocalTeeExpr;
+typedef VarExpr<ExprType::ReturnCall> ReturnCallExpr;
+typedef VarExpr<ExprType::Throw> ThrowExpr;
+typedef VarExpr<ExprType::Rethrow> RethrowExpr;
+
+typedef VarExpr<ExprType::MemoryInit> MemoryInitExpr;
+typedef VarExpr<ExprType::DataDrop> DataDropExpr;
+typedef VarExpr<ExprType::ElemDrop> ElemDropExpr;
+typedef VarExpr<ExprType::TableGet> TableGetExpr;
+typedef VarExpr<ExprType::TableSet> TableSetExpr;
+typedef VarExpr<ExprType::TableGrow> TableGrowExpr;
+typedef VarExpr<ExprType::TableSize> TableSizeExpr;
+typedef VarExpr<ExprType::TableFill> TableFillExpr;
+
+class SelectExpr : public ExprMixin<ExprType::Select> {
+ public:
+ SelectExpr(TypeVector type, const Location& loc = Location())
+ : ExprMixin<ExprType::Select>(loc), result_type(type) {}
+ TypeVector result_type;
+};
+
+class TableInitExpr : public ExprMixin<ExprType::TableInit> {
+ public:
+ TableInitExpr(const Var& segment_index,
+ const Var& table_index,
+ const Location& loc = Location())
+ : ExprMixin<ExprType::TableInit>(loc),
+ segment_index(segment_index),
+ table_index(table_index) {}
+
+ Var segment_index;
+ Var table_index;
+};
+
+class TableCopyExpr : public ExprMixin<ExprType::TableCopy> {
+ public:
+ TableCopyExpr(const Var& dst,
+ const Var& src,
+ const Location& loc = Location())
+ : ExprMixin<ExprType::TableCopy>(loc), dst_table(dst), src_table(src) {}
+
+ Var dst_table;
+ Var src_table;
+};
+
+class CallIndirectExpr : public ExprMixin<ExprType::CallIndirect> {
+ public:
+ explicit CallIndirectExpr(const Location& loc = Location())
+ : ExprMixin<ExprType::CallIndirect>(loc) {}
+
+ FuncDeclaration decl;
+ Var table;
+};
+
+class ReturnCallIndirectExpr : public ExprMixin<ExprType::ReturnCallIndirect> {
+ public:
+ explicit ReturnCallIndirectExpr(const Location &loc = Location())
+ : ExprMixin<ExprType::ReturnCallIndirect>(loc) {}
+
+ FuncDeclaration decl;
+ Var table;
+};
+
+class CallRefExpr : public ExprMixin<ExprType::CallRef> {
+ public:
+ explicit CallRefExpr(const Location &loc = Location())
+ : ExprMixin<ExprType::CallRef>(loc) {}
+
+ // This field is setup only during Validate phase,
+ // so keep that in mind when you use it.
+ Var function_type_index;
+};
+
+template <ExprType TypeEnum>
+class BlockExprBase : public ExprMixin<TypeEnum> {
+ public:
+ explicit BlockExprBase(const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc) {}
+
+ Block block;
+};
+
+typedef BlockExprBase<ExprType::Block> BlockExpr;
+typedef BlockExprBase<ExprType::Loop> LoopExpr;
+
+class IfExpr : public ExprMixin<ExprType::If> {
+ public:
+ explicit IfExpr(const Location& loc = Location())
+ : ExprMixin<ExprType::If>(loc) {}
+
+ Block true_;
+ ExprList false_;
+ Location false_end_loc;
+};
+
+class TryExpr : public ExprMixin<ExprType::Try> {
+ public:
+ explicit TryExpr(const Location& loc = Location())
+ : ExprMixin<ExprType::Try>(loc), kind(TryKind::Plain) {}
+
+ TryKind kind;
+ Block block;
+ CatchVector catches;
+ Var delegate_target;
+};
+
+class BrTableExpr : public ExprMixin<ExprType::BrTable> {
+ public:
+ BrTableExpr(const Location& loc = Location())
+ : ExprMixin<ExprType::BrTable>(loc) {}
+
+ VarVector targets;
+ Var default_target;
+};
+
+class ConstExpr : public ExprMixin<ExprType::Const> {
+ public:
+ ConstExpr(const Const& c, const Location& loc = Location())
+ : ExprMixin<ExprType::Const>(loc), const_(c) {}
+
+ Const const_;
+};
+
+// TODO(binji): Rename this, it is used for more than loads/stores now.
+template <ExprType TypeEnum>
+class LoadStoreExpr : public ExprMixin<TypeEnum> {
+ public:
+ LoadStoreExpr(Opcode opcode,
+ Address align,
+ Address offset,
+ const Location& loc = Location())
+ : ExprMixin<TypeEnum>(loc),
+ opcode(opcode),
+ align(align),
+ offset(offset) {}
+
+ Opcode opcode;
+ Address align;
+ Address offset;
+};
+
+typedef LoadStoreExpr<ExprType::Load> LoadExpr;
+typedef LoadStoreExpr<ExprType::Store> StoreExpr;
+typedef LoadStoreExpr<ExprType::AtomicLoad> AtomicLoadExpr;
+typedef LoadStoreExpr<ExprType::AtomicStore> AtomicStoreExpr;
+typedef LoadStoreExpr<ExprType::AtomicRmw> AtomicRmwExpr;
+typedef LoadStoreExpr<ExprType::AtomicRmwCmpxchg> AtomicRmwCmpxchgExpr;
+typedef LoadStoreExpr<ExprType::AtomicWait> AtomicWaitExpr;
+typedef LoadStoreExpr<ExprType::AtomicNotify> AtomicNotifyExpr;
+typedef LoadStoreExpr<ExprType::LoadSplat> LoadSplatExpr;
+typedef LoadStoreExpr<ExprType::LoadZero> LoadZeroExpr;
+
+class AtomicFenceExpr : public ExprMixin<ExprType::AtomicFence> {
+ public:
+ explicit AtomicFenceExpr(uint32_t consistency_model,
+ const Location& loc = Location())
+ : ExprMixin<ExprType::AtomicFence>(loc),
+ consistency_model(consistency_model) {}
+
+ uint32_t consistency_model;
+};
+
+struct Tag {
+ explicit Tag(string_view name) : name(name.to_string()) {}
+
+ std::string name;
+ FuncDeclaration decl;
+};
+
+class LocalTypes {
+ public:
+ typedef std::pair<Type, Index> Decl;
+ typedef std::vector<Decl> Decls;
+
+ struct const_iterator {
+ const_iterator(Decls::const_iterator decl, Index index)
+ : decl(decl), index(index) {}
+ Type operator*() const { return decl->first; }
+ const_iterator& operator++();
+ const_iterator operator++(int);
+
+ Decls::const_iterator decl;
+ Index index;
+ };
+
+ void Set(const TypeVector&);
+
+ const Decls& decls() const { return decls_; }
+
+ void AppendDecl(Type type, Index count) {
+ if (count != 0) {
+ decls_.emplace_back(type, count);
+ }
+ }
+
+ Index size() const;
+ Type operator[](Index) const;
+
+ const_iterator begin() const { return {decls_.begin(), 0}; }
+ const_iterator end() const { return {decls_.end(), 0}; }
+
+ private:
+ Decls decls_;
+};
+
+inline LocalTypes::const_iterator& LocalTypes::const_iterator::operator++() {
+ ++index;
+ if (index >= decl->second) {
+ ++decl;
+ index = 0;
+ }
+ return *this;
+}
+
+inline LocalTypes::const_iterator LocalTypes::const_iterator::operator++(int) {
+ const_iterator result = *this;
+ operator++();
+ return result;
+}
+
+inline bool operator==(const LocalTypes::const_iterator& lhs,
+ const LocalTypes::const_iterator& rhs) {
+ return lhs.decl == rhs.decl && lhs.index == rhs.index;
+}
+
+inline bool operator!=(const LocalTypes::const_iterator& lhs,
+ const LocalTypes::const_iterator& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+struct Func {
+ explicit Func(string_view name) : name(name.to_string()) {}
+
+ Type GetParamType(Index index) const { return decl.GetParamType(index); }
+ Type GetResultType(Index index) const { return decl.GetResultType(index); }
+ Type GetLocalType(Index index) const;
+ Type GetLocalType(const Var& var) const;
+ Index GetNumParams() const { return decl.GetNumParams(); }
+ Index GetNumLocals() const { return local_types.size(); }
+ Index GetNumParamsAndLocals() const {
+ return GetNumParams() + GetNumLocals();
+ }
+ Index GetNumResults() const { return decl.GetNumResults(); }
+ Index GetLocalIndex(const Var&) const;
+
+ std::string name;
+ FuncDeclaration decl;
+ LocalTypes local_types;
+ BindingHash bindings;
+ ExprList exprs;
+};
+
+struct Global {
+ explicit Global(string_view name) : name(name.to_string()) {}
+
+ std::string name;
+ Type type = Type::Void;
+ bool mutable_ = false;
+ ExprList init_expr;
+};
+
+struct Table {
+ explicit Table(string_view name)
+ : name(name.to_string()), elem_type(Type::FuncRef) {}
+
+ std::string name;
+ Limits elem_limits;
+ Type elem_type;
+};
+
+enum class ElemExprKind {
+ RefNull,
+ RefFunc,
+};
+
+struct ElemExpr {
+ ElemExpr() : kind(ElemExprKind::RefNull), type(Type::FuncRef) {}
+ explicit ElemExpr(Var var) : kind(ElemExprKind::RefFunc), var(var) {}
+ explicit ElemExpr(Type type) : kind(ElemExprKind::RefNull), type(type) {}
+
+ ElemExprKind kind;
+ Var var; // Only used when kind == RefFunc.
+ Type type; // Only used when kind == RefNull
+};
+
+typedef std::vector<ElemExpr> ElemExprVector;
+
+struct ElemSegment {
+ explicit ElemSegment(string_view name) : name(name.to_string()) {}
+ uint8_t GetFlags(const Module*) const;
+
+ SegmentKind kind = SegmentKind::Active;
+ std::string name;
+ Var table_var;
+ Type elem_type;
+ ExprList offset;
+ ElemExprVector elem_exprs;
+};
+
+struct Memory {
+ explicit Memory(string_view name) : name(name.to_string()) {}
+
+ std::string name;
+ Limits page_limits;
+};
+
+struct DataSegment {
+ explicit DataSegment(string_view name) : name(name.to_string()) {}
+ uint8_t GetFlags(const Module*) const;
+
+ SegmentKind kind = SegmentKind::Active;
+ std::string name;
+ Var memory_var;
+ ExprList offset;
+ std::vector<uint8_t> data;
+};
+
+class Import {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Import);
+ Import() = delete;
+ virtual ~Import() = default;
+
+ ExternalKind kind() const { return kind_; }
+
+ std::string module_name;
+ std::string field_name;
+
+ protected:
+ Import(ExternalKind kind) : kind_(kind) {}
+
+ ExternalKind kind_;
+};
+
+template <ExternalKind TypeEnum>
+class ImportMixin : public Import {
+ public:
+ static bool classof(const Import* import) {
+ return import->kind() == TypeEnum;
+ }
+
+ ImportMixin() : Import(TypeEnum) {}
+};
+
+class FuncImport : public ImportMixin<ExternalKind::Func> {
+ public:
+ explicit FuncImport(string_view name = string_view())
+ : ImportMixin<ExternalKind::Func>(), func(name) {}
+
+ Func func;
+};
+
+class TableImport : public ImportMixin<ExternalKind::Table> {
+ public:
+ explicit TableImport(string_view name = string_view())
+ : ImportMixin<ExternalKind::Table>(), table(name) {}
+
+ Table table;
+};
+
+class MemoryImport : public ImportMixin<ExternalKind::Memory> {
+ public:
+ explicit MemoryImport(string_view name = string_view())
+ : ImportMixin<ExternalKind::Memory>(), memory(name) {}
+
+ Memory memory;
+};
+
+class GlobalImport : public ImportMixin<ExternalKind::Global> {
+ public:
+ explicit GlobalImport(string_view name = string_view())
+ : ImportMixin<ExternalKind::Global>(), global(name) {}
+
+ Global global;
+};
+
+class TagImport : public ImportMixin<ExternalKind::Tag> {
+ public:
+ explicit TagImport(string_view name = string_view())
+ : ImportMixin<ExternalKind::Tag>(), tag(name) {}
+
+ Tag tag;
+};
+
+struct Export {
+ std::string name;
+ ExternalKind kind;
+ Var var;
+};
+
+enum class ModuleFieldType {
+ Func,
+ Global,
+ Import,
+ Export,
+ Type,
+ Table,
+ ElemSegment,
+ Memory,
+ DataSegment,
+ Start,
+ Tag
+};
+
+class ModuleField : public intrusive_list_base<ModuleField> {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(ModuleField);
+ ModuleField() = delete;
+ virtual ~ModuleField() = default;
+
+ ModuleFieldType type() const { return type_; }
+
+ Location loc;
+
+ protected:
+ ModuleField(ModuleFieldType type, const Location& loc)
+ : loc(loc), type_(type) {}
+
+ ModuleFieldType type_;
+};
+
+typedef intrusive_list<ModuleField> ModuleFieldList;
+
+template <ModuleFieldType TypeEnum>
+class ModuleFieldMixin : public ModuleField {
+ public:
+ static bool classof(const ModuleField* field) {
+ return field->type() == TypeEnum;
+ }
+
+ explicit ModuleFieldMixin(const Location& loc) : ModuleField(TypeEnum, loc) {}
+};
+
+class FuncModuleField : public ModuleFieldMixin<ModuleFieldType::Func> {
+ public:
+ explicit FuncModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::Func>(loc), func(name) {}
+
+ Func func;
+};
+
+class GlobalModuleField : public ModuleFieldMixin<ModuleFieldType::Global> {
+ public:
+ explicit GlobalModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::Global>(loc), global(name) {}
+
+ Global global;
+};
+
+class ImportModuleField : public ModuleFieldMixin<ModuleFieldType::Import> {
+ public:
+ explicit ImportModuleField(const Location& loc = Location())
+ : ModuleFieldMixin<ModuleFieldType::Import>(loc) {}
+ explicit ImportModuleField(std::unique_ptr<Import> import,
+ const Location& loc = Location())
+ : ModuleFieldMixin<ModuleFieldType::Import>(loc),
+ import(std::move(import)) {}
+
+ std::unique_ptr<Import> import;
+};
+
+class ExportModuleField : public ModuleFieldMixin<ModuleFieldType::Export> {
+ public:
+ explicit ExportModuleField(const Location& loc = Location())
+ : ModuleFieldMixin<ModuleFieldType::Export>(loc) {}
+
+ Export export_;
+};
+
+class TypeModuleField : public ModuleFieldMixin<ModuleFieldType::Type> {
+ public:
+ explicit TypeModuleField(const Location& loc = Location())
+ : ModuleFieldMixin<ModuleFieldType::Type>(loc) {}
+
+ std::unique_ptr<TypeEntry> type;
+};
+
+class TableModuleField : public ModuleFieldMixin<ModuleFieldType::Table> {
+ public:
+ explicit TableModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::Table>(loc), table(name) {}
+
+ Table table;
+};
+
+class ElemSegmentModuleField
+ : public ModuleFieldMixin<ModuleFieldType::ElemSegment> {
+ public:
+ explicit ElemSegmentModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::ElemSegment>(loc),
+ elem_segment(name) {}
+
+ ElemSegment elem_segment;
+};
+
+class MemoryModuleField : public ModuleFieldMixin<ModuleFieldType::Memory> {
+ public:
+ explicit MemoryModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::Memory>(loc), memory(name) {}
+
+ Memory memory;
+};
+
+class DataSegmentModuleField
+ : public ModuleFieldMixin<ModuleFieldType::DataSegment> {
+ public:
+ explicit DataSegmentModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::DataSegment>(loc),
+ data_segment(name) {}
+
+ DataSegment data_segment;
+};
+
+class TagModuleField : public ModuleFieldMixin<ModuleFieldType::Tag> {
+ public:
+ explicit TagModuleField(const Location& loc = Location(),
+ string_view name = string_view())
+ : ModuleFieldMixin<ModuleFieldType::Tag>(loc), tag(name) {}
+
+ Tag tag;
+};
+
+class StartModuleField : public ModuleFieldMixin<ModuleFieldType::Start> {
+ public:
+ explicit StartModuleField(Var start = Var(), const Location& loc = Location())
+ : ModuleFieldMixin<ModuleFieldType::Start>(loc), start(start) {}
+
+ Var start;
+};
+
+struct Module {
+ Index GetFuncTypeIndex(const Var&) const;
+ Index GetFuncTypeIndex(const FuncDeclaration&) const;
+ Index GetFuncTypeIndex(const FuncSignature&) const;
+ const FuncType* GetFuncType(const Var&) const;
+ FuncType* GetFuncType(const Var&);
+ Index GetFuncIndex(const Var&) const;
+ const Func* GetFunc(const Var&) const;
+ Func* GetFunc(const Var&);
+ Index GetTableIndex(const Var&) const;
+ const Table* GetTable(const Var&) const;
+ Table* GetTable(const Var&);
+ Index GetMemoryIndex(const Var&) const;
+ const Memory* GetMemory(const Var&) const;
+ Memory* GetMemory(const Var&);
+ Index GetGlobalIndex(const Var&) const;
+ const Global* GetGlobal(const Var&) const;
+ Global* GetGlobal(const Var&);
+ const Export* GetExport(string_view) const;
+ Tag* GetTag(const Var&) const;
+ Index GetTagIndex(const Var&) const;
+ const DataSegment* GetDataSegment(const Var&) const;
+ DataSegment* GetDataSegment(const Var&);
+ Index GetDataSegmentIndex(const Var&) const;
+ const ElemSegment* GetElemSegment(const Var&) const;
+ ElemSegment* GetElemSegment(const Var&);
+ Index GetElemSegmentIndex(const Var&) const;
+
+ bool IsImport(ExternalKind kind, const Var&) const;
+ bool IsImport(const Export& export_) const {
+ return IsImport(export_.kind, export_.var);
+ }
+
+ // TODO(binji): move this into a builder class?
+ void AppendField(std::unique_ptr<DataSegmentModuleField>);
+ void AppendField(std::unique_ptr<ElemSegmentModuleField>);
+ void AppendField(std::unique_ptr<TagModuleField>);
+ void AppendField(std::unique_ptr<ExportModuleField>);
+ void AppendField(std::unique_ptr<FuncModuleField>);
+ void AppendField(std::unique_ptr<TypeModuleField>);
+ void AppendField(std::unique_ptr<GlobalModuleField>);
+ void AppendField(std::unique_ptr<ImportModuleField>);
+ void AppendField(std::unique_ptr<MemoryModuleField>);
+ void AppendField(std::unique_ptr<StartModuleField>);
+ void AppendField(std::unique_ptr<TableModuleField>);
+ void AppendField(std::unique_ptr<ModuleField>);
+ void AppendFields(ModuleFieldList*);
+
+ Location loc;
+ std::string name;
+ ModuleFieldList fields;
+
+ Index num_tag_imports = 0;
+ Index num_func_imports = 0;
+ Index num_table_imports = 0;
+ Index num_memory_imports = 0;
+ Index num_global_imports = 0;
+
+ // Cached for convenience; the pointers are shared with values that are
+ // stored in either ModuleField or Import.
+ std::vector<Tag*> tags;
+ std::vector<Func*> funcs;
+ std::vector<Global*> globals;
+ std::vector<Import*> imports;
+ std::vector<Export*> exports;
+ std::vector<TypeEntry*> types;
+ std::vector<Table*> tables;
+ std::vector<ElemSegment*> elem_segments;
+ std::vector<Memory*> memories;
+ std::vector<DataSegment*> data_segments;
+ std::vector<Var*> starts;
+
+ BindingHash tag_bindings;
+ BindingHash func_bindings;
+ BindingHash global_bindings;
+ BindingHash export_bindings;
+ BindingHash type_bindings;
+ BindingHash table_bindings;
+ BindingHash memory_bindings;
+ BindingHash data_segment_bindings;
+ BindingHash elem_segment_bindings;
+};
+
+enum class ScriptModuleType {
+ Text,
+ Binary,
+ Quoted,
+};
+
+// A ScriptModule is a module that may not yet be decoded. This allows for text
+// and binary parsing errors to be deferred until validation time.
+class ScriptModule {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(ScriptModule);
+ ScriptModule() = delete;
+ virtual ~ScriptModule() = default;
+
+ ScriptModuleType type() const { return type_; }
+ virtual const Location& location() const = 0;
+
+ protected:
+ explicit ScriptModule(ScriptModuleType type) : type_(type) {}
+
+ ScriptModuleType type_;
+};
+
+template <ScriptModuleType TypeEnum>
+class ScriptModuleMixin : public ScriptModule {
+ public:
+ static bool classof(const ScriptModule* script_module) {
+ return script_module->type() == TypeEnum;
+ }
+
+ ScriptModuleMixin() : ScriptModule(TypeEnum) {}
+};
+
+class TextScriptModule : public ScriptModuleMixin<ScriptModuleType::Text> {
+ public:
+ const Location& location() const override { return module.loc; }
+
+ Module module;
+};
+
+template <ScriptModuleType TypeEnum>
+class DataScriptModule : public ScriptModuleMixin<TypeEnum> {
+ public:
+ const Location& location() const override { return loc; }
+
+ Location loc;
+ std::string name;
+ std::vector<uint8_t> data;
+};
+
+typedef DataScriptModule<ScriptModuleType::Binary> BinaryScriptModule;
+typedef DataScriptModule<ScriptModuleType::Quoted> QuotedScriptModule;
+
+enum class ActionType {
+ Invoke,
+ Get,
+};
+
+class Action {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Action);
+ Action() = delete;
+ virtual ~Action() = default;
+
+ ActionType type() const { return type_; }
+
+ Location loc;
+ Var module_var;
+ std::string name;
+
+ protected:
+ explicit Action(ActionType type, const Location& loc = Location())
+ : loc(loc), type_(type) {}
+
+ ActionType type_;
+};
+
+typedef std::unique_ptr<Action> ActionPtr;
+
+template <ActionType TypeEnum>
+class ActionMixin : public Action {
+ public:
+ static bool classof(const Action* action) {
+ return action->type() == TypeEnum;
+ }
+
+ explicit ActionMixin(const Location& loc = Location())
+ : Action(TypeEnum, loc) {}
+};
+
+class GetAction : public ActionMixin<ActionType::Get> {
+ public:
+ explicit GetAction(const Location& loc = Location())
+ : ActionMixin<ActionType::Get>(loc) {}
+};
+
+class InvokeAction : public ActionMixin<ActionType::Invoke> {
+ public:
+ explicit InvokeAction(const Location& loc = Location())
+ : ActionMixin<ActionType::Invoke>(loc) {}
+
+ ConstVector args;
+};
+
+enum class CommandType {
+ Module,
+ Action,
+ Register,
+ AssertMalformed,
+ AssertInvalid,
+ AssertUnlinkable,
+ AssertUninstantiable,
+ AssertReturn,
+ AssertTrap,
+ AssertExhaustion,
+
+ First = Module,
+ Last = AssertExhaustion,
+};
+static const int kCommandTypeCount = WABT_ENUM_COUNT(CommandType);
+
+class Command {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Command);
+ Command() = delete;
+ virtual ~Command() = default;
+
+ CommandType type;
+
+ protected:
+ explicit Command(CommandType type) : type(type) {}
+};
+
+template <CommandType TypeEnum>
+class CommandMixin : public Command {
+ public:
+ static bool classof(const Command* cmd) { return cmd->type == TypeEnum; }
+ CommandMixin() : Command(TypeEnum) {}
+};
+
+class ModuleCommand : public CommandMixin<CommandType::Module> {
+ public:
+ Module module;
+};
+
+template <CommandType TypeEnum>
+class ActionCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ ActionPtr action;
+};
+
+typedef ActionCommandBase<CommandType::Action> ActionCommand;
+
+class RegisterCommand : public CommandMixin<CommandType::Register> {
+ public:
+ RegisterCommand(string_view module_name, const Var& var)
+ : module_name(module_name), var(var) {}
+
+ std::string module_name;
+ Var var;
+};
+
+class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> {
+ public:
+ ActionPtr action;
+ ConstVector expected;
+};
+
+template <CommandType TypeEnum>
+class AssertTrapCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ ActionPtr action;
+ std::string text;
+};
+
+typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand;
+typedef AssertTrapCommandBase<CommandType::AssertExhaustion>
+ AssertExhaustionCommand;
+
+template <CommandType TypeEnum>
+class AssertModuleCommand : public CommandMixin<TypeEnum> {
+ public:
+ std::unique_ptr<ScriptModule> module;
+ std::string text;
+};
+
+typedef AssertModuleCommand<CommandType::AssertMalformed>
+ AssertMalformedCommand;
+typedef AssertModuleCommand<CommandType::AssertInvalid> AssertInvalidCommand;
+typedef AssertModuleCommand<CommandType::AssertUnlinkable>
+ AssertUnlinkableCommand;
+typedef AssertModuleCommand<CommandType::AssertUninstantiable>
+ AssertUninstantiableCommand;
+
+typedef std::unique_ptr<Command> CommandPtr;
+typedef std::vector<CommandPtr> CommandPtrVector;
+
+struct Script {
+ WABT_DISALLOW_COPY_AND_ASSIGN(Script);
+ Script() = default;
+
+ const Module* GetFirstModule() const;
+ Module* GetFirstModule();
+ const Module* GetModule(const Var&) const;
+
+ CommandPtrVector commands;
+ BindingHash module_bindings;
+};
+
+void MakeTypeBindingReverseMapping(
+ size_t num_types,
+ const BindingHash& bindings,
+ std::vector<std::string>* out_reverse_mapping);
+
+} // namespace wabt
+
+#endif /* WABT_IR_H_ */
diff --git a/third_party/wasm2c/src/leb128.cc b/third_party/wasm2c/src/leb128.cc
new file mode 100644
index 0000000000..6d15e2f363
--- /dev/null
+++ b/third_party/wasm2c/src/leb128.cc
@@ -0,0 +1,361 @@
+/*
+ * 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 "src/leb128.h"
+
+#include <type_traits>
+
+#include "src/stream.h"
+
+#define MAX_U32_LEB128_BYTES 5
+#define MAX_U64_LEB128_BYTES 10
+
+namespace wabt {
+
+Offset U32Leb128Length(uint32_t value) {
+ uint32_t size = 0;
+ do {
+ value >>= 7;
+ size++;
+ } while (value != 0);
+ return size;
+}
+
+#define LEB128_LOOP_UNTIL(end_cond) \
+ do { \
+ uint8_t byte = value & 0x7f; \
+ value >>= 7; \
+ if (end_cond) { \
+ data[length++] = byte; \
+ break; \
+ } else { \
+ data[length++] = byte | 0x80; \
+ } \
+ } while (1)
+
+Offset WriteFixedU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length =
+ WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value);
+ stream->WriteDataAt(offset, data, length, desc);
+ return length;
+}
+
+void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteData(data, length, desc);
+}
+
+void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length =
+ WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value);
+ stream->WriteData(data, length, desc);
+}
+
+// returns the length of the leb128.
+Offset WriteU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteDataAt(offset, data, length, desc);
+ return length;
+}
+
+Offset WriteU32Leb128Raw(uint8_t* dest, uint8_t* dest_end, uint32_t value) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ if (static_cast<Offset>(dest_end - dest) < length) {
+ return 0;
+ }
+ memcpy(dest, data, length);
+ return length;
+}
+
+Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value) {
+ if (end - data < MAX_U32_LEB128_BYTES) {
+ return 0;
+ }
+ data[0] = (value & 0x7f) | 0x80;
+ data[1] = ((value >> 7) & 0x7f) | 0x80;
+ data[2] = ((value >> 14) & 0x7f) | 0x80;
+ data[3] = ((value >> 21) & 0x7f) | 0x80;
+ data[4] = ((value >> 28) & 0x0f);
+ return MAX_U32_LEB128_BYTES;
+}
+
+static void WriteS32Leb128(Stream* stream, int32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ Offset length = 0;
+ if (value < 0) {
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ } else {
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+ }
+
+ stream->WriteData(data, length, desc);
+}
+
+static void WriteS64Leb128(Stream* stream, int64_t value, const char* desc) {
+ uint8_t data[MAX_U64_LEB128_BYTES];
+ Offset length = 0;
+ if (value < 0) {
+ LEB128_LOOP_UNTIL(value == -1 && (byte & 0x40));
+ } else {
+ LEB128_LOOP_UNTIL(value == 0 && !(byte & 0x40));
+ }
+
+ stream->WriteData(data, length, desc);
+}
+
+void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ WriteS32Leb128(stream, Bitcast<int32_t>(value), desc);
+}
+
+void WriteU64Leb128(Stream* stream, uint64_t value, const char* desc) {
+ uint8_t data[MAX_U64_LEB128_BYTES];
+ Offset length = 0;
+ LEB128_LOOP_UNTIL(value == 0);
+ stream->WriteData(data, length, desc);
+}
+
+void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc) {
+ WriteS64Leb128(stream, Bitcast<int64_t>(value), desc);
+}
+
+void WriteFixedS32Leb128(Stream* stream, uint32_t value, const char* desc) {
+ uint8_t data[MAX_U32_LEB128_BYTES];
+ data[0] = (value & 0x7f) | 0x80;
+ data[1] = ((value >> 7) & 0x7f) | 0x80;
+ data[2] = ((value >> 14) & 0x7f) | 0x80;
+ data[3] = ((value >> 21) & 0x7f) | 0x80;
+ // The last byte needs to be sign-extended.
+ data[4] = ((value >> 28) & 0x0f);
+ if (static_cast<int32_t>(value) < 0) {
+ data[4] |= 0x70;
+ }
+ stream->WriteData(data, MAX_U32_LEB128_BYTES, desc);
+}
+
+#undef LEB128_LOOP_UNTIL
+
+#define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift))
+
+#define LEB128_1(type) (BYTE_AT(type, 0, 0))
+#define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type))
+#define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type))
+#define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type))
+#define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type))
+#define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type))
+#define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type))
+#define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type))
+#define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type))
+#define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type))
+
+#define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit))
+#define SIGN_EXTEND(type, value, sign_bit) \
+ (static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \
+ SHIFT_AMOUNT(type, sign_bit))
+
+size_t ReadU32Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint32_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ *out_value = LEB128_1(uint32_t);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ *out_value = LEB128_2(uint32_t);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ *out_value = LEB128_3(uint32_t);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ *out_value = LEB128_4(uint32_t);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ // The top bits set represent values > 32 bits.
+ if (p[4] & 0xf0) {
+ return 0;
+ }
+ *out_value = LEB128_5(uint32_t);
+ return 5;
+ } else {
+ // past the end.
+ *out_value = 0;
+ return 0;
+ }
+}
+
+size_t ReadU64Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint64_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ *out_value = LEB128_1(uint64_t);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ *out_value = LEB128_2(uint64_t);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ *out_value = LEB128_3(uint64_t);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ *out_value = LEB128_4(uint64_t);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ *out_value = LEB128_5(uint64_t);
+ return 5;
+ } else if (p + 5 < end && (p[5] & 0x80) == 0) {
+ *out_value = LEB128_6(uint64_t);
+ return 6;
+ } else if (p + 6 < end && (p[6] & 0x80) == 0) {
+ *out_value = LEB128_7(uint64_t);
+ return 7;
+ } else if (p + 7 < end && (p[7] & 0x80) == 0) {
+ *out_value = LEB128_8(uint64_t);
+ return 8;
+ } else if (p + 8 < end && (p[8] & 0x80) == 0) {
+ *out_value = LEB128_9(uint64_t);
+ return 9;
+ } else if (p + 9 < end && (p[9] & 0x80) == 0) {
+ // The top bits set represent values > 32 bits.
+ if (p[9] & 0xf0) {
+ return 0;
+ }
+ *out_value = LEB128_10(uint64_t);
+ return 10;
+ } else {
+ // past the end.
+ *out_value = 0;
+ return 0;
+ }
+}
+
+size_t ReadS32Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint32_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ uint32_t result = LEB128_1(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 6);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ uint32_t result = LEB128_2(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 13);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ uint32_t result = LEB128_3(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 20);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ uint32_t result = LEB128_4(uint32_t);
+ *out_value = SIGN_EXTEND(int32_t, result, 27);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ // The top bits should be a sign-extension of the sign bit.
+ bool sign_bit_set = (p[4] & 0x8);
+ int top_bits = p[4] & 0xf0;
+ if ((sign_bit_set && top_bits != 0x70) ||
+ (!sign_bit_set && top_bits != 0)) {
+ return 0;
+ }
+ uint32_t result = LEB128_5(uint32_t);
+ *out_value = result;
+ return 5;
+ } else {
+ // Past the end.
+ return 0;
+ }
+}
+
+size_t ReadS64Leb128(const uint8_t* p,
+ const uint8_t* end,
+ uint64_t* out_value) {
+ if (p < end && (p[0] & 0x80) == 0) {
+ uint64_t result = LEB128_1(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 6);
+ return 1;
+ } else if (p + 1 < end && (p[1] & 0x80) == 0) {
+ uint64_t result = LEB128_2(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 13);
+ return 2;
+ } else if (p + 2 < end && (p[2] & 0x80) == 0) {
+ uint64_t result = LEB128_3(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 20);
+ return 3;
+ } else if (p + 3 < end && (p[3] & 0x80) == 0) {
+ uint64_t result = LEB128_4(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 27);
+ return 4;
+ } else if (p + 4 < end && (p[4] & 0x80) == 0) {
+ uint64_t result = LEB128_5(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 34);
+ return 5;
+ } else if (p + 5 < end && (p[5] & 0x80) == 0) {
+ uint64_t result = LEB128_6(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 41);
+ return 6;
+ } else if (p + 6 < end && (p[6] & 0x80) == 0) {
+ uint64_t result = LEB128_7(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 48);
+ return 7;
+ } else if (p + 7 < end && (p[7] & 0x80) == 0) {
+ uint64_t result = LEB128_8(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 55);
+ return 8;
+ } else if (p + 8 < end && (p[8] & 0x80) == 0) {
+ uint64_t result = LEB128_9(uint64_t);
+ *out_value = SIGN_EXTEND(int64_t, result, 62);
+ return 9;
+ } else if (p + 9 < end && (p[9] & 0x80) == 0) {
+ // The top bits should be a sign-extension of the sign bit.
+ bool sign_bit_set = (p[9] & 0x1);
+ int top_bits = p[9] & 0xfe;
+ if ((sign_bit_set && top_bits != 0x7e) ||
+ (!sign_bit_set && top_bits != 0)) {
+ return 0;
+ }
+ uint64_t result = LEB128_10(uint64_t);
+ *out_value = result;
+ return 10;
+ } else {
+ // Past the end.
+ return 0;
+ }
+}
+
+#undef BYTE_AT
+#undef LEB128_1
+#undef LEB128_2
+#undef LEB128_3
+#undef LEB128_4
+#undef LEB128_5
+#undef LEB128_6
+#undef LEB128_7
+#undef LEB128_8
+#undef LEB128_9
+#undef LEB128_10
+#undef SHIFT_AMOUNT
+#undef SIGN_EXTEND
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/leb128.h b/third_party/wasm2c/src/leb128.h
new file mode 100644
index 0000000000..56f803ccc0
--- /dev/null
+++ b/third_party/wasm2c/src/leb128.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_LEB128_H_
+#define WABT_LEB128_H_
+
+#include <cstdint>
+
+#include "src/common.h"
+
+namespace wabt {
+
+class Stream;
+
+// Returns the length of the leb128.
+Offset U32Leb128Length(uint32_t value);
+
+void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc);
+void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc);
+void WriteU64Leb128(Stream* stream, uint64_t value, const char* desc);
+void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc);
+void WriteFixedS32Leb128(Stream* stream, uint32_t value, const char* desc);
+void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc);
+
+Offset WriteU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc);
+
+Offset WriteFixedU32Leb128At(Stream* stream,
+ Offset offset,
+ uint32_t value,
+ const char* desc);
+
+Offset WriteU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value);
+Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value);
+
+// Convenience functions for writing enums as LEB128s.
+template <typename T>
+void WriteU32Leb128(Stream* stream, T value, const char* desc) {
+ WriteU32Leb128(stream, static_cast<uint32_t>(value), desc);
+}
+
+template <typename T>
+void WriteS32Leb128(Stream* stream, T value, const char* desc) {
+ WriteS32Leb128(stream, static_cast<uint32_t>(value), desc);
+}
+
+// Returns the length of the leb128.
+size_t ReadU32Leb128(const uint8_t* p, const uint8_t* end, uint32_t* out_value);
+size_t ReadU64Leb128(const uint8_t* p, const uint8_t* end, uint64_t* out_value);
+size_t ReadS32Leb128(const uint8_t* p, const uint8_t* end, uint32_t* out_value);
+size_t ReadS64Leb128(const uint8_t* p, const uint8_t* end, uint64_t* out_value);
+
+} // namespace wabt
+
+#endif // WABT_LEB128_H_
diff --git a/third_party/wasm2c/src/lexer-keywords.txt b/third_party/wasm2c/src/lexer-keywords.txt
new file mode 100644
index 0000000000..1ff631ba4d
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-keywords.txt
@@ -0,0 +1,631 @@
+struct TokenInfo {
+ TokenInfo(const char* name) : name(name) {}
+ TokenInfo(const char* name, TokenType token_type)
+ : name(name), token_type(token_type) {}
+ TokenInfo(const char* name, Type value_type)
+ : name(name), token_type(TokenType::ValueType), value_type(value_type) {}
+ TokenInfo(const char* name, Type value_type, TokenType token_type)
+ : name(name), token_type(token_type), value_type(value_type) {}
+ TokenInfo(const char* name, TokenType token_type, Opcode opcode)
+ : name(name), token_type(token_type), opcode(opcode) {}
+
+ const char* name;
+ TokenType token_type;
+ union {
+ Type value_type;
+ Opcode opcode;
+ };
+};
+%%
+array, Type::Array, TokenType::Array
+assert_exhaustion, TokenType::AssertExhaustion
+assert_invalid, TokenType::AssertInvalid
+assert_malformed, TokenType::AssertMalformed
+assert_return, TokenType::AssertReturn
+assert_trap, TokenType::AssertTrap
+assert_unlinkable, TokenType::AssertUnlinkable
+atomic.fence, TokenType::AtomicFence, Opcode::AtomicFence
+binary, TokenType::Bin
+block, TokenType::Block, Opcode::Block
+br_if, TokenType::BrIf, Opcode::BrIf
+br_table, TokenType::BrTable, Opcode::BrTable
+br, TokenType::Br, Opcode::Br
+call_indirect, TokenType::CallIndirect, Opcode::CallIndirect
+call_ref, TokenType::CallRef, Opcode::CallRef
+call, TokenType::Call, Opcode::Call
+catch, TokenType::Catch, Opcode::Catch
+catch_all, TokenType::CatchAll, Opcode::CatchAll
+current_memory, TokenType::MemorySize, Opcode::MemorySize
+data.drop, TokenType::DataDrop, Opcode::DataDrop
+data, TokenType::Data
+declare, TokenType::Declare
+delegate, TokenType::Delegate
+do, TokenType::Do
+drop, TokenType::Drop, Opcode::Drop
+elem.drop, TokenType::ElemDrop, Opcode::ElemDrop
+elem, TokenType::Elem
+else, TokenType::Else, Opcode::Else
+end, TokenType::End, Opcode::End
+tag, TokenType::Tag
+extern, Type::ExternRef, TokenType::Extern
+externref, Type::ExternRef
+export, TokenType::Export
+f32.abs, TokenType::Unary, Opcode::F32Abs
+f32.add, TokenType::Binary, Opcode::F32Add
+f32.ceil, TokenType::Unary, Opcode::F32Ceil
+f32.const, TokenType::Const, Opcode::F32Const
+f32.convert_i32_s, TokenType::Convert, Opcode::F32ConvertI32S
+f32.convert_i32_u, TokenType::Convert, Opcode::F32ConvertI32U
+f32.convert_i64_s, TokenType::Convert, Opcode::F32ConvertI64S
+f32.convert_i64_u, TokenType::Convert, Opcode::F32ConvertI64U
+f32.copysign, TokenType::Binary, Opcode::F32Copysign
+f32.demote_f64, TokenType::Convert, Opcode::F32DemoteF64
+f32.div, TokenType::Binary, Opcode::F32Div
+f32.eq, TokenType::Compare, Opcode::F32Eq
+f32.floor, TokenType::Unary, Opcode::F32Floor
+f32.ge, TokenType::Compare, Opcode::F32Ge
+f32.gt, TokenType::Compare, Opcode::F32Gt
+f32.le, TokenType::Compare, Opcode::F32Le
+f32.load, TokenType::Load, Opcode::F32Load
+f32.lt, TokenType::Compare, Opcode::F32Lt
+f32.max, TokenType::Binary, Opcode::F32Max
+f32.min, TokenType::Binary, Opcode::F32Min
+f32.mul, TokenType::Binary, Opcode::F32Mul
+f32.nearest, TokenType::Unary, Opcode::F32Nearest
+f32.neg, TokenType::Unary, Opcode::F32Neg
+f32.ne, TokenType::Compare, Opcode::F32Ne
+f32.reinterpret_i32, TokenType::Convert, Opcode::F32ReinterpretI32
+f32.sqrt, TokenType::Unary, Opcode::F32Sqrt
+f32.store, TokenType::Store, Opcode::F32Store
+f32.sub, TokenType::Binary, Opcode::F32Sub
+f32.trunc, TokenType::Unary, Opcode::F32Trunc
+f32, Type::F32
+f32x4.abs, TokenType::Unary, Opcode::F32X4Abs
+f32x4.add, TokenType::Binary, Opcode::F32X4Add
+f32x4.ceil, TokenType::Unary, Opcode::F32X4Ceil
+f32x4.convert_i32x4_s, TokenType::Unary, Opcode::F32X4ConvertI32X4S
+f32x4.convert_i32x4_u, TokenType::Unary, Opcode::F32X4ConvertI32X4U
+f32x4.div, TokenType::Binary, Opcode::F32X4Div
+f32x4.eq, TokenType::Compare, Opcode::F32X4Eq
+f32x4.extract_lane, TokenType::SimdLaneOp, Opcode::F32X4ExtractLane
+f32x4.floor, TokenType::Unary, Opcode::F32X4Floor
+f32x4.ge, TokenType::Compare, Opcode::F32X4Ge
+f32x4.gt, TokenType::Compare, Opcode::F32X4Gt
+f32x4.le, TokenType::Compare, Opcode::F32X4Le
+f32x4.lt, TokenType::Compare, Opcode::F32X4Lt
+f32x4.max, TokenType::Binary, Opcode::F32X4Max
+f32x4.min, TokenType::Binary, Opcode::F32X4Min
+f32x4.mul, TokenType::Binary, Opcode::F32X4Mul
+f32x4.nearest, TokenType::Unary, Opcode::F32X4Nearest
+f32x4.neg, TokenType::Unary, Opcode::F32X4Neg
+f32x4.ne, TokenType::Compare, Opcode::F32X4Ne
+f32x4.pmax, TokenType::Binary, Opcode::F32X4PMax
+f32x4.pmin, TokenType::Binary, Opcode::F32X4PMin
+f32x4.replace_lane, TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane
+f32x4.splat, TokenType::Unary, Opcode::F32X4Splat
+f32x4.sqrt, TokenType::Unary, Opcode::F32X4Sqrt
+f32x4.sub, TokenType::Binary, Opcode::F32X4Sub
+f32x4.trunc, TokenType::Unary, Opcode::F32X4Trunc
+f32x4.demote_f64x2_zero, TokenType::Unary, Opcode::F32X4DemoteF64X2Zero
+f32x4, TokenType::F32X4
+f64.abs, TokenType::Unary, Opcode::F64Abs
+f64.add, TokenType::Binary, Opcode::F64Add
+f64.ceil, TokenType::Unary, Opcode::F64Ceil
+f64.const, TokenType::Const, Opcode::F64Const
+f64.convert_i32_s, TokenType::Convert, Opcode::F64ConvertI32S
+f64.convert_i32_u, TokenType::Convert, Opcode::F64ConvertI32U
+f64.convert_i64_s, TokenType::Convert, Opcode::F64ConvertI64S
+f64.convert_i64_u, TokenType::Convert, Opcode::F64ConvertI64U
+f64.copysign, TokenType::Binary, Opcode::F64Copysign
+f64.div, TokenType::Binary, Opcode::F64Div
+f64.eq, TokenType::Compare, Opcode::F64Eq
+f64.floor, TokenType::Unary, Opcode::F64Floor
+f64.ge, TokenType::Compare, Opcode::F64Ge
+f64.gt, TokenType::Compare, Opcode::F64Gt
+f64.le, TokenType::Compare, Opcode::F64Le
+f64.load, TokenType::Load, Opcode::F64Load
+f64.lt, TokenType::Compare, Opcode::F64Lt
+f64.max, TokenType::Binary, Opcode::F64Max
+f64.min, TokenType::Binary, Opcode::F64Min
+f64.mul, TokenType::Binary, Opcode::F64Mul
+f64.nearest, TokenType::Unary, Opcode::F64Nearest
+f64.neg, TokenType::Unary, Opcode::F64Neg
+f64.ne, TokenType::Compare, Opcode::F64Ne
+f64.promote_f32, TokenType::Convert, Opcode::F64PromoteF32
+f64.reinterpret_i64, TokenType::Convert, Opcode::F64ReinterpretI64
+f64.sqrt, TokenType::Unary, Opcode::F64Sqrt
+f64.store, TokenType::Store, Opcode::F64Store
+f64.sub, TokenType::Binary, Opcode::F64Sub
+f64.trunc, TokenType::Unary, Opcode::F64Trunc
+f64, Type::F64
+f64x2.abs, TokenType::Unary, Opcode::F64X2Abs
+f64x2.add, TokenType::Binary, Opcode::F64X2Add
+f64x2.ceil, TokenType::Unary, Opcode::F64X2Ceil
+f64x2.div, TokenType::Binary, Opcode::F64X2Div
+f64x2.eq, TokenType::Compare, Opcode::F64X2Eq
+f64x2.extract_lane, TokenType::SimdLaneOp, Opcode::F64X2ExtractLane
+f64x2.floor, TokenType::Unary, Opcode::F64X2Floor
+f64x2.ge, TokenType::Compare, Opcode::F64X2Ge
+f64x2.gt, TokenType::Compare, Opcode::F64X2Gt
+f64x2.le, TokenType::Compare, Opcode::F64X2Le
+f64x2.lt, TokenType::Compare, Opcode::F64X2Lt
+f64x2.max, TokenType::Binary, Opcode::F64X2Max
+f64x2.min, TokenType::Binary, Opcode::F64X2Min
+f64x2.mul, TokenType::Binary, Opcode::F64X2Mul
+f64x2.nearest, TokenType::Unary, Opcode::F64X2Nearest
+f64x2.neg, TokenType::Unary, Opcode::F64X2Neg
+f64x2.ne, TokenType::Compare, Opcode::F64X2Ne
+f64x2.pmax, TokenType::Binary, Opcode::F64X2PMax
+f64x2.pmin, TokenType::Binary, Opcode::F64X2PMin
+f64x2.replace_lane, TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane
+f64x2.splat, TokenType::Unary, Opcode::F64X2Splat
+f64x2.sqrt, TokenType::Unary, Opcode::F64X2Sqrt
+f64x2.sub, TokenType::Binary, Opcode::F64X2Sub
+f64x2.trunc, TokenType::Unary, Opcode::F64X2Trunc
+f64x2.convert_low_i32x4_s, TokenType::Unary, Opcode::F64X2ConvertLowI32X4S
+f64x2.convert_low_i32x4_u, TokenType::Unary, Opcode::F64X2ConvertLowI32X4U
+f64x2.promote_low_f32x4, TokenType::Unary, Opcode::F64X2PromoteLowF32X4
+f64x2, TokenType::F64X2
+field, TokenType::Field
+funcref, Type::FuncRef
+func, Type::FuncRef, TokenType::Func
+get, TokenType::Get
+global.get, TokenType::GlobalGet, Opcode::GlobalGet
+global.set, TokenType::GlobalSet, Opcode::GlobalSet
+global, TokenType::Global
+grow_memory, TokenType::MemoryGrow, Opcode::MemoryGrow
+i16x8.abs, TokenType::Unary, Opcode::I16X8Abs
+i16x8.add_sat_s, TokenType::Binary, Opcode::I16X8AddSatS
+i16x8.add_sat_u, TokenType::Binary, Opcode::I16X8AddSatU
+i16x8.add, TokenType::Binary, Opcode::I16X8Add
+i16x8.all_true, TokenType::Unary, Opcode::I16X8AllTrue
+i16x8.avgr_u, TokenType::Binary, Opcode::I16X8AvgrU
+i16x8.bitmask, TokenType::Unary, Opcode::I16X8Bitmask
+i16x8.eq, TokenType::Compare, Opcode::I16X8Eq
+i16x8.extract_lane_s, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS
+i16x8.extract_lane_u, TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU
+i16x8.ge_s, TokenType::Compare, Opcode::I16X8GeS
+i16x8.ge_u, TokenType::Compare, Opcode::I16X8GeU
+i16x8.gt_s, TokenType::Compare, Opcode::I16X8GtS
+i16x8.gt_u, TokenType::Compare, Opcode::I16X8GtU
+i16x8.le_s, TokenType::Compare, Opcode::I16X8LeS
+i16x8.le_u, TokenType::Compare, Opcode::I16X8LeU
+v128.load8x8_s, TokenType::Load, Opcode::V128Load8X8S
+v128.load8x8_u, TokenType::Load, Opcode::V128Load8X8U
+i16x8.lt_s, TokenType::Compare, Opcode::I16X8LtS
+i16x8.lt_u, TokenType::Compare, Opcode::I16X8LtU
+i16x8.max_s, TokenType::Binary, Opcode::I16X8MaxS
+i16x8.max_u, TokenType::Binary, Opcode::I16X8MaxU
+i16x8.min_s, TokenType::Binary, Opcode::I16X8MinS
+i16x8.min_u, TokenType::Binary, Opcode::I16X8MinU
+i16x8.mul, TokenType::Binary, Opcode::I16X8Mul
+i16x8.narrow_i32x4_s, TokenType::Binary, Opcode::I16X8NarrowI32X4S
+i16x8.narrow_i32x4_u, TokenType::Binary, Opcode::I16X8NarrowI32X4U
+i16x8.neg, TokenType::Unary, Opcode::I16X8Neg
+i16x8.q15mulr_sat_s, TokenType::Binary, Opcode::I16X8Q15mulrSatS
+i16x8.ne, TokenType::Compare, Opcode::I16X8Ne
+i16x8.replace_lane, TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane
+i16x8.shl, TokenType::Binary, Opcode::I16X8Shl
+i16x8.shr_s, TokenType::Binary, Opcode::I16X8ShrS
+i16x8.shr_u, TokenType::Binary, Opcode::I16X8ShrU
+i16x8.splat, TokenType::Unary, Opcode::I16X8Splat
+i16x8.sub_sat_s, TokenType::Binary, Opcode::I16X8SubSatS
+i16x8.sub_sat_u, TokenType::Binary, Opcode::I16X8SubSatU
+i16x8.sub, TokenType::Binary, Opcode::I16X8Sub
+i16x8.extadd_pairwise_i8x16_s, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S
+i16x8.extadd_pairwise_i8x16_u, TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U
+i16x8.extmul_low_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S
+i16x8.extmul_high_i8x16_s, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S
+i16x8.extmul_low_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U
+i16x8.extmul_high_i8x16_u, TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U
+i16x8, TokenType::I16X8
+i16x8.extend_high_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendHighI8X16S
+i16x8.extend_high_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendHighI8X16U
+i16x8.extend_low_i8x16_s, TokenType::Unary, Opcode::I16X8ExtendLowI8X16S
+i16x8.extend_low_i8x16_u, TokenType::Unary, Opcode::I16X8ExtendLowI8X16U
+i32.add, TokenType::Binary, Opcode::I32Add
+i32.and, TokenType::Binary, Opcode::I32And
+i32.atomic.load16_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad16U
+i32.atomic.load8_u, TokenType::AtomicLoad, Opcode::I32AtomicLoad8U
+i32.atomic.load, TokenType::AtomicLoad, Opcode::I32AtomicLoad
+i32.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU
+i32.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU
+i32.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU
+i32.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU
+i32.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU
+i32.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU
+i32.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU
+i32.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU
+i32.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU
+i32.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU
+i32.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU
+i32.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU
+i32.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU
+i32.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU
+i32.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd
+i32.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd
+i32.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg
+i32.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I32AtomicRmwOr
+i32.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I32AtomicRmwSub
+i32.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg
+i32.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I32AtomicRmwXor
+i32.atomic.store16, TokenType::AtomicStore, Opcode::I32AtomicStore16
+i32.atomic.store8, TokenType::AtomicStore, Opcode::I32AtomicStore8
+i32.atomic.store, TokenType::AtomicStore, Opcode::I32AtomicStore
+i32.clz, TokenType::Unary, Opcode::I32Clz
+i32.const, TokenType::Const, Opcode::I32Const
+i32.ctz, TokenType::Unary, Opcode::I32Ctz
+i32.div_s, TokenType::Binary, Opcode::I32DivS
+i32.div_u, TokenType::Binary, Opcode::I32DivU
+i32.eq, TokenType::Compare, Opcode::I32Eq
+i32.eqz, TokenType::Convert, Opcode::I32Eqz
+i32.extend16_s, TokenType::Unary, Opcode::I32Extend16S
+i32.extend8_s, TokenType::Unary, Opcode::I32Extend8S
+i32.ge_s, TokenType::Compare, Opcode::I32GeS
+i32.ge_u, TokenType::Compare, Opcode::I32GeU
+i32.gt_s, TokenType::Compare, Opcode::I32GtS
+i32.gt_u, TokenType::Compare, Opcode::I32GtU
+i32.le_s, TokenType::Compare, Opcode::I32LeS
+i32.le_u, TokenType::Compare, Opcode::I32LeU
+i32.load16_s, TokenType::Load, Opcode::I32Load16S
+i32.load16_u, TokenType::Load, Opcode::I32Load16U
+i32.load8_s, TokenType::Load, Opcode::I32Load8S
+i32.load8_u, TokenType::Load, Opcode::I32Load8U
+i32.load, TokenType::Load, Opcode::I32Load
+i32.lt_s, TokenType::Compare, Opcode::I32LtS
+i32.lt_u, TokenType::Compare, Opcode::I32LtU
+i32.mul, TokenType::Binary, Opcode::I32Mul
+i32.ne, TokenType::Compare, Opcode::I32Ne
+i32.or, TokenType::Binary, Opcode::I32Or
+i32.popcnt, TokenType::Unary, Opcode::I32Popcnt
+i32.reinterpret_f32, TokenType::Convert, Opcode::I32ReinterpretF32
+i32.rem_s, TokenType::Binary, Opcode::I32RemS
+i32.rem_u, TokenType::Binary, Opcode::I32RemU
+i32.rotl, TokenType::Binary, Opcode::I32Rotl
+i32.rotr, TokenType::Binary, Opcode::I32Rotr
+i32.shl, TokenType::Binary, Opcode::I32Shl
+i32.shr_s, TokenType::Binary, Opcode::I32ShrS
+i32.shr_u, TokenType::Binary, Opcode::I32ShrU
+i32.store16, TokenType::Store, Opcode::I32Store16
+i32.store8, TokenType::Store, Opcode::I32Store8
+i32.store, TokenType::Store, Opcode::I32Store
+i32.sub, TokenType::Binary, Opcode::I32Sub
+i32.trunc_f32_s, TokenType::Convert, Opcode::I32TruncF32S
+i32.trunc_f32_u, TokenType::Convert, Opcode::I32TruncF32U
+i32.trunc_f64_s, TokenType::Convert, Opcode::I32TruncF64S
+i32.trunc_f64_u, TokenType::Convert, Opcode::I32TruncF64U
+i32.trunc_sat_f32_s, TokenType::Convert, Opcode::I32TruncSatF32S
+i32.trunc_sat_f32_u, TokenType::Convert, Opcode::I32TruncSatF32U
+i32.trunc_sat_f64_s, TokenType::Convert, Opcode::I32TruncSatF64S
+i32.trunc_sat_f64_u, TokenType::Convert, Opcode::I32TruncSatF64U
+i32, Type::I32
+i32.wrap_i64, TokenType::Convert, Opcode::I32WrapI64
+i32x4.abs, TokenType::Unary, Opcode::I32X4Abs
+i32x4.add, TokenType::Binary, Opcode::I32X4Add
+i32x4.all_true, TokenType::Unary, Opcode::I32X4AllTrue
+i32x4.bitmask, TokenType::Unary, Opcode::I32X4Bitmask
+i32x4.eq, TokenType::Compare, Opcode::I32X4Eq
+i32x4.extract_lane, TokenType::SimdLaneOp, Opcode::I32X4ExtractLane
+i32x4.ge_s, TokenType::Compare, Opcode::I32X4GeS
+i32x4.ge_u, TokenType::Compare, Opcode::I32X4GeU
+i32x4.gt_s, TokenType::Compare, Opcode::I32X4GtS
+i32x4.gt_u, TokenType::Compare, Opcode::I32X4GtU
+i32x4.le_s, TokenType::Compare, Opcode::I32X4LeS
+i32x4.le_u, TokenType::Compare, Opcode::I32X4LeU
+v128.load16x4_s, TokenType::Load, Opcode::V128Load16X4S
+v128.load16x4_u, TokenType::Load, Opcode::V128Load16X4U
+i32x4.lt_s, TokenType::Compare, Opcode::I32X4LtS
+i32x4.lt_u, TokenType::Compare, Opcode::I32X4LtU
+i32x4.max_s, TokenType::Binary, Opcode::I32X4MaxS
+i32x4.max_u, TokenType::Binary, Opcode::I32X4MaxU
+i32x4.min_s, TokenType::Binary, Opcode::I32X4MinS
+i32x4.min_u, TokenType::Binary, Opcode::I32X4MinU
+i32x4.dot_i16x8_s, TokenType::Binary, Opcode::I32X4DotI16X8S
+i32x4.mul, TokenType::Binary, Opcode::I32X4Mul
+i32x4.neg, TokenType::Unary, Opcode::I32X4Neg
+i32x4.ne, TokenType::Compare, Opcode::I32X4Ne
+i32x4.replace_lane, TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane
+i32x4.shl, TokenType::Binary, Opcode::I32X4Shl
+i32x4.shr_s, TokenType::Binary, Opcode::I32X4ShrS
+i32x4.shr_u, TokenType::Binary, Opcode::I32X4ShrU
+i32x4.splat, TokenType::Unary, Opcode::I32X4Splat
+i32x4.sub, TokenType::Binary, Opcode::I32X4Sub
+i32x4.extadd_pairwise_i16x8_s, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S
+i32x4.extadd_pairwise_i16x8_u, TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U
+i32x4.extmul_low_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S
+i32x4.extmul_high_i16x8_s, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S
+i32x4.extmul_low_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U
+i32x4.extmul_high_i16x8_u, TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U
+i32x4, TokenType::I32X4
+i32x4.trunc_sat_f32x4_s, TokenType::Unary, Opcode::I32X4TruncSatF32X4S
+i32x4.trunc_sat_f32x4_u, TokenType::Unary, Opcode::I32X4TruncSatF32X4U
+i32x4.extend_high_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendHighI16X8S
+i32x4.extend_high_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendHighI16X8U
+i32x4.extend_low_i16x8_s, TokenType::Unary, Opcode::I32X4ExtendLowI16X8S
+i32x4.extend_low_i16x8_u, TokenType::Unary, Opcode::I32X4ExtendLowI16X8U
+i32x4.trunc_sat_f64x2_s_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero
+i32x4.trunc_sat_f64x2_u_zero, TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero
+i32.xor, TokenType::Binary, Opcode::I32Xor
+i64.add, TokenType::Binary, Opcode::I64Add
+i64.and, TokenType::Binary, Opcode::I64And
+i64.atomic.load16_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad16U
+i64.atomic.load32_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad32U
+i64.atomic.load8_u, TokenType::AtomicLoad, Opcode::I64AtomicLoad8U
+i64.atomic.load, TokenType::AtomicLoad, Opcode::I64AtomicLoad
+i64.atomic.rmw16.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU
+i64.atomic.rmw16.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU
+i64.atomic.rmw16.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU
+i64.atomic.rmw16.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU
+i64.atomic.rmw16.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU
+i64.atomic.rmw16.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU
+i64.atomic.rmw16.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU
+i64.atomic.rmw32.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU
+i64.atomic.rmw32.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU
+i64.atomic.rmw32.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU
+i64.atomic.rmw32.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU
+i64.atomic.rmw32.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU
+i64.atomic.rmw32.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU
+i64.atomic.rmw32.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU
+i64.atomic.rmw8.add_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU
+i64.atomic.rmw8.and_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU
+i64.atomic.rmw8.cmpxchg_u, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU
+i64.atomic.rmw8.or_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU
+i64.atomic.rmw8.sub_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU
+i64.atomic.rmw8.xchg_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU
+i64.atomic.rmw8.xor_u, TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU
+i64.atomic.rmw.add, TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd
+i64.atomic.rmw.and, TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd
+i64.atomic.rmw.cmpxchg, TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg
+i64.atomic.rmw.or, TokenType::AtomicRmw, Opcode::I64AtomicRmwOr
+i64.atomic.rmw.sub, TokenType::AtomicRmw, Opcode::I64AtomicRmwSub
+i64.atomic.rmw.xchg, TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg
+i64.atomic.rmw.xor, TokenType::AtomicRmw, Opcode::I64AtomicRmwXor
+i64.atomic.store16, TokenType::AtomicStore, Opcode::I64AtomicStore16
+i64.atomic.store32, TokenType::AtomicStore, Opcode::I64AtomicStore32
+i64.atomic.store8, TokenType::AtomicStore, Opcode::I64AtomicStore8
+i64.atomic.store, TokenType::AtomicStore, Opcode::I64AtomicStore
+i64.clz, TokenType::Unary, Opcode::I64Clz
+i64.const, TokenType::Const, Opcode::I64Const
+i64.ctz, TokenType::Unary, Opcode::I64Ctz
+i64.div_s, TokenType::Binary, Opcode::I64DivS
+i64.div_u, TokenType::Binary, Opcode::I64DivU
+i64.eq, TokenType::Compare, Opcode::I64Eq
+i64.eqz, TokenType::Convert, Opcode::I64Eqz
+i64.extend16_s, TokenType::Unary, Opcode::I64Extend16S
+i64.extend32_s, TokenType::Unary, Opcode::I64Extend32S
+i64.extend8_s, TokenType::Unary, Opcode::I64Extend8S
+i64.extend_i32_s, TokenType::Convert, Opcode::I64ExtendI32S
+i64.extend_i32_u, TokenType::Convert, Opcode::I64ExtendI32U
+i64.ge_s, TokenType::Compare, Opcode::I64GeS
+i64.ge_u, TokenType::Compare, Opcode::I64GeU
+i64.gt_s, TokenType::Compare, Opcode::I64GtS
+i64.gt_u, TokenType::Compare, Opcode::I64GtU
+i64.le_s, TokenType::Compare, Opcode::I64LeS
+i64.le_u, TokenType::Compare, Opcode::I64LeU
+i64.load16_s, TokenType::Load, Opcode::I64Load16S
+i64.load16_u, TokenType::Load, Opcode::I64Load16U
+i64.load32_s, TokenType::Load, Opcode::I64Load32S
+i64.load32_u, TokenType::Load, Opcode::I64Load32U
+i64.load8_s, TokenType::Load, Opcode::I64Load8S
+i64.load8_u, TokenType::Load, Opcode::I64Load8U
+i64.load, TokenType::Load, Opcode::I64Load
+i64.lt_s, TokenType::Compare, Opcode::I64LtS
+i64.lt_u, TokenType::Compare, Opcode::I64LtU
+i64.mul, TokenType::Binary, Opcode::I64Mul
+i64.ne, TokenType::Compare, Opcode::I64Ne
+i64.or, TokenType::Binary, Opcode::I64Or
+i64.popcnt, TokenType::Unary, Opcode::I64Popcnt
+i64.reinterpret_f64, TokenType::Convert, Opcode::I64ReinterpretF64
+i64.rem_s, TokenType::Binary, Opcode::I64RemS
+i64.rem_u, TokenType::Binary, Opcode::I64RemU
+i64.rotl, TokenType::Binary, Opcode::I64Rotl
+i64.rotr, TokenType::Binary, Opcode::I64Rotr
+i64.shl, TokenType::Binary, Opcode::I64Shl
+i64.shr_s, TokenType::Binary, Opcode::I64ShrS
+i64.shr_u, TokenType::Binary, Opcode::I64ShrU
+i64.store16, TokenType::Store, Opcode::I64Store16
+i64.store32, TokenType::Store, Opcode::I64Store32
+i64.store8, TokenType::Store, Opcode::I64Store8
+i64.store, TokenType::Store, Opcode::I64Store
+i64.sub, TokenType::Binary, Opcode::I64Sub
+i64.trunc_f32_s, TokenType::Convert, Opcode::I64TruncF32S
+i64.trunc_f32_u, TokenType::Convert, Opcode::I64TruncF32U
+i64.trunc_f64_s, TokenType::Convert, Opcode::I64TruncF64S
+i64.trunc_f64_u, TokenType::Convert, Opcode::I64TruncF64U
+i64.trunc_sat_f32_s, TokenType::Convert, Opcode::I64TruncSatF32S
+i64.trunc_sat_f32_u, TokenType::Convert, Opcode::I64TruncSatF32U
+i64.trunc_sat_f64_s, TokenType::Convert, Opcode::I64TruncSatF64S
+i64.trunc_sat_f64_u, TokenType::Convert, Opcode::I64TruncSatF64U
+i64, Type::I64
+i64x2.add, TokenType::Binary, Opcode::I64X2Add
+i64x2.extract_lane, TokenType::SimdLaneOp, Opcode::I64X2ExtractLane
+v128.load32x2_s, TokenType::Load, Opcode::V128Load32X2S
+v128.load32x2_u, TokenType::Load, Opcode::V128Load32X2U
+i64x2.mul, TokenType::Binary, Opcode::I64X2Mul
+i64x2.eq, TokenType::Binary, Opcode::I64X2Eq
+i64x2.ne, TokenType::Binary, Opcode::I64X2Ne
+i64x2.lt_s, TokenType::Binary, Opcode::I64X2LtS
+i64x2.gt_s, TokenType::Binary, Opcode::I64X2GtS
+i64x2.le_s, TokenType::Binary, Opcode::I64X2LeS
+i64x2.ge_s, TokenType::Binary, Opcode::I64X2GeS
+i64x2.abs, TokenType::Unary, Opcode::I64X2Abs
+i64x2.neg, TokenType::Unary, Opcode::I64X2Neg
+i64x2.all_true, TokenType::Unary, Opcode::I64X2AllTrue
+i64x2.bitmask, TokenType::Unary, Opcode::I64X2Bitmask
+i64x2.extend_low_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendLowI32X4S
+i64x2.extend_high_i32x4_s, TokenType::Unary, Opcode::I64X2ExtendHighI32X4S
+i64x2.extend_low_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendLowI32X4U
+i64x2.extend_high_i32x4_u, TokenType::Unary, Opcode::I64X2ExtendHighI32X4U
+i64x2.replace_lane, TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane
+i64x2.shl, TokenType::Binary, Opcode::I64X2Shl
+i64x2.shr_s, TokenType::Binary, Opcode::I64X2ShrS
+i64x2.shr_u, TokenType::Binary, Opcode::I64X2ShrU
+i64x2.splat, TokenType::Unary, Opcode::I64X2Splat
+i64x2.sub, TokenType::Binary, Opcode::I64X2Sub
+i64x2.extmul_low_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S
+i64x2.extmul_high_i32x4_s, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S
+i64x2.extmul_low_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U
+i64x2.extmul_high_i32x4_u, TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U
+i64x2, TokenType::I64X2
+i64.xor, TokenType::Binary, Opcode::I64Xor
+i8x16.abs, TokenType::Unary, Opcode::I8X16Abs
+i8x16.add_sat_s, TokenType::Binary, Opcode::I8X16AddSatS
+i8x16.add_sat_u, TokenType::Binary, Opcode::I8X16AddSatU
+i8x16.add, TokenType::Binary, Opcode::I8X16Add
+i8x16.all_true, TokenType::Unary, Opcode::I8X16AllTrue
+i8x16.avgr_u, TokenType::Binary, Opcode::I8X16AvgrU
+i8x16.bitmask, TokenType::Unary, Opcode::I8X16Bitmask
+i8x16.eq, TokenType::Compare, Opcode::I8X16Eq
+i8x16.extract_lane_s, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS
+i8x16.extract_lane_u, TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU
+i8x16.ge_s, TokenType::Compare, Opcode::I8X16GeS
+i8x16.ge_u, TokenType::Compare, Opcode::I8X16GeU
+i8x16.gt_s, TokenType::Compare, Opcode::I8X16GtS
+i8x16.gt_u, TokenType::Compare, Opcode::I8X16GtU
+i8x16.le_s, TokenType::Compare, Opcode::I8X16LeS
+i8x16.le_u, TokenType::Compare, Opcode::I8X16LeU
+i8x16.lt_s, TokenType::Compare, Opcode::I8X16LtS
+i8x16.lt_u, TokenType::Compare, Opcode::I8X16LtU
+i8x16.max_s, TokenType::Binary, Opcode::I8X16MaxS
+i8x16.max_u, TokenType::Binary, Opcode::I8X16MaxU
+i8x16.min_s, TokenType::Binary, Opcode::I8X16MinS
+i8x16.min_u, TokenType::Binary, Opcode::I8X16MinU
+i8x16.narrow_i16x8_s, TokenType::Binary, Opcode::I8X16NarrowI16X8S
+i8x16.narrow_i16x8_u, TokenType::Binary, Opcode::I8X16NarrowI16X8U
+i8x16.neg, TokenType::Unary, Opcode::I8X16Neg
+i8x16.popcnt, TokenType::Unary, Opcode::I8X16Popcnt
+i8x16.ne, TokenType::Compare, Opcode::I8X16Ne
+i8x16.replace_lane, TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane
+i8x16.shl, TokenType::Binary, Opcode::I8X16Shl
+i8x16.shr_s, TokenType::Binary, Opcode::I8X16ShrS
+i8x16.shr_u, TokenType::Binary, Opcode::I8X16ShrU
+i8x16.splat, TokenType::Unary, Opcode::I8X16Splat
+i8x16.sub_sat_s, TokenType::Binary, Opcode::I8X16SubSatS
+i8x16.sub_sat_u, TokenType::Binary, Opcode::I8X16SubSatU
+i8x16.sub, TokenType::Binary, Opcode::I8X16Sub
+i8x16, TokenType::I8X16
+if, TokenType::If, Opcode::If
+import, TokenType::Import
+input, TokenType::Input
+invoke, TokenType::Invoke
+item, TokenType::Item
+local.get, TokenType::LocalGet, Opcode::LocalGet
+local.set, TokenType::LocalSet, Opcode::LocalSet
+local.tee, TokenType::LocalTee, Opcode::LocalTee
+local, TokenType::Local
+loop, TokenType::Loop, Opcode::Loop
+memory.atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify
+memory.atomic.wait32, TokenType::AtomicWait, Opcode::MemoryAtomicWait32
+memory.atomic.wait64, TokenType::AtomicWait, Opcode::MemoryAtomicWait64
+memory.copy, TokenType::MemoryCopy, Opcode::MemoryCopy
+memory.fill, TokenType::MemoryFill, Opcode::MemoryFill
+memory.grow, TokenType::MemoryGrow, Opcode::MemoryGrow
+memory.init, TokenType::MemoryInit, Opcode::MemoryInit
+memory.size, TokenType::MemorySize, Opcode::MemorySize
+memory, TokenType::Memory
+module, TokenType::Module
+mut, TokenType::Mut
+nan:arithmetic, TokenType::NanArithmetic
+nan:canonical, TokenType::NanCanonical
+nop, TokenType::Nop, Opcode::Nop
+offset, TokenType::Offset
+output, TokenType::Output
+param, TokenType::Param
+quote, TokenType::Quote
+ref.extern, TokenType::RefExtern
+ref.func, TokenType::RefFunc, Opcode::RefFunc
+ref.is_null, TokenType::RefIsNull, Opcode::RefIsNull
+ref.null, TokenType::RefNull, Opcode::RefNull
+register, TokenType::Register
+result, TokenType::Result
+rethrow, TokenType::Rethrow, Opcode::Rethrow
+return_call_indirect, TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect
+return_call, TokenType::ReturnCall, Opcode::ReturnCall
+return, TokenType::Return, Opcode::Return
+select, TokenType::Select, Opcode::Select
+shared, TokenType::Shared
+start, TokenType::Start
+struct, Type::Struct, TokenType::Struct
+table.copy, TokenType::TableCopy, Opcode::TableCopy
+table.fill, TokenType::TableFill, Opcode::TableFill
+table.get, TokenType::TableGet, Opcode::TableGet
+table.grow, TokenType::TableGrow, Opcode::TableGrow
+table.init, TokenType::TableInit, Opcode::TableInit
+table.set, TokenType::TableSet, Opcode::TableSet
+table.size, TokenType::TableSize, Opcode::TableSize
+table, TokenType::Table
+then, TokenType::Then
+throw, TokenType::Throw, Opcode::Throw
+try, TokenType::Try, Opcode::Try
+type, TokenType::Type
+unreachable, TokenType::Unreachable, Opcode::Unreachable
+v128.andnot, TokenType::Binary, Opcode::V128Andnot
+v128.and, TokenType::Binary, Opcode::V128And
+v128.bitselect, TokenType::Ternary, Opcode::V128BitSelect
+v128.const, TokenType::Const, Opcode::V128Const
+v128.load, TokenType::Load, Opcode::V128Load
+v128.not, TokenType::Unary, Opcode::V128Not
+v128.or, TokenType::Binary, Opcode::V128Or
+v128.any_true, TokenType::Unary, Opcode::V128AnyTrue
+v128.load32_zero, TokenType::Load, Opcode::V128Load32Zero
+v128.load64_zero, TokenType::Load, Opcode::V128Load64Zero
+v128.store, TokenType::Store, Opcode::V128Store
+v128, Type::V128
+v128.xor, TokenType::Binary, Opcode::V128Xor
+v128.load16_splat, TokenType::Load, Opcode::V128Load16Splat
+v128.load32_splat, TokenType::Load, Opcode::V128Load32Splat
+v128.load64_splat, TokenType::Load, Opcode::V128Load64Splat
+v128.load8_splat, TokenType::Load, Opcode::V128Load8Splat
+v128.load8_lane, TokenType::SimdLoadLane, Opcode::V128Load8Lane
+v128.load16_lane, TokenType::SimdLoadLane, Opcode::V128Load16Lane
+v128.load32_lane, TokenType::SimdLoadLane, Opcode::V128Load32Lane
+v128.load64_lane, TokenType::SimdLoadLane, Opcode::V128Load64Lane
+v128.store8_lane, TokenType::SimdStoreLane, Opcode::V128Store8Lane
+v128.store16_lane, TokenType::SimdStoreLane, Opcode::V128Store16Lane
+v128.store32_lane, TokenType::SimdStoreLane, Opcode::V128Store32Lane
+v128.store64_lane, TokenType::SimdStoreLane, Opcode::V128Store64Lane
+i8x16.shuffle, TokenType::SimdShuffleOp, Opcode::I8X16Shuffle
+i8x16.swizzle, TokenType::Binary, Opcode::I8X16Swizzle
+# Deprecated names.
+atomic.notify, TokenType::AtomicNotify, Opcode::MemoryAtomicNotify
+i32.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait32
+i64.atomic.wait, TokenType::AtomicWait, Opcode::MemoryAtomicWait64
+anyfunc, Type::FuncRef
+f32.convert_s/i32, TokenType::Convert, Opcode::F32ConvertI32S
+f32.convert_s/i64, TokenType::Convert, Opcode::F32ConvertI64S
+f32.convert_u/i32, TokenType::Convert, Opcode::F32ConvertI32U
+f32.convert_u/i64, TokenType::Convert, Opcode::F32ConvertI64U
+f32.demote/f64, TokenType::Convert, Opcode::F32DemoteF64
+f32.reinterpret/i32, TokenType::Convert, Opcode::F32ReinterpretI32
+f64.convert_s/i32, TokenType::Convert, Opcode::F64ConvertI32S
+f64.convert_s/i64, TokenType::Convert, Opcode::F64ConvertI64S
+f64.convert_u/i32, TokenType::Convert, Opcode::F64ConvertI32U
+f64.convert_u/i64, TokenType::Convert, Opcode::F64ConvertI64U
+f64.promote/f32, TokenType::Convert, Opcode::F64PromoteF32
+f64.reinterpret/i64, TokenType::Convert, Opcode::F64ReinterpretI64
+get_global, TokenType::GlobalGet, Opcode::GlobalGet
+get_local, TokenType::LocalGet, Opcode::LocalGet
+i32.reinterpret/f32, TokenType::Convert, Opcode::I32ReinterpretF32
+i32.trunc_s/f32, TokenType::Convert, Opcode::I32TruncF32S
+i32.trunc_s/f64, TokenType::Convert, Opcode::I32TruncF64S
+i32.trunc_s:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32S
+i32.trunc_s:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64S
+i32.trunc_u/f32, TokenType::Convert, Opcode::I32TruncF32U
+i32.trunc_u/f64, TokenType::Convert, Opcode::I32TruncF64U
+i32.trunc_u:sat/f32, TokenType::Convert, Opcode::I32TruncSatF32U
+i32.trunc_u:sat/f64, TokenType::Convert, Opcode::I32TruncSatF64U
+i32.wrap/i64, TokenType::Convert, Opcode::I32WrapI64
+i64.extend_s/i32, TokenType::Convert, Opcode::I64ExtendI32S
+i64.extend_u/i32, TokenType::Convert, Opcode::I64ExtendI32U
+i64.reinterpret/f64, TokenType::Convert, Opcode::I64ReinterpretF64
+i64.trunc_s/f32, TokenType::Convert, Opcode::I64TruncF32S
+i64.trunc_s/f64, TokenType::Convert, Opcode::I64TruncF64S
+i64.trunc_s:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32S
+i64.trunc_s:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64S
+i64.trunc_u/f32, TokenType::Convert, Opcode::I64TruncF32U
+i64.trunc_u/f64, TokenType::Convert, Opcode::I64TruncF64U
+i64.trunc_u:sat/f32, TokenType::Convert, Opcode::I64TruncSatF32U
+i64.trunc_u:sat/f64, TokenType::Convert, Opcode::I64TruncSatF64U
+set_global, TokenType::GlobalSet, Opcode::GlobalSet
+set_local, TokenType::LocalSet, Opcode::LocalSet
+tee_local, TokenType::LocalTee, Opcode::LocalTee
diff --git a/third_party/wasm2c/src/lexer-source-line-finder.cc b/third_party/wasm2c/src/lexer-source-line-finder.cc
new file mode 100644
index 0000000000..833cb9009d
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source-line-finder.cc
@@ -0,0 +1,152 @@
+/*
+ * 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 "src/lexer-source-line-finder.h"
+
+#include <algorithm>
+
+#include "src/lexer-source.h"
+
+namespace wabt {
+
+LexerSourceLineFinder::LexerSourceLineFinder(
+ std::unique_ptr<LexerSource> source)
+ : source_(std::move(source)),
+ next_line_start_(0),
+ last_cr_(false),
+ eof_(false) {
+ source_->Seek(0);
+ // Line 0 should not be used; but it makes indexing simpler.
+ line_ranges_.emplace_back(0, 0);
+}
+
+Result LexerSourceLineFinder::GetSourceLine(const Location& loc,
+ Offset max_line_length,
+ SourceLine* out_source_line) {
+ ColumnRange column_range(loc.first_column, loc.last_column);
+ OffsetRange original;
+ CHECK_RESULT(GetLineOffsets(loc.line, &original));
+
+ OffsetRange clamped =
+ ClampSourceLineOffsets(original, column_range, max_line_length);
+ bool has_start_ellipsis = original.start != clamped.start;
+ bool has_end_ellipsis = original.end != clamped.end;
+
+ out_source_line->column_offset = clamped.start - original.start;
+
+ if (has_start_ellipsis) {
+ out_source_line->line += "...";
+ clamped.start += 3;
+ }
+ if (has_end_ellipsis) {
+ clamped.end -= 3;
+ }
+
+ std::vector<char> read_line;
+ CHECK_RESULT(source_->ReadRange(clamped, &read_line));
+ out_source_line->line.append(read_line.begin(), read_line.end());
+
+ if (has_end_ellipsis) {
+ out_source_line->line += "...";
+ }
+
+ return Result::Ok;
+}
+
+bool LexerSourceLineFinder::IsLineCached(int line) const {
+ return static_cast<size_t>(line) < line_ranges_.size();
+}
+
+OffsetRange LexerSourceLineFinder::GetCachedLine(int line) const {
+ assert(IsLineCached(line));
+ return line_ranges_[line];
+}
+
+Result LexerSourceLineFinder::GetLineOffsets(int find_line,
+ OffsetRange* out_range) {
+ if (IsLineCached(find_line)) {
+ *out_range = GetCachedLine(find_line);
+ return Result::Ok;
+ }
+
+ const size_t kBufferSize = 1 << 16;
+ std::vector<char> buffer(kBufferSize);
+
+ assert(!line_ranges_.empty());
+ Offset buffer_file_offset = 0;
+ while (!IsLineCached(find_line) && !eof_) {
+ CHECK_RESULT(source_->Tell(&buffer_file_offset));
+ size_t read_size = source_->Fill(buffer.data(), buffer.size());
+ if (read_size < buffer.size()) {
+ eof_ = true;
+ }
+
+ for (auto iter = buffer.begin(), end = iter + read_size; iter < end;
+ ++iter) {
+ if (*iter == '\n') {
+ // Don't include \n or \r in the line range.
+ Offset line_offset =
+ buffer_file_offset + (iter - buffer.begin()) - last_cr_;
+ line_ranges_.emplace_back(next_line_start_, line_offset);
+ next_line_start_ = line_offset + last_cr_ + 1;
+ }
+ last_cr_ = *iter == '\r';
+ }
+
+ if (eof_) {
+ // Add the final line as an empty range.
+ Offset end = buffer_file_offset + read_size;
+ line_ranges_.emplace_back(next_line_start_, end);
+ }
+ }
+
+ if (IsLineCached(find_line)) {
+ *out_range = GetCachedLine(find_line);
+ return Result::Ok;
+ } else {
+ assert(eof_);
+ return Result::Error;
+ }
+}
+
+// static
+OffsetRange LexerSourceLineFinder::ClampSourceLineOffsets(
+ OffsetRange offset_range,
+ ColumnRange column_range,
+ Offset max_line_length) {
+ Offset line_length = offset_range.size();
+ if (line_length > max_line_length) {
+ size_t column_count = column_range.size();
+ size_t center_on;
+ if (column_count > max_line_length) {
+ // The column range doesn't fit, just center on first_column.
+ center_on = column_range.start - 1;
+ } else {
+ // the entire range fits, display it all in the center.
+ center_on = (column_range.start + column_range.end) / 2 - 1;
+ }
+ if (center_on > max_line_length / 2) {
+ offset_range.start += center_on - max_line_length / 2;
+ }
+ offset_range.start =
+ std::min(offset_range.start, offset_range.end - max_line_length);
+ offset_range.end = offset_range.start + max_line_length;
+ }
+
+ return offset_range;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/lexer-source-line-finder.h b/third_party/wasm2c/src/lexer-source-line-finder.h
new file mode 100644
index 0000000000..baf87b4ea7
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source-line-finder.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_LEXER_SOURCE_LINE_FINDER_H_
+#define WABT_LEXER_SOURCE_LINE_FINDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/common.h"
+#include "src/lexer-source.h"
+#include "src/range.h"
+
+namespace wabt {
+
+class LexerSourceLineFinder {
+ public:
+ struct SourceLine {
+ std::string line;
+ int column_offset;
+ };
+
+ explicit LexerSourceLineFinder(std::unique_ptr<LexerSource>);
+
+ Result GetSourceLine(const Location& loc,
+ Offset max_line_length,
+ SourceLine* out_source_line);
+ Result GetLineOffsets(int line, OffsetRange* out_offsets);
+
+ private:
+ static OffsetRange ClampSourceLineOffsets(OffsetRange line_offset_range,
+ ColumnRange column_range,
+ Offset max_line_length);
+
+ bool IsLineCached(int line) const;
+ OffsetRange GetCachedLine(int line) const;
+
+ std::unique_ptr<LexerSource> source_;
+ std::vector<OffsetRange> line_ranges_;
+ Offset next_line_start_;
+ bool last_cr_; // Last read character was a '\r' (carriage return).
+ bool eof_;
+};
+
+} // namespace wabt
+
+#endif // WABT_LEXER_SOURCE_LINE_FINDER_H_
diff --git a/third_party/wasm2c/src/lexer-source.cc b/third_party/wasm2c/src/lexer-source.cc
new file mode 100644
index 0000000000..896a52e938
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source.cc
@@ -0,0 +1,67 @@
+/*
+ * 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 "src/lexer-source.h"
+
+#include <algorithm>
+
+namespace wabt {
+
+LexerSource::LexerSource(const void* data, Offset size)
+ : data_(data), size_(size), read_offset_(0) {}
+
+std::unique_ptr<LexerSource> LexerSource::Clone() {
+ LexerSource* result = new LexerSource(data_, size_);
+ result->read_offset_ = read_offset_;
+ return std::unique_ptr<LexerSource>(result);
+}
+
+Result LexerSource::Tell(Offset* out_offset) {
+ *out_offset = read_offset_;
+ return Result::Ok;
+}
+
+size_t LexerSource::Fill(void* dest, Offset size) {
+ Offset read_size = std::min(size, size_ - read_offset_);
+ if (read_size > 0) {
+ const void* src = static_cast<const char*>(data_) + read_offset_;
+ memcpy(dest, src, read_size);
+ read_offset_ += read_size;
+ }
+ return read_size;
+}
+
+Result LexerSource::Seek(Offset offset) {
+ if (offset < size_) {
+ read_offset_ = offset;
+ return Result::Ok;
+ }
+ return Result::Error;
+}
+
+Result LexerSource::ReadRange(OffsetRange range, std::vector<char>* out_data) {
+ OffsetRange clamped = range;
+ clamped.start = std::min(clamped.start, size_);
+ clamped.end = std::min(clamped.end, size_);
+ if (clamped.size()) {
+ out_data->resize(clamped.size());
+ const void* src = static_cast<const char*>(data_) + clamped.start;
+ memcpy(out_data->data(), src, clamped.size());
+ }
+ return Result::Ok;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/lexer-source.h b/third_party/wasm2c/src/lexer-source.h
new file mode 100644
index 0000000000..d49d5c6e71
--- /dev/null
+++ b/third_party/wasm2c/src/lexer-source.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_LEXER_SOURCE_H_
+#define WABT_LEXER_SOURCE_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/common.h"
+#include "src/range.h"
+
+namespace wabt {
+
+class LexerSource {
+ public:
+ LexerSource(const void* data, Offset size);
+
+ std::unique_ptr<LexerSource> Clone();
+ Result Tell(Offset* out_offset);
+ size_t Fill(void* dest, size_t size);
+ Result ReadRange(OffsetRange, std::vector<char>* out_data);
+ Result Seek(Offset offset);
+
+ WABT_DISALLOW_COPY_AND_ASSIGN(LexerSource);
+
+ const void* data() { return data_; }
+ Offset size() { return size_; }
+
+ private:
+ const void* data_;
+ Offset size_;
+ Offset read_offset_;
+};
+
+} // namespace wabt
+
+#endif // WABT_LEXER_SOURCE_H_
diff --git a/third_party/wasm2c/src/literal.cc b/third_party/wasm2c/src/literal.cc
new file mode 100644
index 0000000000..0061772e2d
--- /dev/null
+++ b/third_party/wasm2c/src/literal.cc
@@ -0,0 +1,830 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/literal.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cinttypes>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <type_traits>
+
+namespace wabt {
+
+namespace {
+
+template <typename T>
+struct FloatTraitsBase {};
+
+// The "PlusOne" values are used because normal IEEE floats have an implicit
+// leading one, so they have an additional bit of precision.
+
+template <>
+struct FloatTraitsBase<float> {
+ typedef uint32_t Uint;
+ static constexpr int kBits = sizeof(Uint) * 8;
+ static constexpr int kSigBits = 23;
+ static constexpr float kHugeVal = HUGE_VALF;
+ static constexpr int kMaxHexBufferSize = WABT_MAX_FLOAT_HEX;
+
+ static float Strto(const char* s, char** endptr) { return strtof(s, endptr); }
+};
+
+template <>
+struct FloatTraitsBase<double> {
+ typedef uint64_t Uint;
+ static constexpr int kBits = sizeof(Uint) * 8;
+ static constexpr int kSigBits = 52;
+ static constexpr float kHugeVal = HUGE_VAL;
+ static constexpr int kMaxHexBufferSize = WABT_MAX_DOUBLE_HEX;
+
+ static double Strto(const char* s, char** endptr) {
+ return strtod(s, endptr);
+ }
+};
+
+template <typename T>
+struct FloatTraits : FloatTraitsBase<T> {
+ typedef typename FloatTraitsBase<T>::Uint Uint;
+ using FloatTraitsBase<T>::kBits;
+ using FloatTraitsBase<T>::kSigBits;
+
+ static constexpr int kExpBits = kBits - kSigBits - 1;
+ static constexpr int kSignShift = kBits - 1;
+ static constexpr Uint kSigMask = (Uint(1) << kSigBits) - 1;
+ static constexpr int kSigPlusOneBits = kSigBits + 1;
+ static constexpr Uint kSigPlusOneMask = (Uint(1) << kSigPlusOneBits) - 1;
+ static constexpr int kExpMask = (1 << kExpBits) - 1;
+ static constexpr int kMaxExp = 1 << (kExpBits - 1);
+ static constexpr int kMinExp = -kMaxExp + 1;
+ static constexpr int kExpBias = -kMinExp;
+ static constexpr Uint kQuietNanTag = Uint(1) << (kSigBits - 1);
+};
+
+template <typename T>
+class FloatParser {
+ public:
+ typedef FloatTraits<T> Traits;
+ typedef typename Traits::Uint Uint;
+ typedef T Float;
+
+ static Result Parse(LiteralType,
+ const char* s,
+ const char* end,
+ Uint* out_bits);
+
+ private:
+ static bool StringStartsWith(const char* start,
+ const char* end,
+ const char* prefix);
+ static Uint Make(bool sign, int exp, Uint sig);
+ static Uint ShiftAndRoundToNearest(Uint significand,
+ int shift,
+ bool seen_trailing_non_zero);
+
+ static Result ParseFloat(const char* s, const char* end, Uint* out_bits);
+ static Result ParseNan(const char* s, const char* end, Uint* out_bits);
+ static Result ParseHex(const char* s, const char* end, Uint* out_bits);
+ static void ParseInfinity(const char* s, const char* end, Uint* out_bits);
+};
+
+template <typename T>
+class FloatWriter {
+ public:
+ typedef FloatTraits<T> Traits;
+ typedef typename Traits::Uint Uint;
+
+ static void WriteHex(char* out, size_t size, Uint bits);
+};
+
+// Return 1 if the non-NULL-terminated string starting with |start| and ending
+// with |end| starts with the NULL-terminated string |prefix|.
+template <typename T>
+// static
+bool FloatParser<T>::StringStartsWith(const char* start,
+ const char* end,
+ const char* prefix) {
+ while (start < end && *prefix) {
+ if (*start != *prefix) {
+ return false;
+ }
+ start++;
+ prefix++;
+ }
+ return *prefix == 0;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseFloat(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ // Here is the normal behavior for strtof/strtod:
+ //
+ // input | errno | output |
+ // ---------------------------------
+ // overflow | ERANGE | +-HUGE_VAL |
+ // underflow | ERANGE | 0.0 |
+ // otherwise | 0 | value |
+ //
+ // So normally we need to clear errno before calling strto{f,d}, and check
+ // afterward whether it was set to ERANGE.
+ //
+ // glibc seems to have a bug where
+ // strtof("340282356779733661637539395458142568448") will return HUGE_VAL,
+ // but will not set errno to ERANGE. Since this function is only called when
+ // we know that we have parsed a "normal" number (i.e. not "inf"), we know
+ // that if we ever get HUGE_VAL, it must be overflow.
+ //
+ // The WebAssembly spec also ignores underflow, so we don't need to check for
+ // ERANGE at all.
+
+ // WebAssembly floats can contain underscores, but strto* can't parse those,
+ // so remove them first.
+ assert(s <= end);
+ const size_t kBufferSize = end - s + 1; // +1 for \0.
+ char* buffer = static_cast<char*>(alloca(kBufferSize));
+ auto buffer_end =
+ std::copy_if(s, end, buffer, [](char c) -> bool { return c != '_'; });
+ assert(buffer_end < buffer + kBufferSize);
+ *buffer_end = 0;
+
+ char* endptr;
+ Float value = Traits::Strto(buffer, &endptr);
+ if (endptr != buffer_end ||
+ (value == Traits::kHugeVal || value == -Traits::kHugeVal)) {
+ return Result::Error;
+ }
+
+ memcpy(out_bits, &value, sizeof(value));
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+typename FloatParser<T>::Uint FloatParser<T>::Make(bool sign,
+ int exp,
+ Uint sig) {
+ assert(exp >= Traits::kMinExp && exp <= Traits::kMaxExp);
+ assert(sig <= Traits::kSigMask);
+ return (Uint(sign) << Traits::kSignShift) |
+ (Uint(exp + Traits::kExpBias) << Traits::kSigBits) | sig;
+}
+
+// static
+template <typename T>
+typename FloatParser<T>::Uint FloatParser<T>::ShiftAndRoundToNearest(
+ Uint significand,
+ int shift,
+ bool seen_trailing_non_zero) {
+ assert(shift > 0);
+ // Round ties to even.
+ if ((significand & (Uint(1) << shift)) || seen_trailing_non_zero) {
+ significand += Uint(1) << (shift - 1);
+ }
+ significand >>= shift;
+ return significand;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseNan(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "nan"));
+ s += 3;
+
+ Uint tag;
+ if (s != end) {
+ tag = 0;
+ assert(StringStartsWith(s, end, ":0x"));
+ s += 3;
+
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+ uint32_t digit;
+ CHECK_RESULT(ParseHexdigit(*s, &digit));
+ tag = tag * 16 + digit;
+ // Check for overflow.
+ if (tag > Traits::kSigMask) {
+ return Result::Error;
+ }
+ }
+
+ // NaN cannot have a zero tag, that is reserved for infinity.
+ if (tag == 0) {
+ return Result::Error;
+ }
+ } else {
+ tag = Traits::kQuietNanTag;
+ }
+
+ *out_bits = Make(is_neg, Traits::kMaxExp, tag);
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::ParseHex(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "0x"));
+ s += 2;
+
+ // Loop over the significand; everything up to the 'p'.
+ // This code is a bit nasty because we want to support extra zeroes anywhere
+ // without having to use many significand bits.
+ // e.g.
+ // 0x00000001.0p0 => significand = 1, significand_exponent = 0
+ // 0x10000000.0p0 => significand = 1, significand_exponent = 28
+ // 0x0.000001p0 => significand = 1, significand_exponent = -24
+ bool seen_dot = false;
+ bool seen_trailing_non_zero = false;
+ Uint significand = 0;
+ int significand_exponent = 0; // Exponent adjustment due to dot placement.
+ for (; s < end; ++s) {
+ uint32_t digit;
+ if (*s == '_') {
+ continue;
+ } else if (*s == '.') {
+ seen_dot = true;
+ } else if (Succeeded(ParseHexdigit(*s, &digit))) {
+ if (Traits::kBits - Clz(significand) <= Traits::kSigPlusOneBits) {
+ significand = (significand << 4) + digit;
+ if (seen_dot) {
+ significand_exponent -= 4;
+ }
+ } else {
+ if (!seen_trailing_non_zero && digit != 0) {
+ seen_trailing_non_zero = true;
+ }
+ if (!seen_dot) {
+ significand_exponent += 4;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (significand == 0) {
+ // 0 or -0.
+ *out_bits = Make(is_neg, Traits::kMinExp, 0);
+ return Result::Ok;
+ }
+
+ int exponent = 0;
+ bool exponent_is_neg = false;
+ if (s < end) {
+ assert(*s == 'p' || *s == 'P');
+ s++;
+ // Exponent is always positive, but significand_exponent is signed.
+ // significand_exponent_add is negated if exponent will be negative, so it
+ // can be easily summed to see if the exponent is too large (see below).
+ int significand_exponent_add = 0;
+ if (*s == '-') {
+ exponent_is_neg = true;
+ significand_exponent_add = -significand_exponent;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ significand_exponent_add = significand_exponent;
+ }
+
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+
+ uint32_t digit = (*s - '0');
+ assert(digit <= 9);
+ exponent = exponent * 10 + digit;
+ if (exponent + significand_exponent_add >= Traits::kMaxExp) {
+ break;
+ }
+ }
+ }
+
+ if (exponent_is_neg) {
+ exponent = -exponent;
+ }
+
+ int significand_bits = Traits::kBits - Clz(significand);
+ // -1 for the implicit 1 bit of the significand.
+ exponent += significand_exponent + significand_bits - 1;
+
+ if (exponent <= Traits::kMinExp) {
+ // Maybe subnormal.
+ auto update_seen_trailing_non_zero = [&](int shift) {
+ assert(shift > 0);
+ auto mask = (Uint(1) << (shift - 1)) - 1;
+ seen_trailing_non_zero |= (significand & mask) != 0;
+ };
+
+ // Normalize significand.
+ if (significand_bits > Traits::kSigBits) {
+ int shift = significand_bits - Traits::kSigBits;
+ update_seen_trailing_non_zero(shift);
+ significand >>= shift;
+ } else if (significand_bits < Traits::kSigBits) {
+ significand <<= (Traits::kSigBits - significand_bits);
+ }
+
+ int shift = Traits::kMinExp - exponent;
+ if (shift <= Traits::kSigBits) {
+ if (shift) {
+ update_seen_trailing_non_zero(shift);
+ significand =
+ ShiftAndRoundToNearest(significand, shift, seen_trailing_non_zero) &
+ Traits::kSigMask;
+ }
+ exponent = Traits::kMinExp;
+
+ if (significand != 0) {
+ *out_bits = Make(is_neg, exponent, significand);
+ return Result::Ok;
+ }
+ }
+
+ // Not subnormal, too small; return 0 or -0.
+ *out_bits = Make(is_neg, Traits::kMinExp, 0);
+ } else {
+ // Maybe Normal value.
+ if (significand_bits > Traits::kSigPlusOneBits) {
+ significand = ShiftAndRoundToNearest(
+ significand, significand_bits - Traits::kSigPlusOneBits,
+ seen_trailing_non_zero);
+ if (significand > Traits::kSigPlusOneMask) {
+ exponent++;
+ }
+ } else if (significand_bits < Traits::kSigPlusOneBits) {
+ significand <<= (Traits::kSigPlusOneBits - significand_bits);
+ }
+
+ if (exponent >= Traits::kMaxExp) {
+ // Would be inf or -inf, but the spec doesn't allow rounding hex-floats to
+ // infinity.
+ return Result::Error;
+ }
+
+ *out_bits = Make(is_neg, exponent, significand & Traits::kSigMask);
+ }
+
+ return Result::Ok;
+}
+
+// static
+template <typename T>
+void FloatParser<T>::ParseInfinity(const char* s,
+ const char* end,
+ Uint* out_bits) {
+ bool is_neg = false;
+ if (*s == '-') {
+ is_neg = true;
+ s++;
+ } else if (*s == '+') {
+ s++;
+ }
+ assert(StringStartsWith(s, end, "inf"));
+ *out_bits = Make(is_neg, Traits::kMaxExp, 0);
+}
+
+// static
+template <typename T>
+Result FloatParser<T>::Parse(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ Uint* out_bits) {
+#if COMPILER_IS_MSVC
+ if (literal_type == LiteralType::Int && StringStartsWith(s, end, "0x")) {
+ // Some MSVC crt implementation of strtof doesn't support hex strings
+ literal_type = LiteralType::Hexfloat;
+ }
+#endif
+ switch (literal_type) {
+ case LiteralType::Int:
+ case LiteralType::Float:
+ return ParseFloat(s, end, out_bits);
+
+ case LiteralType::Hexfloat:
+ return ParseHex(s, end, out_bits);
+
+ case LiteralType::Infinity:
+ ParseInfinity(s, end, out_bits);
+ return Result::Ok;
+
+ case LiteralType::Nan:
+ return ParseNan(s, end, out_bits);
+ }
+
+ WABT_UNREACHABLE;
+}
+
+// static
+template <typename T>
+void FloatWriter<T>::WriteHex(char* out, size_t size, Uint bits) {
+ static constexpr int kNumNybbles = Traits::kBits / 4;
+ static constexpr int kTopNybbleShift = Traits::kBits - 4;
+ static constexpr Uint kTopNybble = Uint(0xf) << kTopNybbleShift;
+ static const char s_hex_digits[] = "0123456789abcdef";
+
+ char buffer[Traits::kMaxHexBufferSize];
+ char* p = buffer;
+ bool is_neg = (bits >> Traits::kSignShift);
+ int exp = ((bits >> Traits::kSigBits) & Traits::kExpMask) - Traits::kExpBias;
+ Uint sig = bits & Traits::kSigMask;
+
+ if (is_neg) {
+ *p++ = '-';
+ }
+ if (exp == Traits::kMaxExp) {
+ // Infinity or nan.
+ if (sig == 0) {
+ strcpy(p, "inf");
+ p += 3;
+ } else {
+ strcpy(p, "nan");
+ p += 3;
+ if (sig != Traits::kQuietNanTag) {
+ strcpy(p, ":0x");
+ p += 3;
+ // Skip leading zeroes.
+ int num_nybbles = kNumNybbles;
+ while ((sig & kTopNybble) == 0) {
+ sig <<= 4;
+ num_nybbles--;
+ }
+ while (num_nybbles) {
+ Uint nybble = (sig >> kTopNybbleShift) & 0xf;
+ *p++ = s_hex_digits[nybble];
+ sig <<= 4;
+ --num_nybbles;
+ }
+ }
+ }
+ } else {
+ bool is_zero = sig == 0 && exp == Traits::kMinExp;
+ strcpy(p, "0x");
+ p += 2;
+ *p++ = is_zero ? '0' : '1';
+
+ // Shift sig up so the top 4-bits are at the top of the Uint.
+ sig <<= Traits::kBits - Traits::kSigBits;
+
+ if (sig) {
+ if (exp == Traits::kMinExp) {
+ // Subnormal; shift the significand up, and shift out the implicit 1.
+ Uint leading_zeroes = Clz(sig);
+ if (leading_zeroes < Traits::kSignShift) {
+ sig <<= leading_zeroes + 1;
+ } else {
+ sig = 0;
+ }
+ exp -= leading_zeroes;
+ }
+
+ *p++ = '.';
+ while (sig) {
+ int nybble = (sig >> kTopNybbleShift) & 0xf;
+ *p++ = s_hex_digits[nybble];
+ sig <<= 4;
+ }
+ }
+ *p++ = 'p';
+ if (is_zero) {
+ strcpy(p, "+0");
+ p += 2;
+ } else {
+ if (exp < 0) {
+ *p++ = '-';
+ exp = -exp;
+ } else {
+ *p++ = '+';
+ }
+ if (exp >= 1000) {
+ *p++ = '1';
+ }
+ if (exp >= 100) {
+ *p++ = '0' + (exp / 100) % 10;
+ }
+ if (exp >= 10) {
+ *p++ = '0' + (exp / 10) % 10;
+ }
+ *p++ = '0' + exp % 10;
+ }
+ }
+
+ size_t len = p - buffer;
+ if (len >= size) {
+ len = size - 1;
+ }
+ memcpy(out, buffer, len);
+ out[len] = '\0';
+}
+
+} // end anonymous namespace
+
+Result ParseHexdigit(char c, uint32_t* out) {
+ if (static_cast<unsigned int>(c - '0') <= 9) {
+ *out = c - '0';
+ return Result::Ok;
+ } else if (static_cast<unsigned int>(c - 'a') < 6) {
+ *out = 10 + (c - 'a');
+ return Result::Ok;
+ } else if (static_cast<unsigned int>(c - 'A') < 6) {
+ *out = 10 + (c - 'A');
+ return Result::Ok;
+ }
+ return Result::Error;
+}
+
+Result ParseUint64(const char* s, const char* end, uint64_t* out) {
+ if (s == end) {
+ return Result::Error;
+ }
+ uint64_t value = 0;
+ if (*s == '0' && s + 1 < end && s[1] == 'x') {
+ s += 2;
+ if (s == end) {
+ return Result::Error;
+ }
+ constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16;
+ constexpr uint64_t kMaxMod16 = UINT64_MAX % 16;
+ for (; s < end; ++s) {
+ uint32_t digit;
+ if (*s == '_') {
+ continue;
+ }
+ CHECK_RESULT(ParseHexdigit(*s, &digit));
+ // Check for overflow.
+ if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) {
+ return Result::Error;
+ }
+ value = value * 16 + digit;
+ }
+ } else {
+ constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10;
+ constexpr uint64_t kMaxMod10 = UINT64_MAX % 10;
+ for (; s < end; ++s) {
+ if (*s == '_') {
+ continue;
+ }
+ uint32_t digit = (*s - '0');
+ if (digit > 9) {
+ return Result::Error;
+ }
+ // Check for overflow.
+ if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) {
+ return Result::Error;
+ }
+ value = value * 10 + digit;
+ }
+ }
+ if (s != end) {
+ return Result::Error;
+ }
+ *out = value;
+ return Result::Ok;
+}
+
+Result ParseInt64(const char* s,
+ const char* end,
+ uint64_t* out,
+ ParseIntType parse_type) {
+ bool has_sign = false;
+ if (*s == '-' || *s == '+') {
+ if (parse_type == ParseIntType::UnsignedOnly) {
+ return Result::Error;
+ }
+ if (*s == '-') {
+ has_sign = true;
+ }
+ s++;
+ }
+ uint64_t value = 0;
+ Result result = ParseUint64(s, end, &value);
+ if (has_sign) {
+ // abs(INT64_MIN) == INT64_MAX + 1.
+ if (value > static_cast<uint64_t>(INT64_MAX) + 1) {
+ return Result::Error;
+ }
+ value = UINT64_MAX - value + 1;
+ }
+ *out = value;
+ return result;
+}
+
+namespace {
+uint32_t AddWithCarry(uint32_t x, uint32_t y, uint32_t* carry) {
+ // Increments *carry if the addition overflows, otherwise leaves carry alone.
+ if ((0xffffffff - x) < y) ++*carry;
+ return x + y;
+}
+
+void Mul10(v128* v) {
+ // Multiply-by-10 decomposes into (x << 3) + (x << 1). We implement those
+ // operations with carrying from smaller quads of the v128 to the larger
+ // quads.
+
+ constexpr uint32_t kTopThreeBits = 0xe0000000;
+ constexpr uint32_t kTopBit = 0x80000000;
+
+ uint32_t carry_into_v1 =
+ ((v->u32(0) & kTopThreeBits) >> 29) + ((v->u32(0) & kTopBit) >> 31);
+ v->set_u32(0, AddWithCarry(v->u32(0) << 3, v->u32(0) << 1, &carry_into_v1));
+ uint32_t carry_into_v2 =
+ ((v->u32(1) & kTopThreeBits) >> 29) + ((v->u32(1) & kTopBit) >> 31);
+ v->set_u32(1, AddWithCarry(v->u32(1) << 3, v->u32(1) << 1, &carry_into_v2));
+ v->set_u32(1, AddWithCarry(v->u32(1), carry_into_v1, &carry_into_v2));
+ uint32_t carry_into_v3 =
+ ((v->u32(2) & kTopThreeBits) >> 29) + ((v->u32(2) & kTopBit) >> 31);
+ v->set_u32(2, AddWithCarry(v->u32(2) << 3, v->u32(2) << 1, &carry_into_v3));
+ v->set_u32(2, AddWithCarry(v->u32(2), carry_into_v2, &carry_into_v3));
+ v->set_u32(3, v->u32(3) * 10 + carry_into_v3);
+}
+}
+
+Result ParseUint128(const char* s,
+ const char* end,
+ v128* out) {
+ if (s == end) {
+ return Result::Error;
+ }
+
+ out->set_zero();
+
+ while (true) {
+ uint32_t digit = (*s - '0');
+ if (digit > 9) {
+ return Result::Error;
+ }
+
+ uint32_t carry_into_v1 = 0;
+ uint32_t carry_into_v2 = 0;
+ uint32_t carry_into_v3 = 0;
+ uint32_t overflow = 0;
+ out->set_u32(0, AddWithCarry(out->u32(0), digit, &carry_into_v1));
+ out->set_u32(1, AddWithCarry(out->u32(1), carry_into_v1, &carry_into_v2));
+ out->set_u32(2, AddWithCarry(out->u32(2), carry_into_v2, &carry_into_v3));
+ out->set_u32(3, AddWithCarry(out->u32(3), carry_into_v3, &overflow));
+ if (overflow) {
+ return Result::Error;
+ }
+
+ ++s;
+
+ if (s == end) {
+ break;
+ }
+
+ Mul10(out);
+ }
+ return Result::Ok;
+}
+
+template <typename U>
+Result ParseInt(const char* s,
+ const char* end,
+ U* out,
+ ParseIntType parse_type) {
+ typedef typename std::make_signed<U>::type S;
+ uint64_t value;
+ bool has_sign = false;
+ if (*s == '-' || *s == '+') {
+ if (parse_type == ParseIntType::UnsignedOnly) {
+ return Result::Error;
+ }
+ if (*s == '-') {
+ has_sign = true;
+ }
+ s++;
+ }
+ CHECK_RESULT(ParseUint64(s, end, &value));
+
+ if (has_sign) {
+ // abs(INTN_MIN) == INTN_MAX + 1.
+ if (value > static_cast<uint64_t>(std::numeric_limits<S>::max()) + 1) {
+ return Result::Error;
+ }
+ value = std::numeric_limits<U>::max() - value + 1;
+ } else {
+ if (value > static_cast<uint64_t>(std::numeric_limits<U>::max())) {
+ return Result::Error;
+ }
+ }
+ *out = static_cast<U>(value);
+ return Result::Ok;
+}
+
+Result ParseInt8(const char* s,
+ const char* end,
+ uint8_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseInt16(const char* s,
+ const char* end,
+ uint16_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseInt32(const char* s,
+ const char* end,
+ uint32_t* out,
+ ParseIntType parse_type) {
+ return ParseInt(s, end, out, parse_type);
+}
+
+Result ParseFloat(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint32_t* out_bits) {
+ return FloatParser<float>::Parse(literal_type, s, end, out_bits);
+}
+
+Result ParseDouble(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint64_t* out_bits) {
+ return FloatParser<double>::Parse(literal_type, s, end, out_bits);
+}
+
+void WriteFloatHex(char* buffer, size_t size, uint32_t bits) {
+ return FloatWriter<float>::WriteHex(buffer, size, bits);
+}
+
+void WriteDoubleHex(char* buffer, size_t size, uint64_t bits) {
+ return FloatWriter<double>::WriteHex(buffer, size, bits);
+}
+
+void WriteUint128(char* buffer, size_t size, v128 bits) {
+ uint64_t digits;
+ uint64_t remainder;
+ char reversed_buffer[40];
+ size_t len = 0;
+ do {
+ remainder = bits.u32(3);
+
+ for (int i = 3; i != 0; --i) {
+ digits = remainder / 10;
+ remainder = ((remainder - digits * 10) << 32) + bits.u32(i-1);
+ bits.set_u32(i, digits);
+ }
+
+ digits = remainder / 10;
+ remainder = remainder - digits * 10;
+ bits.set_u32(0, digits);
+
+ char remainder_buffer[21];
+ snprintf(remainder_buffer, 21, "%" PRIu64, remainder);
+ int remainder_buffer_len = strlen(remainder_buffer);
+ assert(len + remainder_buffer_len < sizeof(reversed_buffer));
+ memcpy(&reversed_buffer[len], remainder_buffer, remainder_buffer_len);
+ len += remainder_buffer_len;
+ } while (!bits.is_zero());
+ size_t truncated_tail = 0;
+ if (len >= size) {
+ truncated_tail = len - size + 1;
+ len = size - 1;
+ }
+ std::reverse_copy(reversed_buffer + truncated_tail,
+ reversed_buffer + len + truncated_tail,
+ buffer);
+ buffer[len] = '\0';
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/literal.h b/third_party/wasm2c/src/literal.h
new file mode 100644
index 0000000000..b6982613db
--- /dev/null
+++ b/third_party/wasm2c/src/literal.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_LITERAL_H_
+#define WABT_LITERAL_H_
+
+#include <cstdint>
+
+#include "src/common.h"
+
+namespace wabt {
+
+// These functions all return Result::Ok on success and Result::Error on
+// failure.
+//
+// NOTE: the functions are written for use with wast-lexer, assuming that the
+// literal has already matched the patterns defined there. As a result, the
+// only validation that is done is for overflow, not for otherwise bogus input.
+
+enum class LiteralType {
+ Int,
+ Float,
+ Hexfloat,
+ Infinity,
+ Nan,
+};
+
+enum class ParseIntType {
+ UnsignedOnly = 0,
+ SignedAndUnsigned = 1,
+};
+
+/* Size of char buffer required to hold hex representation of a float/double */
+#define WABT_MAX_FLOAT_HEX 20
+#define WABT_MAX_DOUBLE_HEX 40
+
+Result ParseHexdigit(char c, uint32_t* out);
+Result ParseInt8(const char* s,
+ const char* end,
+ uint8_t* out,
+ ParseIntType parse_type);
+Result ParseInt16(const char* s,
+ const char* end,
+ uint16_t* out,
+ ParseIntType parse_type);
+Result ParseInt32(const char* s,
+ const char* end,
+ uint32_t* out,
+ ParseIntType parse_type);
+Result ParseInt64(const char* s,
+ const char* end,
+ uint64_t* out,
+ ParseIntType parse_type);
+Result ParseUint64(const char* s, const char* end, uint64_t* out);
+Result ParseUint128(const char* s, const char* end, v128* out);
+Result ParseFloat(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint32_t* out_bits);
+Result ParseDouble(LiteralType literal_type,
+ const char* s,
+ const char* end,
+ uint64_t* out_bits);
+
+void WriteFloatHex(char* buffer, size_t size, uint32_t bits);
+void WriteDoubleHex(char* buffer, size_t size, uint64_t bits);
+void WriteUint128(char* buffer, size_t size, v128 bits);
+
+} // namespace wabt
+
+#endif /* WABT_LITERAL_H_ */
diff --git a/third_party/wasm2c/src/make-unique.h b/third_party/wasm2c/src/make-unique.h
new file mode 100644
index 0000000000..46d3ac14a5
--- /dev/null
+++ b/third_party/wasm2c/src/make-unique.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_MAKE_UNIQUE_H_
+#define WABT_MAKE_UNIQUE_H_
+
+#include <memory>
+
+namespace wabt {
+
+// This is named MakeUnique instead of make_unique because make_unique has the
+// potential to conflict with std::make_unique if it is defined.
+//
+// On gcc/clang, we currently compile with c++11, which doesn't define
+// std::make_unique, but on MSVC the newest C++ version is always used, which
+// includes std::make_unique. If an argument from the std namespace is used, it
+// will cause ADL to find std::make_unique, and an unqualified call to
+// make_unique will be ambiguous. We can work around this by fully qualifying
+// the call (i.e. wabt::make_unique), but it's simpler to just use a different
+// name. It's also more consistent with other names in the wabt namespace,
+// which use CamelCase.
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+} // namespace wabt
+
+#endif // WABT_MAKE_UNIQUE_H_
diff --git a/third_party/wasm2c/src/opcode-code-table.c b/third_party/wasm2c/src/opcode-code-table.c
new file mode 100644
index 0000000000..c3e06d036e
--- /dev/null
+++ b/third_party/wasm2c/src/opcode-code-table.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/opcode-code-table.h"
+
+#include "config.h"
+
+#include <stdint.h>
+
+typedef enum WabtOpcodeEnum {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ Name,
+#include "opcode.def"
+#undef WABT_OPCODE
+ Invalid,
+} WabtOpcodeEnum;
+
+WABT_STATIC_ASSERT(Invalid <= WABT_OPCODE_CODE_TABLE_SIZE);
+
+/* The array index calculated below must match the one in Opcode::FromCode. */
+uint32_t WabtOpcodeCodeTable[WABT_OPCODE_CODE_TABLE_SIZE] = {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ [(prefix << 8) + code] = Name,
+#include "opcode.def"
+#undef WABT_OPCODE
+};
diff --git a/third_party/wasm2c/src/opcode-code-table.h b/third_party/wasm2c/src/opcode-code-table.h
new file mode 100644
index 0000000000..b223e161da
--- /dev/null
+++ b/third_party/wasm2c/src/opcode-code-table.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef WABT_OPCODE_CODE_TABLE_H_
+#define WABT_OPCODE_CODE_TABLE_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WABT_OPCODE_CODE_TABLE_SIZE 65536
+
+/* This structure is defined in C because C++ doesn't (yet) allow you to use
+ * designated array initializers, i.e. [10] = {foo}.
+ */
+extern uint32_t WabtOpcodeCodeTable[WABT_OPCODE_CODE_TABLE_SIZE];
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* WABT_OPCODE_CODE_TABLE_H_ */
diff --git a/third_party/wasm2c/src/opcode.cc b/third_party/wasm2c/src/opcode.cc
new file mode 100644
index 0000000000..6a70eb4fab
--- /dev/null
+++ b/third_party/wasm2c/src/opcode.cc
@@ -0,0 +1,407 @@
+/*
+ * 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 "src/opcode.h"
+
+#include "src/feature.h"
+
+namespace wabt {
+
+// static
+Opcode::Info Opcode::infos_[] = {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ {text, decomp, Type::rtype, {Type::type1, Type::type2, Type::type3}, \
+ mem_size, prefix, code, PrefixCode(prefix, code)},
+#include "src/opcode.def"
+#undef WABT_OPCODE
+
+ {"<invalid>", "", Type::Void, {Type::Void, Type::Void, Type::Void}, 0, 0, 0, 0},
+};
+
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ /* static */ Opcode Opcode::Name##_Opcode(Opcode::Name);
+#include "src/opcode.def"
+#undef WABT_OPCODE
+
+Opcode::Info Opcode::GetInfo() const {
+ if (enum_ < Invalid) {
+ return infos_[enum_];
+ }
+
+ Info invalid_info = infos_[Opcode::Invalid];
+ DecodeInvalidOpcode(enum_, &invalid_info.prefix, &invalid_info.code);
+ invalid_info.prefix_code = PrefixCode(invalid_info.prefix, invalid_info.code);
+ return invalid_info;
+}
+
+bool Opcode::IsNaturallyAligned(Address alignment) const {
+ Address opcode_align = GetMemorySize();
+ return alignment == WABT_USE_NATURAL_ALIGNMENT || alignment == opcode_align;
+}
+
+Address Opcode::GetAlignment(Address alignment) const {
+ if (alignment == WABT_USE_NATURAL_ALIGNMENT) {
+ return GetMemorySize();
+ }
+ return alignment;
+}
+
+bool Opcode::IsEnabled(const Features& features) const {
+ switch (enum_) {
+ case Opcode::Try:
+ case Opcode::Catch:
+ case Opcode::Delegate:
+ case Opcode::Throw:
+ case Opcode::Rethrow:
+ return features.exceptions_enabled();
+
+ case Opcode::ReturnCallIndirect:
+ case Opcode::ReturnCall:
+ return features.tail_call_enabled();
+
+ case Opcode::I32TruncSatF32S:
+ case Opcode::I32TruncSatF32U:
+ case Opcode::I32TruncSatF64S:
+ case Opcode::I32TruncSatF64U:
+ case Opcode::I64TruncSatF32S:
+ case Opcode::I64TruncSatF32U:
+ case Opcode::I64TruncSatF64S:
+ case Opcode::I64TruncSatF64U:
+ return features.sat_float_to_int_enabled();
+
+ case Opcode::I32Extend8S:
+ case Opcode::I32Extend16S:
+ case Opcode::I64Extend8S:
+ case Opcode::I64Extend16S:
+ case Opcode::I64Extend32S:
+ return features.sign_extension_enabled();
+
+ case Opcode::MemoryAtomicNotify:
+ case Opcode::MemoryAtomicWait32:
+ case Opcode::MemoryAtomicWait64:
+ case Opcode::AtomicFence:
+ case Opcode::I32AtomicLoad:
+ case Opcode::I64AtomicLoad:
+ case Opcode::I32AtomicLoad8U:
+ case Opcode::I32AtomicLoad16U:
+ case Opcode::I64AtomicLoad8U:
+ case Opcode::I64AtomicLoad16U:
+ case Opcode::I64AtomicLoad32U:
+ case Opcode::I32AtomicStore:
+ case Opcode::I64AtomicStore:
+ case Opcode::I32AtomicStore8:
+ case Opcode::I32AtomicStore16:
+ case Opcode::I64AtomicStore8:
+ case Opcode::I64AtomicStore16:
+ case Opcode::I64AtomicStore32:
+ 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:
+ case Opcode::I32AtomicRmwCmpxchg:
+ case Opcode::I64AtomicRmwCmpxchg:
+ case Opcode::I32AtomicRmw8CmpxchgU:
+ case Opcode::I32AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw8CmpxchgU:
+ case Opcode::I64AtomicRmw16CmpxchgU:
+ case Opcode::I64AtomicRmw32CmpxchgU:
+ return features.threads_enabled();
+
+ case Opcode::V128Const:
+ case Opcode::V128Load:
+ case Opcode::V128Store:
+ case Opcode::I8X16Splat:
+ case Opcode::I16X8Splat:
+ case Opcode::I32X4Splat:
+ case Opcode::I64X2Splat:
+ case Opcode::F32X4Splat:
+ case Opcode::F64X2Splat:
+ 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:
+ 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::I8X16Neg:
+ case Opcode::I16X8Neg:
+ case Opcode::I32X4Neg:
+ case Opcode::I64X2Neg:
+ 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::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::V128Not:
+ case Opcode::V128BitSelect:
+ 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::I8X16Eq:
+ case Opcode::I16X8Eq:
+ case Opcode::I32X4Eq:
+ case Opcode::F32X4Eq:
+ case Opcode::F64X2Eq:
+ case Opcode::I8X16Ne:
+ case Opcode::I16X8Ne:
+ case Opcode::I32X4Ne:
+ 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::F32X4Lt:
+ case Opcode::F64X2Lt:
+ case Opcode::I8X16LeS:
+ case Opcode::I8X16LeU:
+ case Opcode::I16X8LeS:
+ case Opcode::I16X8LeU:
+ case Opcode::I32X4LeS:
+ case Opcode::I32X4LeU:
+ 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::F32X4Gt:
+ case Opcode::F64X2Gt:
+ case Opcode::I8X16GeS:
+ case Opcode::I8X16GeU:
+ case Opcode::I16X8GeS:
+ case Opcode::I16X8GeU:
+ case Opcode::I32X4GeS:
+ case Opcode::I32X4GeU:
+ case Opcode::F32X4Ge:
+ case Opcode::F64X2Ge:
+ case Opcode::F32X4Neg:
+ case Opcode::F64X2Neg:
+ case Opcode::F32X4Abs:
+ case Opcode::F64X2Abs:
+ 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::F32X4Sqrt:
+ case Opcode::F64X2Sqrt:
+ case Opcode::F32X4ConvertI32X4S:
+ case Opcode::F32X4ConvertI32X4U:
+ case Opcode::I32X4TruncSatF32X4S:
+ case Opcode::I32X4TruncSatF32X4U:
+ case Opcode::I8X16Swizzle:
+ case Opcode::I8X16Shuffle:
+ case Opcode::V128Load8Splat:
+ case Opcode::V128Load16Splat:
+ case Opcode::V128Load32Splat:
+ case Opcode::V128Load64Splat:
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Load64Lane:
+ case Opcode::V128Store8Lane:
+ case Opcode::V128Store16Lane:
+ case Opcode::V128Store32Lane:
+ case Opcode::V128Store64Lane:
+ case Opcode::I8X16Abs:
+ case Opcode::I16X8Abs:
+ case Opcode::I32X4Abs:
+ return features.simd_enabled();
+
+ case Opcode::MemoryInit:
+ case Opcode::DataDrop:
+ case Opcode::MemoryCopy:
+ case Opcode::MemoryFill:
+ case Opcode::TableInit:
+ case Opcode::ElemDrop:
+ case Opcode::TableCopy:
+ return features.bulk_memory_enabled();
+
+ case Opcode::TableGet:
+ case Opcode::TableSet:
+ case Opcode::TableGrow:
+ case Opcode::TableSize:
+ case Opcode::RefNull:
+ case Opcode::RefIsNull:
+ return features.reference_types_enabled();
+
+ case Opcode::CallRef:
+ return features.function_references_enabled();
+
+ // Interpreter opcodes are never "enabled".
+ case Opcode::InterpAlloca:
+ case Opcode::InterpBrUnless:
+ case Opcode::InterpCallImport:
+ case Opcode::InterpData:
+ case Opcode::InterpDropKeep:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+uint32_t Opcode::GetSimdLaneCount() const {
+ switch (enum_) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::V128Load8Lane:
+ case Opcode::V128Store8Lane:
+ return 16;
+ break;
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::V128Load16Lane:
+ case Opcode::V128Store16Lane:
+ return 8;
+ break;
+ case Opcode::F32X4ExtractLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::V128Load32Lane:
+ case Opcode::V128Store32Lane:
+ return 4;
+ break;
+ case Opcode::F64X2ExtractLane:
+ case Opcode::F64X2ReplaceLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::V128Load64Lane:
+ case Opcode::V128Store64Lane:
+ return 2;
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+// Get the byte sequence for this opcode, including prefix.
+std::vector<uint8_t> Opcode::GetBytes() const {
+ std::vector<uint8_t> result;
+ if (HasPrefix()) {
+ result.push_back(GetPrefix());
+ uint8_t buffer[5];
+ Offset length =
+ WriteU32Leb128Raw(buffer, buffer + sizeof(buffer), GetCode());
+ assert(length != 0);
+ result.insert(result.end(), buffer, buffer + length);
+ } else {
+ result.push_back(GetCode());
+ }
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/opcode.def b/third_party/wasm2c/src/opcode.def
new file mode 100644
index 0000000000..ce80f895bd
--- /dev/null
+++ b/third_party/wasm2c/src/opcode.def
@@ -0,0 +1,569 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_OPCODE
+#error "You must define WABT_OPCODE before including this file."
+#endif
+
+/* *** NOTE *** This list must be kept sorted so it can be binary searched */
+
+/*
+ * tr: result type
+ * t1: type of the 1st parameter
+ * t2: type of the 2nd parameter
+ * t3: type of the 3rd parameter
+ * m: memory size of the operation, if any
+ * prefix: the 1-byte opcode prefix, if any
+ * code: opcode
+ * Name: used to generate the opcode enum
+ * text: a string of the opcode name in the text format
+ * decomp: an optional friendly version of text, used for decompilation.
+ *
+ * tr t1 t2 t3 m prefix code Name text
+ * ========================================================== */
+
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x00, Unreachable, "unreachable", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x01, Nop, "nop", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x02, Block, "block", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x03, Loop, "loop", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x04, If, "if", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x05, Else, "else", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x06, Try, "try", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x07, Catch, "catch", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x08, Throw, "throw", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x09, Rethrow, "rethrow", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0b, End, "end", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0c, Br, "br", "")
+WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0d, BrIf, "br_if", "")
+WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0e, BrTable, "br_table", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0f, Return, "return", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x10, Call, "call", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x11, CallIndirect, "call_indirect", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x12, ReturnCall, "return_call", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x13, ReturnCallIndirect, "return_call_indirect", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x14, CallRef, "call_ref", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x18, Delegate, "delegate", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x19, CatchAll, "catch_all", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x1a, Drop, "drop", "")
+WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1b, Select, "select", "")
+WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1c, SelectT, "select", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x20, LocalGet, "local.get", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x21, LocalSet, "local.set", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x22, LocalTee, "local.tee", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x23, GlobalGet, "global.get", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x24, GlobalSet, "global.set", "")
+WABT_OPCODE(I32, I32, ___, ___, 4, 0, 0x28, I32Load, "i32.load", "")
+WABT_OPCODE(I64, I32, ___, ___, 8, 0, 0x29, I64Load, "i64.load", "")
+WABT_OPCODE(F32, I32, ___, ___, 4, 0, 0x2a, F32Load, "f32.load", "")
+WABT_OPCODE(F64, I32, ___, ___, 8, 0, 0x2b, F64Load, "f64.load", "")
+WABT_OPCODE(I32, I32, ___, ___, 1, 0, 0x2c, I32Load8S, "i32.load8_s", "")
+WABT_OPCODE(I32, I32, ___, ___, 1, 0, 0x2d, I32Load8U, "i32.load8_u", "")
+WABT_OPCODE(I32, I32, ___, ___, 2, 0, 0x2e, I32Load16S, "i32.load16_s", "")
+WABT_OPCODE(I32, I32, ___, ___, 2, 0, 0x2f, I32Load16U, "i32.load16_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 1, 0, 0x30, I64Load8S, "i64.load8_s", "")
+WABT_OPCODE(I64, I32, ___, ___, 1, 0, 0x31, I64Load8U, "i64.load8_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 2, 0, 0x32, I64Load16S, "i64.load16_s", "")
+WABT_OPCODE(I64, I32, ___, ___, 2, 0, 0x33, I64Load16U, "i64.load16_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 4, 0, 0x34, I64Load32S, "i64.load32_s", "")
+WABT_OPCODE(I64, I32, ___, ___, 4, 0, 0x35, I64Load32U, "i64.load32_u", "")
+WABT_OPCODE(___, I32, I32, ___, 4, 0, 0x36, I32Store, "i32.store", "")
+WABT_OPCODE(___, I32, I64, ___, 8, 0, 0x37, I64Store, "i64.store", "")
+WABT_OPCODE(___, I32, F32, ___, 4, 0, 0x38, F32Store, "f32.store", "")
+WABT_OPCODE(___, I32, F64, ___, 8, 0, 0x39, F64Store, "f64.store", "")
+WABT_OPCODE(___, I32, I32, ___, 1, 0, 0x3a, I32Store8, "i32.store8", "")
+WABT_OPCODE(___, I32, I32, ___, 2, 0, 0x3b, I32Store16, "i32.store16", "")
+WABT_OPCODE(___, I32, I64, ___, 1, 0, 0x3c, I64Store8, "i64.store8", "")
+WABT_OPCODE(___, I32, I64, ___, 2, 0, 0x3d, I64Store16, "i64.store16", "")
+WABT_OPCODE(___, I32, I64, ___, 4, 0, 0x3e, I64Store32, "i64.store32", "")
+WABT_OPCODE(I32, ___, ___, ___, 0, 0, 0x3f, MemorySize, "memory.size", "")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x40, MemoryGrow, "memory.grow", "")
+WABT_OPCODE(I32, ___, ___, ___, 0, 0, 0x41, I32Const, "i32.const", "")
+WABT_OPCODE(I64, ___, ___, ___, 0, 0, 0x42, I64Const, "i64.const", "")
+WABT_OPCODE(F32, ___, ___, ___, 0, 0, 0x43, F32Const, "f32.const", "")
+WABT_OPCODE(F64, ___, ___, ___, 0, 0, 0x44, F64Const, "f64.const", "")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x45, I32Eqz, "i32.eqz", "eqz")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x46, I32Eq, "i32.eq", "==")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x47, I32Ne, "i32.ne", "!=")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x48, I32LtS, "i32.lt_s", "<")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x49, I32LtU, "i32.lt_u", "<")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4a, I32GtS, "i32.gt_s", ">")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4b, I32GtU, "i32.gt_u", ">")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4c, I32LeS, "i32.le_s", "<=")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4d, I32LeU, "i32.le_u", "<=")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4e, I32GeS, "i32.ge_s", ">=")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x4f, I32GeU, "i32.ge_u", ">=")
+WABT_OPCODE(I32, I64, ___, ___, 0, 0, 0x50, I64Eqz, "i64.eqz", "eqz")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x51, I64Eq, "i64.eq", "==")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x52, I64Ne, "i64.ne", "!=")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x53, I64LtS, "i64.lt_s", "<")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x54, I64LtU, "i64.lt_u", "<")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x55, I64GtS, "i64.gt_s", ">")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x56, I64GtU, "i64.gt_u", ">")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x57, I64LeS, "i64.le_s", "<=")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x58, I64LeU, "i64.le_u", "<=")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x59, I64GeS, "i64.ge_s", ">=")
+WABT_OPCODE(I32, I64, I64, ___, 0, 0, 0x5a, I64GeU, "i64.ge_u", ">=")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5b, F32Eq, "f32.eq", "==")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5c, F32Ne, "f32.ne", "!=")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5d, F32Lt, "f32.lt", "<")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5e, F32Gt, "f32.gt", ">")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x5f, F32Le, "f32.le", "<=")
+WABT_OPCODE(I32, F32, F32, ___, 0, 0, 0x60, F32Ge, "f32.ge", ">=")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x61, F64Eq, "f64.eq", "==")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x62, F64Ne, "f64.ne", "!=")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x63, F64Lt, "f64.lt", "<")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x64, F64Gt, "f64.gt", ">")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x65, F64Le, "f64.le", "<=")
+WABT_OPCODE(I32, F64, F64, ___, 0, 0, 0x66, F64Ge, "f64.ge", ">=")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x67, I32Clz, "i32.clz", "clz")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x68, I32Ctz, "i32.ctz", "ctz")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0x69, I32Popcnt, "i32.popcnt", "popcnt")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6a, I32Add, "i32.add", "+")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6b, I32Sub, "i32.sub", "-")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6c, I32Mul, "i32.mul", "*")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6d, I32DivS, "i32.div_s", "/")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6e, I32DivU, "i32.div_u", "/")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x6f, I32RemS, "i32.rem_s", "%")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x70, I32RemU, "i32.rem_u", "%")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x71, I32And, "i32.and", "&")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x72, I32Or, "i32.or", "|")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x73, I32Xor, "i32.xor", "^")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x74, I32Shl, "i32.shl", "<<")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x75, I32ShrS, "i32.shr_s", ">>")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x76, I32ShrU, "i32.shr_u", ">>")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x77, I32Rotl, "i32.rotl", "<<")
+WABT_OPCODE(I32, I32, I32, ___, 0, 0, 0x78, I32Rotr, "i32.rotr", ">>")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x79, I64Clz, "i64.clz", "clz")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x7a, I64Ctz, "i64.ctz", "ctz")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0x7b, I64Popcnt, "i64.popcnt", "popcnt")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7c, I64Add, "i64.add", "+")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7d, I64Sub, "i64.sub", "-")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7e, I64Mul, "i64.mul", "*")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x7f, I64DivS, "i64.div_s", "/")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x80, I64DivU, "i64.div_u", "/")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x81, I64RemS, "i64.rem_s", "%")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x82, I64RemU, "i64.rem_u", "%")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x83, I64And, "i64.and", "&")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x84, I64Or, "i64.or", "|")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x85, I64Xor, "i64.xor", "^")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x86, I64Shl, "i64.shl", "<<")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x87, I64ShrS, "i64.shr_s", ">>")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x88, I64ShrU, "i64.shr_u", ">>")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x89, I64Rotl, "i64.rotl", "<<")
+WABT_OPCODE(I64, I64, I64, ___, 0, 0, 0x8a, I64Rotr, "i64.rotr", ">>")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8b, F32Abs, "f32.abs", "abs")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8c, F32Neg, "f32.neg", "-")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8d, F32Ceil, "f32.ceil", "ceil")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8e, F32Floor, "f32.floor", "floor")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x8f, F32Trunc, "f32.trunc", "trunc")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x90, F32Nearest, "f32.nearest", "nearest")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x91, F32Sqrt, "f32.sqrt", "sqrt")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x92, F32Add, "f32.add", "+")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x93, F32Sub, "f32.sub", "-")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x94, F32Mul, "f32.mul", "*")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x95, F32Div, "f32.div", "/")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x96, F32Min, "f32.min", "min")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x97, F32Max, "f32.max", "max")
+WABT_OPCODE(F32, F32, F32, ___, 0, 0, 0x98, F32Copysign, "f32.copysign", "copysign")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x99, F64Abs, "f64.abs", "abs")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9a, F64Neg, "f64.neg", "-")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9b, F64Ceil, "f64.ceil", "ceil")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9c, F64Floor, "f64.floor", "floor")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9d, F64Trunc, "f64.trunc", "trunc")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9e, F64Nearest, "f64.nearest", "nearest")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0x9f, F64Sqrt, "f64.sqrt", "sqrt")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa0, F64Add, "f64.add", "+")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa1, F64Sub, "f64.sub", "-")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa2, F64Mul, "f64.mul", "*")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa3, F64Div, "f64.div", "/")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa4, F64Min, "f64.min", "min")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa5, F64Max, "f64.max", "max")
+WABT_OPCODE(F64, F64, F64, ___, 0, 0, 0xa6, F64Copysign, "f64.copysign", "copysign")
+WABT_OPCODE(I32, I64, ___, ___, 0, 0, 0xa7, I32WrapI64, "i32.wrap_i64", "")
+WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xa8, I32TruncF32S, "i32.trunc_f32_s", "")
+WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xa9, I32TruncF32U, "i32.trunc_f32_u", "")
+WABT_OPCODE(I32, F64, ___, ___, 0, 0, 0xaa, I32TruncF64S, "i32.trunc_f64_s", "")
+WABT_OPCODE(I32, F64, ___, ___, 0, 0, 0xab, I32TruncF64U, "i32.trunc_f64_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 0, 0, 0xac, I64ExtendI32S, "i64.extend_i32_s", "")
+WABT_OPCODE(I64, I32, ___, ___, 0, 0, 0xad, I64ExtendI32U, "i64.extend_i32_u", "")
+WABT_OPCODE(I64, F32, ___, ___, 0, 0, 0xae, I64TruncF32S, "i64.trunc_f32_s", "")
+WABT_OPCODE(I64, F32, ___, ___, 0, 0, 0xaf, I64TruncF32U, "i64.trunc_f32_u", "")
+WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xb0, I64TruncF64S, "i64.trunc_f64_s", "")
+WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xb1, I64TruncF64U, "i64.trunc_f64_u", "")
+WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xb2, F32ConvertI32S, "f32.convert_i32_s", "")
+WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xb3, F32ConvertI32U, "f32.convert_i32_u", "")
+WABT_OPCODE(F32, I64, ___, ___, 0, 0, 0xb4, F32ConvertI64S, "f32.convert_i64_s", "")
+WABT_OPCODE(F32, I64, ___, ___, 0, 0, 0xb5, F32ConvertI64U, "f32.convert_i64_u", "")
+WABT_OPCODE(F32, F64, ___, ___, 0, 0, 0xb6, F32DemoteF64, "f32.demote_f64", "")
+WABT_OPCODE(F64, I32, ___, ___, 0, 0, 0xb7, F64ConvertI32S, "f64.convert_i32_s", "")
+WABT_OPCODE(F64, I32, ___, ___, 0, 0, 0xb8, F64ConvertI32U, "f64.convert_i32_u", "")
+WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xb9, F64ConvertI64S, "f64.convert_i64_s", "")
+WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xba, F64ConvertI64U, "f64.convert_i64_u", "")
+WABT_OPCODE(F64, F32, ___, ___, 0, 0, 0xbb, F64PromoteF32, "f64.promote_f32", "")
+WABT_OPCODE(I32, F32, ___, ___, 0, 0, 0xbc, I32ReinterpretF32, "i32.reinterpret_f32", "")
+WABT_OPCODE(I64, F64, ___, ___, 0, 0, 0xbd, I64ReinterpretF64, "i64.reinterpret_f64", "")
+WABT_OPCODE(F32, I32, ___, ___, 0, 0, 0xbe, F32ReinterpretI32, "f32.reinterpret_i32", "")
+WABT_OPCODE(F64, I64, ___, ___, 0, 0, 0xbf, F64ReinterpretI64, "f64.reinterpret_i64", "")
+
+/* Sign-extension opcodes (--enable-sign-extension) */
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0xC0, I32Extend8S, "i32.extend8_s", "")
+WABT_OPCODE(I32, I32, ___, ___, 0, 0, 0xC1, I32Extend16S, "i32.extend16_s", "")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC2, I64Extend8S, "i64.extend8_s", "")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC3, I64Extend16S, "i64.extend16_s", "")
+WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC4, I64Extend32S, "i64.extend32_s", "")
+
+/* Interpreter-only opcodes */
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe0, InterpAlloca, "alloca", "")
+WABT_OPCODE(___, I32, ___, ___, 0, 0, 0xe1, InterpBrUnless, "br_unless", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe2, InterpCallImport, "call_import", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe3, InterpData, "data", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe4, InterpDropKeep, "drop_keep", "")
+
+/* Saturating float-to-int opcodes (--enable-saturating-float-to-int) */
+WABT_OPCODE(I32, F32, ___, ___, 0, 0xfc, 0x00, I32TruncSatF32S, "i32.trunc_sat_f32_s", "")
+WABT_OPCODE(I32, F32, ___, ___, 0, 0xfc, 0x01, I32TruncSatF32U, "i32.trunc_sat_f32_u", "")
+WABT_OPCODE(I32, F64, ___, ___, 0, 0xfc, 0x02, I32TruncSatF64S, "i32.trunc_sat_f64_s", "")
+WABT_OPCODE(I32, F64, ___, ___, 0, 0xfc, 0x03, I32TruncSatF64U, "i32.trunc_sat_f64_u", "")
+WABT_OPCODE(I64, F32, ___, ___, 0, 0xfc, 0x04, I64TruncSatF32S, "i64.trunc_sat_f32_s", "")
+WABT_OPCODE(I64, F32, ___, ___, 0, 0xfc, 0x05, I64TruncSatF32U, "i64.trunc_sat_f32_u", "")
+WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x06, I64TruncSatF64S, "i64.trunc_sat_f64_s", "")
+WABT_OPCODE(I64, F64, ___, ___, 0, 0xfc, 0x07, I64TruncSatF64U, "i64.trunc_sat_f64_u", "")
+
+/* Bulk-memory (--enable-bulk-memory) */
+WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x08, MemoryInit, "memory.init", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x09, DataDrop, "data.drop", "")
+WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0a, MemoryCopy,"memory.copy", "")
+WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0b, MemoryFill, "memory.fill", "")
+WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0c, TableInit, "table.init", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x0d, ElemDrop, "elem.drop", "")
+WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0e, TableCopy, "table.copy", "")
+
+/* Reference types (--enable-reference-types) */
+WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x25, TableGet, "table.get", "")
+WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x26, TableSet, "table.set", "")
+WABT_OPCODE(___, ___, I32, ___, 0, 0xfc, 0x0f, TableGrow, "table.grow", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x10, TableSize, "table.size", "")
+WABT_OPCODE(___, I32, ___, I32, 0, 0xfc, 0x11, TableFill, "table.fill", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd0, RefNull, "ref.null", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd1, RefIsNull, "ref.is_null", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd2, RefFunc, "ref.func", "")
+
+/* Simd opcodes */
+WABT_OPCODE(V128, I32, ___, ___, 16, 0xfd, 0x00, V128Load, "v128.load", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x01, V128Load8X8S, "v128.load8x8_s", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x02, V128Load8X8U, "v128.load8x8_u", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x03, V128Load16X4S, "v128.load16x4_s", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x04, V128Load16X4U, "v128.load16x4_u", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x05, V128Load32X2S, "v128.load32x2_s", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x06, V128Load32X2U, "v128.load32x2_u", "")
+WABT_OPCODE(V128, I32, ___, ___, 1, 0xfd, 0x07, V128Load8Splat, "v128.load8_splat", "")
+WABT_OPCODE(V128, I32, ___, ___, 2, 0xfd, 0x08, V128Load16Splat, "v128.load16_splat", "")
+WABT_OPCODE(V128, I32, ___, ___, 4, 0xfd, 0x09, V128Load32Splat, "v128.load32_splat", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x0a, V128Load64Splat, "v128.load64_splat", "")
+WABT_OPCODE(___, I32, V128, ___, 16, 0xfd, 0x0b, V128Store, "v128.store", "")
+WABT_OPCODE(V128, ___, ___, ___, 0, 0xfd, 0x0c, V128Const, "v128.const", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x0d, I8X16Shuffle, "i8x16.shuffle", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x0e, I8X16Swizzle, "i8x16.swizzle", "")
+WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x0f, I8X16Splat, "i8x16.splat", "")
+WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x10, I16X8Splat, "i16x8.splat", "")
+WABT_OPCODE(V128, I32, ___, ___, 0, 0xfd, 0x11, I32X4Splat, "i32x4.splat", "")
+WABT_OPCODE(V128, I64, ___, ___, 0, 0xfd, 0x12, I64X2Splat, "i64x2.splat", "")
+WABT_OPCODE(V128, F32, ___, ___, 0, 0xfd, 0x13, F32X4Splat, "f32x4.splat", "")
+WABT_OPCODE(V128, F64, ___, ___, 0, 0xfd, 0x14, F64X2Splat, "f64x2.splat", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x15, I8X16ExtractLaneS, "i8x16.extract_lane_s", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x16, I8X16ExtractLaneU, "i8x16.extract_lane_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x17, I8X16ReplaceLane, "i8x16.replace_lane", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x18, I16X8ExtractLaneS, "i16x8.extract_lane_s", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x19, I16X8ExtractLaneU, "i16x8.extract_lane_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x1a, I16X8ReplaceLane, "i16x8.replace_lane", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x1b, I32X4ExtractLane, "i32x4.extract_lane", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x1c, I32X4ReplaceLane, "i32x4.replace_lane", "")
+WABT_OPCODE(I64, V128, ___, ___, 0, 0xfd, 0x1d, I64X2ExtractLane, "i64x2.extract_lane", "")
+WABT_OPCODE(V128, V128, I64, ___, 0, 0xfd, 0x1e, I64X2ReplaceLane, "i64x2.replace_lane", "")
+WABT_OPCODE(F32, V128, ___, ___, 0, 0xfd, 0x1f, F32X4ExtractLane, "f32x4.extract_lane", "")
+WABT_OPCODE(V128, V128, F32, ___, 0, 0xfd, 0x20, F32X4ReplaceLane, "f32x4.replace_lane", "")
+WABT_OPCODE(F64, V128, ___, ___, 0, 0xfd, 0x21, F64X2ExtractLane, "f64x2.extract_lane", "")
+WABT_OPCODE(V128, V128, F64, ___, 0, 0xfd, 0x22, F64X2ReplaceLane, "f64x2.replace_lane", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x23, I8X16Eq, "i8x16.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x24, I8X16Ne, "i8x16.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x25, I8X16LtS, "i8x16.lt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x26, I8X16LtU, "i8x16.lt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x27, I8X16GtS, "i8x16.gt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x28, I8X16GtU, "i8x16.gt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x29, I8X16LeS, "i8x16.le_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2a, I8X16LeU, "i8x16.le_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2b, I8X16GeS, "i8x16.ge_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2c, I8X16GeU, "i8x16.ge_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2d, I16X8Eq, "i16x8.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2e, I16X8Ne, "i16x8.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x2f, I16X8LtS, "i16x8.lt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x30, I16X8LtU, "i16x8.lt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x31, I16X8GtS, "i16x8.gt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x32, I16X8GtU, "i16x8.gt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x33, I16X8LeS, "i16x8.le_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x34, I16X8LeU, "i16x8.le_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x35, I16X8GeS, "i16x8.ge_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x36, I16X8GeU, "i16x8.ge_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x37, I32X4Eq, "i32x4.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x38, I32X4Ne, "i32x4.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x39, I32X4LtS, "i32x4.lt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3a, I32X4LtU, "i32x4.lt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3b, I32X4GtS, "i32x4.gt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3c, I32X4GtU, "i32x4.gt_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3d, I32X4LeS, "i32x4.le_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3e, I32X4LeU, "i32x4.le_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x3f, I32X4GeS, "i32x4.ge_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x40, I32X4GeU, "i32x4.ge_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x41, F32X4Eq, "f32x4.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x42, F32X4Ne, "f32x4.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x43, F32X4Lt, "f32x4.lt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x44, F32X4Gt, "f32x4.gt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x45, F32X4Le, "f32x4.le", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x46, F32X4Ge, "f32x4.ge", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x47, F64X2Eq, "f64x2.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x48, F64X2Ne, "f64x2.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x49, F64X2Lt, "f64x2.lt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4a, F64X2Gt, "f64x2.gt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4b, F64X2Le, "f64x2.le", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4c, F64X2Ge, "f64x2.ge", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x4d, V128Not, "v128.not", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4e, V128And, "v128.and", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x4f, V128Andnot, "v128.andnot", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x50, V128Or, "v128.or", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x51, V128Xor, "v128.xor", "")
+WABT_OPCODE(V128, V128, V128, V128, 0, 0xfd, 0x52, V128BitSelect, "v128.bitselect", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x53, V128AnyTrue, "v128.any_true", "")
+WABT_OPCODE(V128, I32, V128, ___, 1, 0xfd, 0x54, V128Load8Lane, "v128.load8_lane", "")
+WABT_OPCODE(V128, I32, V128, ___, 2, 0xfd, 0x55, V128Load16Lane, "v128.load16_lane", "")
+WABT_OPCODE(V128, I32, V128, ___, 4, 0xfd, 0x56, V128Load32Lane, "v128.load32_lane", "")
+WABT_OPCODE(V128, I32, V128, ___, 8, 0xfd, 0x57, V128Load64Lane, "v128.load64_lane", "")
+WABT_OPCODE(___, I32, V128, ___, 1, 0xfd, 0x58, V128Store8Lane, "v128.store8_lane", "")
+WABT_OPCODE(___, I32, V128, ___, 2, 0xfd, 0x59, V128Store16Lane, "v128.store16_lane", "")
+WABT_OPCODE(___, I32, V128, ___, 4, 0xfd, 0x5a, V128Store32Lane, "v128.store32_lane", "")
+WABT_OPCODE(___, I32, V128, ___, 8, 0xfd, 0x5b, V128Store64Lane, "v128.store64_lane", "")
+WABT_OPCODE(V128, I32, ___, ___, 4, 0xfd, 0x5c, V128Load32Zero, "v128.load32_zero", "")
+WABT_OPCODE(V128, I32, ___, ___, 8, 0xfd, 0x5d, V128Load64Zero, "v128.load64_zero", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x5e, F32X4DemoteF64X2Zero, "f32x4.demote_f64x2_zero", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x5f, F64X2PromoteLowF32X4, "f64x2.promote_low_f32x4", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x60, I8X16Abs, "i8x16.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x61, I8X16Neg, "i8x16.neg", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x62, I8X16Popcnt, "i8x16.popcnt", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x63, I8X16AllTrue, "i8x16.all_true", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x64, I8X16Bitmask, "i8x16.bitmask", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x65, I8X16NarrowI16X8S, "i8x16.narrow_i16x8_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x66, I8X16NarrowI16X8U, "i8x16.narrow_i16x8_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6b, I8X16Shl, "i8x16.shl", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6c, I8X16ShrS, "i8x16.shr_s", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x6d, I8X16ShrU, "i8x16.shr_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x6e, I8X16Add, "i8x16.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x6f, I8X16AddSatS, "i8x16.add_sat_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x70, I8X16AddSatU, "i8x16.add_sat_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x71, I8X16Sub, "i8x16.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x72, I8X16SubSatS, "i8x16.sub_sat_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x73, I8X16SubSatU, "i8x16.sub_sat_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x76, I8X16MinS, "i8x16.min_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x77, I8X16MinU, "i8x16.min_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x78, I8X16MaxS, "i8x16.max_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x79, I8X16MaxU, "i8x16.max_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x7b, I8X16AvgrU, "i8x16.avgr_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7c, I16X8ExtaddPairwiseI8X16S, "i16x8.extadd_pairwise_i8x16_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7d, I16X8ExtaddPairwiseI8X16U, "i16x8.extadd_pairwise_i8x16_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7e, I32X4ExtaddPairwiseI16X8S, "i32x4.extadd_pairwise_i16x8_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7f, I32X4ExtaddPairwiseI16X8U, "i32x4.extadd_pairwise_i16x8_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x80, I16X8Abs, "i16x8.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x81, I16X8Neg, "i16x8.neg", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x82, I16X8Q15mulrSatS, "i16x8.q15mulr_sat_s", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x83, I16X8AllTrue, "i16x8.all_true", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0x84, I16X8Bitmask, "i16x8.bitmask", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x85, I16X8NarrowI32X4S, "i16x8.narrow_i32x4_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x86, I16X8NarrowI32X4U, "i16x8.narrow_i32x4_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x87, I16X8ExtendLowI8X16S, "i16x8.extend_low_i8x16_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x88, I16X8ExtendHighI8X16S, "i16x8.extend_high_i8x16_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x89, I16X8ExtendLowI8X16U, "i16x8.extend_low_i8x16_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x8a, I16X8ExtendHighI8X16U, "i16x8.extend_high_i8x16_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8b, I16X8Shl, "i16x8.shl", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8c, I16X8ShrS, "i16x8.shr_s", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0x8d, I16X8ShrU, "i16x8.shr_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x8e, I16X8Add, "i16x8.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x8f, I16X8AddSatS, "i16x8.add_sat_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x90, I16X8AddSatU, "i16x8.add_sat_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x91, I16X8Sub, "i16x8.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x92, I16X8SubSatS, "i16x8.sub_sat_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x93, I16X8SubSatU, "i16x8.sub_sat_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x95, I16X8Mul, "i16x8.mul", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x96, I16X8MinS, "i16x8.min_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x97, I16X8MinU, "i16x8.min_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x98, I16X8MaxS, "i16x8.max_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x99, I16X8MaxU, "i16x8.max_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9b, I16X8AvgrU, "i16x8.avgr_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9c, I16X8ExtmulLowI8X16S, "i16x8.extmul_low_i8x16_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9d, I16X8ExtmulHighI8X16S, "i16x8.extmul_high_i8x16_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9e, I16X8ExtmulLowI8X16U, "i16x8.extmul_low_i8x16_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0x9f, I16X8ExtmulHighI8X16U, "i16x8.extmul_high_i8x16_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa0, I32X4Abs, "i32x4.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa1, I32X4Neg, "i32x4.neg", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0xa3, I32X4AllTrue, "i32x4.all_true", "")
+WABT_OPCODE(I32, V128, ___, ___, 0, 0xfd, 0xa4, I32X4Bitmask, "i32x4.bitmask", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa7, I32X4ExtendLowI16X8S, "i32x4.extend_low_i16x8_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa8, I32X4ExtendHighI16X8S, "i32x4.extend_high_i16x8_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xa9, I32X4ExtendLowI16X8U, "i32x4.extend_low_i16x8_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xaa, I32X4ExtendHighI16X8U, "i32x4.extend_high_i16x8_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xab, I32X4Shl, "i32x4.shl", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xac, I32X4ShrS, "i32x4.shr_s", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xad, I32X4ShrU, "i32x4.shr_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xae, I32X4Add, "i32x4.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb1, I32X4Sub, "i32x4.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb5, I32X4Mul, "i32x4.mul", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb6, I32X4MinS, "i32x4.min_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb7, I32X4MinU, "i32x4.min_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb8, I32X4MaxS, "i32x4.max_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xb9, I32X4MaxU, "i32x4.max_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xba, I32X4DotI16X8S, "i32x4.dot_i16x8_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbc, I32X4ExtmulLowI16X8S, "i32x4.extmul_low_i16x8_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbd, I32X4ExtmulHighI16X8S, "i32x4.extmul_high_i16x8_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbe, I32X4ExtmulLowI16X8U, "i32x4.extmul_low_i16x8_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xbf, I32X4ExtmulHighI16X8U, "i32x4.extmul_high_i16x8_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc0, I64X2Abs, "i64x2.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc1, I64X2Neg, "i64x2.neg", "")
+WABT_OPCODE( I32, V128, ___, ___, 0, 0xfd, 0xc3, I64X2AllTrue, "i64x2.all_true", "")
+WABT_OPCODE( I32, V128, ___, ___, 0, 0xfd, 0xc4, I64X2Bitmask, "i64x2.bitmask", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc7, I64X2ExtendLowI32X4S, "i64x2.extend_low_i32x4_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc8, I64X2ExtendHighI32X4S, "i64x2.extend_high_i32x4_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xc9, I64X2ExtendLowI32X4U, "i64x2.extend_low_i32x4_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xca, I64X2ExtendHighI32X4U, "i64x2.extend_high_i32x4_u", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcb, I64X2Shl, "i64x2.shl", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcc, I64X2ShrS, "i64x2.shr_s", "")
+WABT_OPCODE(V128, V128, I32, ___, 0, 0xfd, 0xcd, I64X2ShrU, "i64x2.shr_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xce, I64X2Add, "i64x2.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd1, I64X2Sub, "i64x2.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd5, I64X2Mul, "i64x2.mul", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd6, I64X2Eq, "i64x2.eq", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd7, I64X2Ne, "i64x2.ne", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd8, I64X2LtS, "i64x2.lt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xd9, I64X2GtS, "i64x2.gt_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xda, I64X2LeS, "i64x2.le_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdb, I64X2GeS, "i64x2.ge_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdc, I64X2ExtmulLowI32X4S, "i64x2.extmul_low_i32x4_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdd, I64X2ExtmulHighI32X4S, "i64x2.extmul_high_i32x4_s", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xde, I64X2ExtmulLowI32X4U, "i64x2.extmul_low_i32x4_u", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xdf, I64X2ExtmulHighI32X4U, "i64x2.extmul_high_i32x4_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x67, F32X4Ceil, "f32x4.ceil", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x68, F32X4Floor, "f32x4.floor", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x69, F32X4Trunc, "f32x4.trunc", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x6a, F32X4Nearest, "f32x4.nearest", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x74, F64X2Ceil, "f64x2.ceil", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x75, F64X2Floor, "f64x2.floor", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x7a, F64X2Trunc, "f64x2.trunc", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0x94, F64X2Nearest, "f64x2.nearest", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe0, F32X4Abs, "f32x4.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe1, F32X4Neg, "f32x4.neg", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xe3, F32X4Sqrt, "f32x4.sqrt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe4, F32X4Add, "f32x4.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe5, F32X4Sub, "f32x4.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe6, F32X4Mul, "f32x4.mul", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe7, F32X4Div, "f32x4.div", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe8, F32X4Min, "f32x4.min", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xe9, F32X4Max, "f32x4.max", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xea, F32X4PMin, "f32x4.pmin", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xeb, F32X4PMax, "f32x4.pmax", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xec, F64X2Abs, "f64x2.abs", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xed, F64X2Neg, "f64x2.neg", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xef, F64X2Sqrt, "f64x2.sqrt", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf0, F64X2Add, "f64x2.add", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf1, F64X2Sub, "f64x2.sub", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf2, F64X2Mul, "f64x2.mul", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf3, F64X2Div, "f64x2.div", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf4, F64X2Min, "f64x2.min", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf5, F64X2Max, "f64x2.max", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf6, F64X2PMin, "f64x2.pmin", "")
+WABT_OPCODE(V128, V128, V128, ___, 0, 0xfd, 0xf7, F64X2PMax, "f64x2.pmax", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xf8, I32X4TruncSatF32X4S,"i32x4.trunc_sat_f32x4_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xf9, I32X4TruncSatF32X4U,"i32x4.trunc_sat_f32x4_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfa, F32X4ConvertI32X4S, "f32x4.convert_i32x4_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfb, F32X4ConvertI32X4U, "f32x4.convert_i32x4_u", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfc, I32X4TruncSatF64X2SZero, "i32x4.trunc_sat_f64x2_s_zero", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfd, I32X4TruncSatF64X2UZero, "i32x4.trunc_sat_f64x2_u_zero", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xfe, F64X2ConvertLowI32X4S, "f64x2.convert_low_i32x4_s", "")
+WABT_OPCODE(V128, V128, ___, ___, 0, 0xfd, 0xff, F64X2ConvertLowI32X4U, "f64x2.convert_low_i32x4_u", "")
+
+/* Thread opcodes (--enable-threads) */
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x00, MemoryAtomicNotify, "memory.atomic.notify", "")
+WABT_OPCODE(I32, I32, I32, I64, 4, 0xfe, 0x01, MemoryAtomicWait32, "memory.atomic.wait32", "")
+WABT_OPCODE(I32, I32, I64, I64, 8, 0xfe, 0x02, MemoryAtomicWait64, "memory.atomic.wait64", "")
+WABT_OPCODE(___, ___, ___, ___, 0, 0xfe, 0x03, AtomicFence, "atomic.fence", "")
+WABT_OPCODE(I32, I32, ___, ___, 4, 0xfe, 0x10, I32AtomicLoad, "i32.atomic.load", "")
+WABT_OPCODE(I64, I32, ___, ___, 8, 0xfe, 0x11, I64AtomicLoad, "i64.atomic.load", "")
+WABT_OPCODE(I32, I32, ___, ___, 1, 0xfe, 0x12, I32AtomicLoad8U, "i32.atomic.load8_u", "")
+WABT_OPCODE(I32, I32, ___, ___, 2, 0xfe, 0x13, I32AtomicLoad16U, "i32.atomic.load16_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 1, 0xfe, 0x14, I64AtomicLoad8U, "i64.atomic.load8_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 2, 0xfe, 0x15, I64AtomicLoad16U, "i64.atomic.load16_u", "")
+WABT_OPCODE(I64, I32, ___, ___, 4, 0xfe, 0x16, I64AtomicLoad32U, "i64.atomic.load32_u", "")
+WABT_OPCODE(___, I32, I32, ___, 4, 0xfe, 0x17, I32AtomicStore, "i32.atomic.store", "")
+WABT_OPCODE(___, I32, I64, ___, 8, 0xfe, 0x18, I64AtomicStore, "i64.atomic.store", "")
+WABT_OPCODE(___, I32, I32, ___, 1, 0xfe, 0x19, I32AtomicStore8, "i32.atomic.store8", "")
+WABT_OPCODE(___, I32, I32, ___, 2, 0xfe, 0x1a, I32AtomicStore16, "i32.atomic.store16", "")
+WABT_OPCODE(___, I32, I64, ___, 1, 0xfe, 0x1b, I64AtomicStore8, "i64.atomic.store8", "")
+WABT_OPCODE(___, I32, I64, ___, 2, 0xfe, 0x1c, I64AtomicStore16, "i64.atomic.store16", "")
+WABT_OPCODE(___, I32, I64, ___, 4, 0xfe, 0x1d, I64AtomicStore32, "i64.atomic.store32", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x1e, I32AtomicRmwAdd, "i32.atomic.rmw.add", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x1f, I64AtomicRmwAdd, "i64.atomic.rmw.add", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x20, I32AtomicRmw8AddU, "i32.atomic.rmw8.add_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x21, I32AtomicRmw16AddU, "i32.atomic.rmw16.add_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x22, I64AtomicRmw8AddU, "i64.atomic.rmw8.add_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x23, I64AtomicRmw16AddU, "i64.atomic.rmw16.add_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x24, I64AtomicRmw32AddU, "i64.atomic.rmw32.add_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x25, I32AtomicRmwSub, "i32.atomic.rmw.sub", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x26, I64AtomicRmwSub, "i64.atomic.rmw.sub", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x27, I32AtomicRmw8SubU, "i32.atomic.rmw8.sub_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x28, I32AtomicRmw16SubU, "i32.atomic.rmw16.sub_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x29, I64AtomicRmw8SubU, "i64.atomic.rmw8.sub_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x2a, I64AtomicRmw16SubU, "i64.atomic.rmw16.sub_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x2b, I64AtomicRmw32SubU, "i64.atomic.rmw32.sub_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x2c, I32AtomicRmwAnd, "i32.atomic.rmw.and", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x2d, I64AtomicRmwAnd, "i64.atomic.rmw.and", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x2e, I32AtomicRmw8AndU, "i32.atomic.rmw8.and_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x2f, I32AtomicRmw16AndU, "i32.atomic.rmw16.and_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x30, I64AtomicRmw8AndU, "i64.atomic.rmw8.and_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x31, I64AtomicRmw16AndU, "i64.atomic.rmw16.and_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x32, I64AtomicRmw32AndU, "i64.atomic.rmw32.and_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x33, I32AtomicRmwOr, "i32.atomic.rmw.or", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x34, I64AtomicRmwOr, "i64.atomic.rmw.or", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x35, I32AtomicRmw8OrU, "i32.atomic.rmw8.or_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x36, I32AtomicRmw16OrU, "i32.atomic.rmw16.or_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x37, I64AtomicRmw8OrU, "i64.atomic.rmw8.or_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x38, I64AtomicRmw16OrU, "i64.atomic.rmw16.or_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x39, I64AtomicRmw32OrU, "i64.atomic.rmw32.or_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x3a, I32AtomicRmwXor, "i32.atomic.rmw.xor", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x3b, I64AtomicRmwXor, "i64.atomic.rmw.xor", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x3c, I32AtomicRmw8XorU, "i32.atomic.rmw8.xor_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x3d, I32AtomicRmw16XorU, "i32.atomic.rmw16.xor_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x3e, I64AtomicRmw8XorU, "i64.atomic.rmw8.xor_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x3f, I64AtomicRmw16XorU, "i64.atomic.rmw16.xor_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x40, I64AtomicRmw32XorU, "i64.atomic.rmw32.xor_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 4, 0xfe, 0x41, I32AtomicRmwXchg, "i32.atomic.rmw.xchg", "")
+WABT_OPCODE(I64, I32, I64, ___, 8, 0xfe, 0x42, I64AtomicRmwXchg, "i64.atomic.rmw.xchg", "")
+WABT_OPCODE(I32, I32, I32, ___, 1, 0xfe, 0x43, I32AtomicRmw8XchgU, "i32.atomic.rmw8.xchg_u", "")
+WABT_OPCODE(I32, I32, I32, ___, 2, 0xfe, 0x44, I32AtomicRmw16XchgU, "i32.atomic.rmw16.xchg_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 1, 0xfe, 0x45, I64AtomicRmw8XchgU, "i64.atomic.rmw8.xchg_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 2, 0xfe, 0x46, I64AtomicRmw16XchgU, "i64.atomic.rmw16.xchg_u", "")
+WABT_OPCODE(I64, I32, I64, ___, 4, 0xfe, 0x47, I64AtomicRmw32XchgU, "i64.atomic.rmw32.xchg_u", "")
+WABT_OPCODE(I32, I32, I32, I32, 4, 0xfe, 0x48, I32AtomicRmwCmpxchg, "i32.atomic.rmw.cmpxchg", "")
+WABT_OPCODE(I64, I32, I64, I64, 8, 0xfe, 0x49, I64AtomicRmwCmpxchg, "i64.atomic.rmw.cmpxchg", "")
+WABT_OPCODE(I32, I32, I32, I32, 1, 0xfe, 0x4a, I32AtomicRmw8CmpxchgU, "i32.atomic.rmw8.cmpxchg_u", "")
+WABT_OPCODE(I32, I32, I32, I32, 2, 0xfe, 0x4b, I32AtomicRmw16CmpxchgU, "i32.atomic.rmw16.cmpxchg_u", "")
+WABT_OPCODE(I64, I32, I64, I64, 1, 0xfe, 0x4c, I64AtomicRmw8CmpxchgU, "i64.atomic.rmw8.cmpxchg_u", "")
+WABT_OPCODE(I64, I32, I64, I64, 2, 0xfe, 0x4d, I64AtomicRmw16CmpxchgU, "i64.atomic.rmw16.cmpxchg_u", "")
+WABT_OPCODE(I64, I32, I64, I64, 4, 0xfe, 0x4e, I64AtomicRmw32CmpxchgU, "i64.atomic.rmw32.cmpxchg_u", "")
diff --git a/third_party/wasm2c/src/opcode.h b/third_party/wasm2c/src/opcode.h
new file mode 100644
index 0000000000..94cd4cb8f6
--- /dev/null
+++ b/third_party/wasm2c/src/opcode.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_OPCODE_H_
+#define WABT_OPCODE_H_
+
+#include <vector>
+
+#include "src/common.h"
+#include "src/opcode-code-table.h"
+#include "src/leb128.h"
+
+namespace wabt {
+
+class Features;
+
+struct Opcode {
+ // Opcode enumerations.
+ //
+ // NOTE: this enum does not match the binary encoding.
+ //
+ enum Enum : uint32_t {
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ Name,
+#include "src/opcode.def"
+#undef WABT_OPCODE
+ Invalid,
+ };
+
+// Static opcode objects.
+#define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \
+ text, decomp) \
+ static Opcode Name##_Opcode;
+#include "src/opcode.def"
+#undef WABT_OPCODE
+
+ Opcode() = default; // Provided so Opcode can be member of a union.
+ Opcode(Enum e) : enum_(e) {}
+ operator Enum() const { return enum_; }
+
+ static Opcode FromCode(uint32_t);
+ static Opcode FromCode(uint8_t prefix, uint32_t code);
+ bool HasPrefix() const { return GetInfo().prefix != 0; }
+ uint8_t GetPrefix() const { return GetInfo().prefix; }
+ uint32_t GetCode() const { return GetInfo().code; }
+ size_t GetLength() const { return GetBytes().size(); }
+ const char* GetName() const { return GetInfo().name; }
+ const char* GetDecomp() const {
+ return *GetInfo().decomp ? GetInfo().decomp : GetInfo().name;
+ }
+ Type GetResultType() const { return GetInfo().result_type; }
+ Type GetParamType1() const { return GetInfo().param_types[0]; }
+ Type GetParamType2() const { return GetInfo().param_types[1]; }
+ Type GetParamType3() const { return GetInfo().param_types[2]; }
+ Type GetParamType(int n) const { return GetInfo().param_types[n - 1]; }
+ Address GetMemorySize() const { return GetInfo().memory_size; }
+
+ // If this is a load/store op, the type depends on the memory used.
+ Type GetMemoryParam(Type param,
+ const Limits* limits,
+ bool has_address_operands) {
+ return limits && limits->is_64 && has_address_operands ? Type(Type::I64)
+ : param;
+ }
+
+ // Get the byte sequence for this opcode, including prefix.
+ std::vector<uint8_t> GetBytes() const;
+
+ // Get the lane count of an extract/replace simd op.
+ uint32_t GetSimdLaneCount() const;
+
+ // Return 1 if |alignment| matches the alignment of |opcode|, or if
+ // |alignment| is WABT_USE_NATURAL_ALIGNMENT.
+ bool IsNaturallyAligned(Address alignment) const;
+
+ // If |alignment| is WABT_USE_NATURAL_ALIGNMENT, return the alignment of
+ // |opcode|, else return |alignment|.
+ Address GetAlignment(Address alignment) const;
+
+ static bool IsPrefixByte(uint8_t byte) {
+ return byte == kMathPrefix || byte == kThreadsPrefix || byte == kSimdPrefix;
+ }
+
+ bool IsEnabled(const Features& features) const;
+ bool IsInvalid() const { return enum_ >= Invalid; }
+
+ private:
+ static const uint32_t kMathPrefix = 0xfc;
+ static const uint32_t kThreadsPrefix = 0xfe;
+ static const uint32_t kSimdPrefix = 0xfd;
+
+ struct Info {
+ const char* name;
+ const char* decomp;
+ Type result_type;
+ Type param_types[3];
+ Address memory_size;
+ uint8_t prefix;
+ uint32_t code;
+ uint32_t prefix_code; // See PrefixCode below. Used for fast lookup.
+ };
+
+ static uint32_t PrefixCode(uint8_t prefix, uint32_t code) {
+ // For now, 8 bits is enough for all codes.
+ if (code >= 0x100) {
+ // Clamp to 0xff, since we know that it is an invalid code.
+ code = 0xff;
+ }
+ return (prefix << 8) | code;
+ }
+
+ // The Opcode struct only stores an enumeration (Opcode::Enum) of all valid
+ // opcodes, densely packed. We want to be able to store invalid opcodes as
+ // well, for display to the user. To encode these, we use PrefixCode() to
+ // generate a uint32_t of the prefix/code pair, then negate the value so it
+ // doesn't overlap with the valid enum values. The negation is done using
+ // `~code + 1` since prefix_code is unsigned, and MSVC warns if you use - on
+ // an unsigned value.
+ //
+ // | 0 | Opcode::Invalid | INT32_MAX+1 UINT32_MAX |
+ // |---------------|-------------------------|---------------------------|
+ // | valid opcodes | unused space | invalid opcodes |
+ //
+ static Enum EncodeInvalidOpcode(uint32_t prefix_code) {
+ Enum result = static_cast<Enum>(~prefix_code + 1);
+ assert(result >= Invalid);
+ return result;
+ }
+
+ static void DecodeInvalidOpcode(Enum e,
+ uint8_t* out_prefix,
+ uint32_t* out_code) {
+ uint32_t prefix_code = ~static_cast<uint32_t>(e) + 1;
+ *out_prefix = prefix_code >> 8;
+ *out_code = prefix_code & 0xff;
+ }
+
+ Info GetInfo() const;
+ static Info infos_[];
+
+ Enum enum_;
+};
+
+// static
+inline Opcode Opcode::FromCode(uint32_t code) {
+ return FromCode(0, code);
+}
+
+// static
+inline Opcode Opcode::FromCode(uint8_t prefix, uint32_t code) {
+ uint32_t prefix_code = PrefixCode(prefix, code);
+
+ if (WABT_LIKELY(prefix_code < WABT_ARRAY_SIZE(WabtOpcodeCodeTable))) {
+ uint32_t value = WabtOpcodeCodeTable[prefix_code];
+ // The default value in the table is 0. That's a valid value, but only if
+ // the code is 0 (for nop).
+ if (WABT_LIKELY(value != 0 || code == 0)) {
+ return Opcode(static_cast<Enum>(value));
+ }
+ }
+
+ return Opcode(EncodeInvalidOpcode(prefix_code));
+}
+
+
+} // namespace wabt
+
+#endif // WABT_OPCODE_H_
diff --git a/third_party/wasm2c/src/option-parser.cc b/third_party/wasm2c/src/option-parser.cc
new file mode 100644
index 0000000000..d6a38ae8d9
--- /dev/null
+++ b/third_party/wasm2c/src/option-parser.cc
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/option-parser.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+
+#include "config.h"
+
+#if HAVE_ALLOCA
+#include <alloca.h>
+#endif
+
+namespace wabt {
+
+OptionParser::Option::Option(char short_name,
+ const std::string& long_name,
+ const std::string& metavar,
+ HasArgument has_argument,
+ const std::string& help,
+ const Callback& callback)
+ : short_name(short_name),
+ long_name(long_name),
+ metavar(metavar),
+ has_argument(has_argument == HasArgument::Yes),
+ help(help),
+ callback(callback) {}
+
+OptionParser::Argument::Argument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback)
+ : name(name), count(count), callback(callback) {}
+
+OptionParser::OptionParser(const char* program_name, const char* description)
+ : program_name_(program_name),
+ description_(description),
+ on_error_([this](const std::string& message) { DefaultError(message); }) {
+
+ // Add common options
+ AddOption("help", "Print this help message", [this]() {
+ PrintHelp();
+ exit(0);
+ });
+ AddOption("version", "Print version information", []() {
+ printf("%s\n", CMAKE_PROJECT_VERSION);
+ exit(0);
+ });
+}
+
+void OptionParser::AddOption(const Option& option) {
+ options_.emplace_back(option);
+}
+
+void OptionParser::AddArgument(const std::string& name,
+ ArgumentCount count,
+ const Callback& callback) {
+ arguments_.emplace_back(name, count, callback);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option(short_name, long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(const char* long_name,
+ const char* help,
+ const NullCallback& callback) {
+ Option option('\0', long_name, std::string(), HasArgument::No, help,
+ [callback](const char*) { callback(); });
+ AddOption(option);
+}
+
+void OptionParser::AddOption(char short_name,
+ const char* long_name,
+ const char* metavar,
+ const char* help,
+ const Callback& callback) {
+ Option option(short_name, long_name, metavar, HasArgument::Yes, help,
+ callback);
+ AddOption(option);
+}
+
+void OptionParser::SetErrorCallback(const Callback& callback) {
+ on_error_ = callback;
+}
+
+// static
+int OptionParser::Match(const char* s,
+ const std::string& full,
+ bool has_argument) {
+ int i;
+ for (i = 0;; i++) {
+ if (full[i] == '\0') {
+ // Perfect match. Return +1, so it will be preferred over a longer option
+ // with the same prefix.
+ if (s[i] == '\0') {
+ return i + 1;
+ }
+
+ // We want to fail if s is longer than full, e.g. --foobar vs. --foo.
+ // However, if s ends with an '=', it's OK.
+ if (!(has_argument && s[i] == '=')) {
+ return -1;
+ }
+ break;
+ }
+ if (s[i] == '\0') {
+ break;
+ }
+ if (s[i] != full[i]) {
+ return -1;
+ }
+ }
+ return i;
+}
+
+void OptionParser::Errorf(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ std::string msg(program_name_);
+ msg += ": ";
+ msg += buffer;
+ msg += "\nTry '--help' for more information.";
+ on_error_(msg.c_str());
+}
+
+void OptionParser::DefaultError(const std::string& message) {
+ WABT_FATAL("%s\n", message.c_str());
+}
+
+void OptionParser::HandleArgument(size_t* arg_index, const char* arg_value) {
+ if (*arg_index >= arguments_.size()) {
+ Errorf("unexpected argument '%s'", arg_value);
+ return;
+ }
+ Argument& argument = arguments_[*arg_index];
+ argument.callback(arg_value);
+ argument.handled_count++;
+
+ if (argument.count == ArgumentCount::One) {
+ (*arg_index)++;
+ }
+}
+
+void OptionParser::Parse(int argc, char* argv[]) {
+ size_t arg_index = 0;
+ bool processing_options = true;
+
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (!processing_options || arg[0] != '-') {
+ // Non-option argument.
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ // -- on its own means stop processing args, everything should
+ // be treated as positional.
+ processing_options = false;
+ continue;
+ }
+ // Long option.
+ int best_index = -1;
+ int best_length = 0;
+ int best_count = 0;
+ for (size_t j = 0; j < options_.size(); ++j) {
+ const Option& option = options_[j];
+ if (!option.long_name.empty()) {
+ int match_length =
+ Match(&arg[2], option.long_name, option.has_argument);
+ if (match_length > best_length) {
+ best_index = j;
+ best_length = match_length;
+ best_count = 1;
+ } else if (match_length == best_length && best_length > 0) {
+ best_count++;
+ }
+ }
+ }
+
+ if (best_count > 1) {
+ Errorf("ambiguous option '%s'", arg);
+ continue;
+ } else if (best_count == 0) {
+ Errorf("unknown option '%s'", arg);
+ continue;
+ }
+
+ const Option& best_option = options_[best_index];
+ const char* option_argument = nullptr;
+ if (best_option.has_argument) {
+ if (arg[best_length + 1] != 0 && // This byte is 0 on a full match.
+ arg[best_length + 2] == '=') { // +2 to skip "--".
+ option_argument = &arg[best_length + 3];
+ } else {
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '--%s' requires argument",
+ best_option.long_name.c_str());
+ continue;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ }
+ best_option.callback(option_argument);
+ } else {
+ // Short option.
+ if (arg[1] == '\0') {
+ // Just "-".
+ HandleArgument(&arg_index, arg);
+ continue;
+ }
+
+ // Allow short names to be combined, e.g. "-d -v" => "-dv".
+ for (int k = 1; arg[k]; ++k) {
+ bool matched = false;
+ for (const Option& option : options_) {
+ if (option.short_name && arg[k] == option.short_name) {
+ const char* option_argument = nullptr;
+ if (option.has_argument) {
+ // A short option with a required argument cannot be followed
+ // by other short options_.
+ if (arg[k + 1] != '\0') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+
+ if (i + 1 == argc || argv[i + 1][0] == '-') {
+ Errorf("option '-%c' requires argument", option.short_name);
+ break;
+ }
+ ++i;
+ option_argument = argv[i];
+ }
+ option.callback(option_argument);
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ Errorf("unknown option '-%c'", arg[k]);
+ continue;
+ }
+ }
+ }
+ }
+
+ // For now, all arguments must be provided. Check that the last Argument was
+ // handled at least once.
+ if (!arguments_.empty() && arguments_.back().handled_count == 0) {
+ for (size_t i = arg_index; i < arguments_.size(); ++i) {
+ if (arguments_[i].count != ArgumentCount::ZeroOrMore) {
+ Errorf("expected %s argument.", arguments_[i].name.c_str());
+ }
+ }
+ }
+}
+
+void OptionParser::PrintHelp() {
+ printf("usage: %s [options]", program_name_.c_str());
+
+ for (size_t i = 0; i < arguments_.size(); ++i) {
+ Argument& argument = arguments_[i];
+ switch (argument.count) {
+ case ArgumentCount::One:
+ printf(" %s", argument.name.c_str());
+ break;
+
+ case ArgumentCount::OneOrMore:
+ printf(" %s+", argument.name.c_str());
+ break;
+
+ case ArgumentCount::ZeroOrMore:
+ printf(" [%s]...", argument.name.c_str());
+ break;
+ }
+ }
+
+ printf("\n\n");
+ printf("%s\n", description_.c_str());
+ printf("options:\n");
+
+ const size_t kExtraSpace = 8;
+ size_t longest_name_length = 0;
+ for (const Option& option : options_) {
+ size_t length;
+ if (!option.long_name.empty()) {
+ length = option.long_name.size();
+ if (!option.metavar.empty()) {
+ // +1 for '='.
+ length += option.metavar.size() + 1;
+ }
+ } else {
+ continue;
+ }
+
+ if (length > longest_name_length) {
+ longest_name_length = length;
+ }
+ }
+
+ for (const Option& option : options_) {
+ if (!option.short_name && option.long_name.empty()) {
+ continue;
+ }
+
+ std::string line;
+ if (option.short_name) {
+ line += std::string(" -") + option.short_name + ", ";
+ } else {
+ line += " ";
+ }
+
+ std::string flag;
+ if (!option.long_name.empty()) {
+ flag = "--";
+ if (!option.metavar.empty()) {
+ flag += option.long_name + '=' + option.metavar;
+ } else {
+ flag += option.long_name;
+ }
+ }
+
+ // +2 for "--" of the long flag name.
+ size_t remaining = longest_name_length + kExtraSpace + 2 - flag.size();
+ line += flag + std::string(remaining, ' ');
+
+ if (!option.help.empty()) {
+ line += option.help;
+ }
+ printf("%s\n", line.c_str());
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/option-parser.h b/third_party/wasm2c/src/option-parser.h
new file mode 100644
index 0000000000..051ce784e8
--- /dev/null
+++ b/third_party/wasm2c/src/option-parser.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_OPTION_PARSER_H_
+#define WABT_OPTION_PARSER_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "src/common.h"
+
+namespace wabt {
+
+class OptionParser {
+ public:
+ enum class HasArgument { No, Yes };
+ enum class ArgumentCount { One, OneOrMore, ZeroOrMore };
+
+ struct Option;
+ typedef std::function<void(const char*)> Callback;
+ typedef std::function<void()> NullCallback;
+
+ struct Option {
+ Option(char short_name,
+ const std::string& long_name,
+ const std::string& metavar,
+ HasArgument has_argument,
+ const std::string& help,
+ const Callback&);
+
+ char short_name;
+ std::string long_name;
+ std::string metavar;
+ bool has_argument;
+ std::string help;
+ Callback callback;
+ };
+
+ struct Argument {
+ Argument(const std::string& name, ArgumentCount, const Callback&);
+
+ std::string name;
+ ArgumentCount count;
+ Callback callback;
+ int handled_count = 0;
+ };
+
+ explicit OptionParser(const char* program_name, const char* description);
+
+ void AddOption(const Option&);
+ void AddArgument(const std::string& name, ArgumentCount, const Callback&);
+ void SetErrorCallback(const Callback&);
+ void Parse(int argc, char* argv[]);
+ void PrintHelp();
+
+ // Helper functions.
+ void AddOption(char short_name,
+ const char* long_name,
+ const char* help,
+ const NullCallback&);
+ void AddOption(const char* long_name, const char* help, const NullCallback&);
+ void AddOption(char short_name,
+ const char* long_name,
+ const char* metavar,
+ const char* help,
+ const Callback&);
+
+ private:
+ static int Match(const char* s, const std::string& full, bool has_argument);
+ void WABT_PRINTF_FORMAT(2, 3) Errorf(const char* format, ...);
+ void HandleArgument(size_t* arg_index, const char* arg_value);
+
+ // Print the error and exit(1).
+ void DefaultError(const std::string&);
+
+ std::string program_name_;
+ std::string description_;
+ std::vector<Option> options_;
+ std::vector<Argument> arguments_;
+ Callback on_error_;
+};
+
+} // namespace wabt
+
+#endif /* WABT_OPTION_PARSER_H_ */
diff --git a/third_party/wasm2c/src/prebuilt/.clang-format b/third_party/wasm2c/src/prebuilt/.clang-format
new file mode 100644
index 0000000000..9d159247d5
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/third_party/wasm2c/src/prebuilt/lexer-keywords.cc b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc
new file mode 100644
index 0000000000..9f1f68ed8c
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/lexer-keywords.cc
@@ -0,0 +1,1856 @@
+/* C++ code produced by gperf version 3.1 */
+/* Command-line: gperf -m 50 -L C++ -N InWordSet -E -t -c --output-file=src/prebuilt/lexer-keywords.cc src/lexer-keywords.txt */
+/* Computed positions: -k'1,3,5-9,11-14,16-19,23,$' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
+#endif
+
+#line 1 "src/lexer-keywords.txt"
+struct TokenInfo {
+ TokenInfo(const char* name) : name(name) {}
+ TokenInfo(const char* name, TokenType token_type)
+ : name(name), token_type(token_type) {}
+ TokenInfo(const char* name, Type value_type)
+ : name(name), token_type(TokenType::ValueType), value_type(value_type) {}
+ TokenInfo(const char* name, Type value_type, TokenType token_type)
+ : name(name), token_type(token_type), value_type(value_type) {}
+ TokenInfo(const char* name, TokenType token_type, Opcode opcode)
+ : name(name), token_type(token_type), opcode(opcode) {}
+
+ const char* name;
+ TokenType token_type;
+ union {
+ Type value_type;
+ Opcode opcode;
+ };
+};
+/* maximum key range = 2613, duplicates = 0 */
+
+class Perfect_Hash
+{
+private:
+ static inline unsigned int hash (const char *str, size_t len);
+public:
+ static struct TokenInfo *InWordSet (const char *str, size_t len);
+};
+
+inline unsigned int
+Perfect_Hash::hash (const char *str, size_t len)
+{
+ static unsigned short asso_values[] =
+ {
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 12, 111, 2632, 60,
+ 8, 44, 7, 294, 126, 601, 359, 575, 17, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 8, 30, 67, 24, 77,
+ 27, 13, 7, 561, 624, 10, 13, 25, 40, 8,
+ 9, 53, 353, 537, 140, 7, 7, 9, 162, 131,
+ 317, 751, 95, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632, 2632,
+ 2632, 2632, 2632, 2632, 2632, 2632, 2632
+ };
+ unsigned int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[static_cast<unsigned char>(str[22])];
+ /*FALLTHROUGH*/
+ case 22:
+ case 21:
+ case 20:
+ case 19:
+ hval += asso_values[static_cast<unsigned char>(str[18])];
+ /*FALLTHROUGH*/
+ case 18:
+ hval += asso_values[static_cast<unsigned char>(str[17])];
+ /*FALLTHROUGH*/
+ case 17:
+ hval += asso_values[static_cast<unsigned char>(str[16]+1)];
+ /*FALLTHROUGH*/
+ case 16:
+ hval += asso_values[static_cast<unsigned char>(str[15])];
+ /*FALLTHROUGH*/
+ case 15:
+ case 14:
+ hval += asso_values[static_cast<unsigned char>(str[13])];
+ /*FALLTHROUGH*/
+ case 13:
+ hval += asso_values[static_cast<unsigned char>(str[12])];
+ /*FALLTHROUGH*/
+ case 12:
+ hval += asso_values[static_cast<unsigned char>(str[11])];
+ /*FALLTHROUGH*/
+ case 11:
+ hval += asso_values[static_cast<unsigned char>(str[10])];
+ /*FALLTHROUGH*/
+ case 10:
+ case 9:
+ hval += asso_values[static_cast<unsigned char>(str[8])];
+ /*FALLTHROUGH*/
+ case 8:
+ hval += asso_values[static_cast<unsigned char>(str[7])];
+ /*FALLTHROUGH*/
+ case 7:
+ hval += asso_values[static_cast<unsigned char>(str[6])];
+ /*FALLTHROUGH*/
+ case 6:
+ hval += asso_values[static_cast<unsigned char>(str[5])];
+ /*FALLTHROUGH*/
+ case 5:
+ hval += asso_values[static_cast<unsigned char>(str[4]+1)];
+ /*FALLTHROUGH*/
+ case 4:
+ case 3:
+ hval += asso_values[static_cast<unsigned char>(str[2])];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[static_cast<unsigned char>(str[0])];
+ break;
+ }
+ return hval + asso_values[static_cast<unsigned char>(str[len - 1])];
+}
+
+struct TokenInfo *
+Perfect_Hash::InWordSet (const char *str, size_t len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 611,
+ MIN_WORD_LENGTH = 2,
+ MAX_WORD_LENGTH = 29,
+ MIN_HASH_VALUE = 19,
+ MAX_HASH_VALUE = 2631
+ };
+
+ static struct TokenInfo wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 507 "src/lexer-keywords.txt"
+ {"if", TokenType::If, Opcode::If},
+ {""}, {""}, {""}, {""},
+#line 140 "src/lexer-keywords.txt"
+ {"f64", Type::F64},
+#line 527 "src/lexer-keywords.txt"
+ {"mut", TokenType::Mut},
+#line 82 "src/lexer-keywords.txt"
+ {"f32", Type::F32},
+#line 439 "src/lexer-keywords.txt"
+ {"i64", Type::I64},
+ {""},
+#line 301 "src/lexer-keywords.txt"
+ {"i32", Type::I32},
+ {""}, {""}, {""},
+#line 557 "src/lexer-keywords.txt"
+ {"then", TokenType::Then},
+ {""},
+#line 511 "src/lexer-keywords.txt"
+ {"item", TokenType::Item},
+ {""},
+#line 47 "src/lexer-keywords.txt"
+ {"else", TokenType::Else, Opcode::Else},
+#line 46 "src/lexer-keywords.txt"
+ {"elem", TokenType::Elem},
+ {""}, {""}, {""},
+#line 127 "src/lexer-keywords.txt"
+ {"f64.lt", TokenType::Compare, Opcode::F64Lt},
+#line 70 "src/lexer-keywords.txt"
+ {"f32.lt", TokenType::Compare, Opcode::F32Lt},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 50 "src/lexer-keywords.txt"
+ {"extern", Type::ExternRef, TokenType::Extern},
+ {""}, {""},
+#line 125 "src/lexer-keywords.txt"
+ {"f64.le", TokenType::Compare, Opcode::F64Le},
+#line 68 "src/lexer-keywords.txt"
+ {"f32.le", TokenType::Compare, Opcode::F32Le},
+#line 556 "src/lexer-keywords.txt"
+ {"table", TokenType::Table},
+#line 170 "src/lexer-keywords.txt"
+ {"funcref", Type::FuncRef},
+#line 129 "src/lexer-keywords.txt"
+ {"f64.min", TokenType::Binary, Opcode::F64Min},
+#line 72 "src/lexer-keywords.txt"
+ {"f32.min", TokenType::Binary, Opcode::F32Min},
+ {""}, {""},
+#line 412 "src/lexer-keywords.txt"
+ {"i64.lt_s", TokenType::Compare, Opcode::I64LtS},
+#line 275 "src/lexer-keywords.txt"
+ {"i32.lt_s", TokenType::Compare, Opcode::I32LtS},
+ {""},
+#line 169 "src/lexer-keywords.txt"
+ {"field", TokenType::Field},
+#line 413 "src/lexer-keywords.txt"
+ {"i64.lt_u", TokenType::Compare, Opcode::I64LtU},
+#line 276 "src/lexer-keywords.txt"
+ {"i32.lt_u", TokenType::Compare, Opcode::I32LtU},
+#line 403 "src/lexer-keywords.txt"
+ {"i64.le_s", TokenType::Compare, Opcode::I64LeS},
+#line 268 "src/lexer-keywords.txt"
+ {"i32.le_s", TokenType::Compare, Opcode::I32LeS},
+#line 48 "src/lexer-keywords.txt"
+ {"end", TokenType::End, Opcode::End},
+#line 168 "src/lexer-keywords.txt"
+ {"f64x2", TokenType::F64X2},
+#line 404 "src/lexer-keywords.txt"
+ {"i64.le_u", TokenType::Compare, Opcode::I64LeU},
+#line 269 "src/lexer-keywords.txt"
+ {"i32.le_u", TokenType::Compare, Opcode::I32LeU},
+#line 469 "src/lexer-keywords.txt"
+ {"i64x2", TokenType::I64X2},
+#line 526 "src/lexer-keywords.txt"
+ {"module", TokenType::Module},
+#line 419 "src/lexer-keywords.txt"
+ {"i64.rem_s", TokenType::Binary, Opcode::I64RemS},
+#line 282 "src/lexer-keywords.txt"
+ {"i32.rem_s", TokenType::Binary, Opcode::I32RemS},
+ {""}, {""},
+#line 420 "src/lexer-keywords.txt"
+ {"i64.rem_u", TokenType::Binary, Opcode::I64RemU},
+#line 283 "src/lexer-keywords.txt"
+ {"i32.rem_u", TokenType::Binary, Opcode::I32RemU},
+#line 43 "src/lexer-keywords.txt"
+ {"do", TokenType::Do},
+#line 111 "src/lexer-keywords.txt"
+ {"f64.abs", TokenType::Unary, Opcode::F64Abs},
+#line 53 "src/lexer-keywords.txt"
+ {"f32.abs", TokenType::Unary, Opcode::F32Abs},
+#line 138 "src/lexer-keywords.txt"
+ {"f64.sub", TokenType::Binary, Opcode::F64Sub},
+#line 80 "src/lexer-keywords.txt"
+ {"f32.sub", TokenType::Binary, Opcode::F32Sub},
+#line 531 "src/lexer-keywords.txt"
+ {"offset", TokenType::Offset},
+#line 430 "src/lexer-keywords.txt"
+ {"i64.sub", TokenType::Binary, Opcode::I64Sub},
+#line 292 "src/lexer-keywords.txt"
+ {"i32.sub", TokenType::Binary, Opcode::I32Sub},
+ {""}, {""}, {""},
+#line 554 "src/lexer-keywords.txt"
+ {"table.set", TokenType::TableSet, Opcode::TableSet},
+#line 545 "src/lexer-keywords.txt"
+ {"select", TokenType::Select, Opcode::Select},
+#line 547 "src/lexer-keywords.txt"
+ {"start", TokenType::Start},
+#line 553 "src/lexer-keywords.txt"
+ {"table.init", TokenType::TableInit, Opcode::TableInit},
+#line 171 "src/lexer-keywords.txt"
+ {"func", Type::FuncRef, TokenType::Func},
+ {""},
+#line 133 "src/lexer-keywords.txt"
+ {"f64.ne", TokenType::Compare, Opcode::F64Ne},
+#line 76 "src/lexer-keywords.txt"
+ {"f32.ne", TokenType::Compare, Opcode::F32Ne},
+ {""},
+#line 415 "src/lexer-keywords.txt"
+ {"i64.ne", TokenType::Compare, Opcode::I64Ne},
+#line 278 "src/lexer-keywords.txt"
+ {"i32.ne", TokenType::Compare, Opcode::I32Ne},
+ {""},
+#line 40 "src/lexer-keywords.txt"
+ {"data", TokenType::Data},
+ {""}, {""}, {""}, {""}, {""},
+#line 350 "src/lexer-keywords.txt"
+ {"i64.and", TokenType::Binary, Opcode::I64And},
+#line 227 "src/lexer-keywords.txt"
+ {"i32.and", TokenType::Binary, Opcode::I32And},
+#line 157 "src/lexer-keywords.txt"
+ {"f64x2.ne", TokenType::Compare, Opcode::F64X2Ne},
+ {""},
+#line 153 "src/lexer-keywords.txt"
+ {"f64x2.min", TokenType::Binary, Opcode::F64X2Min},
+#line 446 "src/lexer-keywords.txt"
+ {"i64x2.ne", TokenType::Binary, Opcode::I64X2Ne},
+ {""}, {""},
+#line 130 "src/lexer-keywords.txt"
+ {"f64.mul", TokenType::Binary, Opcode::F64Mul},
+#line 73 "src/lexer-keywords.txt"
+ {"f32.mul", TokenType::Binary, Opcode::F32Mul},
+ {""},
+#line 414 "src/lexer-keywords.txt"
+ {"i64.mul", TokenType::Binary, Opcode::I64Mul},
+#line 277 "src/lexer-keywords.txt"
+ {"i32.mul", TokenType::Binary, Opcode::I32Mul},
+ {""}, {""},
+#line 112 "src/lexer-keywords.txt"
+ {"f64.add", TokenType::Binary, Opcode::F64Add},
+#line 54 "src/lexer-keywords.txt"
+ {"f32.add", TokenType::Binary, Opcode::F32Add},
+ {""},
+#line 349 "src/lexer-keywords.txt"
+ {"i64.add", TokenType::Binary, Opcode::I64Add},
+#line 226 "src/lexer-keywords.txt"
+ {"i32.add", TokenType::Binary, Opcode::I32Add},
+ {""},
+#line 151 "src/lexer-keywords.txt"
+ {"f64x2.lt", TokenType::Compare, Opcode::F64X2Lt},
+#line 114 "src/lexer-keywords.txt"
+ {"f64.const", TokenType::Const, Opcode::F64Const},
+#line 56 "src/lexer-keywords.txt"
+ {"f32.const", TokenType::Const, Opcode::F32Const},
+ {""},
+#line 388 "src/lexer-keywords.txt"
+ {"i64.const", TokenType::Const, Opcode::I64Const},
+#line 256 "src/lexer-keywords.txt"
+ {"i32.const", TokenType::Const, Opcode::I32Const},
+ {""}, {""}, {""},
+#line 546 "src/lexer-keywords.txt"
+ {"shared", TokenType::Shared},
+ {""},
+#line 163 "src/lexer-keywords.txt"
+ {"f64x2.sub", TokenType::Binary, Opcode::F64X2Sub},
+#line 150 "src/lexer-keywords.txt"
+ {"f64x2.le", TokenType::Compare, Opcode::F64X2Le},
+#line 447 "src/lexer-keywords.txt"
+ {"i64x2.lt_s", TokenType::Binary, Opcode::I64X2LtS},
+#line 464 "src/lexer-keywords.txt"
+ {"i64x2.sub", TokenType::Binary, Opcode::I64X2Sub},
+#line 29 "src/lexer-keywords.txt"
+ {"block", TokenType::Block, Opcode::Block},
+ {""}, {""}, {""},
+#line 449 "src/lexer-keywords.txt"
+ {"i64x2.le_s", TokenType::Binary, Opcode::I64X2LeS},
+#line 113 "src/lexer-keywords.txt"
+ {"f64.ceil", TokenType::Unary, Opcode::F64Ceil},
+#line 55 "src/lexer-keywords.txt"
+ {"f32.ceil", TokenType::Unary, Opcode::F32Ceil},
+#line 31 "src/lexer-keywords.txt"
+ {"br_table", TokenType::BrTable, Opcode::BrTable},
+ {""}, {""},
+#line 550 "src/lexer-keywords.txt"
+ {"table.fill", TokenType::TableFill, Opcode::TableFill},
+ {""}, {""}, {""},
+#line 35 "src/lexer-keywords.txt"
+ {"call", TokenType::Call, Opcode::Call},
+ {""}, {""}, {""}, {""},
+#line 32 "src/lexer-keywords.txt"
+ {"br", TokenType::Br, Opcode::Br},
+ {""}, {""}, {""},
+#line 515 "src/lexer-keywords.txt"
+ {"local", TokenType::Local},
+ {""},
+#line 421 "src/lexer-keywords.txt"
+ {"i64.rotl", TokenType::Binary, Opcode::I64Rotl},
+#line 284 "src/lexer-keywords.txt"
+ {"i32.rotl", TokenType::Binary, Opcode::I32Rotl},
+ {""},
+#line 540 "src/lexer-keywords.txt"
+ {"result", TokenType::Result},
+#line 154 "src/lexer-keywords.txt"
+ {"f64x2.mul", TokenType::Binary, Opcode::F64X2Mul},
+ {""},
+#line 544 "src/lexer-keywords.txt"
+ {"return", TokenType::Return, Opcode::Return},
+#line 444 "src/lexer-keywords.txt"
+ {"i64x2.mul", TokenType::Binary, Opcode::I64X2Mul},
+#line 513 "src/lexer-keywords.txt"
+ {"local.set", TokenType::LocalSet, Opcode::LocalSet},
+ {""}, {""}, {""},
+#line 141 "src/lexer-keywords.txt"
+ {"f64x2.abs", TokenType::Unary, Opcode::F64X2Abs},
+#line 555 "src/lexer-keywords.txt"
+ {"table.size", TokenType::TableSize, Opcode::TableSize},
+ {""},
+#line 451 "src/lexer-keywords.txt"
+ {"i64x2.abs", TokenType::Unary, Opcode::I64X2Abs},
+ {""}, {""}, {""}, {""},
+#line 514 "src/lexer-keywords.txt"
+ {"local.tee", TokenType::LocalTee, Opcode::LocalTee},
+ {""},
+#line 548 "src/lexer-keywords.txt"
+ {"struct", Type::Struct, TokenType::Struct},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 126 "src/lexer-keywords.txt"
+ {"f64.load", TokenType::Load, Opcode::F64Load},
+#line 69 "src/lexer-keywords.txt"
+ {"f32.load", TokenType::Load, Opcode::F32Load},
+#line 155 "src/lexer-keywords.txt"
+ {"f64x2.nearest", TokenType::Unary, Opcode::F64X2Nearest},
+#line 411 "src/lexer-keywords.txt"
+ {"i64.load", TokenType::Load, Opcode::I64Load},
+#line 274 "src/lexer-keywords.txt"
+ {"i32.load", TokenType::Load, Opcode::I32Load},
+ {""}, {""}, {""},
+#line 51 "src/lexer-keywords.txt"
+ {"externref", Type::ExternRef},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 143 "src/lexer-keywords.txt"
+ {"f64x2.ceil", TokenType::Unary, Opcode::F64X2Ceil},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 142 "src/lexer-keywords.txt"
+ {"f64x2.add", TokenType::Binary, Opcode::F64X2Add},
+ {""}, {""},
+#line 440 "src/lexer-keywords.txt"
+ {"i64x2.add", TokenType::Binary, Opcode::I64X2Add},
+ {""}, {""},
+#line 390 "src/lexer-keywords.txt"
+ {"i64.div_s", TokenType::Binary, Opcode::I64DivS},
+#line 258 "src/lexer-keywords.txt"
+ {"i32.div_s", TokenType::Binary, Opcode::I32DivS},
+ {""}, {""},
+#line 391 "src/lexer-keywords.txt"
+ {"i64.div_u", TokenType::Binary, Opcode::I64DivU},
+#line 259 "src/lexer-keywords.txt"
+ {"i32.div_u", TokenType::Binary, Opcode::I32DivU},
+ {""},
+#line 386 "src/lexer-keywords.txt"
+ {"i64.atomic.store", TokenType::AtomicStore, Opcode::I64AtomicStore},
+#line 254 "src/lexer-keywords.txt"
+ {"i32.atomic.store", TokenType::AtomicStore, Opcode::I32AtomicStore},
+#line 532 "src/lexer-keywords.txt"
+ {"output", TokenType::Output},
+ {""},
+#line 510 "src/lexer-keywords.txt"
+ {"invoke", TokenType::Invoke},
+ {""}, {""}, {""},
+#line 389 "src/lexer-keywords.txt"
+ {"i64.ctz", TokenType::Unary, Opcode::I64Ctz},
+#line 257 "src/lexer-keywords.txt"
+ {"i32.ctz", TokenType::Unary, Opcode::I32Ctz},
+#line 407 "src/lexer-keywords.txt"
+ {"i64.load32_s", TokenType::Load, Opcode::I64Load32S},
+#line 454 "src/lexer-keywords.txt"
+ {"i64x2.bitmask", TokenType::Unary, Opcode::I64X2Bitmask},
+#line 384 "src/lexer-keywords.txt"
+ {"i64.atomic.store32", TokenType::AtomicStore, Opcode::I64AtomicStore32},
+ {""},
+#line 408 "src/lexer-keywords.txt"
+ {"i64.load32_u", TokenType::Load, Opcode::I64Load32U},
+ {""},
+#line 137 "src/lexer-keywords.txt"
+ {"f64.store", TokenType::Store, Opcode::F64Store},
+#line 79 "src/lexer-keywords.txt"
+ {"f32.store", TokenType::Store, Opcode::F32Store},
+ {""},
+#line 429 "src/lexer-keywords.txt"
+ {"i64.store", TokenType::Store, Opcode::I64Store},
+#line 291 "src/lexer-keywords.txt"
+ {"i32.store", TokenType::Store, Opcode::I32Store},
+ {""}, {""}, {""},
+#line 427 "src/lexer-keywords.txt"
+ {"i64.store32", TokenType::Store, Opcode::I64Store32},
+ {""},
+#line 405 "src/lexer-keywords.txt"
+ {"i64.load16_s", TokenType::Load, Opcode::I64Load16S},
+#line 270 "src/lexer-keywords.txt"
+ {"i32.load16_s", TokenType::Load, Opcode::I32Load16S},
+ {""}, {""},
+#line 406 "src/lexer-keywords.txt"
+ {"i64.load16_u", TokenType::Load, Opcode::I64Load16U},
+#line 271 "src/lexer-keywords.txt"
+ {"i32.load16_u", TokenType::Load, Opcode::I32Load16U},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 62 "src/lexer-keywords.txt"
+ {"f32.demote_f64", TokenType::Convert, Opcode::F32DemoteF64},
+ {""}, {""},
+#line 387 "src/lexer-keywords.txt"
+ {"i64.clz", TokenType::Unary, Opcode::I64Clz},
+#line 255 "src/lexer-keywords.txt"
+ {"i32.clz", TokenType::Unary, Opcode::I32Clz},
+ {""},
+#line 537 "src/lexer-keywords.txt"
+ {"ref.is_null", TokenType::RefIsNull, Opcode::RefIsNull},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 37 "src/lexer-keywords.txt"
+ {"catch_all", TokenType::CatchAll, Opcode::CatchAll},
+ {""},
+#line 22 "src/lexer-keywords.txt"
+ {"assert_invalid", TokenType::AssertInvalid},
+#line 41 "src/lexer-keywords.txt"
+ {"declare", TokenType::Declare},
+#line 26 "src/lexer-keywords.txt"
+ {"assert_unlinkable", TokenType::AssertUnlinkable},
+ {""}, {""}, {""}, {""}, {""},
+#line 630 "src/lexer-keywords.txt"
+ {"set_local", TokenType::LocalSet, Opcode::LocalSet},
+ {""}, {""}, {""}, {""}, {""},
+#line 631 "src/lexer-keywords.txt"
+ {"tee_local", TokenType::LocalTee, Opcode::LocalTee},
+ {""}, {""}, {""}, {""}, {""},
+#line 33 "src/lexer-keywords.txt"
+ {"call_indirect", TokenType::CallIndirect, Opcode::CallIndirect},
+#line 110 "src/lexer-keywords.txt"
+ {"f32x4", TokenType::F32X4},
+#line 34 "src/lexer-keywords.txt"
+ {"call_ref", TokenType::CallRef, Opcode::CallRef},
+ {""},
+#line 339 "src/lexer-keywords.txt"
+ {"i32x4", TokenType::I32X4},
+#line 131 "src/lexer-keywords.txt"
+ {"f64.nearest", TokenType::Unary, Opcode::F64Nearest},
+#line 74 "src/lexer-keywords.txt"
+ {"f32.nearest", TokenType::Unary, Opcode::F32Nearest},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 354 "src/lexer-keywords.txt"
+ {"i64.atomic.load", TokenType::AtomicLoad, Opcode::I64AtomicLoad},
+#line 230 "src/lexer-keywords.txt"
+ {"i32.atomic.load", TokenType::AtomicLoad, Opcode::I32AtomicLoad},
+ {""}, {""},
+#line 538 "src/lexer-keywords.txt"
+ {"ref.null", TokenType::RefNull, Opcode::RefNull},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 139 "src/lexer-keywords.txt"
+ {"f64.trunc", TokenType::Unary, Opcode::F64Trunc},
+#line 81 "src/lexer-keywords.txt"
+ {"f32.trunc", TokenType::Unary, Opcode::F32Trunc},
+ {""}, {""}, {""}, {""},
+#line 431 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_s", TokenType::Convert, Opcode::I64TruncF32S},
+#line 293 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_s", TokenType::Convert, Opcode::I32TruncF32S},
+#line 432 "src/lexer-keywords.txt"
+ {"i64.trunc_f32_u", TokenType::Convert, Opcode::I64TruncF32U},
+#line 294 "src/lexer-keywords.txt"
+ {"i32.trunc_f32_u", TokenType::Convert, Opcode::I32TruncF32U},
+ {""}, {""}, {""},
+#line 27 "src/lexer-keywords.txt"
+ {"atomic.fence", TokenType::AtomicFence, Opcode::AtomicFence},
+ {""}, {""}, {""},
+#line 592 "src/lexer-keywords.txt"
+ {"i64.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
+#line 591 "src/lexer-keywords.txt"
+ {"i32.atomic.wait", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
+ {""},
+#line 101 "src/lexer-keywords.txt"
+ {"f32x4.ne", TokenType::Compare, Opcode::F32X4Ne},
+#line 567 "src/lexer-keywords.txt"
+ {"v128.not", TokenType::Unary, Opcode::V128Not},
+#line 97 "src/lexer-keywords.txt"
+ {"f32x4.min", TokenType::Binary, Opcode::F32X4Min},
+#line 326 "src/lexer-keywords.txt"
+ {"i32x4.ne", TokenType::Compare, Opcode::I32X4Ne},
+#line 120 "src/lexer-keywords.txt"
+ {"f64.div", TokenType::Binary, Opcode::F64Div},
+#line 63 "src/lexer-keywords.txt"
+ {"f32.div", TokenType::Binary, Opcode::F32Div},
+ {""}, {""},
+#line 422 "src/lexer-keywords.txt"
+ {"i64.rotr", TokenType::Binary, Opcode::I64Rotr},
+#line 285 "src/lexer-keywords.txt"
+ {"i32.rotr", TokenType::Binary, Opcode::I32Rotr},
+ {""}, {""},
+#line 321 "src/lexer-keywords.txt"
+ {"i32x4.min_s", TokenType::Binary, Opcode::I32X4MinS},
+#line 560 "src/lexer-keywords.txt"
+ {"type", TokenType::Type},
+ {""}, {""},
+#line 322 "src/lexer-keywords.txt"
+ {"i32x4.min_u", TokenType::Binary, Opcode::I32X4MinU},
+#line 598 "src/lexer-keywords.txt"
+ {"f32.demote/f64", TokenType::Convert, Opcode::F32DemoteF64},
+ {""},
+#line 95 "src/lexer-keywords.txt"
+ {"f32x4.lt", TokenType::Compare, Opcode::F32X4Lt},
+#line 509 "src/lexer-keywords.txt"
+ {"input", TokenType::Input},
+ {""},
+#line 115 "src/lexer-keywords.txt"
+ {"f64.convert_i32_s", TokenType::Convert, Opcode::F64ConvertI32S},
+#line 57 "src/lexer-keywords.txt"
+ {"f32.convert_i32_s", TokenType::Convert, Opcode::F32ConvertI32S},
+ {""}, {""},
+#line 508 "src/lexer-keywords.txt"
+ {"import", TokenType::Import},
+#line 164 "src/lexer-keywords.txt"
+ {"f64x2.trunc", TokenType::Unary, Opcode::F64X2Trunc},
+ {""},
+#line 52 "src/lexer-keywords.txt"
+ {"export", TokenType::Export},
+#line 107 "src/lexer-keywords.txt"
+ {"f32x4.sub", TokenType::Binary, Opcode::F32X4Sub},
+#line 94 "src/lexer-keywords.txt"
+ {"f32x4.le", TokenType::Compare, Opcode::F32X4Le},
+#line 317 "src/lexer-keywords.txt"
+ {"i32x4.lt_s", TokenType::Compare, Opcode::I32X4LtS},
+#line 332 "src/lexer-keywords.txt"
+ {"i32x4.sub", TokenType::Binary, Opcode::I32X4Sub},
+#line 318 "src/lexer-keywords.txt"
+ {"i32x4.lt_u", TokenType::Compare, Opcode::I32X4LtU},
+ {""}, {""}, {""},
+#line 313 "src/lexer-keywords.txt"
+ {"i32x4.le_s", TokenType::Compare, Opcode::I32X4LeS},
+ {""},
+#line 314 "src/lexer-keywords.txt"
+ {"i32x4.le_u", TokenType::Compare, Opcode::I32X4LeU},
+ {""},
+#line 543 "src/lexer-keywords.txt"
+ {"return_call", TokenType::ReturnCall, Opcode::ReturnCall},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 453 "src/lexer-keywords.txt"
+ {"i64x2.all_true", TokenType::Unary, Opcode::I64X2AllTrue},
+ {""},
+#line 562 "src/lexer-keywords.txt"
+ {"v128.andnot", TokenType::Binary, Opcode::V128Andnot},
+#line 563 "src/lexer-keywords.txt"
+ {"v128.and", TokenType::Binary, Opcode::V128And},
+#line 529 "src/lexer-keywords.txt"
+ {"nan:canonical", TokenType::NanCanonical},
+ {""}, {""}, {""},
+#line 352 "src/lexer-keywords.txt"
+ {"i64.atomic.load32_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad32U},
+ {""}, {""},
+#line 98 "src/lexer-keywords.txt"
+ {"f32x4.mul", TokenType::Binary, Opcode::F32X4Mul},
+ {""},
+#line 24 "src/lexer-keywords.txt"
+ {"assert_return", TokenType::AssertReturn},
+#line 324 "src/lexer-keywords.txt"
+ {"i32x4.mul", TokenType::Binary, Opcode::I32X4Mul},
+#line 433 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_s", TokenType::Convert, Opcode::I64TruncF64S},
+#line 295 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_s", TokenType::Convert, Opcode::I32TruncF64S},
+#line 434 "src/lexer-keywords.txt"
+ {"i64.trunc_f64_u", TokenType::Convert, Opcode::I64TruncF64U},
+#line 296 "src/lexer-keywords.txt"
+ {"i32.trunc_f64_u", TokenType::Convert, Opcode::I32TruncF64U},
+#line 83 "src/lexer-keywords.txt"
+ {"f32x4.abs", TokenType::Unary, Opcode::F32X4Abs},
+ {""},
+#line 44 "src/lexer-keywords.txt"
+ {"drop", TokenType::Drop, Opcode::Drop},
+#line 303 "src/lexer-keywords.txt"
+ {"i32x4.abs", TokenType::Unary, Opcode::I32X4Abs},
+ {""},
+#line 144 "src/lexer-keywords.txt"
+ {"f64x2.div", TokenType::Binary, Opcode::F64X2Div},
+ {""}, {""}, {""},
+#line 565 "src/lexer-keywords.txt"
+ {"v128.const", TokenType::Const, Opcode::V128Const},
+ {""}, {""}, {""}, {""},
+#line 23 "src/lexer-keywords.txt"
+ {"assert_malformed", TokenType::AssertMalformed},
+#line 516 "src/lexer-keywords.txt"
+ {"loop", TokenType::Loop, Opcode::Loop},
+ {""}, {""},
+#line 621 "src/lexer-keywords.txt"
+ {"i64.trunc_s/f32", TokenType::Convert, Opcode::I64TruncF32S},
+#line 609 "src/lexer-keywords.txt"
+ {"i32.trunc_s/f32", TokenType::Convert, Opcode::I32TruncF32S},
+#line 625 "src/lexer-keywords.txt"
+ {"i64.trunc_u/f32", TokenType::Convert, Opcode::I64TruncF32U},
+#line 613 "src/lexer-keywords.txt"
+ {"i32.trunc_u/f32", TokenType::Convert, Opcode::I32TruncF32U},
+#line 99 "src/lexer-keywords.txt"
+ {"f32x4.nearest", TokenType::Unary, Opcode::F32X4Nearest},
+#line 395 "src/lexer-keywords.txt"
+ {"i64.extend32_s", TokenType::Unary, Opcode::I64Extend32S},
+ {""},
+#line 159 "src/lexer-keywords.txt"
+ {"f64x2.pmin", TokenType::Binary, Opcode::F64X2PMin},
+#line 147 "src/lexer-keywords.txt"
+ {"f64x2.floor", TokenType::Unary, Opcode::F64X2Floor},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 117 "src/lexer-keywords.txt"
+ {"f64.convert_i64_s", TokenType::Convert, Opcode::F64ConvertI64S},
+#line 59 "src/lexer-keywords.txt"
+ {"f32.convert_i64_s", TokenType::Convert, Opcode::F32ConvertI64S},
+#line 397 "src/lexer-keywords.txt"
+ {"i64.extend_i32_s", TokenType::Convert, Opcode::I64ExtendI32S},
+#line 85 "src/lexer-keywords.txt"
+ {"f32x4.ceil", TokenType::Unary, Opcode::F32X4Ceil},
+ {""}, {""},
+#line 398 "src/lexer-keywords.txt"
+ {"i64.extend_i32_u", TokenType::Convert, Opcode::I64ExtendI32U},
+ {""},
+#line 541 "src/lexer-keywords.txt"
+ {"rethrow", TokenType::Rethrow, Opcode::Rethrow},
+ {""},
+#line 84 "src/lexer-keywords.txt"
+ {"f32x4.add", TokenType::Binary, Opcode::F32X4Add},
+#line 435 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_s", TokenType::Convert, Opcode::I64TruncSatF32S},
+#line 297 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_s", TokenType::Convert, Opcode::I32TruncSatF32S},
+#line 304 "src/lexer-keywords.txt"
+ {"i32x4.add", TokenType::Binary, Opcode::I32X4Add},
+ {""},
+#line 436 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f32_u", TokenType::Convert, Opcode::I64TruncSatF32U},
+#line 298 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f32_u", TokenType::Convert, Opcode::I32TruncSatF32U},
+ {""}, {""},
+#line 564 "src/lexer-keywords.txt"
+ {"v128.bitselect", TokenType::Ternary, Opcode::V128BitSelect},
+ {""},
+#line 383 "src/lexer-keywords.txt"
+ {"i64.atomic.store16", TokenType::AtomicStore, Opcode::I64AtomicStore16},
+#line 252 "src/lexer-keywords.txt"
+ {"i32.atomic.store16", TokenType::AtomicStore, Opcode::I32AtomicStore16},
+ {""},
+#line 373 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8SubU},
+#line 242 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8SubU},
+ {""},
+#line 161 "src/lexer-keywords.txt"
+ {"f64x2.splat", TokenType::Unary, Opcode::F64X2Splat},
+ {""}, {""},
+#line 463 "src/lexer-keywords.txt"
+ {"i64x2.splat", TokenType::Unary, Opcode::I64X2Splat},
+ {""},
+#line 426 "src/lexer-keywords.txt"
+ {"i64.store16", TokenType::Store, Opcode::I64Store16},
+#line 289 "src/lexer-keywords.txt"
+ {"i32.store16", TokenType::Store, Opcode::I32Store16},
+#line 306 "src/lexer-keywords.txt"
+ {"i32x4.bitmask", TokenType::Unary, Opcode::I32X4Bitmask},
+ {""},
+#line 566 "src/lexer-keywords.txt"
+ {"v128.load", TokenType::Load, Opcode::V128Load},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 572 "src/lexer-keywords.txt"
+ {"v128.store", TokenType::Store, Opcode::V128Store},
+#line 370 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AndU},
+#line 239 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AndU},
+ {""},
+#line 533 "src/lexer-keywords.txt"
+ {"param", TokenType::Param},
+#line 542 "src/lexer-keywords.txt"
+ {"return_call_indirect", TokenType::ReturnCallIndirect, Opcode::ReturnCallIndirect},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 600 "src/lexer-keywords.txt"
+ {"f64.convert_s/i32", TokenType::Convert, Opcode::F64ConvertI32S},
+#line 594 "src/lexer-keywords.txt"
+ {"f32.convert_s/i32", TokenType::Convert, Opcode::F32ConvertI32S},
+#line 602 "src/lexer-keywords.txt"
+ {"f64.convert_u/i32", TokenType::Convert, Opcode::F64ConvertI32U},
+#line 596 "src/lexer-keywords.txt"
+ {"f32.convert_u/i32", TokenType::Convert, Opcode::F32ConvertI32U},
+ {""}, {""},
+#line 369 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8AddU},
+#line 238 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8AddU},
+ {""},
+#line 573 "src/lexer-keywords.txt"
+ {"v128", Type::V128},
+#line 622 "src/lexer-keywords.txt"
+ {"i64.trunc_s/f64", TokenType::Convert, Opcode::I64TruncF64S},
+#line 610 "src/lexer-keywords.txt"
+ {"i32.trunc_s/f64", TokenType::Convert, Opcode::I32TruncF64S},
+#line 626 "src/lexer-keywords.txt"
+ {"i64.trunc_u/f64", TokenType::Convert, Opcode::I64TruncF64U},
+#line 614 "src/lexer-keywords.txt"
+ {"i32.trunc_u/f64", TokenType::Convert, Opcode::I32TruncF64U},
+#line 618 "src/lexer-keywords.txt"
+ {"i64.extend_s/i32", TokenType::Convert, Opcode::I64ExtendI32S},
+ {""},
+#line 619 "src/lexer-keywords.txt"
+ {"i64.extend_u/i32", TokenType::Convert, Opcode::I64ExtendI32U},
+ {""}, {""},
+#line 116 "src/lexer-keywords.txt"
+ {"f64.convert_i32_u", TokenType::Convert, Opcode::F64ConvertI32U},
+#line 58 "src/lexer-keywords.txt"
+ {"f32.convert_i32_u", TokenType::Convert, Opcode::F32ConvertI32U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 409 "src/lexer-keywords.txt"
+ {"i64.load8_s", TokenType::Load, Opcode::I64Load8S},
+#line 272 "src/lexer-keywords.txt"
+ {"i32.load8_s", TokenType::Load, Opcode::I32Load8S},
+ {""}, {""},
+#line 410 "src/lexer-keywords.txt"
+ {"i64.load8_u", TokenType::Load, Opcode::I64Load8U},
+#line 273 "src/lexer-keywords.txt"
+ {"i32.load8_u", TokenType::Load, Opcode::I32Load8U},
+#line 376 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I64AtomicRmwAdd},
+#line 245 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.add", TokenType::AtomicRmw, Opcode::I32AtomicRmwAdd},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 366 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32SubU},
+ {""},
+#line 172 "src/lexer-keywords.txt"
+ {"get", TokenType::Get},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 394 "src/lexer-keywords.txt"
+ {"i64.extend16_s", TokenType::Unary, Opcode::I64Extend16S},
+#line 262 "src/lexer-keywords.txt"
+ {"i32.extend16_s", TokenType::Unary, Opcode::I32Extend16S},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 558 "src/lexer-keywords.txt"
+ {"throw", TokenType::Throw, Opcode::Throw},
+ {""},
+#line 377 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I64AtomicRmwAnd},
+#line 246 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.and", TokenType::AtomicRmw, Opcode::I32AtomicRmwAnd},
+ {""},
+#line 30 "src/lexer-keywords.txt"
+ {"br_if", TokenType::BrIf, Opcode::BrIf},
+#line 428 "src/lexer-keywords.txt"
+ {"i64.store8", TokenType::Store, Opcode::I64Store8},
+#line 290 "src/lexer-keywords.txt"
+ {"i32.store8", TokenType::Store, Opcode::I32Store8},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 534 "src/lexer-keywords.txt"
+ {"quote", TokenType::Quote},
+ {""}, {""}, {""}, {""},
+#line 579 "src/lexer-keywords.txt"
+ {"v128.load8_lane", TokenType::SimdLoadLane, Opcode::V128Load8Lane},
+#line 568 "src/lexer-keywords.txt"
+ {"v128.or", TokenType::Binary, Opcode::V128Or},
+ {""}, {""}, {""},
+#line 118 "src/lexer-keywords.txt"
+ {"f64.convert_i64_u", TokenType::Convert, Opcode::F64ConvertI64U},
+#line 60 "src/lexer-keywords.txt"
+ {"f32.convert_i64_u", TokenType::Convert, Opcode::F32ConvertI64U},
+#line 379 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I64AtomicRmwOr},
+#line 248 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.or", TokenType::AtomicRmw, Opcode::I32AtomicRmwOr},
+ {""}, {""}, {""},
+#line 582 "src/lexer-keywords.txt"
+ {"v128.load64_lane", TokenType::SimdLoadLane, Opcode::V128Load64Lane},
+#line 581 "src/lexer-keywords.txt"
+ {"v128.load32_lane", TokenType::SimdLoadLane, Opcode::V128Load32Lane},
+#line 585 "src/lexer-keywords.txt"
+ {"v128.store32_lane", TokenType::SimdStoreLane, Opcode::V128Store32Lane},
+ {""},
+#line 363 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AndU},
+ {""}, {""}, {""}, {""}, {""},
+#line 108 "src/lexer-keywords.txt"
+ {"f32x4.trunc", TokenType::Unary, Opcode::F32X4Trunc},
+ {""}, {""},
+#line 380 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I64AtomicRmwSub},
+#line 249 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.sub", TokenType::AtomicRmw, Opcode::I32AtomicRmwSub},
+#line 551 "src/lexer-keywords.txt"
+ {"table.get", TokenType::TableGet, Opcode::TableGet},
+#line 21 "src/lexer-keywords.txt"
+ {"assert_exhaustion", TokenType::AssertExhaustion},
+ {""},
+#line 535 "src/lexer-keywords.txt"
+ {"ref.extern", TokenType::RefExtern},
+ {""}, {""},
+#line 149 "src/lexer-keywords.txt"
+ {"f64x2.gt", TokenType::Compare, Opcode::F64X2Gt},
+#line 362 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32AddU},
+ {""},
+#line 416 "src/lexer-keywords.txt"
+ {"i64.or", TokenType::Binary, Opcode::I64Or},
+#line 279 "src/lexer-keywords.txt"
+ {"i32.or", TokenType::Binary, Opcode::I32Or},
+#line 124 "src/lexer-keywords.txt"
+ {"f64.gt", TokenType::Compare, Opcode::F64Gt},
+#line 67 "src/lexer-keywords.txt"
+ {"f32.gt", TokenType::Compare, Opcode::F32Gt},
+ {""}, {""}, {""}, {""}, {""},
+#line 148 "src/lexer-keywords.txt"
+ {"f64x2.ge", TokenType::Compare, Opcode::F64X2Ge},
+#line 448 "src/lexer-keywords.txt"
+ {"i64x2.gt_s", TokenType::Binary, Opcode::I64X2GtS},
+#line 305 "src/lexer-keywords.txt"
+ {"i32x4.all_true", TokenType::Unary, Opcode::I32X4AllTrue},
+ {""}, {""},
+#line 123 "src/lexer-keywords.txt"
+ {"f64.ge", TokenType::Compare, Opcode::F64Ge},
+#line 66 "src/lexer-keywords.txt"
+ {"f32.ge", TokenType::Compare, Opcode::F32Ge},
+#line 450 "src/lexer-keywords.txt"
+ {"i64x2.ge_s", TokenType::Binary, Opcode::I64X2GeS},
+ {""}, {""}, {""}, {""}, {""},
+#line 401 "src/lexer-keywords.txt"
+ {"i64.gt_s", TokenType::Compare, Opcode::I64GtS},
+#line 266 "src/lexer-keywords.txt"
+ {"i32.gt_s", TokenType::Compare, Opcode::I32GtS},
+ {""}, {""},
+#line 402 "src/lexer-keywords.txt"
+ {"i64.gt_u", TokenType::Compare, Opcode::I64GtU},
+#line 267 "src/lexer-keywords.txt"
+ {"i32.gt_u", TokenType::Compare, Opcode::I32GtU},
+#line 399 "src/lexer-keywords.txt"
+ {"i64.ge_s", TokenType::Compare, Opcode::I64GeS},
+#line 264 "src/lexer-keywords.txt"
+ {"i32.ge_s", TokenType::Compare, Opcode::I32GeS},
+ {""}, {""},
+#line 400 "src/lexer-keywords.txt"
+ {"i64.ge_u", TokenType::Compare, Opcode::I64GeU},
+#line 265 "src/lexer-keywords.txt"
+ {"i32.ge_u", TokenType::Compare, Opcode::I32GeU},
+ {""},
+#line 88 "src/lexer-keywords.txt"
+ {"f32x4.div", TokenType::Binary, Opcode::F32X4Div},
+ {""}, {""},
+#line 359 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16SubU},
+#line 235 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.sub_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16SubU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 103 "src/lexer-keywords.txt"
+ {"f32x4.pmin", TokenType::Binary, Opcode::F32X4PMin},
+#line 91 "src/lexer-keywords.txt"
+ {"f32x4.floor", TokenType::Unary, Opcode::F32X4Floor},
+#line 571 "src/lexer-keywords.txt"
+ {"v128.load64_zero", TokenType::Load, Opcode::V128Load64Zero},
+#line 570 "src/lexer-keywords.txt"
+ {"v128.load32_zero", TokenType::Load, Opcode::V128Load32Zero},
+#line 586 "src/lexer-keywords.txt"
+ {"v128.store64_lane", TokenType::SimdStoreLane, Opcode::V128Store64Lane},
+#line 353 "src/lexer-keywords.txt"
+ {"i64.atomic.load8_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad8U},
+#line 229 "src/lexer-keywords.txt"
+ {"i32.atomic.load8_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad8U},
+#line 530 "src/lexer-keywords.txt"
+ {"nop", TokenType::Nop, Opcode::Nop},
+ {""},
+#line 136 "src/lexer-keywords.txt"
+ {"f64.sqrt", TokenType::Unary, Opcode::F64Sqrt},
+#line 78 "src/lexer-keywords.txt"
+ {"f32.sqrt", TokenType::Unary, Opcode::F32Sqrt},
+ {""},
+#line 36 "src/lexer-keywords.txt"
+ {"catch", TokenType::Catch, Opcode::Catch},
+#line 175 "src/lexer-keywords.txt"
+ {"global", TokenType::Global},
+ {""}, {""},
+#line 174 "src/lexer-keywords.txt"
+ {"global.set", TokenType::GlobalSet, Opcode::GlobalSet},
+ {""}, {""},
+#line 146 "src/lexer-keywords.txt"
+ {"f64x2.extract_lane", TokenType::SimdLaneOp, Opcode::F64X2ExtractLane},
+#line 128 "src/lexer-keywords.txt"
+ {"f64.max", TokenType::Binary, Opcode::F64Max},
+#line 71 "src/lexer-keywords.txt"
+ {"f32.max", TokenType::Binary, Opcode::F32Max},
+#line 441 "src/lexer-keywords.txt"
+ {"i64x2.extract_lane", TokenType::SimdLaneOp, Opcode::I64X2ExtractLane},
+#line 512 "src/lexer-keywords.txt"
+ {"local.get", TokenType::LocalGet, Opcode::LocalGet},
+#line 423 "src/lexer-keywords.txt"
+ {"i64.shl", TokenType::Binary, Opcode::I64Shl},
+#line 286 "src/lexer-keywords.txt"
+ {"i32.shl", TokenType::Binary, Opcode::I32Shl},
+ {""}, {""}, {""}, {""},
+#line 319 "src/lexer-keywords.txt"
+ {"i32x4.max_s", TokenType::Binary, Opcode::I32X4MaxS},
+ {""}, {""}, {""},
+#line 320 "src/lexer-keywords.txt"
+ {"i32x4.max_u", TokenType::Binary, Opcode::I32X4MaxU},
+#line 105 "src/lexer-keywords.txt"
+ {"f32x4.splat", TokenType::Unary, Opcode::F32X4Splat},
+ {""},
+#line 455 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendLowI32X4S},
+#line 331 "src/lexer-keywords.txt"
+ {"i32x4.splat", TokenType::Unary, Opcode::I32X4Splat},
+#line 457 "src/lexer-keywords.txt"
+ {"i64x2.extend_low_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendLowI32X4U},
+#line 580 "src/lexer-keywords.txt"
+ {"v128.load16_lane", TokenType::SimdLoadLane, Opcode::V128Load16Lane},
+#line 365 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32OrU},
+ {""},
+#line 356 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AndU},
+#line 232 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.and_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AndU},
+ {""}, {""},
+#line 393 "src/lexer-keywords.txt"
+ {"i64.eqz", TokenType::Convert, Opcode::I64Eqz},
+#line 261 "src/lexer-keywords.txt"
+ {"i32.eqz", TokenType::Convert, Opcode::I32Eqz},
+ {""},
+#line 465 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4S},
+ {""},
+#line 467 "src/lexer-keywords.txt"
+ {"i64x2.extmul_low_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulLowI32X4U},
+#line 396 "src/lexer-keywords.txt"
+ {"i64.extend8_s", TokenType::Unary, Opcode::I64Extend8S},
+#line 263 "src/lexer-keywords.txt"
+ {"i32.extend8_s", TokenType::Unary, Opcode::I32Extend8S},
+#line 206 "src/lexer-keywords.txt"
+ {"i16x8.ne", TokenType::Compare, Opcode::I16X8Ne},
+ {""},
+#line 584 "src/lexer-keywords.txt"
+ {"v128.store16_lane", TokenType::SimdStoreLane, Opcode::V128Store16Lane},
+ {""}, {""},
+#line 162 "src/lexer-keywords.txt"
+ {"f64x2.sqrt", TokenType::Unary, Opcode::F64X2Sqrt},
+#line 355 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16AddU},
+#line 231 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.add_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16AddU},
+ {""},
+#line 199 "src/lexer-keywords.txt"
+ {"i16x8.min_s", TokenType::Binary, Opcode::I16X8MinS},
+ {""}, {""}, {""},
+#line 200 "src/lexer-keywords.txt"
+ {"i16x8.min_u", TokenType::Binary, Opcode::I16X8MinU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 152 "src/lexer-keywords.txt"
+ {"f64x2.max", TokenType::Binary, Opcode::F64X2Max},
+ {""}, {""}, {""}, {""},
+#line 460 "src/lexer-keywords.txt"
+ {"i64x2.shl", TokenType::Binary, Opcode::I64X2Shl},
+ {""},
+#line 195 "src/lexer-keywords.txt"
+ {"i16x8.lt_s", TokenType::Compare, Opcode::I16X8LtS},
+#line 214 "src/lexer-keywords.txt"
+ {"i16x8.sub", TokenType::Binary, Opcode::I16X8Sub},
+#line 196 "src/lexer-keywords.txt"
+ {"i16x8.lt_u", TokenType::Compare, Opcode::I16X8LtU},
+ {""},
+#line 42 "src/lexer-keywords.txt"
+ {"delegate", TokenType::Delegate},
+ {""},
+#line 191 "src/lexer-keywords.txt"
+ {"i16x8.le_s", TokenType::Compare, Opcode::I16X8LeS},
+ {""},
+#line 192 "src/lexer-keywords.txt"
+ {"i16x8.le_u", TokenType::Compare, Opcode::I16X8LeU},
+#line 165 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_s", TokenType::Unary, Opcode::F64X2ConvertLowI32X4S},
+ {""},
+#line 166 "src/lexer-keywords.txt"
+ {"f64x2.convert_low_i32x4_u", TokenType::Unary, Opcode::F64X2ConvertLowI32X4U},
+#line 77 "src/lexer-keywords.txt"
+ {"f32.reinterpret_i32", TokenType::Convert, Opcode::F32ReinterpretI32},
+ {""}, {""}, {""},
+#line 437 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_s", TokenType::Convert, Opcode::I64TruncSatF64S},
+#line 299 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_s", TokenType::Convert, Opcode::I32TruncSatF64S},
+ {""}, {""},
+#line 438 "src/lexer-keywords.txt"
+ {"i64.trunc_sat_f64_u", TokenType::Convert, Opcode::I64TruncSatF64U},
+#line 300 "src/lexer-keywords.txt"
+ {"i32.trunc_sat_f64_u", TokenType::Convert, Opcode::I32TruncSatF64U},
+ {""}, {""},
+#line 424 "src/lexer-keywords.txt"
+ {"i64.shr_s", TokenType::Binary, Opcode::I64ShrS},
+#line 287 "src/lexer-keywords.txt"
+ {"i32.shr_s", TokenType::Binary, Opcode::I32ShrS},
+ {""}, {""},
+#line 425 "src/lexer-keywords.txt"
+ {"i64.shr_u", TokenType::Binary, Opcode::I64ShrU},
+#line 288 "src/lexer-keywords.txt"
+ {"i32.shr_u", TokenType::Binary, Opcode::I32ShrU},
+#line 158 "src/lexer-keywords.txt"
+ {"f64x2.pmax", TokenType::Binary, Opcode::F64X2PMax},
+ {""}, {""}, {""},
+#line 201 "src/lexer-keywords.txt"
+ {"i16x8.mul", TokenType::Binary, Opcode::I16X8Mul},
+#line 523 "src/lexer-keywords.txt"
+ {"memory.init", TokenType::MemoryInit, Opcode::MemoryInit},
+#line 442 "src/lexer-keywords.txt"
+ {"v128.load32x2_s", TokenType::Load, Opcode::V128Load32X2S},
+ {""},
+#line 443 "src/lexer-keywords.txt"
+ {"v128.load32x2_u", TokenType::Load, Opcode::V128Load32X2U},
+ {""}, {""}, {""},
+#line 177 "src/lexer-keywords.txt"
+ {"i16x8.abs", TokenType::Unary, Opcode::I16X8Abs},
+ {""}, {""},
+#line 524 "src/lexer-keywords.txt"
+ {"memory.size", TokenType::MemorySize, Opcode::MemorySize},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 601 "src/lexer-keywords.txt"
+ {"f64.convert_s/i64", TokenType::Convert, Opcode::F64ConvertI64S},
+#line 595 "src/lexer-keywords.txt"
+ {"f32.convert_s/i64", TokenType::Convert, Opcode::F32ConvertI64S},
+#line 603 "src/lexer-keywords.txt"
+ {"f64.convert_u/i64", TokenType::Convert, Opcode::F64ConvertI64U},
+#line 597 "src/lexer-keywords.txt"
+ {"f32.convert_u/i64", TokenType::Convert, Opcode::F32ConvertI64U},
+ {""}, {""}, {""},
+#line 607 "src/lexer-keywords.txt"
+ {"get_local", TokenType::LocalGet, Opcode::LocalGet},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 461 "src/lexer-keywords.txt"
+ {"i64x2.shr_s", TokenType::Binary, Opcode::I64X2ShrS},
+#line 358 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16OrU},
+#line 234 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16OrU},
+#line 629 "src/lexer-keywords.txt"
+ {"set_global", TokenType::GlobalSet, Opcode::GlobalSet},
+#line 462 "src/lexer-keywords.txt"
+ {"i64x2.shr_u", TokenType::Binary, Opcode::I64X2ShrU},
+#line 212 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_s", TokenType::Binary, Opcode::I16X8SubSatS},
+ {""},
+#line 213 "src/lexer-keywords.txt"
+ {"i16x8.sub_sat_u", TokenType::Binary, Opcode::I16X8SubSatU},
+ {""}, {""}, {""},
+#line 180 "src/lexer-keywords.txt"
+ {"i16x8.add", TokenType::Binary, Opcode::I16X8Add},
+ {""}, {""}, {""}, {""}, {""},
+#line 135 "src/lexer-keywords.txt"
+ {"f64.reinterpret_i64", TokenType::Convert, Opcode::F64ReinterpretI64},
+ {""},
+#line 536 "src/lexer-keywords.txt"
+ {"ref.func", TokenType::RefFunc, Opcode::RefFunc},
+ {""}, {""}, {""}, {""}, {""},
+#line 521 "src/lexer-keywords.txt"
+ {"memory.fill", TokenType::MemoryFill, Opcode::MemoryFill},
+ {""}, {""}, {""}, {""}, {""},
+#line 134 "src/lexer-keywords.txt"
+ {"f64.promote_f32", TokenType::Convert, Opcode::F64PromoteF32},
+#line 183 "src/lexer-keywords.txt"
+ {"i16x8.bitmask", TokenType::Unary, Opcode::I16X8Bitmask},
+ {""}, {""},
+#line 93 "src/lexer-keywords.txt"
+ {"f32x4.gt", TokenType::Compare, Opcode::F32X4Gt},
+#line 160 "src/lexer-keywords.txt"
+ {"f64x2.replace_lane", TokenType::SimdLaneOp, Opcode::F64X2ReplaceLane},
+#line 578 "src/lexer-keywords.txt"
+ {"v128.load8_splat", TokenType::Load, Opcode::V128Load8Splat},
+ {""},
+#line 459 "src/lexer-keywords.txt"
+ {"i64x2.replace_lane", TokenType::SimdLaneOp, Opcode::I64X2ReplaceLane},
+ {""},
+#line 599 "src/lexer-keywords.txt"
+ {"f32.reinterpret/i32", TokenType::Convert, Opcode::F32ReinterpretI32},
+ {""}, {""}, {""}, {""}, {""},
+#line 92 "src/lexer-keywords.txt"
+ {"f32x4.ge", TokenType::Compare, Opcode::F32X4Ge},
+#line 311 "src/lexer-keywords.txt"
+ {"i32x4.gt_s", TokenType::Compare, Opcode::I32X4GtS},
+ {""},
+#line 312 "src/lexer-keywords.txt"
+ {"i32x4.gt_u", TokenType::Compare, Opcode::I32X4GtU},
+ {""}, {""}, {""},
+#line 309 "src/lexer-keywords.txt"
+ {"i32x4.ge_s", TokenType::Compare, Opcode::I32X4GeS},
+ {""},
+#line 310 "src/lexer-keywords.txt"
+ {"i32x4.ge_u", TokenType::Compare, Opcode::I32X4GeU},
+ {""}, {""}, {""}, {""},
+#line 368 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XorU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 574 "src/lexer-keywords.txt"
+ {"v128.xor", TokenType::Binary, Opcode::V128Xor},
+ {""}, {""}, {""},
+#line 577 "src/lexer-keywords.txt"
+ {"v128.load64_splat", TokenType::Load, Opcode::V128Load64Splat},
+#line 576 "src/lexer-keywords.txt"
+ {"v128.load32_splat", TokenType::Load, Opcode::V128Load32Splat},
+#line 552 "src/lexer-keywords.txt"
+ {"table.grow", TokenType::TableGrow, Opcode::TableGrow},
+ {""}, {""},
+#line 315 "src/lexer-keywords.txt"
+ {"v128.load16x4_s", TokenType::Load, Opcode::V128Load16X4S},
+ {""},
+#line 316 "src/lexer-keywords.txt"
+ {"v128.load16x4_u", TokenType::Load, Opcode::V128Load16X4U},
+ {""},
+#line 372 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8OrU},
+#line 241 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.or_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8OrU},
+ {""},
+#line 178 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_s", TokenType::Binary, Opcode::I16X8AddSatS},
+ {""},
+#line 179 "src/lexer-keywords.txt"
+ {"i16x8.add_sat_u", TokenType::Binary, Opcode::I16X8AddSatU},
+ {""}, {""},
+#line 25 "src/lexer-keywords.txt"
+ {"assert_trap", TokenType::AssertTrap},
+ {""}, {""},
+#line 323 "src/lexer-keywords.txt"
+ {"i32x4.dot_i16x8_s", TokenType::Binary, Opcode::I32X4DotI16X8S},
+#line 367 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw32XchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 90 "src/lexer-keywords.txt"
+ {"f32x4.extract_lane", TokenType::SimdLaneOp, Opcode::F32X4ExtractLane},
+ {""},
+#line 497 "src/lexer-keywords.txt"
+ {"i8x16.ne", TokenType::Compare, Opcode::I8X16Ne},
+#line 308 "src/lexer-keywords.txt"
+ {"i32x4.extract_lane", TokenType::SimdLaneOp, Opcode::I32X4ExtractLane},
+ {""}, {""}, {""}, {""},
+#line 605 "src/lexer-keywords.txt"
+ {"f64.reinterpret/i64", TokenType::Convert, Opcode::F64ReinterpretI64},
+ {""}, {""},
+#line 491 "src/lexer-keywords.txt"
+ {"i8x16.min_s", TokenType::Binary, Opcode::I8X16MinS},
+ {""}, {""}, {""},
+#line 492 "src/lexer-keywords.txt"
+ {"i8x16.min_u", TokenType::Binary, Opcode::I8X16MinU},
+#line 351 "src/lexer-keywords.txt"
+ {"i64.atomic.load16_u", TokenType::AtomicLoad, Opcode::I64AtomicLoad16U},
+#line 228 "src/lexer-keywords.txt"
+ {"i32.atomic.load16_u", TokenType::AtomicLoad, Opcode::I32AtomicLoad16U},
+ {""}, {""}, {""},
+#line 561 "src/lexer-keywords.txt"
+ {"unreachable", TokenType::Unreachable, Opcode::Unreachable},
+#line 604 "src/lexer-keywords.txt"
+ {"f64.promote/f32", TokenType::Convert, Opcode::F64PromoteF32},
+#line 583 "src/lexer-keywords.txt"
+ {"v128.store8_lane", TokenType::SimdStoreLane, Opcode::V128Store8Lane},
+ {""}, {""}, {""}, {""}, {""},
+#line 122 "src/lexer-keywords.txt"
+ {"f64.floor", TokenType::Unary, Opcode::F64Floor},
+#line 65 "src/lexer-keywords.txt"
+ {"f32.floor", TokenType::Unary, Opcode::F32Floor},
+#line 487 "src/lexer-keywords.txt"
+ {"i8x16.lt_s", TokenType::Compare, Opcode::I8X16LtS},
+#line 505 "src/lexer-keywords.txt"
+ {"i8x16.sub", TokenType::Binary, Opcode::I8X16Sub},
+#line 488 "src/lexer-keywords.txt"
+ {"i8x16.lt_u", TokenType::Compare, Opcode::I8X16LtU},
+#line 344 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendLowI16X8S},
+#line 539 "src/lexer-keywords.txt"
+ {"register", TokenType::Register},
+#line 345 "src/lexer-keywords.txt"
+ {"i32x4.extend_low_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendLowI16X8U},
+#line 485 "src/lexer-keywords.txt"
+ {"i8x16.le_s", TokenType::Compare, Opcode::I8X16LeS},
+ {""},
+#line 486 "src/lexer-keywords.txt"
+ {"i8x16.le_u", TokenType::Compare, Opcode::I8X16LeU},
+#line 528 "src/lexer-keywords.txt"
+ {"nan:arithmetic", TokenType::NanArithmetic},
+#line 106 "src/lexer-keywords.txt"
+ {"f32x4.sqrt", TokenType::Unary, Opcode::F32X4Sqrt},
+ {""}, {""}, {""}, {""}, {""},
+#line 335 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8S},
+ {""},
+#line 337 "src/lexer-keywords.txt"
+ {"i32x4.extmul_low_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulLowI16X8U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 96 "src/lexer-keywords.txt"
+ {"f32x4.max", TokenType::Binary, Opcode::F32X4Max},
+ {""}, {""}, {""}, {""},
+#line 328 "src/lexer-keywords.txt"
+ {"i32x4.shl", TokenType::Binary, Opcode::I32X4Shl},
+ {""}, {""}, {""},
+#line 361 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XorU},
+#line 237 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XorU},
+ {""}, {""}, {""}, {""},
+#line 471 "src/lexer-keywords.txt"
+ {"i8x16.abs", TokenType::Unary, Opcode::I8X16Abs},
+#line 302 "src/lexer-keywords.txt"
+ {"i32.wrap_i64", TokenType::Convert, Opcode::I32WrapI64},
+ {""}, {""},
+#line 20 "src/lexer-keywords.txt"
+ {"array", Type::Array, TokenType::Array},
+#line 506 "src/lexer-keywords.txt"
+ {"i8x16", TokenType::I8X16},
+ {""}, {""},
+#line 575 "src/lexer-keywords.txt"
+ {"v128.load16_splat", TokenType::Load, Opcode::V128Load16Splat},
+#line 417 "src/lexer-keywords.txt"
+ {"i64.popcnt", TokenType::Unary, Opcode::I64Popcnt},
+#line 280 "src/lexer-keywords.txt"
+ {"i32.popcnt", TokenType::Unary, Opcode::I32Popcnt},
+ {""},
+#line 181 "src/lexer-keywords.txt"
+ {"i16x8.all_true", TokenType::Unary, Opcode::I16X8AllTrue},
+ {""},
+#line 86 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_s", TokenType::Unary, Opcode::F32X4ConvertI32X4S},
+ {""},
+#line 87 "src/lexer-keywords.txt"
+ {"f32x4.convert_i32x4_u", TokenType::Unary, Opcode::F32X4ConvertI32X4U},
+ {""},
+#line 45 "src/lexer-keywords.txt"
+ {"elem.drop", TokenType::ElemDrop, Opcode::ElemDrop},
+ {""}, {""},
+#line 221 "src/lexer-keywords.txt"
+ {"i16x8", TokenType::I16X8},
+#line 102 "src/lexer-keywords.txt"
+ {"f32x4.pmax", TokenType::Binary, Opcode::F32X4PMax},
+ {""}, {""}, {""},
+#line 39 "src/lexer-keywords.txt"
+ {"data.drop", TokenType::DataDrop, Opcode::DataDrop},
+ {""},
+#line 360 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw16XchgU},
+#line 236 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw16XchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 503 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_s", TokenType::Binary, Opcode::I8X16SubSatS},
+ {""},
+#line 504 "src/lexer-keywords.txt"
+ {"i8x16.sub_sat_u", TokenType::Binary, Opcode::I8X16SubSatU},
+ {""}, {""}, {""},
+#line 474 "src/lexer-keywords.txt"
+ {"i8x16.add", TokenType::Binary, Opcode::I8X16Add},
+ {""}, {""}, {""},
+#line 121 "src/lexer-keywords.txt"
+ {"f64.eq", TokenType::Compare, Opcode::F64Eq},
+#line 64 "src/lexer-keywords.txt"
+ {"f32.eq", TokenType::Compare, Opcode::F32Eq},
+ {""},
+#line 392 "src/lexer-keywords.txt"
+ {"i64.eq", TokenType::Compare, Opcode::I64Eq},
+#line 260 "src/lexer-keywords.txt"
+ {"i32.eq", TokenType::Compare, Opcode::I32Eq},
+ {""}, {""},
+#line 470 "src/lexer-keywords.txt"
+ {"i64.xor", TokenType::Binary, Opcode::I64Xor},
+#line 348 "src/lexer-keywords.txt"
+ {"i32.xor", TokenType::Binary, Opcode::I32Xor},
+#line 623 "src/lexer-keywords.txt"
+ {"i64.trunc_s:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32S},
+#line 611 "src/lexer-keywords.txt"
+ {"i32.trunc_s:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32S},
+#line 627 "src/lexer-keywords.txt"
+ {"i64.trunc_u:sat/f32", TokenType::Convert, Opcode::I64TruncSatF32U},
+#line 615 "src/lexer-keywords.txt"
+ {"i32.trunc_u:sat/f32", TokenType::Convert, Opcode::I32TruncSatF32U},
+ {""}, {""}, {""}, {""},
+#line 477 "src/lexer-keywords.txt"
+ {"i8x16.bitmask", TokenType::Unary, Opcode::I8X16Bitmask},
+#line 167 "src/lexer-keywords.txt"
+ {"f64x2.promote_low_f32x4", TokenType::Unary, Opcode::F64X2PromoteLowF32X4},
+#line 329 "src/lexer-keywords.txt"
+ {"i32x4.shr_s", TokenType::Binary, Opcode::I32X4ShrS},
+ {""}, {""}, {""},
+#line 330 "src/lexer-keywords.txt"
+ {"i32x4.shr_u", TokenType::Binary, Opcode::I32X4ShrU},
+ {""},
+#line 518 "src/lexer-keywords.txt"
+ {"memory.atomic.wait32", TokenType::AtomicWait, Opcode::MemoryAtomicWait32},
+ {""}, {""}, {""}, {""}, {""},
+#line 49 "src/lexer-keywords.txt"
+ {"tag", TokenType::Tag},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 197 "src/lexer-keywords.txt"
+ {"i16x8.max_s", TokenType::Binary, Opcode::I16X8MaxS},
+ {""}, {""}, {""},
+#line 198 "src/lexer-keywords.txt"
+ {"i16x8.max_u", TokenType::Binary, Opcode::I16X8MaxU},
+ {""}, {""}, {""},
+#line 211 "src/lexer-keywords.txt"
+ {"i16x8.splat", TokenType::Unary, Opcode::I16X8Splat},
+ {""},
+#line 593 "src/lexer-keywords.txt"
+ {"anyfunc", Type::FuncRef},
+ {""}, {""}, {""}, {""}, {""},
+#line 104 "src/lexer-keywords.txt"
+ {"f32x4.replace_lane", TokenType::SimdLaneOp, Opcode::F32X4ReplaceLane},
+ {""},
+#line 617 "src/lexer-keywords.txt"
+ {"i32.wrap/i64", TokenType::Convert, Opcode::I32WrapI64},
+#line 327 "src/lexer-keywords.txt"
+ {"i32x4.replace_lane", TokenType::SimdLaneOp, Opcode::I32X4ReplaceLane},
+ {""}, {""},
+#line 385 "src/lexer-keywords.txt"
+ {"i64.atomic.store8", TokenType::AtomicStore, Opcode::I64AtomicStore8},
+#line 253 "src/lexer-keywords.txt"
+ {"i32.atomic.store8", TokenType::AtomicStore, Opcode::I32AtomicStore8},
+ {""},
+#line 145 "src/lexer-keywords.txt"
+ {"f64x2.eq", TokenType::Compare, Opcode::F64X2Eq},
+ {""}, {""},
+#line 445 "src/lexer-keywords.txt"
+ {"i64x2.eq", TokenType::Binary, Opcode::I64X2Eq},
+ {""}, {""}, {""},
+#line 472 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_s", TokenType::Binary, Opcode::I8X16AddSatS},
+ {""},
+#line 473 "src/lexer-keywords.txt"
+ {"i8x16.add_sat_u", TokenType::Binary, Opcode::I8X16AddSatU},
+ {""}, {""}, {""}, {""}, {""},
+#line 193 "src/lexer-keywords.txt"
+ {"v128.load8x8_s", TokenType::Load, Opcode::V128Load8X8S},
+ {""}, {""}, {""},
+#line 194 "src/lexer-keywords.txt"
+ {"v128.load8x8_u", TokenType::Load, Opcode::V128Load8X8U},
+ {""}, {""}, {""}, {""}, {""},
+#line 624 "src/lexer-keywords.txt"
+ {"i64.trunc_s:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64S},
+#line 612 "src/lexer-keywords.txt"
+ {"i32.trunc_s:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64S},
+#line 628 "src/lexer-keywords.txt"
+ {"i64.trunc_u:sat/f64", TokenType::Convert, Opcode::I64TruncSatF64U},
+#line 616 "src/lexer-keywords.txt"
+ {"i32.trunc_u:sat/f64", TokenType::Convert, Opcode::I32TruncSatF64U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 519 "src/lexer-keywords.txt"
+ {"memory.atomic.wait64", TokenType::AtomicWait, Opcode::MemoryAtomicWait64},
+ {""},
+#line 132 "src/lexer-keywords.txt"
+ {"f64.neg", TokenType::Unary, Opcode::F64Neg},
+#line 75 "src/lexer-keywords.txt"
+ {"f32.neg", TokenType::Unary, Opcode::F32Neg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 156 "src/lexer-keywords.txt"
+ {"f64x2.neg", TokenType::Unary, Opcode::F64X2Neg},
+ {""}, {""},
+#line 452 "src/lexer-keywords.txt"
+ {"i64x2.neg", TokenType::Unary, Opcode::I64X2Neg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 588 "src/lexer-keywords.txt"
+ {"i8x16.swizzle", TokenType::Binary, Opcode::I8X16Swizzle},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 364 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw32.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw32CmpxchgU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 333 "src/lexer-keywords.txt"
+ {"i32x4.extadd_pairwise_i16x8_s", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8S},
+#line 173 "src/lexer-keywords.txt"
+ {"global.get", TokenType::GlobalGet, Opcode::GlobalGet},
+#line 334 "src/lexer-keywords.txt"
+ {"i32x4.extadd_pairwise_i16x8_u", TokenType::Unary, Opcode::I32X4ExtaddPairwiseI16X8U},
+#line 475 "src/lexer-keywords.txt"
+ {"i8x16.all_true", TokenType::Unary, Opcode::I8X16AllTrue},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 549 "src/lexer-keywords.txt"
+ {"table.copy", TokenType::TableCopy, Opcode::TableCopy},
+ {""}, {""},
+#line 340 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_s", TokenType::Unary, Opcode::I32X4TruncSatF32X4S},
+ {""}, {""}, {""},
+#line 341 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f32x4_u", TokenType::Unary, Opcode::I32X4TruncSatF32X4U},
+ {""}, {""},
+#line 569 "src/lexer-keywords.txt"
+ {"v128.any_true", TokenType::Unary, Opcode::V128AnyTrue},
+ {""}, {""},
+#line 109 "src/lexer-keywords.txt"
+ {"f32x4.demote_f64x2_zero", TokenType::Unary, Opcode::F32X4DemoteF64X2Zero},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 189 "src/lexer-keywords.txt"
+ {"i16x8.gt_s", TokenType::Compare, Opcode::I16X8GtS},
+ {""},
+#line 190 "src/lexer-keywords.txt"
+ {"i16x8.gt_u", TokenType::Compare, Opcode::I16X8GtU},
+ {""}, {""}, {""},
+#line 187 "src/lexer-keywords.txt"
+ {"i16x8.ge_s", TokenType::Compare, Opcode::I16X8GeS},
+ {""},
+#line 188 "src/lexer-keywords.txt"
+ {"i16x8.ge_u", TokenType::Compare, Opcode::I16X8GeU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 489 "src/lexer-keywords.txt"
+ {"i8x16.max_s", TokenType::Binary, Opcode::I8X16MaxS},
+#line 281 "src/lexer-keywords.txt"
+ {"i32.reinterpret_f32", TokenType::Convert, Opcode::I32ReinterpretF32},
+ {""}, {""},
+#line 490 "src/lexer-keywords.txt"
+ {"i8x16.max_u", TokenType::Binary, Opcode::I8X16MaxU},
+ {""}, {""}, {""},
+#line 502 "src/lexer-keywords.txt"
+ {"i8x16.splat", TokenType::Unary, Opcode::I8X16Splat},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 382 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I64AtomicRmwXor},
+#line 251 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xor", TokenType::AtomicRmw, Opcode::I32AtomicRmwXor},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 185 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_s", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneS},
+ {""},
+#line 186 "src/lexer-keywords.txt"
+ {"i16x8.extract_lane_u", TokenType::SimdLaneOp, Opcode::I16X8ExtractLaneU},
+ {""},
+#line 357 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw16CmpxchgU},
+#line 233 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw16.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw16CmpxchgU},
+ {""}, {""}, {""},
+#line 375 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XorU},
+#line 244 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xor_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XorU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 371 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmw8CmpxchgU},
+#line 240 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.cmpxchg_u", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmw8CmpxchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 89 "src/lexer-keywords.txt"
+ {"f32x4.eq", TokenType::Compare, Opcode::F32X4Eq},
+ {""}, {""},
+#line 307 "src/lexer-keywords.txt"
+ {"i32x4.eq", TokenType::Compare, Opcode::I32X4Eq},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 606 "src/lexer-keywords.txt"
+ {"get_global", TokenType::GlobalGet, Opcode::GlobalGet},
+ {""}, {""},
+#line 346 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_s_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2SZero},
+ {""},
+#line 347 "src/lexer-keywords.txt"
+ {"i32x4.trunc_sat_f64x2_u_zero", TokenType::Unary, Opcode::I32X4TruncSatF64X2UZero},
+ {""}, {""}, {""}, {""}, {""},
+#line 418 "src/lexer-keywords.txt"
+ {"i64.reinterpret_f64", TokenType::Convert, Opcode::I64ReinterpretF64},
+ {""}, {""}, {""}, {""}, {""},
+#line 208 "src/lexer-keywords.txt"
+ {"i16x8.shl", TokenType::Binary, Opcode::I16X8Shl},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 608 "src/lexer-keywords.txt"
+ {"i32.reinterpret/f32", TokenType::Convert, Opcode::I32ReinterpretF32},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 100 "src/lexer-keywords.txt"
+ {"f32x4.neg", TokenType::Unary, Opcode::F32X4Neg},
+ {""}, {""},
+#line 325 "src/lexer-keywords.txt"
+ {"i32x4.neg", TokenType::Unary, Opcode::I32X4Neg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 559 "src/lexer-keywords.txt"
+ {"try", TokenType::Try, Opcode::Try},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 209 "src/lexer-keywords.txt"
+ {"i16x8.shr_s", TokenType::Binary, Opcode::I16X8ShrS},
+ {""}, {""}, {""},
+#line 210 "src/lexer-keywords.txt"
+ {"i16x8.shr_u", TokenType::Binary, Opcode::I16X8ShrU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 525 "src/lexer-keywords.txt"
+ {"memory", TokenType::Memory},
+ {""},
+#line 483 "src/lexer-keywords.txt"
+ {"i8x16.gt_s", TokenType::Compare, Opcode::I8X16GtS},
+ {""},
+#line 484 "src/lexer-keywords.txt"
+ {"i8x16.gt_u", TokenType::Compare, Opcode::I8X16GtU},
+ {""}, {""}, {""},
+#line 481 "src/lexer-keywords.txt"
+ {"i8x16.ge_s", TokenType::Compare, Opcode::I8X16GeS},
+#line 620 "src/lexer-keywords.txt"
+ {"i64.reinterpret/f64", TokenType::Convert, Opcode::I64ReinterpretF64},
+#line 482 "src/lexer-keywords.txt"
+ {"i8x16.ge_u", TokenType::Compare, Opcode::I8X16GeU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 28 "src/lexer-keywords.txt"
+ {"binary", TokenType::Bin},
+ {""}, {""},
+#line 182 "src/lexer-keywords.txt"
+ {"i16x8.avgr_u", TokenType::Binary, Opcode::I16X8AvgrU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 207 "src/lexer-keywords.txt"
+ {"i16x8.replace_lane", TokenType::SimdLaneOp, Opcode::I16X8ReplaceLane},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 479 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_s", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneS},
+ {""},
+#line 480 "src/lexer-keywords.txt"
+ {"i8x16.extract_lane_u", TokenType::SimdLaneOp, Opcode::I8X16ExtractLaneU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 499 "src/lexer-keywords.txt"
+ {"i8x16.shl", TokenType::Binary, Opcode::I8X16Shl},
+ {""}, {""}, {""}, {""}, {""},
+#line 587 "src/lexer-keywords.txt"
+ {"i8x16.shuffle", TokenType::SimdShuffleOp, Opcode::I8X16Shuffle},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 215 "src/lexer-keywords.txt"
+ {"i16x8.extadd_pairwise_i8x16_s", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16S},
+ {""},
+#line 216 "src/lexer-keywords.txt"
+ {"i16x8.extadd_pairwise_i8x16_u", TokenType::Unary, Opcode::I16X8ExtaddPairwiseI8X16U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 456 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_s", TokenType::Unary, Opcode::I64X2ExtendHighI32X4S},
+ {""},
+#line 458 "src/lexer-keywords.txt"
+ {"i64x2.extend_high_i32x4_u", TokenType::Unary, Opcode::I64X2ExtendHighI32X4U},
+ {""}, {""}, {""}, {""},
+#line 224 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendLowI8X16S},
+ {""},
+#line 225 "src/lexer-keywords.txt"
+ {"i16x8.extend_low_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendLowI8X16U},
+ {""}, {""}, {""},
+#line 466 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_s", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4S},
+ {""},
+#line 468 "src/lexer-keywords.txt"
+ {"i64x2.extmul_high_i32x4_u", TokenType::Binary, Opcode::I64X2ExtmulHighI32X4U},
+ {""}, {""}, {""}, {""},
+#line 217 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16S},
+ {""},
+#line 219 "src/lexer-keywords.txt"
+ {"i16x8.extmul_low_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulLowI8X16U},
+ {""}, {""}, {""}, {""}, {""},
+#line 496 "src/lexer-keywords.txt"
+ {"i8x16.popcnt", TokenType::Unary, Opcode::I8X16Popcnt},
+ {""},
+#line 500 "src/lexer-keywords.txt"
+ {"i8x16.shr_s", TokenType::Binary, Opcode::I8X16ShrS},
+ {""}, {""}, {""},
+#line 501 "src/lexer-keywords.txt"
+ {"i8x16.shr_u", TokenType::Binary, Opcode::I8X16ShrU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 378 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I64AtomicRmwCmpxchg},
+#line 247 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.cmpxchg", TokenType::AtomicRmwCmpxchg, Opcode::I32AtomicRmwCmpxchg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 522 "src/lexer-keywords.txt"
+ {"memory.grow", TokenType::MemoryGrow, Opcode::MemoryGrow},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 476 "src/lexer-keywords.txt"
+ {"i8x16.avgr_u", TokenType::Binary, Opcode::I8X16AvgrU},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 498 "src/lexer-keywords.txt"
+ {"i8x16.replace_lane", TokenType::SimdLaneOp, Opcode::I8X16ReplaceLane},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""},
+#line 119 "src/lexer-keywords.txt"
+ {"f64.copysign", TokenType::Binary, Opcode::F64Copysign},
+#line 61 "src/lexer-keywords.txt"
+ {"f32.copysign", TokenType::Binary, Opcode::F32Copysign},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 590 "src/lexer-keywords.txt"
+ {"atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
+#line 517 "src/lexer-keywords.txt"
+ {"memory.atomic.notify", TokenType::AtomicNotify, Opcode::MemoryAtomicNotify},
+#line 184 "src/lexer-keywords.txt"
+ {"i16x8.eq", TokenType::Compare, Opcode::I16X8Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 204 "src/lexer-keywords.txt"
+ {"i16x8.neg", TokenType::Unary, Opcode::I16X8Neg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 374 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I64AtomicRmw8XchgU},
+#line 243 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw8.xchg_u", TokenType::AtomicRmw, Opcode::I32AtomicRmw8XchgU},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 205 "src/lexer-keywords.txt"
+ {"i16x8.q15mulr_sat_s", TokenType::Binary, Opcode::I16X8Q15mulrSatS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 202 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_s", TokenType::Binary, Opcode::I16X8NarrowI32X4S},
+ {""},
+#line 203 "src/lexer-keywords.txt"
+ {"i16x8.narrow_i32x4_u", TokenType::Binary, Opcode::I16X8NarrowI32X4U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 38 "src/lexer-keywords.txt"
+ {"current_memory", TokenType::MemorySize, Opcode::MemorySize},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 478 "src/lexer-keywords.txt"
+ {"i8x16.eq", TokenType::Compare, Opcode::I8X16Eq},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""},
+#line 495 "src/lexer-keywords.txt"
+ {"i8x16.neg", TokenType::Unary, Opcode::I8X16Neg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 176 "src/lexer-keywords.txt"
+ {"grow_memory", TokenType::MemoryGrow, Opcode::MemoryGrow},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""},
+#line 342 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_s", TokenType::Unary, Opcode::I32X4ExtendHighI16X8S},
+ {""},
+#line 343 "src/lexer-keywords.txt"
+ {"i32x4.extend_high_i16x8_u", TokenType::Unary, Opcode::I32X4ExtendHighI16X8U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 336 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_s", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8S},
+ {""},
+#line 338 "src/lexer-keywords.txt"
+ {"i32x4.extmul_high_i16x8_u", TokenType::Binary, Opcode::I32X4ExtmulHighI16X8U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""},
+#line 520 "src/lexer-keywords.txt"
+ {"memory.copy", TokenType::MemoryCopy, Opcode::MemoryCopy},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 222 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_s", TokenType::Unary, Opcode::I16X8ExtendHighI8X16S},
+ {""},
+#line 223 "src/lexer-keywords.txt"
+ {"i16x8.extend_high_i8x16_u", TokenType::Unary, Opcode::I16X8ExtendHighI8X16U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 218 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_s", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16S},
+ {""},
+#line 220 "src/lexer-keywords.txt"
+ {"i16x8.extmul_high_i8x16_u", TokenType::Binary, Opcode::I16X8ExtmulHighI8X16U},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""},
+#line 381 "src/lexer-keywords.txt"
+ {"i64.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I64AtomicRmwXchg},
+#line 250 "src/lexer-keywords.txt"
+ {"i32.atomic.rmw.xchg", TokenType::AtomicRmw, Opcode::I32AtomicRmwXchg},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+#line 493 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_s", TokenType::Binary, Opcode::I8X16NarrowI16X8S},
+ {""},
+#line 494 "src/lexer-keywords.txt"
+ {"i8x16.narrow_i16x8_u", TokenType::Binary, Opcode::I8X16NarrowI16X8U}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ unsigned int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ const char *s = wordlist[key].name;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c.include.c b/third_party/wasm2c/src/prebuilt/wasm2c.include.c
new file mode 100644
index 0000000000..de62978629
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c.include.c
@@ -0,0 +1,439 @@
+/* Generated from 'wasm2c.c.tmpl' by wasm2c_tmpl.py, do not edit! */
+const char SECTION_NAME(includes)[] =
+"/* Automically generated by wasm2c */\n"
+"#include <math.h>\n"
+"#include <string.h>\n"
+"#include <stdlib.h>\n"
+;
+
+const char SECTION_NAME(declarations)[] =
+"#if defined(_MSC_VER)\n"
+"# define UNLIKELY(x) (x)\n"
+"# define LIKELY(x) (x)\n"
+"#else\n"
+"# define UNLIKELY(x) __builtin_expect(!!(x), 0)\n"
+"# define LIKELY(x) __builtin_expect(!!(x), 1)\n"
+"#endif\n"
+"\n"
+"#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)\n"
+"\n"
+"#ifndef FUNC_PROLOGUE\n"
+"#define FUNC_PROLOGUE\n"
+"#endif\n"
+"\n"
+"#ifndef FUNC_EPILOGUE\n"
+"#define FUNC_EPILOGUE\n"
+"#endif\n"
+"\n"
+"#ifdef EXTERNAL_CALLBACK_PROLOGUE\n"
+"#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x) \\\n"
+" if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \\\n"
+" EXTERNAL_CALLBACK_PROLOGUE; \\\n"
+" }\n"
+"#else\n"
+"#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x)\n"
+"#endif\n"
+"\n"
+"#ifdef EXTERNAL_CALLBACK_EPILOGUE\n"
+"#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x) \\\n"
+" if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \\\n"
+" EXTERNAL_CALLBACK_EPILOGUE; \\\n"
+" }\n"
+"#else\n"
+"#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x)\n"
+"#endif\n"
+"\n"
+"#define UNREACHABLE (void) TRAP(UNREACHABLE)\n"
+"\n"
+"#define CALL_INDIRECT_VOID(table, t, ft, x, func_types, ...) \\\n"
+" if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \\\n"
+" EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \\\n"
+" ((t)table.data[x].func)(__VA_ARGS__); \\\n"
+" EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \\\n"
+" } else { \\\n"
+" wasm_rt_callback_error_trap(&table, x, func_types[ft]); \\\n"
+" }\n"
+"\n"
+"#define CALL_INDIRECT_RES(res, table, t, ft, x, func_types, ...) \\\n"
+" if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \\\n"
+" EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \\\n"
+" res = ((t)table.data[x].func)(__VA_ARGS__); \\\n"
+" EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \\\n"
+" } else { \\\n"
+" wasm_rt_callback_error_trap(&table, x, func_types[ft]); \\\n"
+" }\n"
+"\n"
+"#if defined(WASM2C_MALLOC_FAIL_CALLBACK)\n"
+"void WASM2C_MALLOC_FAIL_CALLBACK(u32 ptr_size);\n"
+"# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size) \\\n"
+" if (!ptr) { \\\n"
+" WASM2C_MALLOC_FAIL_CALLBACK(ptr_size); \\\n"
+" }\n"
+"#else\n"
+"# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size)\n"
+"#endif\n"
+"\n"
+"#if defined(WASM_CHECK_SHADOW_MEMORY)\n"
+"# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_load(mem, func_name, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_store(mem, func_name, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size) wasm2c_shadow_memory_reserve(mem, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size) wasm2c_shadow_memory_dlmalloc(mem, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr) wasm2c_shadow_memory_dlfree(mem, ptr)\n"
+"# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr) wasm2c_shadow_memory_mark_globals_heap_boundary(mem, ptr)\n"
+"#else\n"
+"# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size)\n"
+"# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr)\n"
+"# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr)\n"
+"#endif\n"
+"\n"
+"#ifdef WASM_USE_GUARD_PAGES\n"
+"# define MEMCHECK(mem, a, t)\n"
+"#else\n"
+"# define MEMCHECK(mem, a, t) if (UNLIKELY((a) + sizeof(t) > mem->size)) { (void) TRAP(OOB); }\n"
+"#endif\n"
+"\n"
+"#if defined(WASM_USE_GUARD_PAGES) && UINTPTR_MAX == 0xffffffff\n"
+"// on 32-bit platforms we have to mask memory access into range\n"
+"# define MEM_ACCESS_REF(mem, addr) &mem->data[addr & mem->mem_mask]\n"
+"#else\n"
+"# define MEM_ACCESS_REF(mem, addr) &mem->data[addr]\n"
+"#endif\n"
+"\n"
+"#if defined(WASM_USING_GLOBAL_HEAP)\n"
+"# undef MEM_ACCESS_REF\n"
+"# define MEM_ACCESS_REF(mem, addr) (char*) addr\n"
+"#endif\n"
+"\n"
+"#ifdef __GNUC__\n"
+"#define wasm_asm __asm__\n"
+"#else\n"
+"#define wasm_asm(X)\n"
+"#endif\n"
+"\n"
+"#if WABT_BIG_ENDIAN\n"
+"static inline void load_data(void *dest, const void *src, size_t n) {\n"
+" size_t i = 0;\n"
+" u8 *dest_chars = dest;\n"
+" memcpy(dest, src, n);\n"
+" for (i = 0; i < (n>>1); i++) {\n"
+" u8 cursor = dest_chars[i];\n"
+" dest_chars[i] = dest_chars[n - i - 1];\n"
+" dest_chars[n - i - 1] = cursor;\n"
+" }\n"
+"}\n"
+"#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[m.size - o - s]), i, s); \\\n"
+" WASM2C_SHADOW_MEMORY_RESERVE(&m, m.size - o - s, s); \\\n"
+" WASM2C_SHADOW_MEMORY_STORE(&m, \"GlobalDataLoad\", m.size - o - s, s); \\\n"
+"}\n"
+"\n"
+"#define DEFINE_LOAD(name, t1, t2, t3) \\\n"
+" static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \\\n"
+" MEMCHECK(mem, addr, t1); \\\n"
+" t1 result; \\\n"
+" memcpy(&result, MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1)), sizeof(t1)); \\\n"
+" WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, mem->size - addr - sizeof(t1), sizeof(t1)); \\\n"
+" wasm_asm(\"\" ::\"r\"(result)); \\\n"
+" return (t3)(t2)result; \\\n"
+" }\n"
+"\n"
+"#define DEFINE_STORE(name, t1, t2) \\\n"
+" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \\\n"
+" MEMCHECK(mem, addr, t1); \\\n"
+" t1 wrapped = (t1)value; \\\n"
+" memcpy(MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1), &wrapped, sizeof(t1)); \\\n"
+" WASM2C_SHADOW_MEMORY_STORE(mem, func_name, mem->size - addr - sizeof(t1)), sizeof(t1)); \\\n"
+" }\n"
+"#else\n"
+"static inline void load_data(void *dest, const void *src, size_t n) {\n"
+" memcpy(dest, src, n);\n"
+"}\n"
+"#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[o]), i, s); \\\n"
+" WASM2C_SHADOW_MEMORY_RESERVE(&m, o, s); \\\n"
+" WASM2C_SHADOW_MEMORY_STORE(&m, \"GlobalDataLoad\", o, s); \\\n"
+"}\n"
+"\n"
+"#define DEFINE_LOAD(name, t1, t2, t3) \\\n"
+" static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \\\n"
+" MEMCHECK(mem, addr, t1); \\\n"
+" t1 result; \\\n"
+" memcpy(&result, MEM_ACCESS_REF(mem, addr), sizeof(t1)); \\\n"
+" WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, addr, sizeof(t1)); \\\n"
+" wasm_asm(\"\" ::\"r\"(result)); \\\n"
+" return (t3)(t2)result; \\\n"
+" }\n"
+"\n"
+"#define DEFINE_STORE(name, t1, t2) \\\n"
+" static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \\\n"
+" MEMCHECK(mem, addr, t1); \\\n"
+" t1 wrapped = (t1)value; \\\n"
+" memcpy(MEM_ACCESS_REF(mem, addr), &wrapped, sizeof(t1)); \\\n"
+" WASM2C_SHADOW_MEMORY_STORE(mem, func_name, addr, sizeof(t1)); \\\n"
+" }\n"
+"#endif\n"
+"\n"
+"DEFINE_LOAD(i32_load, u32, u32, u32);\n"
+"DEFINE_LOAD(i64_load, u64, u64, u64);\n"
+"DEFINE_LOAD(f32_load, f32, f32, f32);\n"
+"DEFINE_LOAD(f64_load, f64, f64, f64);\n"
+"DEFINE_LOAD(i32_load8_s, s8, s32, u32);\n"
+"DEFINE_LOAD(i64_load8_s, s8, s64, u64);\n"
+"DEFINE_LOAD(i32_load8_u, u8, u32, u32);\n"
+"DEFINE_LOAD(i64_load8_u, u8, u64, u64);\n"
+"DEFINE_LOAD(i32_load16_s, s16, s32, u32);\n"
+"DEFINE_LOAD(i64_load16_s, s16, s64, u64);\n"
+"DEFINE_LOAD(i32_load16_u, u16, u32, u32);\n"
+"DEFINE_LOAD(i64_load16_u, u16, u64, u64);\n"
+"DEFINE_LOAD(i64_load32_s, s32, s64, u64);\n"
+"DEFINE_LOAD(i64_load32_u, u32, u64, u64);\n"
+"DEFINE_STORE(i32_store, u32, u32);\n"
+"DEFINE_STORE(i64_store, u64, u64);\n"
+"DEFINE_STORE(f32_store, f32, f32);\n"
+"DEFINE_STORE(f64_store, f64, f64);\n"
+"DEFINE_STORE(i32_store8, u8, u32);\n"
+"DEFINE_STORE(i32_store16, u16, u32);\n"
+"DEFINE_STORE(i64_store8, u8, u64);\n"
+"DEFINE_STORE(i64_store16, u16, u64);\n"
+"DEFINE_STORE(i64_store32, u32, u64);\n"
+"\n"
+"#if defined(_MSC_VER)\n"
+"#include <intrin.h>\n"
+"\n"
+"// Adapted from https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h\n"
+"\n"
+"static inline int I64_CLZ(unsigned long long v) {\n"
+" unsigned long r = 0;\n"
+"#if defined(_M_AMD64) || defined(_M_ARM)\n"
+" if (_BitScanReverse64(&r, v)) {\n"
+" return 63 - r;\n"
+" }\n"
+"#else\n"
+" if (_BitScanReverse(&r, (unsigned long) (v >> 32))) {\n"
+" return 31 - r;\n"
+" } else if (_BitScanReverse(&r, (unsigned long) v)) {\n"
+" return 63 - r;\n"
+" }\n"
+"#endif\n"
+" return 64;\n"
+"}\n"
+"\n"
+"static inline int I32_CLZ(unsigned long v) {\n"
+" unsigned long r = 0;\n"
+" if (_BitScanReverse(&r, v)) {\n"
+" return 31 - r;\n"
+" }\n"
+" return 32;\n"
+"}\n"
+"\n"
+"static inline int I64_CTZ(unsigned long long v) {\n"
+" if (!v) {\n"
+" return 64;\n"
+" }\n"
+" unsigned long r = 0;\n"
+"#if defined(_M_AMD64) || defined(_M_ARM)\n"
+" _BitScanForward64(&r, v);\n"
+" return (int) r;\n"
+"#else\n"
+" if (_BitScanForward(&r, (unsigned int) (v))) {\n"
+" return (int) (r);\n"
+" }\n"
+"\n"
+" _BitScanForward(&r, (unsigned int) (v >> 32));\n"
+" return (int) (r + 32);\n"
+"#endif\n"
+"}\n"
+"\n"
+"static inline int I32_CTZ(unsigned long v) {\n"
+" if (!v) {\n"
+" return 32;\n"
+" }\n"
+" unsigned long r = 0;\n"
+" _BitScanForward(&r, v);\n"
+" return (int) r;\n"
+"}\n"
+"\n"
+"#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \\\n"
+" static inline u32 f_n(T x) { \\\n"
+" x = x - ((x >> 1) & (T)~(T)0/3); \\\n"
+" x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \\\n"
+" x = (x + (x >> 4)) & (T)~(T)0/255*15; \\\n"
+" return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \\\n"
+" }\n"
+"\n"
+"POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32)\n"
+"POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64)\n"
+"\n"
+"#undef POPCOUNT_DEFINE_PORTABLE\n"
+"\n"
+"#else\n"
+"# define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)\n"
+"# define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)\n"
+"# define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)\n"
+"# define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)\n"
+"# define I32_POPCNT(x) (__builtin_popcount(x))\n"
+"# define I64_POPCNT(x) (__builtin_popcountll(x))\n"
+"#endif\n"
+"\n"
+"#define DIV_S(ut, min, x, y) \\\n"
+" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n"
+" : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \\\n"
+" : (ut)((x) / (y)))\n"
+"\n"
+"#define REM_S(ut, min, x, y) \\\n"
+" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \\\n"
+" : (UNLIKELY((x) == min && (y) == -1)) ? 0 \\\n"
+" : (ut)((x) % (y)))\n"
+"\n"
+"#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)\n"
+"#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)\n"
+"#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)\n"
+"#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)\n"
+"\n"
+"#define DIVREM_U(op, x, y) \\\n"
+" ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))\n"
+"\n"
+"#define DIV_U(x, y) DIVREM_U(/, x, y)\n"
+"#define REM_U(x, y) DIVREM_U(%, x, y)\n"
+"\n"
+"#define ROTL(x, y, mask) \\\n"
+" (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))\n"
+"#define ROTR(x, y, mask) \\\n"
+" (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))\n"
+"\n"
+"#define I32_ROTL(x, y) ROTL(x, y, 31)\n"
+"#define I64_ROTL(x, y) ROTL(x, y, 63)\n"
+"#define I32_ROTR(x, y) ROTR(x, y, 31)\n"
+"#define I64_ROTR(x, y) ROTR(x, y, 63)\n"
+"\n"
+"#define FMIN(x, y) \\\n"
+" ((UNLIKELY((x) != (x))) ? NAN \\\n"
+" : (UNLIKELY((y) != (y))) ? NAN \\\n"
+" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \\\n"
+" : (x < y) ? x : y)\n"
+"\n"
+"#define FMAX(x, y) \\\n"
+" ((UNLIKELY((x) != (x))) ? NAN \\\n"
+" : (UNLIKELY((y) != (y))) ? NAN \\\n"
+" : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \\\n"
+" : (x > y) ? x : y)\n"
+"\n"
+"#define TRUNC_S(ut, st, ft, min, minop, max, x) \\\n"
+" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n"
+" : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \\\n"
+" : (ut)(st)(x))\n"
+"\n"
+"#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x)\n"
+"#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x)\n"
+"#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x)\n"
+"#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x)\n"
+"\n"
+"#define TRUNC_U(ut, ft, max, x) \\\n"
+" ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \\\n"
+" : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \\\n"
+" : (ut)(x))\n"
+"\n"
+"#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x)\n"
+"#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x)\n"
+"#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x)\n"
+"#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x)\n"
+"\n"
+"#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \\\n"
+" ((UNLIKELY((x) != (x))) ? 0 \\\n"
+" : (UNLIKELY(!((x)minop(min)))) ? smin \\\n"
+" : (UNLIKELY(!((x) < (max)))) ? smax \\\n"
+" : (ut)(st)(x))\n"
+"\n"
+"#define I32_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, INT32_MAX, x)\n"
+"#define I64_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, INT64_MAX, x)\n"
+"#define I32_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., INT32_MAX, x)\n"
+"#define I64_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, INT64_MAX, x)\n"
+"\n"
+"#define TRUNC_SAT_U(ut, ft, max, smax, x) \\\n"
+" ((UNLIKELY((x) != (x))) ? 0 \\\n"
+" : (UNLIKELY(!((x) > (ft)-1))) ? 0 \\\n"
+" : (UNLIKELY(!((x) < (max)))) ? smax \\\n"
+" : (ut)(x))\n"
+"\n"
+"#define I32_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x)\n"
+"#define I64_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x)\n"
+"#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x)\n"
+"#define I64_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x)\n"
+"\n"
+"#define DEFINE_REINTERPRET(name, t1, t2) \\\n"
+" static inline t2 name(t1 x) { \\\n"
+" t2 result; \\\n"
+" memcpy(&result, &x, sizeof(result)); \\\n"
+" return result; \\\n"
+" }\n"
+"\n"
+"DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)\n"
+"DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)\n"
+"DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)\n"
+"DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)\n"
+;
+
+const char SECTION_NAME(sandboxapis)[] =
+"//test\n"
+"\n"
+"static u32 add_wasm2c_callback(void* sbx_ptr, u32 func_type_idx, void* func_ptr, wasm_rt_elem_target_class_t func_class) {\n"
+" wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);\n"
+" for (u32 i = 1; i < table->max_size; i++) {\n"
+" if (i >= table->size) {\n"
+" wasm_rt_expand_table(table);\n"
+" }\n"
+" if (table->data[i].func == 0) {\n"
+" table->data[i] = (wasm_rt_elem_t){ func_class, func_type_idx, (wasm_rt_anyfunc_t) func_ptr };\n"
+" return i;\n"
+" }\n"
+" }\n"
+" (void) TRAP(CALL_INDIRECT_TABLE_EXPANSION);\n"
+"}\n"
+"\n"
+"static void remove_wasm2c_callback(void* sbx_ptr, u32 callback_idx) {\n"
+" wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);\n"
+" table->data[callback_idx].func = 0;\n"
+"}\n"
+"\n"
+"static u32 lookup_wasm2c_func_index(void* sbx_ptr, u32 param_count, u32 result_count, wasm_rt_type_t* types) {\n"
+" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;\n"
+" return wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, param_count, result_count, types);\n"
+"}\n"
+"\n"
+"static void* create_wasm2c_sandbox(uint32_t max_wasm_pages) {\n"
+" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) calloc(sizeof(wasm2c_sandbox_t), 1);\n"
+" if (!init_memory(sbx, max_wasm_pages)) {\n"
+" free(sbx);\n"
+" return 0;\n"
+" }\n"
+" init_func_types(sbx);\n"
+" init_globals(sbx);\n"
+" init_table(sbx);\n"
+" wasm_rt_init_wasi(&(sbx->wasi_data));\n"
+" init_module_starts();\n"
+" // w2c___wasm_call_ctors(sbx);\n"
+" return sbx;\n"
+"}\n"
+"\n"
+"static void destroy_wasm2c_sandbox(void* aSbx) {\n"
+" wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) aSbx;\n"
+" cleanup_memory(sbx);\n"
+" cleanup_func_types(sbx);\n"
+" cleanup_table(sbx);\n"
+" wasm_rt_cleanup_wasi(&(sbx->wasi_data));\n"
+" free(sbx);\n"
+"}\n"
+"\n"
+"FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)() {\n"
+" wasm2c_sandbox_funcs_t ret;\n"
+" ret.wasm_rt_sys_init = &wasm_rt_sys_init;\n"
+" ret.create_wasm2c_sandbox = &create_wasm2c_sandbox;\n"
+" ret.destroy_wasm2c_sandbox = &destroy_wasm2c_sandbox;\n"
+" ret.lookup_wasm2c_nonfunc_export = &lookup_wasm2c_nonfunc_export;\n"
+" ret.lookup_wasm2c_func_index = &lookup_wasm2c_func_index;\n"
+" ret.add_wasm2c_callback = &add_wasm2c_callback;\n"
+" ret.remove_wasm2c_callback = &remove_wasm2c_callback;\n"
+" return ret;\n"
+"}\n"
+;
diff --git a/third_party/wasm2c/src/prebuilt/wasm2c.include.h b/third_party/wasm2c/src/prebuilt/wasm2c.include.h
new file mode 100644
index 0000000000..fcd2ff4b55
--- /dev/null
+++ b/third_party/wasm2c/src/prebuilt/wasm2c.include.h
@@ -0,0 +1,55 @@
+/* Generated from 'wasm2c.h.tmpl' by wasm2c_tmpl.py, do not edit! */
+const char SECTION_NAME(top)[] =
+"/* Automically generated by wasm2c */\n"
+"#ifdef __cplusplus\n"
+"extern \"C\" {\n"
+"#endif\n"
+"\n"
+"#include <stdint.h>\n"
+"\n"
+"#include \"wasm-rt.h\"\n"
+"\n"
+"#ifndef WASM_RT_MODULE_PREFIX\n"
+"#define WASM_RT_MODULE_PREFIX\n"
+"#endif\n"
+"\n"
+"#define WASM_RT_PASTE_(x, y) x ## y\n"
+"#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)\n"
+"#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)\n"
+"\n"
+"#define WASM_CURR_ADD_PREFIX(x) WASM_RT_PASTE(WASM_CURR_MODULE_PREFIX, x)\n"
+"\n"
+"/* TODO(binji): only use stdint.h types in header */\n"
+"typedef uint8_t u8;\n"
+"typedef int8_t s8;\n"
+"typedef uint16_t u16;\n"
+"typedef int16_t s16;\n"
+"typedef uint32_t u32;\n"
+"typedef int32_t s32;\n"
+"typedef uint64_t u64;\n"
+"typedef int64_t s64;\n"
+"typedef float f32;\n"
+"typedef double f64;\n"
+"\n"
+"#ifndef WASM_DONT_EXPORT_FUNCS\n"
+"# if defined(_WIN32)\n"
+"# define FUNC_EXPORT __declspec(dllexport)\n"
+"# else\n"
+"# define FUNC_EXPORT\n"
+"# endif\n"
+"#else\n"
+"# define FUNC_EXPORT\n"
+"#endif\n"
+"\n"
+"FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)();\n"
+"\n"
+"struct wasm2c_sandbox_t;\n"
+"typedef struct wasm2c_sandbox_t wasm2c_sandbox_t;\n"
+;
+
+const char SECTION_NAME(bottom)[] =
+"\n"
+"#ifdef __cplusplus\n"
+"}\n"
+"#endif\n"
+;
diff --git a/third_party/wasm2c/src/range.h b/third_party/wasm2c/src/range.h
new file mode 100644
index 0000000000..fa0310ee9a
--- /dev/null
+++ b/third_party/wasm2c/src/range.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_RANGE_H_
+#define WABT_RANGE_H_
+
+namespace wabt {
+
+template <typename T>
+struct Range {
+ Range() : start(0), end(0) {}
+ Range(T start, T end) : start(start), end(end) {}
+ T start;
+ T end;
+
+ T size() const { return end - start; }
+};
+
+typedef Range<Offset> OffsetRange;
+typedef Range<int> ColumnRange;
+
+} // namespace wabt
+
+#endif // WABT_RANGE_H_
diff --git a/third_party/wasm2c/src/resolve-names.cc b/third_party/wasm2c/src/resolve-names.cc
new file mode 100644
index 0000000000..099a8e5754
--- /dev/null
+++ b/third_party/wasm2c/src/resolve-names.cc
@@ -0,0 +1,595 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/resolve-names.h"
+
+#include <cassert>
+#include <cstdio>
+
+#include "src/cast.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/wast-lexer.h"
+
+namespace wabt {
+
+namespace {
+
+class NameResolver : public ExprVisitor::DelegateNop {
+ public:
+ NameResolver(Script* script, Errors* errors);
+
+ Result VisitModule(Module* module);
+ Result VisitScript(Script* script);
+
+ // 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 OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) 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 OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) 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 OnRefFuncExpr(RefFuncExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+
+ private:
+ void PrintError(const Location* loc, const char* fmt, ...);
+ void PushLabel(const std::string& label);
+ void PopLabel();
+ void CheckDuplicateBindings(const BindingHash* bindings, const char* desc);
+ void PrintDuplicateBindingsError(const BindingHash::value_type&,
+ const BindingHash::value_type&,
+ const char* desc);
+ void ResolveLabelVar(Var* var);
+ void ResolveVar(const BindingHash* bindings, Var* var, const char* desc);
+ void ResolveFuncVar(Var* var);
+ void ResolveGlobalVar(Var* var);
+ void ResolveFuncTypeVar(Var* var);
+ void ResolveTableVar(Var* var);
+ void ResolveMemoryVar(Var* var);
+ void ResolveTagVar(Var* var);
+ void ResolveDataSegmentVar(Var* var);
+ void ResolveElemSegmentVar(Var* var);
+ void ResolveLocalVar(Var* var);
+ void ResolveBlockDeclarationVar(BlockDeclaration* decl);
+ void VisitFunc(Func* func);
+ void VisitExport(Export* export_);
+ void VisitGlobal(Global* global);
+ void VisitTag(Tag* tag);
+ void VisitElemSegment(ElemSegment* segment);
+ void VisitDataSegment(DataSegment* segment);
+ void VisitScriptModule(ScriptModule* script_module);
+ void VisitCommand(Command* command);
+
+ Errors* errors_ = nullptr;
+ Script* script_ = nullptr;
+ Module* current_module_ = nullptr;
+ Func* current_func_ = nullptr;
+ ExprVisitor visitor_;
+ std::vector<std::string> labels_;
+ Result result_ = Result::Ok;
+};
+
+NameResolver::NameResolver(Script* script, Errors* errors)
+ : errors_(errors),
+ script_(script),
+ visitor_(this) {}
+
+} // end anonymous namespace
+
+void WABT_PRINTF_FORMAT(3, 4) NameResolver::PrintError(const Location* loc,
+ const char* format,
+ ...) {
+ result_ = Result::Error;
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, *loc, buffer);
+}
+
+void NameResolver::PushLabel(const std::string& label) {
+ labels_.push_back(label);
+}
+
+void NameResolver::PopLabel() {
+ labels_.pop_back();
+}
+
+void NameResolver::CheckDuplicateBindings(const BindingHash* bindings,
+ const char* desc) {
+ bindings->FindDuplicates([this, desc](const BindingHash::value_type& a,
+ const BindingHash::value_type& b) {
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+}
+
+void NameResolver::PrintDuplicateBindingsError(const BindingHash::value_type& a,
+ const BindingHash::value_type& b,
+ const char* desc) {
+ // Choose the location that is later in the file.
+ const Location& a_loc = a.second.loc;
+ const Location& b_loc = b.second.loc;
+ const Location& loc = a_loc.line > b_loc.line ? a_loc : b_loc;
+ PrintError(&loc, "redefinition of %s \"%s\"", desc, a.first.c_str());
+}
+
+void NameResolver::ResolveLabelVar(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()) {
+ var->set_index(labels_.size() - i - 1);
+ return;
+ }
+ }
+ PrintError(&var->loc, "undefined label variable \"%s\"",
+ var->name().c_str());
+ }
+}
+
+void NameResolver::ResolveVar(const BindingHash* bindings,
+ Var* var,
+ const char* desc) {
+ if (var->is_name()) {
+ Index index = bindings->FindIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined %s variable \"%s\"", desc,
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveFuncVar(Var* var) {
+ ResolveVar(&current_module_->func_bindings, var, "function");
+}
+
+void NameResolver::ResolveGlobalVar(Var* var) {
+ ResolveVar(&current_module_->global_bindings, var, "global");
+}
+
+void NameResolver::ResolveFuncTypeVar(Var* var) {
+ ResolveVar(&current_module_->type_bindings, var, "type");
+}
+
+void NameResolver::ResolveTableVar(Var* var) {
+ ResolveVar(&current_module_->table_bindings, var, "table");
+}
+
+void NameResolver::ResolveMemoryVar(Var* var) {
+ ResolveVar(&current_module_->memory_bindings, var, "memory");
+}
+
+void NameResolver::ResolveTagVar(Var* var) {
+ ResolveVar(&current_module_->tag_bindings, var, "tag");
+}
+
+void NameResolver::ResolveDataSegmentVar(Var* var) {
+ ResolveVar(&current_module_->data_segment_bindings, var, "data segment");
+}
+
+void NameResolver::ResolveElemSegmentVar(Var* var) {
+ ResolveVar(&current_module_->elem_segment_bindings, var, "elem segment");
+}
+
+void NameResolver::ResolveLocalVar(Var* var) {
+ if (var->is_name()) {
+ if (!current_func_) {
+ return;
+ }
+
+ Index index = current_func_->GetLocalIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined local variable \"%s\"",
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveBlockDeclarationVar(BlockDeclaration* decl) {
+ if (decl->has_func_type) {
+ ResolveFuncTypeVar(&decl->type_var);
+ }
+}
+
+Result NameResolver::BeginBlockExpr(BlockExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndBlockExpr(BlockExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::BeginLoopExpr(LoopExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndLoopExpr(LoopExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrExpr(BrExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrIfExpr(BrIfExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrTableExpr(BrTableExpr* expr) {
+ for (Var& target : expr->targets)
+ ResolveLabelVar(&target);
+ ResolveLabelVar(&expr->default_target);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallExpr(CallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallExpr(ReturnCallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginIfExpr(IfExpr* expr) {
+ PushLabel(expr->true_.label);
+ ResolveBlockDeclarationVar(&expr->true_.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndIfExpr(IfExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalGetExpr(LocalGetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalSetExpr(LocalSetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnDataDropExpr(DataDropExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnElemDropExpr(ElemDropExpr* expr) {
+ ResolveElemSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableCopyExpr(TableCopyExpr* expr) {
+ ResolveTableVar(&expr->dst_table);
+ ResolveTableVar(&expr->src_table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableInitExpr(TableInitExpr* expr) {
+ ResolveElemSegmentVar(&expr->segment_index);
+ ResolveTableVar(&expr->table_index);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGetExpr(TableGetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSetExpr(TableSetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGrowExpr(TableGrowExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSizeExpr(TableSizeExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableFillExpr(TableFillExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRefFuncExpr(RefFuncExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginTryExpr(TryExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndTryExpr(TryExpr*) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnCatchExpr(TryExpr*, Catch* catch_) {
+ if (!catch_->IsCatchAll()) {
+ ResolveTagVar(&catch_->var);
+ }
+ return Result::Ok;
+}
+
+Result NameResolver::OnDelegateExpr(TryExpr* expr) {
+ // Pop the label here as a try-delegate has no `end` instruction.
+ PopLabel();
+
+ // We resolve *after* popping the label in order to ensure that the
+ // delegate label starts counting after the current try-delegate.
+ ResolveLabelVar(&expr->delegate_target);
+
+ return Result::Ok;
+}
+
+Result NameResolver::OnThrowExpr(ThrowExpr* expr) {
+ ResolveTagVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRethrowExpr(RethrowExpr* expr) {
+ // Note: the variable refers to corresponding (enclosing) catch, using the try
+ // block label for context.
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+void NameResolver::VisitFunc(Func* func) {
+ current_func_ = func;
+ if (func->decl.has_func_type) {
+ ResolveFuncTypeVar(&func->decl.type_var);
+ }
+
+ func->bindings.FindDuplicates(
+ [=](const BindingHash::value_type& a, const BindingHash::value_type& b) {
+ const char* desc =
+ (a.second.index < func->GetNumParams()) ? "parameter" : "local";
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+
+ visitor_.VisitFunc(func);
+ current_func_ = nullptr;
+}
+
+void NameResolver::VisitExport(Export* export_) {
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ ResolveFuncVar(&export_->var);
+ break;
+
+ case ExternalKind::Table:
+ ResolveTableVar(&export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ ResolveMemoryVar(&export_->var);
+ break;
+
+ case ExternalKind::Global:
+ ResolveGlobalVar(&export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ ResolveTagVar(&export_->var);
+ break;
+ }
+}
+
+void NameResolver::VisitGlobal(Global* global) {
+ visitor_.VisitExprList(global->init_expr);
+}
+
+void NameResolver::VisitTag(Tag* tag) {
+ if (tag->decl.has_func_type) {
+ ResolveFuncTypeVar(&tag->decl.type_var);
+ }
+}
+
+void NameResolver::VisitElemSegment(ElemSegment* segment) {
+ ResolveTableVar(&segment->table_var);
+ visitor_.VisitExprList(segment->offset);
+ for (ElemExpr& elem_expr : segment->elem_exprs) {
+ if (elem_expr.kind == ElemExprKind::RefFunc) {
+ ResolveFuncVar(&elem_expr.var);
+ }
+ }
+}
+
+void NameResolver::VisitDataSegment(DataSegment* segment) {
+ ResolveMemoryVar(&segment->memory_var);
+ visitor_.VisitExprList(segment->offset);
+}
+
+Result NameResolver::VisitModule(Module* module) {
+ current_module_ = module;
+ CheckDuplicateBindings(&module->elem_segment_bindings, "elem");
+ CheckDuplicateBindings(&module->func_bindings, "function");
+ CheckDuplicateBindings(&module->global_bindings, "global");
+ CheckDuplicateBindings(&module->type_bindings, "type");
+ CheckDuplicateBindings(&module->table_bindings, "table");
+ CheckDuplicateBindings(&module->memory_bindings, "memory");
+ CheckDuplicateBindings(&module->tag_bindings, "tag");
+
+ for (Func* func : module->funcs)
+ VisitFunc(func);
+ for (Export* export_ : module->exports)
+ VisitExport(export_);
+ for (Global* global : module->globals)
+ VisitGlobal(global);
+ for (Tag* tag : module->tags)
+ VisitTag(tag);
+ for (ElemSegment* elem_segment : module->elem_segments)
+ VisitElemSegment(elem_segment);
+ for (DataSegment* data_segment : module->data_segments)
+ VisitDataSegment(data_segment);
+ for (Var* start : module->starts)
+ ResolveFuncVar(start);
+ current_module_ = nullptr;
+ return result_;
+}
+
+void NameResolver::VisitScriptModule(ScriptModule* script_module) {
+ if (auto* tsm = dyn_cast<TextScriptModule>(script_module)) {
+ VisitModule(&tsm->module);
+ }
+}
+
+void NameResolver::VisitCommand(Command* command) {
+ switch (command->type) {
+ case CommandType::Module:
+ VisitModule(&cast<ModuleCommand>(command)->module);
+ break;
+
+ case CommandType::Action:
+ case CommandType::AssertReturn:
+ case CommandType::AssertTrap:
+ case CommandType::AssertExhaustion:
+ case CommandType::Register:
+ /* Don't resolve a module_var, since it doesn't really behave like other
+ * vars. You can't reference a module by index. */
+ break;
+
+ case CommandType::AssertMalformed:
+ /* Malformed modules should not be text; the whole point of this
+ * assertion is to test for malformed binary modules. */
+ break;
+
+ case CommandType::AssertInvalid: {
+ auto* assert_invalid_command = cast<AssertInvalidCommand>(command);
+ /* The module may be invalid because the names cannot be resolved; we
+ * don't want to print errors or fail if that's the case, but we still
+ * should try to resolve names when possible. */
+ Errors errors;
+ NameResolver new_resolver(script_, &errors);
+ new_resolver.VisitScriptModule(assert_invalid_command->module.get());
+ break;
+ }
+
+ case CommandType::AssertUnlinkable:
+ VisitScriptModule(cast<AssertUnlinkableCommand>(command)->module.get());
+ break;
+
+ case CommandType::AssertUninstantiable:
+ VisitScriptModule(
+ cast<AssertUninstantiableCommand>(command)->module.get());
+ break;
+ }
+}
+
+Result NameResolver::VisitScript(Script* script) {
+ for (const std::unique_ptr<Command>& command : script->commands)
+ VisitCommand(command.get());
+ return result_;
+}
+
+Result ResolveNamesModule(Module* module, Errors* errors) {
+ NameResolver resolver(nullptr, errors);
+ return resolver.VisitModule(module);
+}
+
+Result ResolveNamesScript(Script* script, Errors* errors) {
+ NameResolver resolver(script, errors);
+ return resolver.VisitScript(script);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/resolve-names.h b/third_party/wasm2c/src/resolve-names.h
new file mode 100644
index 0000000000..04f2115c6d
--- /dev/null
+++ b/third_party/wasm2c/src/resolve-names.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_RESOLVE_NAMES_H_
+#define WABT_RESOLVE_NAMES_H_
+
+#include "src/common.h"
+#include "src/error.h"
+
+namespace wabt {
+
+struct Module;
+struct Script;
+
+Result ResolveNamesModule(Module*, Errors*);
+Result ResolveNamesScript(Script*, Errors*);
+
+} // namespace wabt
+
+#endif /* WABT_RESOLVE_NAMES_H_ */
diff --git a/third_party/wasm2c/src/result.h b/third_party/wasm2c/src/result.h
new file mode 100644
index 0000000000..a40faab78b
--- /dev/null
+++ b/third_party/wasm2c/src/result.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_RESULT_H_
+#define WABT_RESULT_H_
+
+namespace wabt {
+
+struct Result {
+ enum Enum {
+ Ok,
+ Error,
+ };
+
+ Result() : Result(Ok) {}
+ Result(Enum e) : enum_(e) {}
+ operator Enum() const { return enum_; }
+ Result& operator|=(Result rhs);
+
+ private:
+ Enum enum_;
+};
+
+inline Result operator|(Result lhs, Result rhs) {
+ return (lhs == Result::Error || rhs == Result::Error) ? Result::Error
+ : Result::Ok;
+}
+
+inline Result& Result::operator|=(Result rhs) {
+ enum_ = *this | rhs;
+ return *this;
+}
+
+inline bool Succeeded(Result result) {
+ return result == Result::Ok;
+}
+inline bool Failed(Result result) {
+ return result == Result::Error;
+}
+
+#define CHECK_RESULT(expr) \
+ do { \
+ if (Failed(expr)) { \
+ return ::wabt::Result::Error; \
+ } \
+ } while (0)
+
+} // namespace wabt
+
+#endif // WABT_RESULT_H_
diff --git a/third_party/wasm2c/src/shared-validator.cc b/third_party/wasm2c/src/shared-validator.cc
new file mode 100644
index 0000000000..51c0edea6e
--- /dev/null
+++ b/third_party/wasm2c/src/shared-validator.cc
@@ -0,0 +1,1261 @@
+/*
+ * Copyright 2020 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/shared-validator.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <limits>
+
+namespace wabt {
+
+TypeVector SharedValidator::ToTypeVector(Index count, const Type* types) {
+ return TypeVector(&types[0], &types[count]);
+}
+
+SharedValidator::SharedValidator(Errors* errors, const ValidateOptions& options)
+ : options_(options), errors_(errors), typechecker_(options.features) {
+ typechecker_.set_error_callback(
+ [this](const char* msg) { OnTypecheckerError(msg); });
+}
+
+Result WABT_PRINTF_FORMAT(3, 4) SharedValidator::PrintError(const Location& loc,
+ const char* format,
+ ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, loc, buffer);
+ return Result::Error;
+}
+
+void SharedValidator::OnTypecheckerError(const char* msg) {
+ PrintError(*expr_loc_, "%s", msg);
+}
+
+Result SharedValidator::OnFuncType(const Location& loc,
+ Index param_count,
+ const Type* param_types,
+ Index result_count,
+ const Type* result_types) {
+ Result result = Result::Ok;
+ if (!options_.features.multi_value_enabled() && result_count > 1) {
+ result |=
+ PrintError(loc, "multiple result values not currently supported.");
+ }
+ func_types_.emplace(num_types_++,
+ FuncType{ToTypeVector(param_count, param_types),
+ ToTypeVector(result_count, result_types)});
+ return result;
+}
+
+Result SharedValidator::OnStructType(const Location&,
+ Index field_count,
+ TypeMut* fields) {
+ struct_types_.emplace(num_types_++, StructType{TypeMutVector(
+ &fields[0], &fields[field_count])});
+ return Result::Ok;
+}
+
+Result SharedValidator::OnArrayType(const Location&, TypeMut field) {
+ array_types_.emplace(num_types_++, ArrayType{field});
+ return Result::Ok;
+}
+
+Result SharedValidator::OnFunction(const Location& loc, Var sig_var) {
+ Result result = Result::Ok;
+ FuncType type;
+ result |= CheckFuncTypeIndex(sig_var, &type);
+ funcs_.push_back(type);
+ return result;
+}
+
+Result SharedValidator::CheckLimits(const Location& loc,
+ const Limits& limits,
+ uint64_t absolute_max,
+ const char* desc) {
+ Result result = Result::Ok;
+ if (limits.initial > absolute_max) {
+ result |=
+ PrintError(loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")",
+ desc, limits.initial, absolute_max);
+ }
+
+ if (limits.has_max) {
+ if (limits.max > absolute_max) {
+ result |= PrintError(loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")",
+ desc, limits.max, absolute_max);
+ }
+
+ if (limits.max < limits.initial) {
+ result |= PrintError(
+ loc, "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", desc,
+ limits.max, desc, limits.initial);
+ }
+ }
+ return result;
+}
+
+Result SharedValidator::OnTable(const Location& loc,
+ Type elem_type,
+ const Limits& limits) {
+ Result result = Result::Ok;
+ if (tables_.size() > 0 && !options_.features.reference_types_enabled()) {
+ result |= PrintError(loc, "only one table allowed");
+ }
+ result |= CheckLimits(loc, limits, UINT32_MAX, "elems");
+
+ if (limits.is_shared) {
+ result |= PrintError(loc, "tables may not be shared");
+ }
+ if (elem_type != Type::FuncRef &&
+ !options_.features.reference_types_enabled()) {
+ result |= PrintError(loc, "tables must have funcref type");
+ }
+ if (!elem_type.IsRef()) {
+ result |= PrintError(loc, "tables must have reference types");
+ }
+
+ tables_.push_back(TableType{elem_type, limits});
+ return result;
+}
+
+Result SharedValidator::OnMemory(const Location& loc, const Limits& limits) {
+ Result result = Result::Ok;
+ if (memories_.size() > 0) {
+ result |= PrintError(loc, "only one memory block allowed");
+ }
+ result |= CheckLimits(
+ loc, limits, limits.is_64 ? WABT_MAX_PAGES64 : WABT_MAX_PAGES32, "pages");
+
+ if (limits.is_shared) {
+ if (!options_.features.threads_enabled()) {
+ result |= PrintError(loc, "memories may not be shared");
+ } else if (!limits.has_max) {
+ result |= PrintError(loc, "shared memories must have max sizes");
+ }
+ }
+
+ memories_.push_back(MemoryType{limits});
+ return result;
+}
+
+Result SharedValidator::OnGlobalImport(const Location& loc,
+ Type type,
+ bool mutable_) {
+ Result result = Result::Ok;
+ if (mutable_ && !options_.features.mutable_globals_enabled()) {
+ result |= PrintError(loc, "mutable globals cannot be imported");
+ }
+ globals_.push_back(GlobalType{type, mutable_});
+ ++num_imported_globals_;
+ return result;
+}
+
+Result SharedValidator::OnGlobal(const Location& loc,
+ Type type,
+ bool mutable_) {
+ globals_.push_back(GlobalType{type, mutable_});
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckType(const Location& loc,
+ Type actual,
+ Type expected,
+ const char* desc) {
+ if (Failed(TypeChecker::CheckType(actual, expected))) {
+ PrintError(loc, "type mismatch at %s. got %s, expected %s", desc,
+ actual.GetName(), expected.GetName());
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::OnGlobalInitExpr_Const(const Location& loc,
+ Type actual) {
+ return CheckType(loc, actual, globals_.back().type,
+ "global initializer expression");
+}
+
+Result SharedValidator::OnGlobalInitExpr_GlobalGet(const Location& loc,
+ Var ref_global_var) {
+ Result result = Result::Ok;
+ GlobalType ref_global;
+ CHECK_RESULT(CheckGlobalIndex(ref_global_var, &ref_global));
+
+ if (ref_global_var.index() >= num_imported_globals_) {
+ result |= PrintError(
+ ref_global_var.loc,
+ "initializer expression can only reference an imported global");
+ }
+
+ if (ref_global.mutable_) {
+ result |= PrintError(
+ loc, "initializer expression cannot reference a mutable global");
+ }
+
+ result |= CheckType(loc, ref_global.type, globals_.back().type,
+ "global initializer expression");
+ return result;
+}
+
+Result SharedValidator::OnGlobalInitExpr_RefNull(const Location& loc,
+ Type type) {
+ return CheckType(loc, type, globals_.back().type,
+ "global initializer expression");
+}
+
+Result SharedValidator::OnGlobalInitExpr_RefFunc(const Location& loc,
+ Var func_var) {
+ Result result = Result::Ok;
+ result |= CheckFuncIndex(func_var);
+ init_expr_funcs_.push_back(func_var);
+ result |= CheckType(loc, Type::FuncRef, globals_.back().type,
+ "global initializer expression");
+ return result;
+}
+
+Result SharedValidator::OnGlobalInitExpr_Other(const Location& loc) {
+ return PrintError(
+ loc,
+ "invalid global initializer expression, must be a constant expression");
+}
+
+Result SharedValidator::OnTag(const Location& loc, Var sig_var) {
+ Result result = Result::Ok;
+ FuncType type;
+ result |= CheckFuncTypeIndex(sig_var, &type);
+ if (!type.results.empty()) {
+ result |= PrintError(loc, "Tag signature must have 0 results.");
+ }
+ tags_.push_back(TagType{type.params});
+ return result;
+}
+
+Result SharedValidator::OnExport(const Location& loc,
+ ExternalKind kind,
+ Var item_var,
+ string_view name) {
+ Result result = Result::Ok;
+ auto name_str = name.to_string();
+ if (export_names_.find(name_str) != export_names_.end()) {
+ result |= PrintError(loc, "duplicate export \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(name));
+ }
+ export_names_.insert(name_str);
+
+ switch (kind) {
+ case ExternalKind::Func:
+ result |= CheckFuncIndex(item_var);
+ break;
+
+ case ExternalKind::Table:
+ result |= CheckTableIndex(item_var);
+ break;
+
+ case ExternalKind::Memory:
+ result |= CheckMemoryIndex(item_var);
+ break;
+
+ case ExternalKind::Global:
+ result |= CheckGlobalIndex(item_var);
+ break;
+
+ case ExternalKind::Tag:
+ result |= CheckTagIndex(item_var);
+ break;
+ }
+ return result;
+}
+
+Result SharedValidator::OnStart(const Location& loc, Var func_var) {
+ Result result = Result::Ok;
+ if (starts_++ > 0) {
+ result |= PrintError(loc, "only one start function allowed");
+ }
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ if (func_type.params.size() != 0) {
+ result |= PrintError(loc, "start function must be nullary");
+ }
+ if (func_type.results.size() != 0) {
+ result |= PrintError(loc, "start function must not return anything");
+ }
+ return result;
+}
+
+Result SharedValidator::OnElemSegment(const Location& loc,
+ Var table_var,
+ SegmentKind kind) {
+ Result result = Result::Ok;
+ if (kind == SegmentKind::Active) {
+ result |= CheckTableIndex(table_var);
+ }
+ elems_.push_back(ElemType{Type::Void}); // Updated in OnElemSegmentElemType.
+ return result;
+}
+
+void SharedValidator::OnElemSegmentElemType(Type elem_type) {
+ elems_.back().element = elem_type;
+}
+
+Result SharedValidator::OnElemSegmentInitExpr_Const(const Location& loc,
+ Type type) {
+ return CheckType(loc, type, Type::I32, "elem segment offset");
+}
+
+Result SharedValidator::OnElemSegmentInitExpr_GlobalGet(const Location& loc,
+ Var global_var) {
+ Result result = Result::Ok;
+ GlobalType ref_global;
+ result |= CheckGlobalIndex(global_var, &ref_global);
+
+ if (ref_global.mutable_) {
+ result |= PrintError(
+ loc, "initializer expression cannot reference a mutable global");
+ }
+
+ result |= CheckType(loc, ref_global.type, Type::I32, "elem segment offset");
+ return result;
+}
+
+Result SharedValidator::OnElemSegmentInitExpr_Other(const Location& loc) {
+ return PrintError(loc,
+ "invalid elem segment offset, must be a constant "
+ "expression; either i32.const or "
+ "global.get.");
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_RefNull(const Location& loc,
+ Type type) {
+ return CheckType(loc, type, elems_.back().element, "elem expression");
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_RefFunc(const Location& loc,
+ Var func_var) {
+ Result result = Result::Ok;
+ result |= CheckFuncIndex(func_var);
+ declared_funcs_.insert(func_var.index());
+ return result;
+}
+
+Result SharedValidator::OnElemSegmentElemExpr_Other(const Location& loc) {
+ return PrintError(loc,
+ "invalid elem expression expression; must be either "
+ "ref.null or ref.func.");
+}
+
+void SharedValidator::OnDataCount(Index count) {
+ data_segments_ = count;
+}
+
+Result SharedValidator::OnDataSegment(const Location& loc,
+ Var memory_var,
+ SegmentKind kind) {
+ Result result = Result::Ok;
+ if (kind == SegmentKind::Active) {
+ result |= CheckMemoryIndex(memory_var);
+ }
+ return result;
+}
+
+Result SharedValidator::OnDataSegmentInitExpr_Const(const Location& loc,
+ Type type) {
+ auto required =
+ memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType();
+ return CheckType(loc, type, required, "data segment offset");
+}
+
+Result SharedValidator::OnDataSegmentInitExpr_GlobalGet(const Location& loc,
+ Var global_var) {
+ Result result = Result::Ok;
+ GlobalType ref_global;
+ result |= CheckGlobalIndex(global_var, &ref_global);
+
+ if (ref_global.mutable_) {
+ result |= PrintError(
+ loc, "initializer expression cannot reference a mutable global");
+ }
+
+ auto required =
+ memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType();
+ result |= CheckType(loc, ref_global.type, required, "data segment offset");
+ return result;
+}
+
+Result SharedValidator::OnDataSegmentInitExpr_Other(const Location& loc) {
+ return PrintError(loc,
+ "invalid data segment offset, must be a constant "
+ "expression; either iXX.const or "
+ "global.get.");
+}
+
+Result SharedValidator::CheckDeclaredFunc(Var func_var) {
+ if (declared_funcs_.count(func_var.index()) == 0) {
+ return PrintError(func_var.loc,
+ "function is not declared in any elem sections");
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::EndModule() {
+ // Verify that any ref.func used in init expressions for globals are
+ // mentioned in an elems section. This can't be done while process the
+ // globals because the global section comes before the elem section.
+ Result result = Result::Ok;
+ for (Var func_var : init_expr_funcs_) {
+ result |= CheckDeclaredFunc(func_var);
+ }
+ return result;
+}
+
+Result SharedValidator::CheckIndex(Var var, Index max_index, const char* desc) {
+ if (var.index() >= max_index) {
+ return PrintError(
+ var.loc, "%s variable out of range: %" PRIindex " (max %" PRIindex ")",
+ desc, var.index(), max_index);
+ }
+ return Result::Ok;
+}
+
+template <typename T>
+Result SharedValidator::CheckIndexWithValue(Var var,
+ const std::vector<T>& values,
+ T* out,
+ const char* desc) {
+ Result result = CheckIndex(var, values.size(), desc);
+ if (out) {
+ *out = Succeeded(result) ? values[var.index()] : T{};
+ }
+ return result;
+}
+
+Result SharedValidator::CheckLocalIndex(Var local_var, Type* out_type) {
+ auto iter = std::upper_bound(
+ locals_.begin(), locals_.end(), local_var.index(),
+ [](Index index, const LocalDecl& decl) { return index < decl.end; });
+ if (iter == locals_.end()) {
+ // TODO: better error
+ return PrintError(local_var.loc, "local variable out of range (max %u)",
+ GetLocalCount());
+ }
+ *out_type = iter->type;
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckFuncTypeIndex(Var sig_var, FuncType* out) {
+ Result result = CheckIndex(sig_var, num_types_, "function type");
+ if (Failed(result)) {
+ *out = FuncType{};
+ return Result::Error;
+ }
+
+ auto iter = func_types_.find(sig_var.index());
+ if (iter == func_types_.end()) {
+ return PrintError(sig_var.loc, "type %d is not a function",
+ sig_var.index());
+ }
+
+ if (out) {
+ *out = iter->second;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckFuncIndex(Var func_var, FuncType* out) {
+ return CheckIndexWithValue(func_var, funcs_, out, "function");
+}
+
+Result SharedValidator::CheckMemoryIndex(Var memory_var, MemoryType* out) {
+ return CheckIndexWithValue(memory_var, memories_, out, "memory");
+}
+
+Result SharedValidator::CheckTableIndex(Var table_var, TableType* out) {
+ return CheckIndexWithValue(table_var, tables_, out, "table");
+}
+
+Result SharedValidator::CheckGlobalIndex(Var global_var, GlobalType* out) {
+ return CheckIndexWithValue(global_var, globals_, out, "global");
+}
+
+Result SharedValidator::CheckTagIndex(Var tag_var, TagType* out) {
+ return CheckIndexWithValue(tag_var, tags_, out, "tag");
+}
+
+Result SharedValidator::CheckElemSegmentIndex(Var elem_segment_var,
+ ElemType* out) {
+ return CheckIndexWithValue(elem_segment_var, elems_, out, "elem_segment");
+}
+
+Result SharedValidator::CheckDataSegmentIndex(Var data_segment_var) {
+ return CheckIndex(data_segment_var, data_segments_, "data_segment");
+}
+
+Result SharedValidator::CheckBlockSignature(const Location& loc,
+ Opcode opcode,
+ Type sig_type,
+ TypeVector* out_param_types,
+ TypeVector* out_result_types) {
+ Result result = Result::Ok;
+
+ if (sig_type.IsIndex()) {
+ Index sig_index = sig_type.GetIndex();
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(Var(sig_index, loc), &func_type);
+
+ if (!func_type.params.empty() && !options_.features.multi_value_enabled()) {
+ result |= PrintError(loc, "%s params not currently supported.",
+ opcode.GetName());
+ }
+ // Multiple results without --enable-multi-value is checked above in
+ // OnType.
+
+ *out_param_types = func_type.params;
+ *out_result_types = func_type.results;
+ } else {
+ out_param_types->clear();
+ *out_result_types = sig_type.GetInlineVector();
+ }
+
+ return result;
+}
+
+Result SharedValidator::BeginFunctionBody(const Location& loc,
+ Index func_index) {
+ expr_loc_ = &loc;
+ locals_.clear();
+ if (func_index < funcs_.size()) {
+ for (Type type : funcs_[func_index].params) {
+ // TODO: Coalesce parameters of the same type?
+ locals_.push_back(LocalDecl{type, GetLocalCount() + 1});
+ }
+ return typechecker_.BeginFunction(funcs_[func_index].results);
+ } else {
+ // Signature isn't available, use empty.
+ return typechecker_.BeginFunction(TypeVector());
+ }
+}
+
+Result SharedValidator::EndFunctionBody(const Location& loc) {
+ return typechecker_.EndFunction();
+}
+
+Result SharedValidator::OnLocalDecl(const Location& loc,
+ Index count,
+ Type type) {
+ const auto max_locals = std::numeric_limits<Index>::max();
+ if (count > max_locals - GetLocalCount()) {
+ PrintError(loc, "local count must be < 0x10000000");
+ return Result::Error;
+ }
+ locals_.push_back(LocalDecl{type, GetLocalCount() + count});
+ return Result::Ok;
+}
+
+Index SharedValidator::GetLocalCount() const {
+ return locals_.empty() ? 0 : locals_.back().end;
+}
+
+static bool is_power_of_two(uint32_t x) {
+ return x && ((x & (x - 1)) == 0);
+}
+
+Result SharedValidator::CheckAlign(const Location& loc,
+ Address alignment,
+ Address natural_alignment) {
+ if (!is_power_of_two(alignment)) {
+ PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2",
+ alignment);
+ return Result::Error;
+ }
+ if (alignment > natural_alignment) {
+ PrintError(
+ loc,
+ "alignment must not be larger than natural alignment (%" PRIaddress ")",
+ natural_alignment);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::CheckAtomicAlign(const Location& loc,
+ Address alignment,
+ Address natural_alignment) {
+ if (!is_power_of_two(alignment)) {
+ PrintError(loc, "alignment (%" PRIaddress ") must be a power of 2",
+ alignment);
+ return Result::Error;
+ }
+ if (alignment != natural_alignment) {
+ PrintError(loc,
+ "alignment must be equal to natural alignment (%" PRIaddress ")",
+ natural_alignment);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result SharedValidator::OnAtomicFence(const Location& loc,
+ uint32_t consistency_model) {
+ Result result = Result::Ok;
+ if (consistency_model != 0) {
+ result |= PrintError(
+ loc, "unexpected atomic.fence consistency model (expected 0): %u",
+ consistency_model);
+ }
+ result |= typechecker_.OnAtomicFence(consistency_model);
+ return result;
+}
+
+Result SharedValidator::OnAtomicLoad(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicNotify(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicNotify(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicRmwCmpxchg(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicRmwCmpxchg(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicRmw(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicRmw(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicStore(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicStore(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnAtomicWait(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAtomicAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnAtomicWait(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnBinary(const Location& loc, Opcode opcode) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnBinary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnBlock(const Location& loc, Type sig_type) {
+ Result result = Result::Ok;
+ TypeVector param_types, result_types;
+ expr_loc_ = &loc;
+ result |= CheckBlockSignature(loc, Opcode::Block, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnBlock(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnBr(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnBr(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnBrIf(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnBrIf(depth.index());
+ return result;
+}
+
+Result SharedValidator::BeginBrTable(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.BeginBrTable();
+ return result;
+}
+
+Result SharedValidator::OnBrTableTarget(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnBrTableTarget(depth.index());
+ return result;
+}
+
+Result SharedValidator::EndBrTable(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.EndBrTable();
+ return result;
+}
+
+Result SharedValidator::OnCall(const Location& loc, Var func_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ result |= typechecker_.OnCall(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnCallIndirect(const Location& loc,
+ Var sig_var,
+ Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(sig_var, &func_type);
+ result |= CheckTableIndex(table_var);
+ result |= typechecker_.OnCallIndirect(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnCallRef(const Location& loc, Index* function_type_index) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ Index func_index;
+ result |= typechecker_.OnFuncRef(&func_index);
+ if (Failed(result)) {
+ return result;
+ }
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(Var(func_index, loc), &func_type);
+ result |= typechecker_.OnCall(func_type.params, func_type.results);
+ if (Succeeded(result)) {
+ *function_type_index = func_index;
+ }
+ return result;
+}
+
+Result SharedValidator::OnCatch(const Location& loc,
+ Var tag_var,
+ bool is_catch_all) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ if (is_catch_all) {
+ TypeVector empty;
+ result |= typechecker_.OnCatch(empty);
+ } else {
+ TagType tag_type;
+ result |= CheckTagIndex(tag_var, &tag_type);
+ result |= typechecker_.OnCatch(tag_type.params);
+ }
+ return result;
+}
+
+Result SharedValidator::OnCompare(const Location& loc, Opcode opcode) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnCompare(opcode);
+ return result;
+}
+
+Result SharedValidator::OnConst(const Location& loc, Type type) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnConst(type);
+ return result;
+}
+
+Result SharedValidator::OnConvert(const Location& loc, Opcode opcode) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnConvert(opcode);
+ return result;
+}
+
+Result SharedValidator::OnDataDrop(const Location& loc, Var segment_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= CheckDataSegmentIndex(segment_var);
+ result |= typechecker_.OnDataDrop(segment_var.index());
+ return result;
+}
+
+Result SharedValidator::OnDelegate(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnDelegate(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnDrop(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnDrop();
+ return result;
+}
+
+Result SharedValidator::OnElemDrop(const Location& loc, Var segment_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= CheckElemSegmentIndex(segment_var);
+ result |= typechecker_.OnElemDrop(segment_var.index());
+ return result;
+}
+
+Result SharedValidator::OnElse(const Location& loc) {
+ Result result = Result::Ok;
+ result |= typechecker_.OnElse();
+ return result;
+}
+
+Result SharedValidator::OnEnd(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnEnd();
+ return result;
+}
+
+Result SharedValidator::OnGlobalGet(const Location& loc, Var global_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ GlobalType global_type;
+ result |= CheckGlobalIndex(global_var, &global_type);
+ result |= typechecker_.OnGlobalGet(global_type.type);
+ return result;
+}
+
+Result SharedValidator::OnGlobalSet(const Location& loc, Var global_var) {
+ Result result = Result::Ok;
+ GlobalType global_type;
+ result |= CheckGlobalIndex(global_var, &global_type);
+ if (!global_type.mutable_) {
+ result |= PrintError(
+ loc, "can't global.set on immutable global at index %" PRIindex ".",
+ global_var.index());
+ }
+ expr_loc_ = &loc;
+ result |= typechecker_.OnGlobalSet(global_type.type);
+ return result;
+}
+
+Result SharedValidator::OnIf(const Location& loc, Type sig_type) {
+ Result result = Result::Ok;
+ TypeVector param_types, result_types;
+ expr_loc_ = &loc;
+ result |= CheckBlockSignature(loc, Opcode::If, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnIf(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnLoad(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLoadSplat(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLoadZero(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnLoad(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnLocalGet(const Location& loc, Var local_var) {
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ expr_loc_ = &loc;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalGet(type);
+ return result;
+}
+
+Result SharedValidator::OnLocalSet(const Location& loc, Var local_var) {
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ expr_loc_ = &loc;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalSet(type);
+ return result;
+}
+
+Result SharedValidator::OnLocalTee(const Location& loc, Var local_var) {
+ Result result = Result::Ok;
+ Type type = Type::Any;
+ expr_loc_ = &loc;
+ result |= CheckLocalIndex(local_var, &type);
+ result |= typechecker_.OnLocalTee(type);
+ return result;
+}
+
+Result SharedValidator::OnLoop(const Location& loc, Type sig_type) {
+ Result result = Result::Ok;
+ TypeVector param_types, result_types;
+ expr_loc_ = &loc;
+ result |= CheckBlockSignature(loc, Opcode::Loop, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnLoop(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnMemoryCopy(const Location& loc) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= typechecker_.OnMemoryCopy(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryFill(const Location& loc) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= typechecker_.OnMemoryFill(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryGrow(const Location& loc) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= typechecker_.OnMemoryGrow(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemoryInit(const Location& loc, Var segment_var) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckDataSegmentIndex(segment_var);
+ result |= typechecker_.OnMemoryInit(segment_var.index(), mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnMemorySize(const Location& loc) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= typechecker_.OnMemorySize(mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnNop(const Location& loc) {
+ expr_loc_ = &loc;
+ return Result::Ok;
+}
+
+Result SharedValidator::OnRefFunc(const Location& loc, Var func_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= CheckDeclaredFunc(func_var);
+ result |= typechecker_.OnRefFuncExpr(func_var.index());
+ return result;
+}
+
+Result SharedValidator::OnRefIsNull(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnRefIsNullExpr();
+ return result;
+}
+
+Result SharedValidator::OnRefNull(const Location& loc, Type type) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnRefNullExpr(type);
+ return result;
+}
+
+Result SharedValidator::OnRethrow(const Location& loc, Var depth) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnRethrow(depth.index());
+ return result;
+}
+
+Result SharedValidator::OnReturnCall(const Location& loc, Var func_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ FuncType func_type;
+ result |= CheckFuncIndex(func_var, &func_type);
+ result |= typechecker_.OnReturnCall(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnReturnCallIndirect(const Location& loc,
+ Var sig_var,
+ Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= CheckTableIndex(table_var);
+ FuncType func_type;
+ result |= CheckFuncTypeIndex(sig_var, &func_type);
+ result |=
+ typechecker_.OnReturnCallIndirect(func_type.params, func_type.results);
+ return result;
+}
+
+Result SharedValidator::OnReturn(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnReturn();
+ return result;
+}
+
+Result SharedValidator::OnSelect(const Location& loc,
+ Index result_count,
+ Type* result_types) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ if (result_count > 1) {
+ result |=
+ PrintError(loc, "invalid arity in select instruction: %" PRIindex ".",
+ result_count);
+ } else {
+ result |= typechecker_.OnSelect(ToTypeVector(result_count, result_types));
+ }
+ return result;
+}
+
+Result SharedValidator::OnSimdLaneOp(const Location& loc,
+ Opcode opcode,
+ uint64_t value) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnSimdLaneOp(opcode, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdLoadLane(const Location& loc,
+ Opcode opcode,
+ Address alignment,
+ uint64_t value) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnSimdLoadLane(opcode, mt.limits, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdStoreLane(const Location& loc,
+ Opcode opcode,
+ Address alignment,
+ uint64_t value) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnSimdStoreLane(opcode, mt.limits, value);
+ return result;
+}
+
+Result SharedValidator::OnSimdShuffleOp(const Location& loc,
+ Opcode opcode,
+ v128 value) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnSimdShuffleOp(opcode, value);
+ return result;
+}
+
+Result SharedValidator::OnStore(const Location& loc,
+ Opcode opcode,
+ Address alignment) {
+ Result result = Result::Ok;
+ MemoryType mt;
+ expr_loc_ = &loc;
+ result |= CheckMemoryIndex(Var(0, loc), &mt);
+ result |= CheckAlign(loc, alignment, opcode.GetMemorySize());
+ result |= typechecker_.OnStore(opcode, mt.limits);
+ return result;
+}
+
+Result SharedValidator::OnTableCopy(const Location& loc,
+ Var dst_var,
+ Var src_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType dst_table;
+ TableType src_table;
+ result |= CheckTableIndex(dst_var, &dst_table);
+ result |= CheckTableIndex(src_var, &src_table);
+ result |= typechecker_.OnTableCopy();
+ result |= CheckType(loc, src_table.element, dst_table.element, "table.copy");
+ return result;
+}
+
+Result SharedValidator::OnTableFill(const Location& loc, Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableFill(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableGet(const Location& loc, Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableGet(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableGrow(const Location& loc, Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableGrow(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableInit(const Location& loc,
+ Var segment_var,
+ Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType table_type;
+ ElemType elem_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= CheckElemSegmentIndex(segment_var, &elem_type);
+ result |= typechecker_.OnTableInit(table_var.index(), segment_var.index());
+ result |= CheckType(loc, elem_type.element, table_type.element, "table.init");
+ return result;
+}
+
+Result SharedValidator::OnTableSet(const Location& loc, Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TableType table_type;
+ result |= CheckTableIndex(table_var, &table_type);
+ result |= typechecker_.OnTableSet(table_type.element);
+ return result;
+}
+
+Result SharedValidator::OnTableSize(const Location& loc, Var table_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= CheckTableIndex(table_var);
+ result |= typechecker_.OnTableSize();
+ return result;
+}
+
+Result SharedValidator::OnTernary(const Location& loc, Opcode opcode) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnTernary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnThrow(const Location& loc, Var tag_var) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ TagType tag_type;
+ result |= CheckTagIndex(tag_var, &tag_type);
+ result |= typechecker_.OnThrow(tag_type.params);
+ return result;
+}
+
+Result SharedValidator::OnTry(const Location& loc, Type sig_type) {
+ Result result = Result::Ok;
+ TypeVector param_types, result_types;
+ expr_loc_ = &loc;
+ result |= CheckBlockSignature(loc, Opcode::Try, sig_type, &param_types,
+ &result_types);
+ result |= typechecker_.OnTry(param_types, result_types);
+ return result;
+}
+
+Result SharedValidator::OnUnary(const Location& loc, Opcode opcode) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnUnary(opcode);
+ return result;
+}
+
+Result SharedValidator::OnUnreachable(const Location& loc) {
+ Result result = Result::Ok;
+ expr_loc_ = &loc;
+ result |= typechecker_.OnUnreachable();
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/shared-validator.h b/third_party/wasm2c/src/shared-validator.h
new file mode 100644
index 0000000000..415e484eae
--- /dev/null
+++ b/third_party/wasm2c/src/shared-validator.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef WABT_SHARED_VALIDATOR_H_
+#define WABT_SHARED_VALIDATOR_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "src/common.h"
+#include "src/error.h"
+#include "src/feature.h"
+#include "src/ir.h"
+#include "src/opcode.h"
+#include "src/type-checker.h"
+
+#include "src/binary-reader.h" // For TypeMut.
+
+namespace wabt {
+
+struct ValidateOptions {
+ ValidateOptions() = default;
+ ValidateOptions(const Features& features) : features(features) {}
+
+ Features features;
+};
+
+class SharedValidator {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(SharedValidator);
+ SharedValidator(Errors*, const ValidateOptions& options);
+
+ // TODO: Move into SharedValidator?
+ using Label = TypeChecker::Label;
+ size_t type_stack_size() const { return typechecker_.type_stack_size(); }
+ Result GetLabel(Index depth, Label** out_label) {
+ return typechecker_.GetLabel(depth, out_label);
+ }
+
+ Result WABT_PRINTF_FORMAT(3, 4)
+ PrintError(const Location& loc, const char* fmt, ...);
+
+ void OnTypecheckerError(const char* msg);
+
+ Index GetLocalCount() const;
+
+ Result EndModule();
+
+ Result OnFuncType(const Location&,
+ Index param_count,
+ const Type* param_types,
+ Index result_count,
+ const Type* result_types);
+ Result OnStructType(const Location&, Index field_count, TypeMut* fields);
+ Result OnArrayType(const Location&, TypeMut field);
+
+ Result OnFunction(const Location&, Var sig_var);
+ Result OnTable(const Location&, Type elem_type, const Limits&);
+ Result OnMemory(const Location&, const Limits&);
+ Result OnGlobalImport(const Location&, Type type, bool mutable_);
+ Result OnGlobal(const Location&, Type type, bool mutable_);
+ Result OnGlobalInitExpr_Const(const Location&, Type);
+ Result OnGlobalInitExpr_GlobalGet(const Location&, Var global_var);
+ Result OnGlobalInitExpr_RefNull(const Location&, Type type);
+ Result OnGlobalInitExpr_RefFunc(const Location&, Var func_var);
+ Result OnGlobalInitExpr_Other(const Location&);
+ Result OnTag(const Location&, Var sig_var);
+
+ Result OnExport(const Location&,
+ ExternalKind,
+ Var item_var,
+ string_view name);
+
+ Result OnStart(const Location&, Var func_var);
+
+ Result OnElemSegment(const Location&, Var table_var, SegmentKind);
+ void OnElemSegmentElemType(Type elem_type);
+ Result OnElemSegmentInitExpr_Const(const Location&, Type);
+ Result OnElemSegmentInitExpr_GlobalGet(const Location&, Var global_var);
+ Result OnElemSegmentInitExpr_Other(const Location&);
+ Result OnElemSegmentElemExpr_RefNull(const Location&, Type type);
+ Result OnElemSegmentElemExpr_RefFunc(const Location&, Var func_var);
+ Result OnElemSegmentElemExpr_Other(const Location&);
+
+ void OnDataCount(Index count);
+
+ Result OnDataSegment(const Location&, Var memory_var, SegmentKind);
+ Result OnDataSegmentInitExpr_Const(const Location&, Type);
+ Result OnDataSegmentInitExpr_GlobalGet(const Location&, Var global_var);
+ Result OnDataSegmentInitExpr_Other(const Location&);
+
+ Result BeginFunctionBody(const Location&, Index func_index);
+ Result EndFunctionBody(const Location&);
+ Result OnLocalDecl(const Location&, Index count, Type type);
+
+ Result OnAtomicFence(const Location&, uint32_t consistency_model);
+ Result OnAtomicLoad(const Location&, Opcode, Address align);
+ Result OnAtomicNotify(const Location&, Opcode, Address align);
+ Result OnAtomicRmwCmpxchg(const Location&, Opcode, Address align);
+ Result OnAtomicRmw(const Location&, Opcode, Address align);
+ Result OnAtomicStore(const Location&, Opcode, Address align);
+ Result OnAtomicWait(const Location&, Opcode, Address align);
+ Result OnBinary(const Location&, Opcode);
+ Result OnBlock(const Location&, Type sig_type);
+ Result OnBr(const Location&, Var depth);
+ Result OnBrIf(const Location&, Var depth);
+ Result BeginBrTable(const Location&);
+ Result OnBrTableTarget(const Location&, Var depth);
+ Result EndBrTable(const Location&);
+ Result OnCall(const Location&, Var func_var);
+ Result OnCallIndirect(const Location&, Var sig_var, Var table_var);
+ Result OnCallRef(const Location&, Index* function_type_index);
+ Result OnCatch(const Location&, Var tag_var, bool is_catch_all);
+ Result OnCompare(const Location&, Opcode);
+ Result OnConst(const Location&, Type);
+ Result OnConvert(const Location&, Opcode);
+ Result OnDataDrop(const Location&, Var segment_var);
+ Result OnDelegate(const Location&, Var depth);
+ Result OnDrop(const Location&);
+ Result OnElemDrop(const Location&, Var segment_var);
+ Result OnElse(const Location&);
+ Result OnEnd(const Location&);
+ Result OnGlobalGet(const Location&, Var);
+ Result OnGlobalSet(const Location&, Var);
+ Result OnIf(const Location&, Type sig_type);
+ Result OnLoad(const Location&, Opcode, Address align);
+ Result OnLoadSplat(const Location&, Opcode, Address align);
+ Result OnLoadZero(const Location&, Opcode, Address align);
+ Result OnLocalGet(const Location&, Var);
+ Result OnLocalSet(const Location&, Var);
+ Result OnLocalTee(const Location&, Var);
+ Result OnLoop(const Location&, Type sig_type);
+ Result OnMemoryCopy(const Location&);
+ Result OnMemoryFill(const Location&);
+ Result OnMemoryGrow(const Location&);
+ Result OnMemoryInit(const Location&, Var segment_var);
+ Result OnMemorySize(const Location&);
+ Result OnNop(const Location&);
+ Result OnRefFunc(const Location&, Var func_var);
+ Result OnRefIsNull(const Location&);
+ Result OnRefNull(const Location&, Type type);
+ Result OnRethrow(const Location&, Var depth);
+ Result OnReturnCall(const Location&, Var func_var);
+ Result OnReturnCallIndirect(const Location&, Var sig_var, Var table_var);
+ Result OnReturn(const Location&);
+ Result OnSelect(const Location&, Index result_count, Type* result_types);
+ Result OnSimdLaneOp(const Location&, Opcode, uint64_t lane_idx);
+ Result OnSimdLoadLane(const Location&, Opcode, Address align, uint64_t lane_idx);
+ Result OnSimdStoreLane(const Location&, Opcode, Address align, uint64_t lane_idx);
+ Result OnSimdShuffleOp(const Location&, Opcode, v128 lane_idx);
+ Result OnStore(const Location&, Opcode, Address align);
+ Result OnTableCopy(const Location&, Var dst_var, Var src_var);
+ Result OnTableFill(const Location&, Var table_var);
+ Result OnTableGet(const Location&, Var table_var);
+ Result OnTableGrow(const Location&, Var table_var);
+ Result OnTableInit(const Location&, Var segment_var, Var table_var);
+ Result OnTableSet(const Location&, Var table_var);
+ Result OnTableSize(const Location&, Var table_var);
+ Result OnTernary(const Location&, Opcode);
+ Result OnThrow(const Location&, Var tag_var);
+ Result OnTry(const Location&, Type sig_type);
+ Result OnUnary(const Location&, Opcode);
+ Result OnUnreachable(const Location&);
+
+ private:
+ struct FuncType {
+ FuncType() = default;
+ FuncType(const TypeVector& params, const TypeVector& results)
+ : params(params), results(results) {}
+
+ TypeVector params;
+ TypeVector results;
+ };
+
+ struct StructType {
+ StructType() = default;
+ StructType(const TypeMutVector& fields) : fields(fields) {}
+
+ TypeMutVector fields;
+ };
+
+ struct ArrayType {
+ ArrayType() = default;
+ ArrayType(TypeMut field) : field(field) {}
+
+ TypeMut field;
+ };
+
+ struct TableType {
+ TableType() = default;
+ TableType(Type element, Limits limits) : element(element), limits(limits) {}
+
+ Type element = Type::Any;
+ Limits limits;
+ };
+
+ struct MemoryType {
+ MemoryType() = default;
+ MemoryType(Limits limits) : limits(limits) {}
+
+ Limits limits;
+ };
+
+ struct GlobalType {
+ GlobalType() = default;
+ GlobalType(Type type, bool mutable_) : type(type), mutable_(mutable_) {}
+
+ Type type = Type::Any;
+ bool mutable_ = true;
+ };
+
+ struct TagType {
+ TypeVector params;
+ };
+
+ struct ElemType {
+ ElemType() = default;
+ ElemType(Type element) : element(element) {}
+
+ Type element;
+ };
+
+ struct LocalDecl {
+ Type type;
+ Index end;
+ };
+
+ Result CheckType(const Location&,
+ Type actual,
+ Type expected,
+ const char* desc);
+ Result CheckLimits(const Location&,
+ const Limits&,
+ uint64_t absolute_max,
+ const char* desc);
+
+ Result CheckLocalIndex(Var local_var, Type* out_type);
+
+ Result CheckDeclaredFunc(Var func_var);
+
+ Result CheckIndex(Var var, Index max_index, const char* desc);
+ template <typename T>
+ Result CheckIndexWithValue(Var var,
+ const std::vector<T>& values,
+ T* out,
+ const char* desc);
+ Result CheckFuncTypeIndex(Var sig_var, FuncType* out = nullptr);
+ Result CheckFuncIndex(Var func_var, FuncType* out = nullptr);
+ Result CheckTableIndex(Var table_var, TableType* out = nullptr);
+ Result CheckMemoryIndex(Var memory_var, MemoryType* out = nullptr);
+ Result CheckGlobalIndex(Var global_var, GlobalType* out = nullptr);
+ Result CheckTagIndex(Var tag_var, TagType* out = nullptr);
+ Result CheckElemSegmentIndex(Var elem_segment_var, ElemType* out = nullptr);
+ Result CheckDataSegmentIndex(Var data_segment_var);
+
+ Result CheckAlign(const Location&, Address align, Address natural_align);
+ Result CheckAtomicAlign(const Location&, Address align, Address natural_align);
+
+ Result CheckBlockSignature(const Location&,
+ Opcode,
+ Type sig_type,
+ TypeVector* out_param_types,
+ TypeVector* out_result_types);
+
+ TypeVector ToTypeVector(Index count, const Type* types);
+
+ ValidateOptions options_;
+ Errors* errors_;
+ TypeChecker typechecker_; // TODO: Move into SharedValidator.
+ // Cached for access by OnTypecheckerError.
+ const Location* expr_loc_ = nullptr;
+
+ Index num_types_ = 0;
+ std::map<Index, FuncType> func_types_;
+ std::map<Index, StructType> struct_types_;
+ std::map<Index, ArrayType> array_types_;
+
+ std::vector<FuncType> funcs_; // Includes imported and defined.
+ std::vector<TableType> tables_; // Includes imported and defined.
+ std::vector<MemoryType> memories_; // Includes imported and defined.
+ std::vector<GlobalType> globals_; // Includes imported and defined.
+ std::vector<TagType> tags_; // Includes imported and defined.
+ std::vector<ElemType> elems_;
+ Index starts_ = 0;
+ Index num_imported_globals_ = 0;
+ Index data_segments_ = 0;
+
+ // Includes parameters, since this is only used for validating
+ // local.{get,set,tee} instructions.
+ std::vector<LocalDecl> locals_;
+
+ std::set<std::string> export_names_; // Used to check for duplicates.
+ std::set<Index> declared_funcs_; // TODO: optimize?
+ std::vector<Var> init_expr_funcs_;
+};
+
+} // namespace wabt
+
+#endif // WABT_SHARED_VALIDATOR_H_
diff --git a/third_party/wasm2c/src/stream.cc b/third_party/wasm2c/src/stream.cc
new file mode 100644
index 0000000000..e52c51b94a
--- /dev/null
+++ b/third_party/wasm2c/src/stream.cc
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/stream.h"
+
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+
+#define DUMP_OCTETS_PER_LINE 16
+#define DUMP_OCTETS_PER_GROUP 2
+
+#define ERROR0(msg) fprintf(stderr, "%s:%d: " msg, __FILE__, __LINE__)
+#define ERROR(fmt, ...) \
+ fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__)
+
+namespace wabt {
+
+Stream::Stream(Stream* log_stream)
+ : offset_(0), result_(Result::Ok), log_stream_(log_stream) {}
+
+void Stream::AddOffset(ssize_t delta) {
+ offset_ += delta;
+}
+
+void Stream::WriteDataAt(size_t at,
+ const void* src,
+ size_t size,
+ const char* desc,
+ PrintChars print_chars) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->WriteMemoryDump(src, size, at, print_chars, nullptr, desc);
+ }
+ result_ = WriteDataImpl(at, src, size);
+}
+
+void Stream::WriteData(const void* src,
+ size_t size,
+ const char* desc,
+ PrintChars print_chars) {
+ WriteDataAt(offset_, src, size, desc, print_chars);
+ offset_ += size;
+}
+
+void Stream::MoveData(size_t dst_offset, size_t src_offset, size_t size) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->Writef(
+ "; move data: [%" PRIzx ", %" PRIzx ") -> [%" PRIzx ", %" PRIzx ")\n",
+ src_offset, src_offset + size, dst_offset, dst_offset + size);
+ }
+ result_ = MoveDataImpl(dst_offset, src_offset, size);
+}
+
+void Stream::Truncate(size_t size) {
+ if (Failed(result_)) {
+ return;
+ }
+ if (log_stream_) {
+ log_stream_->Writef("; truncate to %" PRIzd " (0x%" PRIzx ")\n", size,
+ size);
+ }
+ result_ = TruncateImpl(size);
+ if (Succeeded(result_) && offset_ > size) {
+ offset_ = size;
+ }
+}
+
+void Stream::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ WriteData(buffer, length);
+}
+
+void Stream::WriteMemoryDump(const void* start,
+ size_t size,
+ size_t offset,
+ PrintChars print_chars,
+ const char* prefix,
+ const char* desc) {
+ const uint8_t* p = static_cast<const uint8_t*>(start);
+ const uint8_t* end = p + size;
+ while (p < end) {
+ const uint8_t* line = p;
+ const uint8_t* line_end = p + DUMP_OCTETS_PER_LINE;
+ if (prefix) {
+ Writef("%s", prefix);
+ }
+ Writef("%07" PRIzx ": ", reinterpret_cast<intptr_t>(p) -
+ reinterpret_cast<intptr_t>(start) + offset);
+ while (p < line_end) {
+ for (int i = 0; i < DUMP_OCTETS_PER_GROUP; ++i, ++p) {
+ if (p < end) {
+ Writef("%02x", *p);
+ } else {
+ WriteChar(' ');
+ WriteChar(' ');
+ }
+ }
+ WriteChar(' ');
+ }
+
+ if (print_chars == PrintChars::Yes) {
+ WriteChar(' ');
+ p = line;
+ for (int i = 0; i < DUMP_OCTETS_PER_LINE && p < end; ++i, ++p)
+ WriteChar(isprint(*p) ? *p : '.');
+ }
+
+ /* if there are multiple lines, only print the desc on the last one */
+ if (p >= end && desc) {
+ Writef(" ; %s", desc);
+ }
+ WriteChar('\n');
+ }
+}
+
+Result OutputBuffer::WriteToFile(string_view filename) const {
+ std::string filename_str = filename.to_string();
+ FILE* file = fopen(filename_str.c_str(), "wb");
+ if (!file) {
+ ERROR("unable to open %s for writing\n", filename_str.c_str());
+ return Result::Error;
+ }
+
+ if (data.empty()) {
+ fclose(file);
+ return Result::Ok;
+ }
+
+ ssize_t bytes = fwrite(data.data(), 1, data.size(), file);
+ if (bytes < 0 || static_cast<size_t>(bytes) != data.size()) {
+ ERROR("failed to write %" PRIzd " bytes to %s\n", data.size(),
+ filename_str.c_str());
+ fclose(file);
+ return Result::Error;
+ }
+
+ fclose(file);
+ return Result::Ok;
+}
+
+MemoryStream::MemoryStream(Stream* log_stream)
+ : Stream(log_stream), buf_(new OutputBuffer()) {}
+
+MemoryStream::MemoryStream(std::unique_ptr<OutputBuffer>&& buf,
+ Stream* log_stream)
+ : Stream(log_stream), buf_(std::move(buf)) {}
+
+std::unique_ptr<OutputBuffer> MemoryStream::ReleaseOutputBuffer() {
+ return std::move(buf_);
+}
+
+void MemoryStream::Clear() {
+ if (buf_)
+ buf_->clear();
+ else
+ buf_.reset(new OutputBuffer());
+}
+
+Result MemoryStream::WriteDataImpl(size_t dst_offset,
+ const void* src,
+ size_t size) {
+ if (size == 0) {
+ return Result::Ok;
+ }
+ size_t end = dst_offset + size;
+ if (end > buf_->data.size()) {
+ buf_->data.resize(end);
+ }
+ uint8_t* dst = &buf_->data[dst_offset];
+ memcpy(dst, src, size);
+ return Result::Ok;
+}
+
+Result MemoryStream::MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) {
+ if (size == 0) {
+ return Result::Ok;
+ }
+ size_t src_end = src_offset + size;
+ size_t dst_end = dst_offset + size;
+ size_t end = src_end > dst_end ? src_end : dst_end;
+ if (end > buf_->data.size()) {
+ buf_->data.resize(end);
+ }
+
+ uint8_t* dst = &buf_->data[dst_offset];
+ uint8_t* src = &buf_->data[src_offset];
+ memmove(dst, src, size);
+ return Result::Ok;
+}
+
+Result MemoryStream::TruncateImpl(size_t size) {
+ if (size > buf_->data.size()) {
+ return Result::Error;
+ }
+ buf_->data.resize(size);
+ return Result::Ok;
+}
+
+FileStream::FileStream(string_view filename, Stream* log_stream)
+ : Stream(log_stream), file_(nullptr), offset_(0), should_close_(false) {
+ std::string filename_str = filename.to_string();
+ file_ = fopen(filename_str.c_str(), "wb");
+
+ // TODO(binji): this is pretty cheesy, should come up with a better API.
+ if (file_) {
+ should_close_ = true;
+ } else {
+ ERROR("fopen name=\"%s\" failed, errno=%d\n", filename_str.c_str(), errno);
+ }
+}
+
+FileStream::FileStream(FILE* file, Stream* log_stream)
+ : Stream(log_stream), file_(file), offset_(0), should_close_(false) {}
+
+FileStream::FileStream(FileStream&& other) {
+ *this = std::move(other);
+}
+
+FileStream& FileStream::operator=(FileStream&& other) {
+ file_ = other.file_;
+ offset_ = other.offset_;
+ should_close_ = other.should_close_;
+ other.file_ = nullptr;
+ other.offset_ = 0;
+ other.should_close_ = false;
+ return *this;
+}
+
+FileStream::~FileStream() {
+ // We don't want to close existing files (stdout/sterr, for example).
+ if (should_close_) {
+ fclose(file_);
+ }
+}
+
+void FileStream::Flush() {
+ if (file_) fflush(file_);
+}
+
+Result FileStream::WriteDataImpl(size_t at, const void* data, size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ if (size == 0) {
+ return Result::Ok;
+ }
+ if (at != offset_) {
+ if (fseek(file_, at, SEEK_SET) != 0) {
+ ERROR("fseek offset=%" PRIzd " failed, errno=%d\n", size, errno);
+ return Result::Error;
+ }
+ offset_ = at;
+ }
+ if (fwrite(data, size, 1, file_) != 1) {
+ ERROR("fwrite size=%" PRIzd " failed, errno=%d\n", size, errno);
+ return Result::Error;
+ }
+ offset_ += size;
+ return Result::Ok;
+}
+
+Result FileStream::MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ if (size == 0) {
+ return Result::Ok;
+ }
+ // TODO(binji): implement if needed.
+ ERROR0("FileStream::MoveDataImpl not implemented!\n");
+ return Result::Error;
+}
+
+Result FileStream::TruncateImpl(size_t size) {
+ if (!file_) {
+ return Result::Error;
+ }
+ // TODO(binji): implement if needed.
+ ERROR0("FileStream::TruncateImpl not implemented!\n");
+ return Result::Error;
+}
+
+// static
+std::unique_ptr<FileStream> FileStream::CreateStdout() {
+ return std::unique_ptr<FileStream>(new FileStream(stdout));
+}
+
+// static
+std::unique_ptr<FileStream> FileStream::CreateStderr() {
+ return std::unique_ptr<FileStream>(new FileStream(stderr));
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/stream.h b/third_party/wasm2c/src/stream.h
new file mode 100644
index 0000000000..66582fa605
--- /dev/null
+++ b/third_party/wasm2c/src/stream.h
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_STREAM_H_
+#define WABT_STREAM_H_
+
+#include <cassert>
+#include <memory>
+#include <vector>
+
+#include "src/common.h"
+
+namespace wabt {
+
+/* whether to display the ASCII characters in the debug output for
+ * write_memory */
+enum class PrintChars {
+ No = 0,
+ Yes = 1,
+};
+
+class Stream {
+ public:
+ explicit Stream(Stream* log_stream = nullptr);
+ virtual ~Stream() = default;
+
+ size_t offset() { return offset_; }
+ Result result() { return result_; }
+
+ void set_log_stream(Stream* stream) {
+ assert(stream);
+ log_stream_ = stream;
+ }
+
+ Stream& log_stream() {
+ assert(log_stream_);
+ return *log_stream_;
+ }
+
+ bool has_log_stream() const { return log_stream_ != nullptr; }
+
+ void ClearOffset() { offset_ = 0; }
+ void AddOffset(ssize_t delta);
+
+ void WriteData(const void* src,
+ size_t size,
+ const char* desc = nullptr,
+ PrintChars = PrintChars::No);
+
+ template <typename T>
+ void WriteData(const std::vector<T> src,
+ const char* desc,
+ PrintChars print_chars = PrintChars::No) {
+ if (!src.empty()) {
+ WriteData(src.data(), src.size() * sizeof(T), desc, print_chars);
+ }
+ }
+
+ void WriteDataAt(size_t offset,
+ const void* src,
+ size_t size,
+ const char* desc = nullptr,
+ PrintChars = PrintChars::No);
+
+ void MoveData(size_t dst_offset, size_t src_offset, size_t size);
+
+ void Truncate(size_t size);
+
+ void WABT_PRINTF_FORMAT(2, 3) Writef(const char* format, ...);
+
+ // Specified as uint32_t instead of uint8_t so we can check if the value
+ // given is in range before wrapping.
+ void WriteU8(uint32_t value,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ assert(value <= UINT8_MAX);
+ Write(static_cast<uint8_t>(value), desc, print_chars);
+ }
+ void WriteU32(uint32_t value,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ Write(value, desc, print_chars);
+ }
+ void WriteU64(uint64_t value,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ Write(value, desc, print_chars);
+ }
+ void WriteU128(v128 value,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ Write(value, desc, print_chars);
+ }
+
+ void WriteChar(char c,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ WriteU8(static_cast<unsigned char>(c), desc, print_chars);
+ }
+
+ // Dump memory as text, similar to the xxd format.
+ void WriteMemoryDump(const void* start,
+ size_t size,
+ size_t offset = 0,
+ PrintChars print_chars = PrintChars::No,
+ const char* prefix = nullptr,
+ const char* desc = nullptr);
+
+ // Convenience functions for writing enums.
+ template <typename T>
+ void WriteU8Enum(T value,
+ const char* desc = nullptr,
+ PrintChars print_chars = PrintChars::No) {
+ WriteU8(static_cast<uint32_t>(value), desc, print_chars);
+ }
+
+ virtual void Flush() {}
+
+ protected:
+ virtual Result WriteDataImpl(size_t offset,
+ const void* data,
+ size_t size) = 0;
+ virtual Result MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) = 0;
+ virtual Result TruncateImpl(size_t size) = 0;
+
+ private:
+ template <typename T>
+ void Write(const T& data, const char* desc, PrintChars print_chars) {
+#if WABT_BIG_ENDIAN
+ char tmp[sizeof(T)];
+ memcpy(tmp, &data, sizeof(tmp));
+ SwapBytesSized(tmp, sizeof(tmp));
+ WriteData(tmp, sizeof(tmp), desc, print_chars);
+#else
+ WriteData(&data, sizeof(data), desc, print_chars);
+#endif
+ }
+
+ size_t offset_;
+ Result result_;
+ // Not owned. If non-null, log all writes to this stream.
+ Stream* log_stream_;
+};
+
+struct OutputBuffer {
+ Result WriteToFile(string_view filename) const;
+
+ void clear() { data.clear(); }
+ size_t size() const { return data.size(); }
+
+ std::vector<uint8_t> data;
+};
+
+class MemoryStream : public Stream {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(MemoryStream);
+ explicit MemoryStream(Stream* log_stream = nullptr);
+ explicit MemoryStream(std::unique_ptr<OutputBuffer>&&,
+ Stream* log_stream = nullptr);
+
+ OutputBuffer& output_buffer() { return *buf_; }
+ std::unique_ptr<OutputBuffer> ReleaseOutputBuffer();
+
+ void Clear();
+
+ Result WriteToFile(string_view filename) {
+ return buf_->WriteToFile(filename);
+ }
+
+ protected:
+ Result WriteDataImpl(size_t offset, const void* data, size_t size) override;
+ Result MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) override;
+ Result TruncateImpl(size_t size) override;
+
+ private:
+ std::unique_ptr<OutputBuffer> buf_;
+};
+
+class FileStream : public Stream {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(FileStream);
+ explicit FileStream(string_view filename, Stream* log_stream = nullptr);
+ explicit FileStream(FILE*, Stream* log_stream = nullptr);
+ FileStream(FileStream&&);
+ FileStream& operator=(FileStream&&);
+ ~FileStream() override;
+
+ static std::unique_ptr<FileStream> CreateStdout();
+ static std::unique_ptr<FileStream> CreateStderr();
+
+ bool is_open() const { return file_ != nullptr; }
+
+ void Flush() override;
+
+ protected:
+ Result WriteDataImpl(size_t offset, const void* data, size_t size) override;
+ Result MoveDataImpl(size_t dst_offset,
+ size_t src_offset,
+ size_t size) override;
+ Result TruncateImpl(size_t size) override;
+
+ private:
+ FILE* file_;
+ size_t offset_;
+ bool should_close_;
+};
+
+} // namespace wabt
+
+#endif /* WABT_STREAM_H_ */
diff --git a/third_party/wasm2c/src/string-view.cc b/third_party/wasm2c/src/string-view.cc
new file mode 100644
index 0000000000..bb32410a8e
--- /dev/null
+++ b/third_party/wasm2c/src/string-view.cc
@@ -0,0 +1,196 @@
+/*
+ * 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 "src/string-view.h"
+
+#include <algorithm>
+#include <limits>
+
+namespace wabt {
+
+void string_view::remove_prefix(size_type n) {
+ assert(n <= size_);
+ data_ += n;
+ size_ -= n;
+}
+
+void string_view::remove_suffix(size_type n) {
+ assert(n <= size_);
+ size_ -= n;
+}
+
+void string_view::swap(string_view& s) noexcept {
+ std::swap(data_, s.data_);
+ std::swap(size_, s.size_);
+}
+
+string_view::operator std::string() const {
+ return std::string(data_, size_);
+}
+
+std::string string_view::to_string() const {
+ return std::string(data_, size_);
+}
+
+constexpr string_view::size_type string_view::max_size() const noexcept {
+ return std::numeric_limits<size_type>::max();
+}
+
+string_view::size_type string_view::copy(char* s,
+ size_type n,
+ size_type pos) const {
+ assert(pos <= size_);
+ size_t count = std::min(n, size_ - pos);
+ traits_type::copy(s, data_ + pos, count);
+ return count;
+}
+
+string_view string_view::substr(size_type pos, size_type n) const {
+ assert(pos <= size_);
+ size_t count = std::min(n, size_ - pos);
+ return string_view(data_ + pos, count);
+}
+
+int string_view::compare(string_view s) const noexcept {
+ size_type rlen = std::min(size_, s.size_);
+ int result = traits_type::compare(data_, s.data_, rlen);
+ if (result != 0 || size_ == s.size_) {
+ return result;
+ }
+ return size_ < s.size_ ? -1 : 1;
+}
+
+int string_view::compare(size_type pos1, size_type n1, string_view s) const {
+ return substr(pos1, n1).compare(s);
+}
+
+int string_view::compare(size_type pos1,
+ size_type n1,
+ string_view s,
+ size_type pos2,
+ size_type n2) const {
+ return substr(pos1, n1).compare(s.substr(pos2, n2));
+}
+
+int string_view::compare(const char* s) const {
+ return compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type n1, const char* s) const {
+ return substr(pos1, n1).compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1,
+ size_type n1,
+ const char* s,
+ size_type n2) const {
+ return substr(pos1, n1).compare(string_view(s, n2));
+}
+
+string_view::size_type string_view::find(string_view s, size_type pos) const
+ noexcept {
+ pos = std::min(pos, size_);
+ const_iterator iter = std::search(begin() + pos, end(), s.begin(), s.end());
+ return iter == end() ? npos : iter - begin();
+}
+
+string_view::size_type string_view::find(char c, size_type pos) const noexcept {
+ return find(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find(const char* s,
+ size_type pos,
+ size_type n) const {
+ return find(string_view(s, n), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos) const {
+ return find(string_view(s), pos);
+}
+
+string_view::size_type string_view::rfind(string_view s, size_type pos) const
+ noexcept {
+ pos = std::min(std::min(pos, size_ - s.size_) + s.size_, size_);
+ reverse_iterator iter = std::search(reverse_iterator(begin() + pos), rend(),
+ s.rbegin(), s.rend());
+ return iter == rend() ? npos : (rend() - iter - s.size_);
+}
+
+string_view::size_type string_view::rfind(char c, size_type pos) const
+ noexcept {
+ return rfind(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s,
+ size_type pos,
+ size_type n) const {
+ return rfind(string_view(s, n), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos) const {
+ return rfind(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_of(string_view s,
+ size_type pos) const
+ noexcept {
+ pos = std::min(pos, size_);
+ const_iterator iter =
+ std::find_first_of(begin() + pos, end(), s.begin(), s.end());
+ return iter == end() ? npos : iter - begin();
+}
+
+string_view::size_type string_view::find_first_of(char c, size_type pos) const
+ noexcept {
+ return find_first_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s,
+ size_type pos,
+ size_type n) const {
+ return find_first_of(string_view(s, n), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s,
+ size_type pos) const {
+ return find_first_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_of(string_view s,
+ size_type pos) const noexcept {
+ pos = std::min(pos, size_ - 1);
+ reverse_iterator iter = std::find_first_of(
+ reverse_iterator(begin() + (pos + 1)), rend(), s.begin(), s.end());
+ return iter == rend() ? npos : (rend() - iter - 1);
+}
+
+string_view::size_type string_view::find_last_of(char c, size_type pos) const
+ noexcept {
+ return find_last_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s,
+ size_type pos,
+ size_type n) const {
+ return find_last_of(string_view(s, n), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s,
+ size_type pos) const {
+ return find_last_of(string_view(s), pos);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/string-view.h b/third_party/wasm2c/src/string-view.h
new file mode 100644
index 0000000000..534e675c73
--- /dev/null
+++ b/third_party/wasm2c/src/string-view.h
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_STRING_VIEW_H_
+#define WABT_STRING_VIEW_H_
+
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <ostream>
+#include <string>
+
+#include "src/hash-util.h"
+
+namespace wabt {
+
+// This is a simplified implemention of C++17's basic_string_view template.
+//
+// Missing features:
+// * Not a template
+// * No allocator support
+// * Some functions are not implemented
+// * Asserts instead of exceptions
+// * Some functions are not constexpr because we don't compile in C++17 mode
+
+class string_view {
+ public:
+ typedef std::char_traits<char> traits_type;
+ typedef char value_type;
+ typedef char* pointer;
+ typedef const char* const_pointer;
+ typedef char& reference;
+ typedef const char& const_reference;
+ typedef const char* const_iterator;
+ typedef const_iterator iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef const_reverse_iterator reverse_iterator;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ static const size_type npos = size_type(-1);
+
+ // construction and assignment
+ constexpr string_view() noexcept;
+ constexpr string_view(const string_view&) noexcept = default;
+ string_view& operator=(const string_view&) noexcept = default;
+ string_view(const std::string& str) noexcept;
+ /*constexpr*/ string_view(const char* str);
+ constexpr string_view(const char* str, size_type len);
+
+ // iterator support
+ constexpr const_iterator begin() const noexcept;
+ constexpr const_iterator end() const noexcept;
+ constexpr const_iterator cbegin() const noexcept;
+ constexpr const_iterator cend() const noexcept;
+ const_reverse_iterator rbegin() const noexcept;
+ const_reverse_iterator rend() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+
+ // capacity
+ constexpr size_type size() const noexcept;
+ constexpr size_type length() const noexcept;
+ constexpr size_type max_size() const noexcept;
+ constexpr bool empty() const noexcept;
+
+ // element access
+ constexpr const_reference operator[](size_type pos) const;
+ /*constexpr*/ const_reference at(size_type pos) const;
+ /*constexpr*/ const_reference front() const;
+ /*constexpr*/ const_reference back() const;
+ constexpr const_pointer data() const noexcept;
+
+ // modifiers
+ /*constexpr*/ void remove_prefix(size_type n);
+ /*constexpr*/ void remove_suffix(size_type n);
+ /*constexpr*/ void swap(string_view& s) noexcept;
+
+ // string operations
+ explicit operator std::string() const;
+ std::string to_string() const;
+ size_type copy(char* s, size_type n, size_type pos = 0) const;
+ /*constexpr*/ string_view substr(size_type pos = 0, size_type n = npos) const;
+ /*constexpr*/ int compare(string_view s) const noexcept;
+ /*constexpr*/ int compare(size_type pos1, size_type n1, string_view s) const;
+ /*constexpr*/ int compare(size_type pos1,
+ size_type n1,
+ string_view s,
+ size_type pos2,
+ size_type n2) const;
+ /*constexpr*/ int compare(const char* s) const;
+ /*constexpr*/ int compare(size_type pos1, size_type n1, const char* s) const;
+ /*constexpr*/ int compare(size_type pos1,
+ size_type n1,
+ const char* s,
+ size_type n2) const;
+ /*constexpr*/ size_type find(string_view s, size_type pos = 0) const noexcept;
+ /*constexpr*/ size_type find(char c, size_type pos = 0) const noexcept;
+ /*constexpr*/ size_type find(const char* s, size_type pos, size_type n) const;
+ /*constexpr*/ size_type find(const char* s, size_type pos = 0) const;
+ /*constexpr*/ size_type rfind(string_view s, size_type pos = npos) const
+ noexcept;
+ /*constexpr*/ size_type rfind(char c, size_type pos = npos) const noexcept;
+ /*constexpr*/ size_type rfind(const char* s,
+ size_type pos,
+ size_type n) const;
+ /*constexpr*/ size_type rfind(const char* s, size_type pos = npos) const;
+ /*constexpr*/ size_type find_first_of(string_view s, size_type pos = 0) const
+ noexcept;
+ /*constexpr*/ size_type find_first_of(char c, size_type pos = 0) const
+ noexcept;
+ /*constexpr*/ size_type find_first_of(const char* s,
+ size_type pos,
+ size_type n) const;
+ /*constexpr*/ size_type find_first_of(const char* s, size_type pos = 0) const;
+ /*constexpr*/ size_type find_last_of(string_view s,
+ size_type pos = npos) const noexcept;
+ /*constexpr*/ size_type find_last_of(char c, size_type pos = npos) const
+ noexcept;
+ /*constexpr*/ size_type find_last_of(const char* s,
+ size_type pos,
+ size_type n) const;
+ /*constexpr*/ size_type find_last_of(const char* s,
+ size_type pos = npos) const;
+
+// TODO(binji): These are defined by C++17 basic_string_view but not
+// implemented here.
+#if 0
+ constexpr size_type find_first_not_of(string_view s, size_type pos = 0) const
+ noexcept;
+ constexpr size_type find_first_not_of(char c, size_type pos = 0) const
+ noexcept;
+ constexpr size_type find_first_not_of(const char* s,
+ size_type pos,
+ size_type n) const;
+ constexpr size_type find_first_not_of(const char* s, size_type pos = 0) const;
+ constexpr size_type find_last_not_of(string_view s,
+ size_type pos = npos) const noexcept;
+ constexpr size_type find_last_not_of(char c, size_type pos = npos) const
+ noexcept;
+ constexpr size_type find_last_not_of(const char* s,
+ size_type pos,
+ size_type n) const;
+ constexpr size_type find_last_not_of(const char* s,
+ size_type pos = npos) const;
+#endif
+
+ private:
+ const char* data_;
+ size_type size_;
+};
+
+// non-member comparison functions
+inline bool operator==(string_view x, string_view y) noexcept {
+ return x.compare(y) == 0;
+}
+
+inline bool operator!=(string_view x, string_view y) noexcept {
+ return x.compare(y) != 0;
+}
+
+inline bool operator<(string_view x, string_view y) noexcept {
+ return x.compare(y) < 0;
+}
+
+inline bool operator>(string_view x, string_view y) noexcept {
+ return x.compare(y) > 0;
+}
+
+inline bool operator<=(string_view x, string_view y) noexcept {
+ return x.compare(y) <= 0;
+}
+
+inline bool operator>=(string_view x, string_view y) noexcept {
+ return x.compare(y) >= 0;
+}
+
+// non-member append.
+inline std::string& operator+=(std::string& x, string_view y) {
+ x.append(y.data(), y.size());
+ return x;
+}
+
+inline std::string operator+(string_view x, string_view y) {
+ std::string s;
+ s.reserve(x.size() + y.size());
+ s.append(x.data(), x.size());
+ s.append(y.data(), y.size());
+ return s;
+}
+
+inline std::string operator+(const std::string& x, string_view y) {
+ return string_view(x) + y;
+}
+
+inline std::string operator+(string_view x, const std::string& y) {
+ return x + string_view(y);
+}
+
+inline std::string operator+(const char* x, string_view y) {
+ return string_view(x) + y;
+}
+
+inline std::string operator+(string_view x, const char* y) {
+ return x + string_view(y);
+}
+
+inline void cat_concatenate(std::string&) {}
+
+template<typename T, typename ...Ts>
+void cat_concatenate(std::string& s, const T& t, const Ts&... args) {
+ s += t;
+ cat_concatenate(s, args...);
+}
+
+inline size_t cat_compute_size() { return 0; }
+
+template<typename T, typename ...Ts>
+size_t cat_compute_size(const T& t, const Ts&... args) {
+ return string_view(t).size() + cat_compute_size(args...);
+}
+
+// Is able to concatenate any combination of string/string_view/char*
+template<typename ...Ts> std::string cat(const Ts&... args) {
+ std::string s;
+ s.reserve(cat_compute_size(args...));
+ cat_concatenate(s, args...);
+ return s;
+}
+
+inline constexpr string_view::string_view() noexcept
+ : data_(nullptr), size_(0) {}
+
+inline string_view::string_view(const std::string& str) noexcept
+ : data_(str.data()), size_(str.size()) {}
+
+inline string_view::string_view(const char* str)
+ : data_(str), size_(traits_type::length(str)) {}
+
+inline constexpr string_view::string_view(const char* str, size_type len)
+ : data_(str), size_(len) {}
+
+inline constexpr string_view::const_iterator string_view::begin() const
+ noexcept {
+ return data_;
+}
+
+inline constexpr string_view::const_iterator string_view::end() const noexcept {
+ return data_ + size_;
+}
+
+inline constexpr string_view::const_iterator string_view::cbegin() const
+ noexcept {
+ return data_;
+}
+
+inline constexpr string_view::const_iterator string_view::cend() const
+ noexcept {
+ return data_ + size_;
+}
+
+inline string_view::const_reverse_iterator string_view::rbegin() const
+ noexcept {
+ return const_reverse_iterator(end());
+}
+
+inline string_view::const_reverse_iterator string_view::rend() const noexcept {
+ return const_reverse_iterator(begin());
+}
+
+inline string_view::const_reverse_iterator string_view::crbegin() const
+ noexcept {
+ return const_reverse_iterator(cend());
+}
+
+inline string_view::const_reverse_iterator string_view::crend() const noexcept {
+ return const_reverse_iterator(cbegin());
+}
+
+constexpr inline string_view::size_type string_view::size() const noexcept {
+ return size_;
+}
+
+constexpr inline string_view::size_type string_view::length() const noexcept {
+ return size_;
+}
+
+constexpr inline bool string_view::empty() const noexcept {
+ return size_ == 0;
+}
+
+constexpr inline string_view::const_reference string_view::operator[](
+ size_type pos) const {
+ return data_[pos];
+}
+
+inline string_view::const_reference string_view::at(size_type pos) const {
+ assert(pos < size_);
+ return data_[pos];
+}
+
+inline string_view::const_reference string_view::front() const {
+ assert(!empty());
+ return *data_;
+}
+
+inline string_view::const_reference string_view::back() const {
+ assert(!empty());
+ return data_[size_ - 1];
+}
+
+constexpr inline string_view::const_pointer string_view::data() const noexcept {
+ return data_;
+}
+
+inline std::ostream& operator<<(std::ostream& os, string_view sv) {
+ os.write(sv.data(), sv.size());
+ return os;
+}
+
+} // namespace wabt
+
+namespace std {
+
+// hash support
+template <>
+struct hash<::wabt::string_view> {
+ ::wabt::hash_code operator()(const ::wabt::string_view& sv) {
+ return ::wabt::HashRange(sv.begin(), sv.end());
+ }
+};
+
+} // namespace std
+
+#endif // WABT_STRING_VIEW_H_
diff --git a/third_party/wasm2c/src/test-binary-reader.cc b/third_party/wasm2c/src/test-binary-reader.cc
new file mode 100644
index 0000000000..a96ccdb7a3
--- /dev/null
+++ b/third_party/wasm2c/src/test-binary-reader.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 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 "gtest/gtest.h"
+
+#include "src/binary-reader.h"
+#include "src/binary-reader-nop.h"
+#include "src/leb128.h"
+#include "src/opcode.h"
+
+using namespace wabt;
+
+namespace {
+
+struct BinaryReaderError : BinaryReaderNop {
+ bool OnError(const Error& error) override {
+ first_error = error;
+ return true; // Error handled.
+ }
+
+ Error first_error;
+};
+
+} // End of anonymous namespace
+
+TEST(BinaryReader, DisabledOpcodes) {
+ // Use the default features.
+ ReadBinaryOptions options;
+
+ // Loop through all opcodes.
+ for (uint32_t i = 0; i < static_cast<uint32_t>(Opcode::Invalid); ++i) {
+ Opcode opcode(static_cast<Opcode::Enum>(i));
+ if (opcode.IsEnabled(options.features)) {
+ continue;
+ }
+
+ // Use a shorter name to make the clang-formatted table below look nicer.
+ std::vector<uint8_t> b = opcode.GetBytes();
+ assert(b.size() <= 3);
+ b.resize(3);
+
+ uint8_t data[] = {
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, // magic + version
+ 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, // type section: 1 type, (func)
+ 0x03, 0x02, 0x01, 0x00, // func section: 1 func, type 0
+ 0x0a, 0x07, 0x01, 0x05, 0x00, // code section: 1 func, 0 locals
+ b[0], b[1], b[2], // The instruction, padded with zeroes
+ 0x0b, // end
+ };
+ const size_t size = sizeof(data);
+
+ BinaryReaderError reader;
+ Result result = ReadBinary(data, size, &reader, options);
+ EXPECT_EQ(Result::Error, result);
+
+ // This relies on the binary reader checking whether the opcode is allowed
+ // before reading any further data needed by the instruction.
+ const std::string& message = reader.first_error.message;
+ EXPECT_EQ(0u, message.find("unexpected opcode"))
+ << "Got error message: " << message;
+ }
+}
diff --git a/third_party/wasm2c/src/test-circular-array.cc b/third_party/wasm2c/src/test-circular-array.cc
new file mode 100644
index 0000000000..aefea514e2
--- /dev/null
+++ b/third_party/wasm2c/src/test-circular-array.cc
@@ -0,0 +1,284 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include <memory>
+
+#include "src/circular-array.h"
+
+using namespace wabt;
+
+namespace {
+
+struct TestObject {
+ static int construct_count;
+ static int destruct_count;
+
+ TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) {
+ construct_count++;
+ }
+
+ TestObject(const TestObject& other) {
+ *this = other;
+ construct_count++;
+ }
+
+ TestObject& operator=(const TestObject& other) {
+ data = other.data;
+ data2 = other.data2;
+ return *this;
+ }
+
+ TestObject(TestObject&& other) { *this = std::move(other); }
+
+ TestObject& operator=(TestObject&& other) {
+ data = other.data;
+ data2 = other.data2;
+ other.moved = true;
+ return *this;
+ }
+
+ ~TestObject() {
+ if (!moved) {
+ destruct_count++;
+ }
+ }
+
+ int data = 0;
+ int data2 = 0;
+ bool moved = false;
+};
+
+// static
+int TestObject::construct_count = 0;
+// static
+int TestObject::destruct_count = 0;
+
+class CircularArrayTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Reset to 0 even if the previous test leaked objects to keep the tests
+ // independent.
+ TestObject::construct_count = 0;
+ TestObject::destruct_count = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(0, TestObject::construct_count - TestObject::destruct_count)
+ << "construct count: " << TestObject::construct_count
+ << ", destruct_count: " << TestObject::destruct_count;
+ }
+
+ template <size_t N>
+ void AssertCircularArrayEq(const CircularArray<TestObject, N>& ca,
+ const std::vector<int>& expected) {
+ if (expected.empty()) {
+ ASSERT_EQ(0U, ca.size());
+ ASSERT_TRUE(ca.empty());
+ } else {
+ ASSERT_EQ(expected.size(), ca.size());
+ ASSERT_FALSE(ca.empty());
+
+ ASSERT_EQ(expected.front(), ca.front().data);
+ ASSERT_EQ(expected.back(), ca.back().data);
+
+ for (size_t i = 0; i < ca.size(); ++i) {
+ ASSERT_EQ(expected[i], ca.at(i).data);
+ ASSERT_EQ(expected[i], ca[i].data);
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+// Basic API tests
+
+TEST_F(CircularArrayTest, default_constructor) {
+ CircularArray<TestObject, 2> ca;
+}
+
+TEST_F(CircularArrayTest, at) {
+ CircularArray<TestObject, 2> ca;
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, ca.at(0).data);
+ ASSERT_EQ(2, ca.at(1).data);
+}
+
+TEST_F(CircularArrayTest, const_at) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, cca.at(0).data);
+ ASSERT_EQ(2, cca.at(1).data);
+}
+
+TEST_F(CircularArrayTest, operator_brackets) {
+ CircularArray<TestObject, 2> ca;
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, ca[0].data);
+ ASSERT_EQ(2, ca[1].data);
+}
+
+TEST_F(CircularArrayTest, const_operator_brackets) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+
+ ASSERT_EQ(1, cca[0].data);
+ ASSERT_EQ(2, cca[1].data);
+}
+
+TEST_F(CircularArrayTest, back) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, ca.back().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2, ca.back().data);
+}
+
+TEST_F(CircularArrayTest, const_back) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, cca.back().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2, cca.back().data);
+}
+
+TEST_F(CircularArrayTest, empty) {
+ CircularArray<TestObject, 2> ca;
+
+ ASSERT_TRUE(ca.empty());
+
+ ca.push_back(TestObject(1));
+ ASSERT_FALSE(ca.empty());
+
+ ca.push_back(TestObject(2));
+ ASSERT_FALSE(ca.empty());
+
+ ca.pop_back();
+ ASSERT_FALSE(ca.empty());
+
+ ca.pop_back();
+ ASSERT_TRUE(ca.empty());
+}
+
+TEST_F(CircularArrayTest, front) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, ca.front().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(1, ca.front().data);
+}
+
+TEST_F(CircularArrayTest, const_front) {
+ CircularArray<TestObject, 2> ca;
+ const auto& cca = ca;
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1, cca.front().data);
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(1, cca.front().data);
+}
+
+TEST_F(CircularArrayTest, size) {
+ CircularArray<TestObject, 2> ca;
+
+ ASSERT_EQ(0U, ca.size());
+
+ ca.push_back(TestObject(1));
+ ASSERT_EQ(1U, ca.size());
+
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2U, ca.size());
+
+ ca.pop_back();
+ ASSERT_EQ(1U, ca.size());
+
+ ca.pop_back();
+ ASSERT_EQ(0U, ca.size());
+}
+
+TEST_F(CircularArrayTest, clear) {
+ CircularArray<TestObject, 2> ca;
+
+ ca.push_back(TestObject(1));
+ ca.push_back(TestObject(2));
+ ASSERT_EQ(2U, ca.size());
+
+ ca.clear();
+ ASSERT_EQ(0U, ca.size());
+}
+
+// More involved tests
+
+TEST_F(CircularArrayTest, circular) {
+ CircularArray<TestObject, 4> ca;
+
+ ca.push_back(TestObject(1));
+ AssertCircularArrayEq(ca, {1});
+
+ ca.push_back(TestObject(2));
+ AssertCircularArrayEq(ca, {1, 2});
+
+ ca.push_back(TestObject(3));
+ AssertCircularArrayEq(ca, {1, 2, 3});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {2, 3});
+
+ ca.push_back(TestObject(4));
+ AssertCircularArrayEq(ca, {2, 3, 4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {3, 4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {4});
+
+ ca.push_back(TestObject(5));
+ AssertCircularArrayEq(ca, {4, 5});
+
+ ca.push_back(TestObject(6));
+ AssertCircularArrayEq(ca, {4, 5, 6});
+
+ ca.pop_back();
+ AssertCircularArrayEq(ca, {4, 5});
+
+ ca.pop_back();
+ AssertCircularArrayEq(ca, {4});
+
+ ca.pop_front();
+ AssertCircularArrayEq(ca, {});
+}
diff --git a/third_party/wasm2c/src/test-filenames.cc b/third_party/wasm2c/src/test-filenames.cc
new file mode 100644
index 0000000000..5b0282fce7
--- /dev/null
+++ b/third_party/wasm2c/src/test-filenames.cc
@@ -0,0 +1,61 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include "src/filenames.h"
+
+using namespace wabt;
+
+namespace {
+
+void assert_string_view_eq(const char* s, string_view sv) {
+ size_t len = std::strlen(s);
+ ASSERT_EQ(len, sv.size());
+ for (size_t i = 0; i < len; ++i) {
+ ASSERT_EQ(s[i], sv[i]);
+ }
+}
+
+} // end anonymous namespace
+
+TEST(filenames, GetBasename) {
+ assert_string_view_eq("foo.txt", GetBasename("/bar/dir/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("bar/dir/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("/foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("\\bar\\dir\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("bar\\dir\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("\\foo.txt"));
+ assert_string_view_eq("foo.txt", GetBasename("foo.txt"));
+ assert_string_view_eq("foo", GetBasename("foo"));
+ assert_string_view_eq("", GetBasename(""));
+}
+
+TEST(filenames, GetExtension) {
+ assert_string_view_eq(".txt", GetExtension("/bar/dir/foo.txt"));
+ assert_string_view_eq(".txt", GetExtension("bar/dir/foo.txt"));
+ assert_string_view_eq(".txt", GetExtension("foo.txt"));
+ assert_string_view_eq("", GetExtension("foo"));
+ assert_string_view_eq("", GetExtension(""));
+}
+
+TEST(filenames, StripExtension) {
+ assert_string_view_eq("/bar/dir/foo", StripExtension("/bar/dir/foo.txt"));
+ assert_string_view_eq("bar/dir/foo", StripExtension("bar/dir/foo.txt"));
+ assert_string_view_eq("foo", StripExtension("foo.txt"));
+ assert_string_view_eq("foo", StripExtension("foo"));
+ assert_string_view_eq("", StripExtension(""));
+}
diff --git a/third_party/wasm2c/src/test-hexfloat.cc b/third_party/wasm2c/src/test-hexfloat.cc
new file mode 100644
index 0000000000..7b493518ec
--- /dev/null
+++ b/third_party/wasm2c/src/test-hexfloat.cc
@@ -0,0 +1,264 @@
+/*
+ * 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 <cstdio>
+#include <thread>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "src/literal.h"
+
+#define FOREACH_UINT32_MULTIPLIER 1
+
+#define FOREACH_UINT32(bits) \
+ uint32_t last_bits = 0; \
+ uint32_t bits = shard; \
+ int last_top_byte = -1; \
+ for (; bits >= last_bits; \
+ last_bits = bits, bits += num_threads_ * FOREACH_UINT32_MULTIPLIER)
+
+#define LOG_COMPLETION(bits) \
+ if (shard == 0) { \
+ int top_byte = bits >> 24; \
+ if (top_byte != last_top_byte) { \
+ printf("value: 0x%08x (%d%%)\r", bits, \
+ static_cast<int>(static_cast<double>(bits) * 100 / UINT32_MAX)); \
+ fflush(stdout); \
+ last_top_byte = top_byte; \
+ } \
+ }
+
+#define LOG_DONE() \
+ if (shard == 0) { \
+ printf("done.\n"); \
+ fflush(stdout); \
+ }
+
+using namespace wabt;
+
+template <typename T, typename F>
+T bit_cast(F value) {
+ T result;
+ memcpy(&result, &value, sizeof(result));
+ return result;
+}
+
+static bool is_infinity_or_nan(uint32_t float_bits) {
+ return ((float_bits >> 23) & 0xff) == 0xff;
+}
+
+static bool is_infinity_or_nan(uint64_t double_bits) {
+ return ((double_bits >> 52) & 0x7ff) == 0x7ff;
+}
+
+class ThreadedTest : public ::testing::Test {
+ protected:
+ static const int kDefaultNumThreads = 2;
+
+ virtual void SetUp() {
+ num_threads_ = std::thread::hardware_concurrency();
+ if (num_threads_ == 0)
+ num_threads_ = kDefaultNumThreads;
+ }
+
+ virtual void RunShard(int shard) = 0;
+
+ void RunThreads() {
+ std::vector<std::thread> threads;
+
+ for (int i = 0; i < num_threads_; ++i) {
+ threads.emplace_back(&ThreadedTest::RunShard, this, i);
+ }
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+ }
+
+ int num_threads_;
+};
+
+/* floats */
+class AllFloatsParseTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ float value = bit_cast<float>(bits);
+ int len = snprintf(buffer, sizeof(buffer), "%a", value);
+
+ uint32_t me;
+ ASSERT_EQ(Result::Ok,
+ ParseFloat(LiteralType::Hexfloat, buffer, buffer + len, &me));
+ ASSERT_EQ(me, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsParseTest, Run) {
+ RunThreads();
+}
+
+class AllFloatsWriteTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ WriteFloatHex(buffer, sizeof(buffer), bits);
+
+ char* endptr;
+ float them_float = strtof(buffer, &endptr);
+ uint32_t them_bits = bit_cast<uint32_t>(them_float);
+ ASSERT_EQ(bits, them_bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsWriteTest, Run) {
+ RunThreads();
+}
+
+class AllFloatsRoundtripTest : public ThreadedTest {
+ protected:
+ static LiteralType ClassifyFloat(uint32_t float_bits) {
+ if (is_infinity_or_nan(float_bits)) {
+ if (float_bits & 0x7fffff) {
+ return LiteralType::Nan;
+ } else {
+ return LiteralType::Infinity;
+ }
+ } else {
+ return LiteralType::Hexfloat;
+ }
+ }
+
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(bits) {
+ LOG_COMPLETION(bits);
+ WriteFloatHex(buffer, sizeof(buffer), bits);
+ int len = strlen(buffer);
+
+ uint32_t new_bits;
+ ASSERT_EQ(Result::Ok, ParseFloat(ClassifyFloat(bits), buffer,
+ buffer + len, &new_bits));
+ ASSERT_EQ(new_bits, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(AllFloatsRoundtripTest, Run) {
+ RunThreads();
+}
+
+/* doubles */
+class ManyDoublesParseTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ double value = bit_cast<double>(bits);
+ int len = snprintf(buffer, sizeof(buffer), "%a", value);
+
+ uint64_t me;
+ ASSERT_EQ(Result::Ok,
+ ParseDouble(LiteralType::Hexfloat, buffer, buffer + len, &me));
+ ASSERT_EQ(me, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesParseTest, Run) {
+ RunThreads();
+}
+
+class ManyDoublesWriteTest : public ThreadedTest {
+ protected:
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ if (is_infinity_or_nan(bits))
+ continue;
+
+ WriteDoubleHex(buffer, sizeof(buffer), bits);
+
+ char* endptr;
+ double them_double = strtod(buffer, &endptr);
+ uint64_t them_bits = bit_cast<uint64_t>(them_double);
+ ASSERT_EQ(bits, them_bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesWriteTest, Run) {
+ RunThreads();
+}
+
+class ManyDoublesRoundtripTest : public ThreadedTest {
+ protected:
+ static LiteralType ClassifyDouble(uint64_t double_bits) {
+ if (is_infinity_or_nan(double_bits)) {
+ if (double_bits & 0xfffffffffffffULL) {
+ return LiteralType::Nan;
+ } else {
+ return LiteralType::Infinity;
+ }
+ } else {
+ return LiteralType::Hexfloat;
+ }
+ }
+
+ virtual void RunShard(int shard) {
+ char buffer[100];
+ FOREACH_UINT32(halfbits) {
+ LOG_COMPLETION(halfbits);
+ uint64_t bits = (static_cast<uint64_t>(halfbits) << 32) | halfbits;
+ WriteDoubleHex(buffer, sizeof(buffer), bits);
+ int len = strlen(buffer);
+
+ uint64_t new_bits;
+ ASSERT_EQ(Result::Ok, ParseDouble(ClassifyDouble(bits), buffer,
+ buffer + len, &new_bits));
+ ASSERT_EQ(new_bits, bits);
+ }
+ LOG_DONE();
+ }
+};
+
+TEST_F(ManyDoublesRoundtripTest, Run) {
+ RunThreads();
+}
diff --git a/third_party/wasm2c/src/test-interp.cc b/third_party/wasm2c/src/test-interp.cc
new file mode 100644
index 0000000000..3a77194cad
--- /dev/null
+++ b/third_party/wasm2c/src/test-interp.cc
@@ -0,0 +1,693 @@
+/*
+ * Copyright 2020 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 "gtest/gtest.h"
+
+#include "src/binary-reader.h"
+#include "src/error-formatter.h"
+
+#include "src/interp/binary-reader-interp.h"
+#include "src/interp/interp.h"
+
+using namespace wabt;
+using namespace wabt::interp;
+
+class InterpTest : public ::testing::Test {
+ public:
+ void ReadModule(const std::vector<u8>& data) {
+ Errors errors;
+ ReadBinaryOptions options;
+ Result result = ReadBinaryInterp(data.data(), data.size(), options, &errors,
+ &module_desc_);
+ ASSERT_EQ(Result::Ok, result)
+ << FormatErrorsToString(errors, Location::Type::Binary);
+ }
+
+ void Instantiate(const RefVec& imports = RefVec{}) {
+ mod_ = Module::New(store_, module_desc_);
+ RefPtr<Trap> trap;
+ inst_ = Instance::Instantiate(store_, mod_.ref(), imports, &trap);
+ ASSERT_TRUE(inst_) << trap->message();
+ }
+
+ DefinedFunc::Ptr GetFuncExport(interp::Index index) {
+ EXPECT_LT(index, inst_->exports().size());
+ return store_.UnsafeGet<DefinedFunc>(inst_->exports()[index]);
+ }
+
+ void ExpectBufferStrEq(OutputBuffer& buf, const char* str) {
+ std::string buf_str(buf.data.begin(), buf.data.end());
+ EXPECT_STREQ(buf_str.c_str(), str);
+ }
+
+ Store store_;
+ ModuleDesc module_desc_;
+ Module::Ptr mod_;
+ Instance::Ptr inst_;
+};
+
+
+TEST_F(InterpTest, Empty) {
+ ASSERT_TRUE(mod_.empty());
+ ReadModule({0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00});
+ Instantiate();
+ ASSERT_FALSE(mod_.empty());
+}
+
+TEST_F(InterpTest, MVP) {
+ // (module
+ // (type (;0;) (func (param i32) (result i32)))
+ // (type (;1;) (func (param f32) (result f32)))
+ // (type (;2;) (func))
+ // (import "foo" "bar" (func (;0;) (type 0)))
+ // (func (;1;) (type 1) (param f32) (result f32)
+ // (f32.const 0x1.5p+5 (;=42;)))
+ // (func (;2;) (type 2))
+ // (table (;0;) 1 2 funcref)
+ // (memory (;0;) 1)
+ // (global (;0;) i32 (i32.const 1))
+ // (export "quux" (func 1))
+ // (start 2)
+ // (elem (;0;) (i32.const 0) 0 1)
+ // (data (;0;) (i32.const 2) "hello"))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7d, 0x01, 0x7d, 0x60, 0x00, 0x00,
+ 0x02, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72, 0x00,
+ 0x00, 0x03, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01,
+ 0x02, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x06, 0x01, 0x7f, 0x00, 0x41,
+ 0x01, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x71, 0x75, 0x75, 0x78, 0x00, 0x01,
+ 0x08, 0x01, 0x02, 0x09, 0x08, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x02, 0x00,
+ 0x01, 0x0a, 0x0c, 0x02, 0x07, 0x00, 0x43, 0x00, 0x00, 0x28, 0x42, 0x0b,
+ 0x02, 0x00, 0x0b, 0x0b, 0x0b, 0x01, 0x00, 0x41, 0x02, 0x0b, 0x05, 0x68,
+ 0x65, 0x6c, 0x6c, 0x6f,
+ });
+
+ EXPECT_EQ(3u, module_desc_.func_types.size());
+ EXPECT_EQ(1u, module_desc_.imports.size());
+ EXPECT_EQ(2u, module_desc_.funcs.size());
+ EXPECT_EQ(1u, module_desc_.tables.size());
+ EXPECT_EQ(1u, module_desc_.memories.size());
+ EXPECT_EQ(1u, module_desc_.globals.size());
+ EXPECT_EQ(0u, module_desc_.tags.size());
+ EXPECT_EQ(1u, module_desc_.exports.size());
+ EXPECT_EQ(1u, module_desc_.starts.size());
+ EXPECT_EQ(1u, module_desc_.elems.size());
+ EXPECT_EQ(1u, module_desc_.datas.size());
+}
+
+namespace {
+
+// (func (export "fac") (param $n i32) (result i32)
+// (local $result i32)
+// (local.set $result (i32.const 1))
+// (loop (result i32)
+// (local.set $result
+// (i32.mul
+// (br_if 1 (local.get $result) (i32.eqz (local.get $n)))
+// (local.get $n)))
+// (local.set $n (i32.sub (local.get $n) (i32.const 1)))
+// (br 0)))
+const std::vector<u8> s_fac_module = {
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01,
+ 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07,
+ 0x01, 0x03, 0x66, 0x61, 0x63, 0x00, 0x00, 0x0a, 0x22, 0x01, 0x20,
+ 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x01, 0x03, 0x7f, 0x20, 0x01,
+ 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x6c, 0x21, 0x01, 0x20,
+ 0x00, 0x41, 0x01, 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x0b,
+};
+
+} // namespace
+
+TEST_F(InterpTest, Disassemble) {
+ ReadModule(s_fac_module);
+
+ MemoryStream stream;
+ module_desc_.istream.Disassemble(&stream);
+ auto buf = stream.ReleaseOutputBuffer();
+
+ ExpectBufferStrEq(*buf,
+R"( 0| alloca 1
+ 8| i32.const 1
+ 16| local.set $2, %[-1]
+ 24| local.get $1
+ 32| local.get $3
+ 40| i32.eqz %[-1]
+ 44| br_unless @60, %[-1]
+ 52| br @116
+ 60| local.get $3
+ 68| i32.mul %[-2], %[-1]
+ 72| local.set $2, %[-1]
+ 80| local.get $2
+ 88| i32.const 1
+ 96| i32.sub %[-2], %[-1]
+ 100| local.set $3, %[-1]
+ 108| br @24
+ 116| drop_keep $2 $1
+ 128| return
+)");
+}
+
+TEST_F(InterpTest, Fac) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = func->Call(store_, {Value::Make(5)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(120u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, Fac_Trace) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ MemoryStream stream;
+ Result result = func->Call(store_, {Value::Make(2)}, results, &trap, &stream);
+ ASSERT_EQ(Result::Ok, result);
+
+ auto buf = stream.ReleaseOutputBuffer();
+ ExpectBufferStrEq(*buf,
+R"(#0. 0: V:1 | alloca 1
+#0. 8: V:2 | i32.const 1
+#0. 16: V:3 | local.set $2, 1
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 2
+#0. 44: V:4 | br_unless @60, 0
+#0. 60: V:3 | local.get $3
+#0. 68: V:4 | i32.mul 1, 2
+#0. 72: V:3 | local.set $2, 2
+#0. 80: V:2 | local.get $2
+#0. 88: V:3 | i32.const 1
+#0. 96: V:4 | i32.sub 2, 1
+#0. 100: V:3 | local.set $3, 1
+#0. 108: V:2 | br @24
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 1
+#0. 44: V:4 | br_unless @60, 0
+#0. 60: V:3 | local.get $3
+#0. 68: V:4 | i32.mul 2, 1
+#0. 72: V:3 | local.set $2, 2
+#0. 80: V:2 | local.get $2
+#0. 88: V:3 | i32.const 1
+#0. 96: V:4 | i32.sub 1, 1
+#0. 100: V:3 | local.set $3, 0
+#0. 108: V:2 | br @24
+#0. 24: V:2 | local.get $1
+#0. 32: V:3 | local.get $3
+#0. 40: V:4 | i32.eqz 0
+#0. 44: V:4 | br_unless @60, 1
+#0. 52: V:3 | br @116
+#0. 116: V:3 | drop_keep $2 $1
+#0. 128: V:1 | return
+)");
+}
+
+TEST_F(InterpTest, Local_Trace) {
+ // (func (export "a")
+ // (local i32 i64 f32 f64)
+ // (local.set 0 (i32.const 0))
+ // (local.set 1 (i64.const 1))
+ // (local.set 2 (f32.const 2))
+ // (local.set 3 (f64.const 3)))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01,
+ 0x61, 0x00, 0x00, 0x0a, 0x26, 0x01, 0x24, 0x04, 0x01, 0x7f, 0x01,
+ 0x7e, 0x01, 0x7d, 0x01, 0x7c, 0x41, 0x00, 0x21, 0x00, 0x42, 0x01,
+ 0x21, 0x01, 0x43, 0x00, 0x00, 0x00, 0x40, 0x21, 0x02, 0x44, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x21, 0x03, 0x0b,
+ });
+
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ MemoryStream stream;
+ Result result = func->Call(store_, {}, results, &trap, &stream);
+ ASSERT_EQ(Result::Ok, result);
+
+ auto buf = stream.ReleaseOutputBuffer();
+ ExpectBufferStrEq(*buf,
+R"(#0. 0: V:0 | alloca 4
+#0. 8: V:4 | i32.const 0
+#0. 16: V:5 | local.set $5, 0
+#0. 24: V:4 | i64.const 1
+#0. 36: V:5 | local.set $4, 1
+#0. 44: V:4 | f32.const 2
+#0. 52: V:5 | local.set $3, 2
+#0. 60: V:4 | f64.const 3
+#0. 72: V:5 | local.set $2, 3
+#0. 80: V:4 | drop_keep $4 $0
+#0. 92: V:0 | return
+)");
+}
+
+TEST_F(InterpTest, HostFunc) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (result i32)
+ // (call $f (i32.const 1)))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a,
+ 0x02, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x01, 0x7f,
+ 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, 0x03, 0x02,
+ 0x01, 0x01, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x08, 0x01, 0x06, 0x00, 0x41, 0x01, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ results[0] = Value::Make(params[0].Get<u32>() + 1);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = GetFuncExport(0)->Call(store_, {}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(2u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostFunc_PingPong) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (param i32) (result i32)
+ // (call $f (i32.add (local.get 0) (i32.const 1))))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func = HostFunc::New(
+ store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ auto val = params[0].Get<u32>();
+ if (val < 10) {
+ return GetFuncExport(0)->Call(store_, {Value::Make(val * 2)}, results,
+ out_trap);
+ }
+ results[0] = Value::Make(val);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ // Should produce the following calls:
+ // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(11u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostFunc_PingPong_SameThread) {
+ // (import "" "f" (func $f (param i32) (result i32)))
+ // (func (export "g") (param i32) (result i32)
+ // (call $f (i32.add (local.get 0) (i32.const 1))))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
+ 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a,
+ 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b,
+ });
+
+ auto thread = Thread::New(store_, {});
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [&](Thread& t, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ auto val = params[0].Get<u32>();
+ if (val < 10) {
+ return GetFuncExport(0)->Call(t, {Value::Make(val * 2)},
+ results, out_trap);
+ }
+ results[0] = Value::Make(val);
+ return Result::Ok;
+ });
+
+ Instantiate({host_func->self()});
+
+ // Should produce the following calls:
+ // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11
+
+ Values results;
+ Trap::Ptr trap;
+ Result result = GetFuncExport(0)->Call(*thread, {Value::Make(1)}, results, &trap);
+
+ ASSERT_EQ(Result::Ok, result);
+ EXPECT_EQ(1u, results.size());
+ EXPECT_EQ(11u, results[0].Get<u32>());
+}
+
+TEST_F(InterpTest, HostTrap) {
+ // (import "host" "a" (func $0))
+ // (func $1 call $0)
+ // (start $1)
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74,
+ 0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01,
+ 0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{}, {}},
+ [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ *out_trap = Trap::New(store_, "boom");
+ return Result::Error;
+ });
+
+ mod_ = Module::New(store_, module_desc_);
+ RefPtr<Trap> trap;
+ Instance::Instantiate(store_, mod_.ref(), {host_func->self()}, &trap);
+
+ ASSERT_TRUE(trap);
+ ASSERT_EQ("boom", trap->message());
+}
+
+TEST_F(InterpTest, Rot13) {
+ // (import "host" "mem" (memory $mem 1))
+ // (import "host" "fill_buf" (func $fill_buf (param i32 i32) (result i32)))
+ // (import "host" "buf_done" (func $buf_done (param i32 i32)))
+ //
+ // (func $rot13c (param $c i32) (result i32)
+ // (local $uc i32)
+ //
+ // ;; No change if < 'A'.
+ // (if (i32.lt_u (get_local $c) (i32.const 65))
+ // (return (get_local $c)))
+ //
+ // ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111
+ // (set_local $uc (i32.and (get_local $c) (i32.const 0xdf)))
+ //
+ // ;; In range ['A', 'M'] return |c| + 13.
+ // (if (i32.le_u (get_local $uc) (i32.const 77))
+ // (return (i32.add (get_local $c) (i32.const 13))))
+ //
+ // ;; In range ['N', 'Z'] return |c| - 13.
+ // (if (i32.le_u (get_local $uc) (i32.const 90))
+ // (return (i32.sub (get_local $c) (i32.const 13))))
+ //
+ // ;; No change for everything else.
+ // (return (get_local $c))
+ // )
+ //
+ // (func (export "rot13")
+ // (local $size i32)
+ // (local $i i32)
+ //
+ // ;; Ask host to fill memory [0, 1024) with data.
+ // (call $fill_buf (i32.const 0) (i32.const 1024))
+ //
+ // ;; The host returns the size filled.
+ // (set_local $size)
+ //
+ // ;; Loop over all bytes and rot13 them.
+ // (block $exit
+ // (loop $top
+ // ;; if (i >= size) break
+ // (if (i32.ge_u (get_local $i) (get_local $size)) (br $exit))
+ //
+ // ;; mem[i] = rot13c(mem[i])
+ // (i32.store8
+ // (get_local $i)
+ // (call $rot13c
+ // (i32.load8_u (get_local $i))))
+ //
+ // ;; i++
+ // (set_local $i (i32.add (get_local $i) (i32.const 1)))
+ // (br $top)
+ // )
+ // )
+ //
+ // (call $buf_done (i32.const 0) (get_local $size))
+ // )
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60,
+ 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01,
+ 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x2d, 0x03, 0x04, 0x68, 0x6f,
+ 0x73, 0x74, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x00, 0x01, 0x04, 0x68, 0x6f,
+ 0x73, 0x74, 0x08, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x00,
+ 0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x08, 0x62, 0x75, 0x66, 0x5f, 0x64,
+ 0x6f, 0x6e, 0x65, 0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x03, 0x07, 0x09,
+ 0x01, 0x05, 0x72, 0x6f, 0x74, 0x31, 0x33, 0x00, 0x03, 0x0a, 0x74, 0x02,
+ 0x39, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x41, 0xc1, 0x00, 0x49, 0x04, 0x40,
+ 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0xdf, 0x01, 0x71, 0x21, 0x01,
+ 0x20, 0x01, 0x41, 0xcd, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x0d,
+ 0x6a, 0x0f, 0x0b, 0x20, 0x01, 0x41, 0xda, 0x00, 0x4d, 0x04, 0x40, 0x20,
+ 0x00, 0x41, 0x0d, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x0f, 0x0b, 0x38, 0x01,
+ 0x02, 0x7f, 0x41, 0x00, 0x41, 0x80, 0x08, 0x10, 0x00, 0x21, 0x00, 0x02,
+ 0x40, 0x03, 0x40, 0x20, 0x01, 0x20, 0x00, 0x4f, 0x04, 0x40, 0x0c, 0x02,
+ 0x0b, 0x20, 0x01, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x10, 0x02, 0x3a, 0x00,
+ 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b,
+ 0x41, 0x00, 0x20, 0x00, 0x10, 0x01, 0x0b,
+ });
+
+ auto host_func =
+ HostFunc::New(store_, FuncType{{ValueType::I32}, {ValueType::I32}},
+ [](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ results[0] = Value::Make(params[0].Get<u32>() + 1);
+ return Result::Ok;
+ });
+
+ std::string string_data = "Hello, WebAssembly!";
+
+ auto memory = Memory::New(store_, MemoryType{Limits{1}});
+
+ auto fill_buf = [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ // (param $ptr i32) (param $max_size i32) (result $size i32)
+ EXPECT_EQ(2u, params.size());
+ EXPECT_EQ(1u, results.size());
+
+ u32 ptr = params[0].Get<u32>();
+ u32 max_size = params[1].Get<u32>();
+ u32 size = std::min(max_size, u32(string_data.size()));
+
+ EXPECT_LT(ptr + size, memory->ByteSize());
+
+ std::copy(string_data.begin(), string_data.begin() + size,
+ memory->UnsafeData() + ptr);
+
+ results[0].Set(size);
+ return Result::Ok;
+ };
+ auto fill_buf_func = HostFunc::New(
+ store_, FuncType{{ValueType::I32, ValueType::I32}, {ValueType::I32}},
+ fill_buf);
+
+ auto buf_done = [&](Thread& thread, const Values& params, Values& results,
+ Trap::Ptr* out_trap) -> Result {
+ // (param $ptr i32) (param $size i32)
+ EXPECT_EQ(2u, params.size());
+ EXPECT_EQ(0u, results.size());
+
+ u32 ptr = params[0].Get<u32>();
+ u32 size = params[1].Get<u32>();
+
+ EXPECT_LT(ptr + size, memory->ByteSize());
+
+ string_data.resize(size);
+ std::copy(memory->UnsafeData() + ptr, memory->UnsafeData() + ptr + size,
+ string_data.begin());
+
+ return Result::Ok;
+ };
+ auto buf_done_func = HostFunc::New(
+ store_, FuncType{{ValueType::I32, ValueType::I32}, {}}, buf_done);
+
+ Instantiate({memory->self(), fill_buf_func->self(), buf_done_func->self()});
+
+ auto rot13 = GetFuncExport(0);
+
+ Values results;
+ Trap::Ptr trap;
+ ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap));
+
+ ASSERT_EQ("Uryyb, JroNffrzoyl!", string_data);
+
+ ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap));
+
+ ASSERT_EQ("Hello, WebAssembly!", string_data);
+}
+
+class InterpGCTest : public InterpTest {
+ public:
+ void SetUp() override {
+ before_new = store_.object_count();
+ }
+
+ void TearDown() override {
+ // Reset instance and module, in case they were allocated.
+ inst_.reset();
+ mod_.reset();
+
+ store_.Collect();
+ EXPECT_EQ(before_new, store_.object_count());
+ }
+
+ size_t before_new;
+};
+
+TEST_F(InterpGCTest, Collect_Basic) {
+ auto foreign = Foreign::New(store_, nullptr);
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 1, after_new);
+
+ // Remove root, but object is not destroyed until collect.
+ foreign.reset();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_GlobalCycle) {
+ auto gt = GlobalType{ValueType::ExternRef, Mutability::Var};
+ auto g1 = Global::New(store_, gt, Value::Make(Ref::Null));
+ auto g2 = Global::New(store_, gt, Value::Make(g1->self()));
+ g1->Set(store_, g2->self());
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 2, after_new);
+
+ // Remove g1 root, but it's kept alive by g2.
+ g1.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+
+ // Remove g2 root, now both should be removed.
+ g2.reset();
+}
+
+TEST_F(InterpGCTest, Collect_TableCycle) {
+ auto tt = TableType{ValueType::ExternRef, Limits{2}};
+ auto t1 = Table::New(store_, tt);
+ auto t2 = Table::New(store_, tt);
+ auto t3 = Table::New(store_, tt);
+
+ t1->Set(store_, 0, t1->self()); // t1 references itself.
+ t2->Set(store_, 0, t3->self());
+ t3->Set(store_, 0, t2->self()); // t2 and t3 reference each other.
+ t3->Set(store_, 1, t1->self()); // t3 also references t1.
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 3, after_new);
+
+ // Remove t1 and t2 roots, but their kept alive by t3.
+ t1.reset();
+ t2.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+
+ // Remove t3 root, now all should be removed.
+ t3.reset();
+}
+
+TEST_F(InterpGCTest, Collect_Func) {
+ ReadModule(s_fac_module);
+ Instantiate();
+ auto func = GetFuncExport(0);
+
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 3, after_new); // module, instance, func.
+
+ // Reset module and instance roots, but they'll be kept alive by the func.
+ mod_.reset();
+ inst_.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_InstanceImport) {
+ // (import "" "f" (func))
+ // (import "" "t" (table 0 funcref))
+ // (import "" "m" (memory 0))
+ // (import "" "g" (global i32))
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x02, 0x19, 0x04, 0x00, 0x01, 0x66, 0x00, 0x00,
+ 0x00, 0x01, 0x74, 0x01, 0x70, 0x00, 0x00, 0x00, 0x01, 0x6d, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x03, 0x7f, 0x00,
+ });
+ auto f = HostFunc::New(store_, FuncType{{}, {}},
+ [](Thread& thread, const Values&, Values&,
+ Trap::Ptr*) -> Result { return Result::Ok; });
+ auto t = Table::New(store_, TableType{ValueType::FuncRef, Limits{0}});
+ auto m = Memory::New(store_, MemoryType{Limits{0}});
+ auto g = Global::New(store_, GlobalType{ValueType::I32, Mutability::Const},
+ Value::Make(5));
+
+ Instantiate({f->self(), t->self(), m->self(), g->self()});
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 6, after_new); // module, instance, f, t, m, g
+
+ // Instance keeps all imports alive.
+ f.reset();
+ t.reset();
+ m.reset();
+ g.reset();
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+TEST_F(InterpGCTest, Collect_InstanceExport) {
+ // (func (export "f"))
+ // (global (export "g") i32 (i32.const 0))
+ // (table (export "t") 0 funcref)
+ // (memory (export "m") 0)
+ ReadModule({
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
+ 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x04, 0x01, 0x70,
+ 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x00, 0x06, 0x06, 0x01, 0x7f,
+ 0x00, 0x41, 0x00, 0x0b, 0x07, 0x11, 0x04, 0x01, 0x66, 0x00, 0x00,
+ 0x01, 0x67, 0x03, 0x00, 0x01, 0x74, 0x01, 0x00, 0x01, 0x6d, 0x02,
+ 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
+ });
+ Instantiate();
+ auto after_new = store_.object_count();
+ EXPECT_EQ(before_new + 6, after_new); // module, instance, f, t, m, g
+
+ // Instance keeps all exports alive.
+ store_.Collect();
+ EXPECT_EQ(after_new, store_.object_count());
+}
+
+// TODO: Test for Thread keeping references alive as locals/params/stack values.
+// This requires better tracking of references than currently exists in the
+// interpreter. (see TODOs in Select/LocalGet/GlobalGet)
diff --git a/third_party/wasm2c/src/test-intrusive-list.cc b/third_party/wasm2c/src/test-intrusive-list.cc
new file mode 100644
index 0000000000..dfc3dbb378
--- /dev/null
+++ b/third_party/wasm2c/src/test-intrusive-list.cc
@@ -0,0 +1,584 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include <memory>
+
+#include "src/intrusive-list.h"
+#include "src/make-unique.h"
+
+using namespace wabt;
+
+namespace {
+
+struct TestObject : intrusive_list_base<TestObject> {
+ static int creation_count;
+
+ TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) {
+ ++creation_count;
+ }
+
+ // Allow move.
+ TestObject(TestObject&& other) {
+ // Don't increment creation_count; we're moving from other.
+ *this = std::move(other);
+ }
+
+ TestObject& operator=(TestObject&& other) {
+ data = other.data;
+ data2 = other.data2;
+ other.moved = true;
+ return *this;
+ }
+
+ // Disallow copy.
+ TestObject(const TestObject&) = delete;
+ TestObject& operator=(const TestObject&) = delete;
+
+ ~TestObject() {
+ if (!moved) {
+ creation_count--;
+ }
+ }
+
+ int data;
+ int data2;
+ bool moved = false;
+};
+
+// static
+int TestObject::creation_count = 0;
+
+typedef intrusive_list<TestObject> TestObjectList;
+
+class IntrusiveListTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Reset to 0 even if the previous test leaked objects to keep the tests
+ // independent.
+ TestObject::creation_count = 0;
+ }
+
+ virtual void TearDown() { ASSERT_EQ(0, TestObject::creation_count); }
+
+ TestObjectList NewList(const std::vector<int>& data_values) {
+ TestObjectList result;
+ for (auto data_value : data_values)
+ result.emplace_back(data_value);
+ return result;
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected) {
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count++], node.data);
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected,
+ const std::vector<int>& expected2) {
+ assert(expected.size() == expected2.size());
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count], node.data);
+ ASSERT_EQ(expected2[count], node.data2);
+ count++;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+};
+
+} // end anonymous namespace
+
+TEST_F(IntrusiveListTest, default_constructor) {
+ TestObjectList list;
+}
+
+TEST_F(IntrusiveListTest, node_constructor) {
+ TestObjectList list(MakeUnique<TestObject>(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, node_move_constructor) {
+ TestObjectList list(TestObject(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, move_constructor) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2(std::move(list1));
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, move_assignment_operator) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2;
+
+ list2 = std::move(list1);
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+namespace {
+
+class IntrusiveListIteratorTest : public IntrusiveListTest {
+ protected:
+ virtual void SetUp() {
+ IntrusiveListTest::SetUp();
+
+ list_.emplace_back(1);
+ list_.emplace_back(2);
+ list_.emplace_back(3);
+ }
+
+ virtual void TearDown() {
+ list_.clear();
+
+ IntrusiveListTest::TearDown();
+ }
+
+ template <typename Iter>
+ void TestForward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ ASSERT_EQ(expected[count], first->data);
+ ++first;
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ template <typename Iter>
+ void TestBackward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ --last;
+ ASSERT_EQ(expected[count], last->data);
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ TestObjectList list_;
+ const TestObjectList& clist_ = list_;
+};
+
+} // end of anonymous namespace
+
+TEST_F(IntrusiveListIteratorTest, begin_end_forward) {
+ TestForward(list_.begin(), list_.end(), {1, 2, 3});
+ TestForward(clist_.begin(), clist_.end(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_forward) {
+ TestForward(list_.rbegin(), list_.rend(), {3, 2, 1});
+ TestForward(clist_.rbegin(), clist_.rend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_forward) {
+ TestForward(list_.cbegin(), list_.cend(), {1, 2, 3});
+ TestForward(clist_.cbegin(), clist_.cend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_forward) {
+ TestForward(list_.crbegin(), list_.crend(), {3, 2, 1});
+ TestForward(clist_.crbegin(), clist_.crend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, begin_end_backward) {
+ TestBackward(list_.begin(), list_.end(), {3, 2, 1});
+ TestBackward(clist_.begin(), clist_.end(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_backward) {
+ TestBackward(list_.rbegin(), list_.rend(), {1, 2, 3});
+ TestBackward(clist_.rbegin(), clist_.rend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_backward) {
+ TestBackward(list_.cbegin(), list_.cend(), {3, 2, 1});
+ TestBackward(clist_.cbegin(), clist_.cend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_backward) {
+ TestBackward(list_.crbegin(), list_.crend(), {1, 2, 3});
+ TestBackward(clist_.crbegin(), clist_.crend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, size_empty) {
+ TestObjectList list;
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+
+ list.emplace_back(1);
+ ASSERT_EQ(1U, list.size());
+ ASSERT_FALSE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, front_back) {
+ TestObjectList list;
+
+ list.emplace_back(1);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(1, list.back().data);
+
+ list.emplace_back(2);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(2, list.back().data);
+
+ const TestObjectList& clist = list;
+ ASSERT_EQ(1, clist.front().data);
+ ASSERT_EQ(2, clist.back().data);
+}
+
+TEST_F(IntrusiveListTest, emplace_front) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_front(1, 100);
+ list.emplace_front(2, 200);
+ list.emplace_front(3, 300);
+ list.emplace_front(4, 400);
+
+ AssertListEq(list, {4, 3, 2, 1}, {400, 300, 200, 100});
+}
+
+TEST_F(IntrusiveListTest, emplace_back) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_back(1, 100);
+ list.emplace_back(2, 200);
+ list.emplace_back(3, 300);
+ list.emplace_back(4, 400);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, push_front_pointer) {
+ TestObjectList list;
+
+ list.push_front(MakeUnique<TestObject>(1));
+ list.push_front(MakeUnique<TestObject>(2));
+ list.push_front(MakeUnique<TestObject>(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_pointer) {
+ TestObjectList list;
+
+ list.push_back(MakeUnique<TestObject>(1));
+ list.push_back(MakeUnique<TestObject>(2));
+ list.push_back(MakeUnique<TestObject>(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, push_front_move) {
+ TestObjectList list;
+
+ list.push_front(TestObject(1));
+ list.push_front(TestObject(2));
+ list.push_front(TestObject(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_move) {
+ TestObjectList list;
+
+ list.push_back(TestObject(1));
+ list.push_back(TestObject(2));
+ list.push_back(TestObject(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, pop_front) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_front();
+ AssertListEq(list, {2, 3, 4});
+ list.pop_front();
+ AssertListEq(list, {3, 4});
+ list.pop_front();
+ AssertListEq(list, {4});
+ list.pop_front();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, pop_back) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_back();
+ AssertListEq(list, {1, 2, 3});
+ list.pop_back();
+ AssertListEq(list, {1, 2});
+ list.pop_back();
+ AssertListEq(list, {1});
+ list.pop_back();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_front) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_front());
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {2, 3});
+
+ std::unique_ptr<TestObject> t2(list.extract_front());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract_front());
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_back) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_back());
+ ASSERT_EQ(3, t1->data);
+ AssertListEq(list, {1, 2});
+
+ std::unique_ptr<TestObject> t2(list.extract_back());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1});
+
+ std::unique_ptr<TestObject> t3(list.extract_back());
+ ASSERT_EQ(1, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, emplace) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace(list.begin(), 2, 200);
+ list.emplace(list.end(), 4, 400);
+ list.emplace(std::next(list.begin()), 3, 300);
+ list.emplace(list.begin(), 1, 100);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, insert_pointer) {
+ TestObjectList list;
+
+ list.insert(list.begin(), MakeUnique<TestObject>(2));
+ list.insert(list.end(), MakeUnique<TestObject>(4));
+ list.insert(std::next(list.begin()), MakeUnique<TestObject>(3));
+ list.insert(list.begin(), MakeUnique<TestObject>(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, insert_move) {
+ TestObjectList list;
+
+ list.insert(list.begin(), TestObject(2));
+ list.insert(list.end(), TestObject(4));
+ list.insert(std::next(list.begin()), TestObject(3));
+ list.insert(list.begin(), TestObject(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, extract) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ std::unique_ptr<TestObject> t2(list.extract(t2_iter));
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1, 3, 4});
+
+ std::unique_ptr<TestObject> t4(list.extract(t4_iter));
+ ASSERT_EQ(4, t4->data);
+ AssertListEq(list, {1, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract(t1_iter));
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract(t3_iter));
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ // erase returns an iterator to the following node.
+ ASSERT_EQ(3, list.erase(t2_iter)->data);
+ AssertListEq(list, {1, 3, 4});
+
+ ASSERT_EQ(list.end(), list.erase(t4_iter));
+ AssertListEq(list, {1, 3});
+
+ ASSERT_EQ(3, list.erase(t1_iter)->data);
+ AssertListEq(list, {3});
+
+ ASSERT_EQ(list.end(), list.erase(t3_iter));
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase_range) {
+ TestObjectList list = NewList({1, 2, 3, 4, 5, 6});
+
+ // OK to erase an empty range.
+ list.erase(list.begin(), list.begin());
+ list.erase(list.end(), list.end());
+
+ // Erase the first element (1).
+ list.erase(list.begin(), std::next(list.begin()));
+ AssertListEq(list, {2, 3, 4, 5, 6});
+
+ // Erase the last element (6).
+ list.erase(std::prev(list.end()), list.end());
+ AssertListEq(list, {2, 3, 4, 5});
+
+ // Erase [3, 4] => [2, 5]
+ list.erase(std::next(list.begin()), std::prev(list.end()));
+ AssertListEq(list, {2, 5});
+
+ // Erase the rest.
+ list.erase(list.begin(), list.end());
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, swap) {
+ TestObjectList list1 = NewList({1, 2, 3, 4});
+
+ TestObjectList list2 = NewList({100, 200, 300});
+
+ AssertListEq(list1, {1, 2, 3, 4});
+ AssertListEq(list2, {100, 200, 300});
+
+ list1.swap(list2);
+
+ AssertListEq(list1, {100, 200, 300});
+ AssertListEq(list2, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, clear) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ ASSERT_FALSE(list.empty());
+
+ list.clear();
+
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, splice_list) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2);
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3);
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+ AssertListEq(list3, {});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4);
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_list_move) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ list1.splice(list1.begin(), NewList({100, 200}));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+
+ // Splice at end.
+ list1.splice(list1.end(), NewList({500, 600, 700}));
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+
+ // Splice in the middle.
+ list1.splice(std::next(list1.begin(), 4), NewList({400}));
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+}
+
+TEST_F(IntrusiveListTest, splice_node) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2, list2.begin());
+ AssertListEq(list1, {100, 1, 2, 3});
+ AssertListEq(list2, {200});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin(), 2));
+ AssertListEq(list1, {100, 1, 2, 3, 700});
+ AssertListEq(list3, {500, 600});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 3), list4, list4.begin());
+ AssertListEq(list1, {100, 1, 2, 400, 3, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_range) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200, 300});
+ list1.splice(list1.begin(), list2, list2.begin(), std::prev(list2.end()));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {300});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin()), list3.end());
+ AssertListEq(list1, {100, 200, 1, 2, 3, 600, 700});
+ AssertListEq(list3, {500});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4, list4.begin(), list4.end());
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 600, 700});
+ AssertListEq(list4, {});
+}
diff --git a/third_party/wasm2c/src/test-literal.cc b/third_party/wasm2c/src/test-literal.cc
new file mode 100644
index 0000000000..bce7a0b8f0
--- /dev/null
+++ b/third_party/wasm2c/src/test-literal.cc
@@ -0,0 +1,838 @@
+/*
+ * Copyright 2018 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 <cassert>
+#include <cstdio>
+#include <thread>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "src/literal.h"
+
+using namespace wabt;
+
+namespace {
+
+enum ParseIntTypeCombo {
+ UnsignedOnly,
+ SignedAndUnsigned,
+ Both,
+};
+
+template <typename T, typename F>
+void AssertIntEquals(T expected,
+ const char* s,
+ F&& parse_int,
+ ParseIntTypeCombo parse_type = Both) {
+ const char* const end = s + strlen(s);
+ T actual;
+ if (parse_type == UnsignedOnly || parse_type == Both) {
+ ASSERT_EQ(Result::Ok,
+ parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+ ASSERT_EQ(expected, actual);
+ } else {
+ ASSERT_EQ(Result::Error,
+ parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+ }
+
+ if (parse_type == SignedAndUnsigned || parse_type == Both) {
+ ASSERT_EQ(Result::Ok,
+ parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(expected, actual);
+ } else {
+ ASSERT_EQ(Result::Error,
+ parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ }
+}
+
+void AssertInt8Equals(uint8_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt8, parse_type);
+}
+
+void AssertInt16Equals(uint16_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt16, parse_type);
+}
+
+void AssertInt32Equals(uint32_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt32, parse_type);
+}
+
+void AssertInt64Equals(uint64_t expected,
+ const char* s,
+ ParseIntTypeCombo parse_type = Both) {
+ AssertIntEquals(expected, s, ParseInt64, parse_type);
+}
+
+void AssertUint128Equals(v128 expected,
+ const char* s) {
+ const char* const end = s + strlen(s);
+ v128 actual;
+ ASSERT_EQ(Result::Ok, ParseUint128(s, end, &actual)) << s;
+ ASSERT_EQ(expected, actual);
+}
+
+void AssertInt8Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint8_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt8(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt8(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt16Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint16_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt16(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt16(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt32Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint32_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt32(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt32(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertInt64Fails(const char* s) {
+ const char* const end = s + strlen(s);
+ uint64_t actual;
+ ASSERT_EQ(Result::Error,
+ ParseInt64(s, end, &actual, ParseIntType::SignedAndUnsigned))
+ << s;
+ ASSERT_EQ(Result::Error,
+ ParseInt64(s, end, &actual, ParseIntType::UnsignedOnly))
+ << s;
+}
+
+void AssertUint64Equals(uint64_t expected, const char* s) {
+ uint64_t actual;
+ ASSERT_EQ(Result::Ok, ParseUint64(s, s + strlen(s), &actual)) << s;
+ ASSERT_EQ(expected, actual);
+}
+
+void AssertUint64Fails(const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Error, ParseUint64(s, s + strlen(s), &actual_bits)) << s;
+}
+
+void AssertUint128Fails(const char* s) {
+ v128 actual;
+ ASSERT_EQ(Result::Error, ParseUint128(s, s + strlen(s), &actual)) << s;
+}
+
+void AssertHexFloatEquals(uint32_t expected_bits, const char* s) {
+ uint32_t actual_bits;
+ ASSERT_EQ(Result::Ok,
+ ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+ ASSERT_EQ(expected_bits, actual_bits) << s;
+}
+
+void AssertHexFloatFails(const char* s) {
+ uint32_t actual_bits;
+ ASSERT_EQ(Result::Error,
+ ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+}
+
+void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Ok,
+ ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+ ASSERT_EQ(expected_bits, actual_bits);
+}
+
+void AssertHexDoubleFails(const char* s) {
+ uint64_t actual_bits;
+ ASSERT_EQ(Result::Error,
+ ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
+ << s;
+}
+
+} // end anonymous namespace
+
+TEST(ParseInt8, Both) {
+ AssertInt8Equals(0, "0");
+ AssertInt8Equals(100, "100");
+ AssertInt8Equals(123, "123");
+ AssertInt8Equals(127, "127");
+ AssertInt8Equals(255, "255");
+ AssertInt8Equals(0xca, "0xca");
+ AssertInt8Equals(0x7f, "0x7f");
+ AssertInt8Equals(0x80, "0x80");
+ AssertInt8Equals(0xff, "0xff");
+}
+
+TEST(ParseInt8, SignedAndUnsigned) {
+ AssertInt8Equals(128, "-128", SignedAndUnsigned);
+ AssertInt8Equals(-0x80, "-0x80", SignedAndUnsigned);
+ AssertInt8Equals(255, "-1", SignedAndUnsigned);
+ AssertInt8Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt8Equals(1, "+1", SignedAndUnsigned);
+ AssertInt8Equals(-0x7b, "-0x7B", SignedAndUnsigned);
+ AssertInt8Equals(0xab, "+0xab", SignedAndUnsigned);
+}
+
+TEST(ParseInt8, Invalid) {
+ AssertInt8Fails("");
+ AssertInt8Fails("-100hello");
+ AssertInt8Fails("0XAB");
+ AssertInt8Fails("0xga");
+ AssertInt8Fails("two");
+}
+
+TEST(ParseInt8, Underscores) {
+ AssertInt8Equals(123, "12_3", Both);
+ AssertInt8Equals(123, "+12_3", SignedAndUnsigned);
+ AssertInt8Equals(-123, "-1_23", SignedAndUnsigned);
+ AssertInt8Equals(19, "1______9", Both);
+ AssertInt8Equals(0xab, "0xa_b", Both);
+ AssertInt8Equals(0xab, "+0xa_b", SignedAndUnsigned);
+ AssertInt8Equals(-0x7b, "-0x7_b", SignedAndUnsigned);
+}
+
+TEST(ParseInt8, Overflow) {
+ AssertInt8Fails("256");
+ AssertInt8Fails("-129");
+ AssertInt8Fails("0x100");
+ AssertInt8Fails("-0x81");
+ AssertInt8Fails("1231231231231231231231");
+}
+
+TEST(ParseInt16, Both) {
+ AssertInt16Equals(0, "0");
+ AssertInt16Equals(1000, "1000");
+ AssertInt16Equals(12345, "12345");
+ AssertInt16Equals(32767, "32767");
+ AssertInt16Equals(65535, "65535");
+ AssertInt16Equals(0xcafe, "0xcafe");
+ AssertInt16Equals(0x7fff, "0x7fff");
+ AssertInt16Equals(0x8000, "0x8000");
+ AssertInt16Equals(0xffff, "0xffff");
+}
+
+TEST(ParseInt16, SignedAndUnsigned) {
+ AssertInt16Equals(32768, "-32768", SignedAndUnsigned);
+ AssertInt16Equals(-0x8000, "-0x8000", SignedAndUnsigned);
+ AssertInt16Equals(65535, "-1", SignedAndUnsigned);
+ AssertInt16Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt16Equals(1, "+1", SignedAndUnsigned);
+ AssertInt16Equals(-0x7bcd, "-0x7BCD", SignedAndUnsigned);
+ AssertInt16Equals(0xabcd, "+0xabcd", SignedAndUnsigned);
+}
+
+TEST(ParseInt16, Invalid) {
+ AssertInt16Fails("");
+ AssertInt16Fails("-100hello");
+ AssertInt16Fails("0XABCD");
+ AssertInt16Fails("0xgabb");
+ AssertInt16Fails("two");
+}
+
+TEST(ParseInt16, Underscores) {
+ AssertInt16Equals(12345, "12_345", Both);
+ AssertInt16Equals(12345, "+12_345", SignedAndUnsigned);
+ AssertInt16Equals(-12345, "-123_45", SignedAndUnsigned);
+ AssertInt16Equals(19, "1______9", Both);
+ AssertInt16Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt16Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt16Equals(-0x7bcd, "-0x7_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt16, Overflow) {
+ AssertInt16Fails("65536");
+ AssertInt16Fails("-32769");
+ AssertInt16Fails("0x10000");
+ AssertInt16Fails("-0x8001");
+ AssertInt16Fails("1231231231231231231231");
+}
+
+TEST(ParseInt32, Both) {
+ AssertInt32Equals(0, "0");
+ AssertInt32Equals(1000, "1000");
+ AssertInt32Equals(123456789, "123456789");
+ AssertInt32Equals(2147483647, "2147483647");
+ AssertInt32Equals(4294967295u, "4294967295");
+ AssertInt32Equals(0xcafef00du, "0xcafef00d");
+ AssertInt32Equals(0x7fffffff, "0x7fffffff");
+ AssertInt32Equals(0x80000000u, "0x80000000");
+ AssertInt32Equals(0xffffffffu, "0xffffffff");
+}
+
+TEST(ParseInt32, SignedAndUnsigned) {
+ AssertInt32Equals(2147483648, "-2147483648", SignedAndUnsigned);
+ AssertInt32Equals(-0x80000000u, "-0x80000000", SignedAndUnsigned);
+ AssertInt32Equals(4294967295u, "-1", SignedAndUnsigned);
+ AssertInt32Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt32Equals(1, "+1", SignedAndUnsigned);
+ AssertInt32Equals(-0xabcd, "-0xABCD", SignedAndUnsigned);
+ AssertInt32Equals(0xabcd, "+0xabcd", SignedAndUnsigned);
+}
+
+TEST(ParseInt32, Invalid) {
+ AssertInt32Fails("");
+ AssertInt32Fails("-100hello");
+ AssertInt32Fails("0XABCDEF");
+ AssertInt32Fails("0xgabba");
+ AssertInt32Fails("two");
+}
+
+TEST(ParseInt32, Underscores) {
+ AssertInt32Equals(123456789, "12_345_6789", Both);
+ AssertInt32Equals(123456789, "+12_345_6789", SignedAndUnsigned);
+ AssertInt32Equals(-123456789, "-12345_6789", SignedAndUnsigned);
+ AssertInt32Equals(19, "1______9", Both);
+ AssertInt32Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt32Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt32Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt32, Overflow) {
+ AssertInt32Fails("4294967296");
+ AssertInt32Fails("-2147483649");
+ AssertInt32Fails("0x100000000");
+ AssertInt32Fails("-0x80000001");
+ AssertInt32Fails("1231231231231231231231");
+}
+
+TEST(ParseInt64, Both) {
+ AssertInt64Equals(0, "0");
+ AssertInt64Equals(1000, "1000");
+ AssertInt64Equals(123456789, "123456789");
+ AssertInt64Equals(9223372036854775807ull, "9223372036854775807");
+ AssertInt64Equals(18446744073709551615ull, "18446744073709551615");
+ AssertInt64Equals(0x7fffffffffffffffull, "0x7fffffffffffffff");
+ AssertInt64Equals(0x8000000000000000ull, "0x8000000000000000");
+ AssertInt64Equals(0xffffffffffffffffull, "0xffffffffffffffff");
+}
+
+TEST(ParseInt64, SignedAndUnsigned) {
+ AssertInt64Equals(9223372036854775808ull, "-9223372036854775808",
+ SignedAndUnsigned);
+ AssertInt64Equals(18446744073709551615ull, "-1", SignedAndUnsigned);
+ AssertInt64Equals(-1, "-0x1", SignedAndUnsigned);
+ AssertInt64Equals(1, "+1", SignedAndUnsigned);
+ AssertInt64Equals(-0x0bcdefabcdefabcdull, "-0x0BCDEFABCDEFABCD",
+ SignedAndUnsigned);
+ AssertInt64Equals(0xabcdefabcdefabcdull, "+0xabcdefabcdefabcd",
+ SignedAndUnsigned);
+}
+
+TEST(ParseInt64, Invalid) {
+ AssertInt64Fails("");
+ AssertInt64Fails("-100hello");
+ AssertInt64Fails("0XABCDEF");
+ AssertInt64Fails("0xgabba");
+ AssertInt64Fails("two");
+}
+
+TEST(ParseInt64, Underscores) {
+ AssertInt64Equals(123456789, "12_345_6789", Both);
+ AssertInt64Equals(123456789, "+12_345_6789", SignedAndUnsigned);
+ AssertInt64Equals(-123456789, "-12345_6789", SignedAndUnsigned);
+ AssertInt64Equals(19, "1______9", Both);
+ AssertInt64Equals(0xabcd, "0xa_b_c_d", Both);
+ AssertInt64Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
+ AssertInt64Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
+}
+
+TEST(ParseInt64, Overflow) {
+ AssertInt64Fails("18446744073709551616");
+ AssertInt64Fails("-9223372036854775809");
+ AssertInt32Fails("0x10000000000000000");
+ AssertInt32Fails("-0x80000000000000001");
+ AssertInt64Fails("1231231231231231231231");
+}
+
+TEST(ParseUint64, Basic) {
+ AssertUint64Equals(0, "0");
+ AssertUint64Equals(1000, "1000");
+ AssertUint64Equals(123456789, "123456789");
+ AssertUint64Equals(1844674407370955161ull, "1844674407370955161");
+ AssertUint64Equals(18446744073709551615ull, "18446744073709551615");
+
+ AssertUint64Equals(0, "0x0");
+ AssertUint64Equals(0x1000, "0x1000");
+ AssertUint64Equals(0x123456789, "0x123456789");
+ AssertUint64Equals(0xabcdef, "0xabcdef");
+ AssertUint64Equals(0xffffffffffffffull, "0xffffffffffffff");
+ AssertUint64Equals(0xfffffffffffffffull, "0xfffffffffffffff");
+
+ AssertUint64Equals(0xabcdefabcdefabcdull, "0xabcdefabcdefabcd");
+}
+
+TEST(ParseUint64, NoOctal) {
+ AssertUint64Equals(100, "0100");
+ AssertUint64Equals(888, "0000888");
+}
+
+TEST(ParseUint64, Invalid) {
+ AssertUint64Fails("");
+ AssertUint64Fails("-100");
+ AssertUint64Fails("0XABCDEF");
+ AssertUint64Fails("0xgabba");
+ AssertUint64Fails("two");
+}
+
+TEST(ParseUint64, Underscores) {
+ AssertUint64Equals(123456789, "12_345_6789");
+ AssertUint64Equals(19, "1______9");
+ AssertUint64Equals(0xabcd, "0xa_b_c_d");
+}
+
+TEST(ParseUint64, Overflow) {
+ AssertUint64Fails("0x10000000000000000");
+ AssertUint64Fails("18446744073709551616");
+ AssertUint64Fails("62857453058642199420");
+ AssertUint64Fails("82000999361882825820");
+ AssertUint64Fails("126539114687237086210");
+ AssertUint64Fails("10000000000000000000000000000000000000000");
+}
+
+TEST(ParseUint128, Basic) {
+ AssertUint128Equals({0, 0, 0, 0}, "0");
+ AssertUint128Equals({1, 0, 0, 0}, "1");
+ AssertUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201},
+ "5332529520247008778714484145835150861");
+ AssertUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+ "340282366920938463463374607431768211455");
+ AssertUint128Equals({0, 0, 1, 0}, "18446744073709551616");
+}
+
+TEST(ParseUint128, Invalid) {
+ AssertUint128Fails("");
+ AssertUint128Fails("-1");
+ AssertUint128Fails("340282366920938463463374607431768211456");
+ AssertUint128Fails("123a456");
+}
+
+TEST(ParseFloat, NonCanonical) {
+ AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0");
+ AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0");
+ AssertHexFloatEquals(0x3f800000, "0x0.0000000000000000000001p88");
+}
+
+TEST(ParseFloat, Basic) {
+ AssertHexFloatEquals(0, "0x0p0");
+ AssertHexFloatEquals(0x3f800000, "0x1");
+}
+
+TEST(ParseFloat, Rounding) {
+ // |------- 23 bits -----| V-- extra bit
+ //
+ // 11111111111111111111101 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7ffffd, "0x1.fffffap127");
+ // 11111111111111111111101 1 ==> round up
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffbp127");
+ // 11111111111111111111110 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffcp127");
+ // 11111111111111111111110 1 ==> round down
+ AssertHexFloatEquals(0x7f7ffffe, "0x1.fffffdp127");
+ // 11111111111111111111111 0 ==> no rounding
+ AssertHexFloatEquals(0x7f7fffff, "0x1.fffffep127");
+}
+
+// Duplicate the spec tests here for easier debugging.
+TEST(ParseFloat, RoundingSpec) {
+ static const struct {
+ const char* input;
+ uint32_t output;
+ } kTests[] = {
+ {"+0x1.00000100000000000p-50", 0x26800000},
+ {"-0x1.00000100000000000p-50", 0xa6800000},
+ {"+0x1.00000100000000001p-50", 0x26800001},
+ {"-0x1.00000100000000001p-50", 0xa6800001},
+ {"+0x1.000001fffffffffffp-50", 0x26800001},
+ {"-0x1.000001fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000200000000000p-50", 0x26800001},
+ {"-0x1.00000200000000000p-50", 0xa6800001},
+ {"+0x1.00000200000000001p-50", 0x26800001},
+ {"-0x1.00000200000000001p-50", 0xa6800001},
+ {"+0x1.000002fffffffffffp-50", 0x26800001},
+ {"-0x1.000002fffffffffffp-50", 0xa6800001},
+ {"+0x1.00000300000000000p-50", 0x26800002},
+ {"-0x1.00000300000000000p-50", 0xa6800002},
+ {"+0x1.00000300000000001p-50", 0x26800002},
+ {"-0x1.00000300000000001p-50", 0xa6800002},
+ {"+0x1.000003fffffffffffp-50", 0x26800002},
+ {"-0x1.000003fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000400000000000p-50", 0x26800002},
+ {"-0x1.00000400000000000p-50", 0xa6800002},
+ {"+0x1.00000400000000001p-50", 0x26800002},
+ {"-0x1.00000400000000001p-50", 0xa6800002},
+ {"+0x1.000004fffffffffffp-50", 0x26800002},
+ {"-0x1.000004fffffffffffp-50", 0xa6800002},
+ {"+0x1.00000500000000000p-50", 0x26800002},
+ {"-0x1.00000500000000000p-50", 0xa6800002},
+ {"+0x1.00000500000000001p-50", 0x26800003},
+ {"-0x1.00000500000000001p-50", 0xa6800003},
+ {"+0x4000.004000000p-64", 0x26800000},
+ {"-0x4000.004000000p-64", 0xa6800000},
+ {"+0x4000.004000001p-64", 0x26800001},
+ {"-0x4000.004000001p-64", 0xa6800001},
+ {"+0x4000.007ffffffp-64", 0x26800001},
+ {"-0x4000.007ffffffp-64", 0xa6800001},
+ {"+0x4000.008000000p-64", 0x26800001},
+ {"-0x4000.008000000p-64", 0xa6800001},
+ {"+0x4000.008000001p-64", 0x26800001},
+ {"-0x4000.008000001p-64", 0xa6800001},
+ {"+0x4000.00bffffffp-64", 0x26800001},
+ {"-0x4000.00bffffffp-64", 0xa6800001},
+ {"+0x4000.00c000000p-64", 0x26800002},
+ {"-0x4000.00c000000p-64", 0xa6800002},
+ {"+0x4000.00c000001p-64", 0x26800002},
+ {"-0x4000.00c000001p-64", 0xa6800002},
+ {"+0x4000.00fffffffp-64", 0x26800002},
+ {"-0x4000.00fffffffp-64", 0xa6800002},
+ {"+0x4000.010000001p-64", 0x26800002},
+ {"-0x4000.010000001p-64", 0xa6800002},
+ {"+0x4000.013ffffffp-64", 0x26800002},
+ {"-0x4000.013ffffffp-64", 0xa6800002},
+ {"+0x4000.014000001p-64", 0x26800003},
+ {"-0x4000.014000001p-64", 0xa6800003},
+ {"+0x1.00000100000000000p+50", 0x58800000},
+ {"-0x1.00000100000000000p+50", 0xd8800000},
+ {"+0x1.00000100000000001p+50", 0x58800001},
+ {"-0x1.00000100000000001p+50", 0xd8800001},
+ {"+0x1.000001fffffffffffp+50", 0x58800001},
+ {"-0x1.000001fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000200000000000p+50", 0x58800001},
+ {"-0x1.00000200000000000p+50", 0xd8800001},
+ {"+0x1.00000200000000001p+50", 0x58800001},
+ {"-0x1.00000200000000001p+50", 0xd8800001},
+ {"+0x1.000002fffffffffffp+50", 0x58800001},
+ {"-0x1.000002fffffffffffp+50", 0xd8800001},
+ {"+0x1.00000300000000000p+50", 0x58800002},
+ {"-0x1.00000300000000000p+50", 0xd8800002},
+ {"+0x1.00000300000000001p+50", 0x58800002},
+ {"-0x1.00000300000000001p+50", 0xd8800002},
+ {"+0x1.000003fffffffffffp+50", 0x58800002},
+ {"-0x1.000003fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000400000000000p+50", 0x58800002},
+ {"-0x1.00000400000000000p+50", 0xd8800002},
+ {"+0x1.00000400000000001p+50", 0x58800002},
+ {"-0x1.00000400000000001p+50", 0xd8800002},
+ {"+0x1.000004fffffffffffp+50", 0x58800002},
+ {"-0x1.000004fffffffffffp+50", 0xd8800002},
+ {"+0x1.00000500000000000p+50", 0x58800002},
+ {"-0x1.00000500000000000p+50", 0xd8800002},
+ {"+0x1.00000500000000001p+50", 0x58800003},
+ {"-0x1.00000500000000001p+50", 0xd8800003},
+ {"+0x4000004000000", 0x58800000},
+ {"-0x4000004000000", 0xd8800000},
+ {"+0x4000004000001", 0x58800001},
+ {"-0x4000004000001", 0xd8800001},
+ {"+0x4000007ffffff", 0x58800001},
+ {"-0x4000007ffffff", 0xd8800001},
+ {"+0x4000008000000", 0x58800001},
+ {"-0x4000008000000", 0xd8800001},
+ {"+0x4000008000001", 0x58800001},
+ {"-0x4000008000001", 0xd8800001},
+ {"+0x400000bffffff", 0x58800001},
+ {"-0x400000bffffff", 0xd8800001},
+ {"+0x400000c000000", 0x58800002},
+ {"-0x400000c000000", 0xd8800002},
+ {"+0x0.00000100000000000p-126", 0x0},
+ {"-0x0.00000100000000000p-126", 0x80000000},
+ {"+0x0.00000100000000001p-126", 0x1},
+ {"-0x0.00000100000000001p-126", 0x80000001},
+ {"+0x0.00000101000000000p-126", 0x1},
+ {"+0x0.000001fffffffffffp-126", 0x1},
+ {"-0x0.000001fffffffffffp-126", 0x80000001},
+ {"+0x0.00000200000000000p-126", 0x1},
+ {"-0x0.00000200000000000p-126", 0x80000001},
+ {"+0x0.00000200000000001p-126", 0x1},
+ {"-0x0.00000200000000001p-126", 0x80000001},
+ {"+0x0.000002fffffffffffp-126", 0x1},
+ {"-0x0.000002fffffffffffp-126", 0x80000001},
+ {"+0x0.00000300000000000p-126", 0x2},
+ {"-0x0.00000300000000000p-126", 0x80000002},
+ {"+0x0.00000300000000001p-126", 0x2},
+ {"-0x0.00000300000000001p-126", 0x80000002},
+ {"+0x0.000003fffffffffffp-126", 0x2},
+ {"-0x0.000003fffffffffffp-126", 0x80000002},
+ {"+0x0.00000400000000000p-126", 0x2},
+ {"-0x0.00000400000000000p-126", 0x80000002},
+ {"+0x0.00000400000000001p-126", 0x2},
+ {"-0x0.00000400000000001p-126", 0x80000002},
+ {"+0x0.000004fffffffffffp-126", 0x2},
+ {"-0x0.000004fffffffffffp-126", 0x80000002},
+ {"+0x0.00000500000000000p-126", 0x2},
+ {"-0x0.00000500000000000p-126", 0x80000002},
+ {"+0x0.00000500000000001p-126", 0x3},
+ {"-0x0.00000500000000001p-126", 0x80000003},
+ {"+0x1.fffffe8p127", 0x7f7fffff},
+ {"-0x1.fffffe8p127", 0xff7fffff},
+ {"+0x1.fffffefffffff8p127", 0x7f7fffff},
+ {"-0x1.fffffefffffff8p127", 0xff7fffff},
+ {"+0x1.fffffefffffffffffp127", 0x7f7fffff},
+ {"-0x1.fffffefffffffffffp127", 0xff7fffff},
+ };
+
+ for (auto test: kTests) {
+ AssertHexFloatEquals(test.output, test.input);
+ }
+}
+
+TEST(ParseFloat, OutOfRange) {
+ AssertHexFloatFails("0x1p128");
+ AssertHexFloatFails("-0x1p128");
+ AssertHexFloatFails("0x1.ffffffp127");
+ AssertHexFloatFails("-0x1.ffffffp127");
+}
+
+TEST(ParseDouble, NonCanonical) {
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x00000000000000000000001.0p0");
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x1.00000000000000000000000p0");
+ AssertHexDoubleEquals(0x3ff0000000000000, "0x0.0000000000000000000001p88");
+}
+
+TEST(ParseDouble, Rounding) {
+ // |-------------------- 52 bits ---------------------| V-- extra bit
+ //
+ // 1111111111111111111111111111111111111111111111111101 0 ==> no rounding
+ AssertHexDoubleEquals(0x7feffffffffffffd, "0x1.ffffffffffffd0p1023");
+ // 1111111111111111111111111111111111111111111111111101 1 ==> round up
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffd8p1023");
+ // 1111111111111111111111111111111111111111111111111110 0 ==> no rounding
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe0p1023");
+ // 1111111111111111111111111111111111111111111111111110 1 ==> round down
+ AssertHexDoubleEquals(0x7feffffffffffffe, "0x1.ffffffffffffe8p1023");
+ // 1111111111111111111111111111111111111111111111111111 0 ==> no rounding
+ AssertHexDoubleEquals(0x7fefffffffffffff, "0x1.fffffffffffff0p1023");
+}
+
+TEST(ParseDouble, OutOfRange) {
+ AssertHexDoubleFails("0x1p1024");
+ AssertHexDoubleFails("-0x1p1024");
+ AssertHexDoubleFails("0x1.fffffffffffff8p1023");
+ AssertHexDoubleFails("-0x1.fffffffffffff8p1023");
+}
+
+// Duplicate the spec tests here for easier debugging.
+TEST(ParseDouble, RoundingSpec) {
+ static const struct {
+ const char* input;
+ uint64_t output;
+ } kTests[] = {
+ {"+0x1.000000000000080000000000p-600", 1905022642377719808ull},
+ {"-0x1.000000000000080000000000p-600", 11128394679232495616ull},
+ {"+0x1.000000000000080000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000080000000001p-600", 11128394679232495617ull},
+ {"+0x1.0000000000000fffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.0000000000000fffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000000p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000000p-600", 11128394679232495617ull},
+ {"+0x1.000000000000100000000001p-600", 1905022642377719809ull},
+ {"-0x1.000000000000100000000001p-600", 11128394679232495617ull},
+ {"+0x1.00000000000017ffffffffffp-600", 1905022642377719809ull},
+ {"-0x1.00000000000017ffffffffffp-600", 11128394679232495617ull},
+ {"+0x1.000000000000180000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000180000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000180000000001p-600", 11128394679232495618ull},
+ {"+0x1.0000000000001fffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.0000000000001fffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000000p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000000p-600", 11128394679232495618ull},
+ {"+0x1.000000000000200000000001p-600", 1905022642377719810ull},
+ {"-0x1.000000000000200000000001p-600", 11128394679232495618ull},
+ {"+0x1.00000000000027ffffffffffp-600", 1905022642377719810ull},
+ {"-0x1.00000000000027ffffffffffp-600", 11128394679232495618ull},
+ {"+0x1.000000000000280000000001p-600", 1905022642377719811ull},
+ {"-0x1.000000000000280000000001p-600", 11128394679232495619ull},
+ {"+0x8000000.000000400000000000p-627", 1905022642377719808ull},
+ {"-0x8000000.000000400000000000p-627", 11128394679232495616ull},
+ {"+0x8000000.000000400000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000400000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.0000007fffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.0000007fffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000000p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000000p-627", 11128394679232495617ull},
+ {"+0x8000000.000000800000000001p-627", 1905022642377719809ull},
+ {"-0x8000000.000000800000000001p-627", 11128394679232495617ull},
+ {"+0x8000000.000000bfffffffffffp-627", 1905022642377719809ull},
+ {"-0x8000000.000000bfffffffffffp-627", 11128394679232495617ull},
+ {"+0x8000000.000000c00000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000000c00000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000000c00000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.000000ffffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.000000ffffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000000p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000000p-627", 11128394679232495618ull},
+ {"+0x8000000.000001000000000001p-627", 1905022642377719810ull},
+ {"-0x8000000.000001000000000001p-627", 11128394679232495618ull},
+ {"+0x8000000.0000013fffffffffffp-627", 1905022642377719810ull},
+ {"-0x8000000.0000013fffffffffffp-627", 11128394679232495618ull},
+ {"+0x8000000.000001400000000001p-627", 1905022642377719811ull},
+ {"-0x8000000.000001400000000001p-627", 11128394679232495619ull},
+ {"+0x1.000000000000080000000000p+600", 7309342195222315008ull},
+ {"-0x1.000000000000080000000000p+600", 16532714232077090816ull},
+ {"+0x1.000000000000080000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000080000000001p+600", 16532714232077090817ull},
+ {"+0x1.0000000000000fffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.0000000000000fffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000000p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000000p+600", 16532714232077090817ull},
+ {"+0x1.000000000000100000000001p+600", 7309342195222315009ull},
+ {"-0x1.000000000000100000000001p+600", 16532714232077090817ull},
+ {"+0x1.00000000000017ffffffffffp+600", 7309342195222315009ull},
+ {"-0x1.00000000000017ffffffffffp+600", 16532714232077090817ull},
+ {"+0x1.000000000000180000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000180000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000180000000001p+600", 16532714232077090818ull},
+ {"+0x1.0000000000001fffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.0000000000001fffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000200000000001p+600", 7309342195222315010ull},
+ {"-0x1.000000000000200000000001p+600", 16532714232077090818ull},
+ {"+0x1.00000000000027ffffffffffp+600", 7309342195222315010ull},
+ {"-0x1.00000000000027ffffffffffp+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000000p+600", 7309342195222315010ull},
+ {"-0x1.000000000000280000000000p+600", 16532714232077090818ull},
+ {"+0x1.000000000000280000000001p+600", 7309342195222315011ull},
+ {"-0x1.000000000000280000000001p+600", 16532714232077090819ull},
+ {"+0x2000000000000100000000000", 5044031582654955520ull},
+ {"-0x2000000000000100000000000", 14267403619509731328ull},
+ {"+0x2000000000000100000000001", 5044031582654955521ull},
+ {"-0x2000000000000100000000001", 14267403619509731329ull},
+ {"+0x20000000000001fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000001fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000200000000000", 5044031582654955521ull},
+ {"-0x2000000000000200000000000", 14267403619509731329ull},
+ {"+0x2000000000000200000000001", 5044031582654955521ull},
+ {"-0x2000000000000200000000001", 14267403619509731329ull},
+ {"+0x20000000000002fffffffffff", 5044031582654955521ull},
+ {"-0x20000000000002fffffffffff", 14267403619509731329ull},
+ {"+0x2000000000000300000000000", 5044031582654955522ull},
+ {"-0x2000000000000300000000000", 14267403619509731330ull},
+ {"+0x2000000000000300000000001", 5044031582654955522ull},
+ {"-0x2000000000000300000000001", 14267403619509731330ull},
+ {"+0x20000000000003fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000003fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000400000000000", 5044031582654955522ull},
+ {"-0x2000000000000400000000000", 14267403619509731330ull},
+ {"+0x2000000000000400000000001", 5044031582654955522ull},
+ {"-0x2000000000000400000000001", 14267403619509731330ull},
+ {"+0x20000000000004fffffffffff", 5044031582654955522ull},
+ {"-0x20000000000004fffffffffff", 14267403619509731330ull},
+ {"+0x2000000000000500000000000", 5044031582654955522ull},
+ {"-0x2000000000000500000000000", 14267403619509731330ull},
+ {"+0x2000000000000500000000001", 5044031582654955523ull},
+ {"-0x2000000000000500000000001", 14267403619509731331ull},
+ {"+0x0.000000000000080000000000p-1022", 0ull},
+ {"-0x0.000000000000080000000000p-1022", 9223372036854775808ull},
+ {"+0x0.000000000000080000000001p-1022", 1ull},
+ {"-0x0.000000000000080000000001p-1022", 9223372036854775809ull},
+ {"+0x0.0000000000000fffffffffffp-1022", 1ull},
+ {"-0x0.0000000000000fffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000000p-1022", 1ull},
+ {"-0x0.000000000000100000000000p-1022", 9223372036854775809ull},
+ {"+0x0.000000000000100000000001p-1022", 1ull},
+ {"-0x0.000000000000100000000001p-1022", 9223372036854775809ull},
+ {"+0x0.00000000000017ffffffffffp-1022", 1ull},
+ {"-0x0.00000000000017ffffffffffp-1022", 9223372036854775809ull},
+ {"+0x0.000000000000180000000000p-1022", 2ull},
+ {"-0x0.000000000000180000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000180000000001p-1022", 2ull},
+ {"-0x0.000000000000180000000001p-1022", 9223372036854775810ull},
+ {"+0x0.0000000000001fffffffffffp-1022", 2ull},
+ {"-0x0.0000000000001fffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000000p-1022", 2ull},
+ {"-0x0.000000000000200000000000p-1022", 9223372036854775810ull},
+ {"+0x0.000000000000200000000001p-1022", 2ull},
+ {"-0x0.000000000000200000000001p-1022", 9223372036854775810ull},
+ {"+0x0.00000000000027ffffffffffp-1022", 2ull},
+ {"-0x0.00000000000027ffffffffffp-1022", 9223372036854775810ull},
+ {"+0x0.000000000000280000000000p-1022", 2ull},
+ {"-0x0.000000000000280000000000p-1022", 9223372036854775810ull},
+ {"+0x1.000000000000280000000001p-1022", 4503599627370499ull},
+ {"-0x1.000000000000280000000001p-1022", 9227875636482146307ull},
+ {"+0x1.fffffffffffff4p1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff4p1023", 18442240474082181119ull},
+ {"+0x1.fffffffffffff7ffffffp1023", 9218868437227405311ull},
+ {"-0x1.fffffffffffff7ffffffp1023", 18442240474082181119ull},
+ };
+
+ for (auto test: kTests) {
+ AssertHexDoubleEquals(test.output, test.input);
+ }
+}
+
+void AssertWriteUint128Equals(const v128& value, const std::string& expected) {
+ assert(expected.length() < 128);
+ char buffer[128];
+ WriteUint128(buffer, 128, value);
+ std::string actual(buffer, buffer + expected.length());
+ ASSERT_EQ(expected, actual);
+ ASSERT_EQ(buffer[expected.length()], '\0');
+}
+
+TEST(WriteUint128, Basic) {
+ AssertWriteUint128Equals({0, 0, 0, 0}, "0");
+ AssertWriteUint128Equals({1, 0, 0, 0}, "1");
+ AssertWriteUint128Equals({0x100f0e0d, 0x0c0b0a09, 0x08070605, 0x04030201},
+ "5332529520247008778714484145835150861");
+ AssertWriteUint128Equals({0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff},
+ "272314856204801878456120017448021860915");
+ AssertWriteUint128Equals({0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
+ "340282366920938463463374607431768211455");
+ AssertWriteUint128Equals({0, 0, 1, 0}, "18446744073709551616");
+}
+
+TEST(WriteUint128, BufferTooSmall) {
+ {
+ char buffer[20];
+ WriteUint128(buffer, 20, {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff});
+ ASSERT_EQ(buffer[19], '\0');
+ std::string actual(buffer, buffer + 19);
+ ASSERT_EQ("3402823669209384634", actual);
+ }
+
+ {
+ char buffer[3];
+ WriteUint128(buffer, 3, {123, 0, 0, 0});
+ ASSERT_EQ(buffer[0], '1');
+ ASSERT_EQ(buffer[1], '2');
+ ASSERT_EQ(buffer[2], '\0');
+ }
+}
diff --git a/third_party/wasm2c/src/test-option-parser.cc b/third_party/wasm2c/src/test-option-parser.cc
new file mode 100644
index 0000000000..9295c58157
--- /dev/null
+++ b/third_party/wasm2c/src/test-option-parser.cc
@@ -0,0 +1,181 @@
+// Copyright 2019 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 "gtest/gtest.h"
+
+#include <string>
+
+#include "src/option-parser.h"
+
+using namespace wabt;
+
+#define ERROR_ENDING "\nTry '--help' for more information."
+
+TEST(OptionParser, LongFlag) {
+ bool flag = false;
+ OptionParser parser("prog", "desc");
+ parser.AddOption("flag", "help", [&]() { flag = true; });
+ const char* args[] = {"prog name", "--flag"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(true, flag);
+}
+
+TEST(OptionParser, ShortAndLongFlag) {
+ int count = 0;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('f', "flag", "help", [&]() { ++count; });
+ const char* args[] = {"prog name", "-f", "--flag", "-f", "--flag"};
+ parser.Parse(5, const_cast<char**>(args));
+ EXPECT_EQ(4, count);
+}
+
+TEST(OptionParser, ShortFlagCombined) {
+ int count = 0;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('a', "a", "help", [&]() { count += 1; });
+ parser.AddOption('b', "b", "help", [&]() { count += 2; });
+ const char* args[] = {"prog name", "-aa", "-abb"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ(7, count);
+}
+
+TEST(OptionParser, UnknownShortOption) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name", "-f"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unknown option '-f'" ERROR_ENDING);
+}
+
+TEST(OptionParser, UnknownLongOption) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name", "--foo"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unknown option '--foo'" ERROR_ENDING);
+}
+
+TEST(OptionParser, ShortAndLongParam) {
+ std::string param;
+ OptionParser parser("prog", "desc");
+ parser.AddOption('p', "param", "metavar", "help",
+ [&](const char* arg) { param += arg; });
+ const char* args[] = {"prog name", "-p", "h", "--param", "el", "--param=lo"};
+ parser.Parse(6, const_cast<char**>(args));
+ EXPECT_EQ("hello", param);
+}
+
+TEST(OptionParser, MissingParam) {
+ std::string error;
+ std::string param;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddOption('p', "param", "metavar", "help",
+ [&](const char* arg) { param = arg; });
+ const char* args[] = {"prog name", "--param"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ("", param);
+ EXPECT_EQ(error, "prog: option '--param' requires argument" ERROR_ENDING);
+}
+
+TEST(OptionParser, MissingArgument) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) {});
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ const char* args[] = {"prog name"};
+ parser.Parse(1, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: expected arg argument." ERROR_ENDING);
+}
+
+TEST(OptionParser, FlagCombinedAfterShortParam) {
+ std::string error;
+ std::string param;
+ bool has_x = false;
+
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddOption('p', "p", "metavar", "help",
+ [&](const char* arg) { param = arg; });
+ parser.AddOption('x', "x", "help", [&]() { has_x = true; });
+ const char* args[] = {"prog name", "-px", "stuff"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ("", param);
+ EXPECT_TRUE(has_x);
+ EXPECT_EQ(error, "prog: unexpected argument 'stuff'" ERROR_ENDING);
+}
+
+
+TEST(OptionParser, OneArgument) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) { argument = arg; });
+ const char* args[] = {"prog name", "hello"};
+ parser.Parse(2, const_cast<char**>(args));
+ EXPECT_EQ("hello", argument);
+}
+
+TEST(OptionParser, TooManyArguments) {
+ std::string error;
+ OptionParser parser("prog", "desc");
+ parser.SetErrorCallback([&](const char* msg) { error = msg; });
+ parser.AddArgument("arg", OptionParser::ArgumentCount::One,
+ [&](const char* arg) {});
+ const char* args[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ(error, "prog: unexpected argument 'goodbye'" ERROR_ENDING);
+}
+
+TEST(OptionParser, OneOrMoreArguments) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::OneOrMore,
+ [&](const char* arg) { argument += arg; });
+ const char* args[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args));
+ EXPECT_EQ("hellogoodbye", argument);
+}
+
+TEST(OptionParser, ZeroOrMoreArguments) {
+ std::string argument;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+
+ const char* args_none[] = {"prog name"};
+ parser.Parse(1, const_cast<char**>(args_none));
+ EXPECT_EQ("", argument);
+
+ const char* args_many[] = {"prog name", "hello", "goodbye"};
+ parser.Parse(3, const_cast<char**>(args_many));
+ EXPECT_EQ("hellogoodbye", argument);
+}
+
+TEST(OptionParser, StopProccessing) {
+ std::string argument;
+ bool has_x = false;
+ OptionParser parser("prog", "desc");
+ parser.AddArgument("arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [&](const char* arg) { argument += arg; });
+ parser.AddOption('x', "x", "help", [&]() { has_x = true; });
+
+ const char* args_many[] = {"prog name", "-x", "--", "foo", "-x", "-y", "bar"};
+ parser.Parse(7, const_cast<char**>(args_many));
+ EXPECT_TRUE(has_x);
+ EXPECT_EQ("foo-x-ybar", argument);
+}
diff --git a/third_party/wasm2c/src/test-string-view.cc b/third_party/wasm2c/src/test-string-view.cc
new file mode 100644
index 0000000000..4a4499dd57
--- /dev/null
+++ b/third_party/wasm2c/src/test-string-view.cc
@@ -0,0 +1,415 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include "src/string-view.h"
+
+#include <cstring>
+#include <functional>
+
+using namespace wabt;
+
+namespace {
+
+void assert_string_view_eq(const char* s, string_view sv) {
+ size_t len = std::strlen(s);
+ ASSERT_EQ(len, sv.size());
+ for (size_t i = 0; i < len; ++i) {
+ ASSERT_EQ(s[i], sv[i]);
+ }
+}
+
+constexpr string_view::size_type npos = string_view::npos;
+
+} // end anonymous namespace
+
+TEST(string_view, default_constructor) {
+ assert_string_view_eq("", string_view());
+}
+
+TEST(string_view, copy_constructor) {
+ string_view sv1("copy");
+ assert_string_view_eq("copy", string_view(sv1));
+
+ string_view sv2;
+ assert_string_view_eq("", string_view(sv2));
+}
+
+TEST(string_view, assignment_operator) {
+ string_view sv1;
+ sv1 = string_view("assign");
+ assert_string_view_eq("assign", sv1);
+
+ string_view sv2;
+ sv2 = string_view();
+ assert_string_view_eq("", sv2);
+}
+
+TEST(string_view, string_constructor) {
+ assert_string_view_eq("", string_view(std::string()));
+ assert_string_view_eq("string", string_view(std::string("string")));
+}
+
+TEST(string_view, cstr_constructor) {
+ assert_string_view_eq("", string_view(""));
+ assert_string_view_eq("cstr", string_view("cstr"));
+}
+
+TEST(string_view, cstr_len_constructor) {
+ assert_string_view_eq("", string_view("foo-bar-baz", 0));
+ assert_string_view_eq("foo", string_view("foo-bar-baz", 3));
+ assert_string_view_eq("foo-bar", string_view("foo-bar-baz", 7));
+}
+
+TEST(string_view, begin_end) {
+ string_view sv("012345");
+
+ char count = 0;
+ for (auto iter = sv.begin(), end = sv.end(); iter != end; ++iter) {
+ ASSERT_EQ('0' + count, *iter);
+ ++count;
+ }
+ ASSERT_EQ(6, count);
+}
+
+TEST(string_view, cbegin_cend) {
+ const string_view sv("012345");
+
+ char count = 0;
+ for (auto iter = sv.cbegin(), end = sv.cend(); iter != end; ++iter) {
+ ASSERT_EQ('0' + count, *iter);
+ ++count;
+ }
+ ASSERT_EQ(6, count);
+}
+
+TEST(string_view, rbegin_rend) {
+ string_view sv("012345");
+
+ char count = 0;
+ for (auto iter = sv.rbegin(), end = sv.rend(); iter != end; ++iter) {
+ ASSERT_EQ('5' - count, *iter);
+ ++count;
+ }
+ ASSERT_EQ(6, count);
+}
+
+TEST(string_view, crbegin_crend) {
+ const string_view sv("012345");
+
+ char count = 0;
+ for (auto iter = sv.crbegin(), end = sv.crend(); iter != end; ++iter) {
+ ASSERT_EQ('5' - count, *iter);
+ ++count;
+ }
+ ASSERT_EQ(6, count);
+}
+
+TEST(string_view, size) {
+ string_view sv1;
+ ASSERT_EQ(0U, sv1.size());
+
+ string_view sv2("");
+ ASSERT_EQ(0U, sv2.size());
+
+ string_view sv3("hello");
+ ASSERT_EQ(5U, sv3.size());
+}
+
+TEST(string_view, length) {
+ string_view sv1;
+ ASSERT_EQ(0U, sv1.length());
+
+ string_view sv2("hello");
+ ASSERT_EQ(5U, sv2.length());
+}
+
+TEST(string_view, empty) {
+ string_view sv1;
+ ASSERT_TRUE(sv1.empty());
+
+ string_view sv2("bye");
+ ASSERT_FALSE(sv2.empty());
+}
+
+TEST(string_view, operator_bracket) {
+ string_view sv("words");
+ ASSERT_EQ('w', sv[0]);
+ ASSERT_EQ('o', sv[1]);
+ ASSERT_EQ('r', sv[2]);
+ ASSERT_EQ('d', sv[3]);
+ ASSERT_EQ('s', sv[4]);
+}
+
+TEST(string_view, at) {
+ string_view sv("words");
+ ASSERT_EQ('w', sv.at(0));
+ ASSERT_EQ('o', sv.at(1));
+ ASSERT_EQ('r', sv.at(2));
+ ASSERT_EQ('d', sv.at(3));
+ ASSERT_EQ('s', sv.at(4));
+}
+
+TEST(string_view, front) {
+ string_view sv("words");
+ ASSERT_EQ('w', sv.front());
+}
+
+TEST(string_view, back) {
+ string_view sv("words");
+ ASSERT_EQ('s', sv.back());
+}
+
+TEST(string_view, data) {
+ const char* cstr = "words";
+ string_view sv(cstr);
+ ASSERT_EQ(cstr, sv.data());
+}
+
+TEST(string_view, remove_prefix) {
+ string_view sv("words");
+ sv.remove_prefix(2);
+ assert_string_view_eq("rds", sv);
+}
+
+TEST(string_view, remove_suffix) {
+ string_view sv("words");
+ sv.remove_suffix(2);
+ assert_string_view_eq("wor", sv);
+}
+
+TEST(string_view, swap) {
+ string_view sv1("hello");
+ string_view sv2("bye");
+
+ sv1.swap(sv2);
+
+ assert_string_view_eq("bye", sv1);
+ assert_string_view_eq("hello", sv2);
+}
+
+TEST(string_view, operator_std_string) {
+ string_view sv1("hi");
+ std::string s(sv1);
+
+ ASSERT_EQ(2U, s.size());
+ ASSERT_EQ('h', s[0]);
+ ASSERT_EQ('i', s[1]);
+}
+
+TEST(string_view, copy) {
+ string_view sv("words");
+ char buffer[10] = {0};
+
+ sv.copy(buffer, 10, 2);
+ ASSERT_EQ('r', buffer[0]);
+ ASSERT_EQ('d', buffer[1]);
+ ASSERT_EQ('s', buffer[2]);
+ for (int i = 3; i < 10; ++i) {
+ ASSERT_EQ(0, buffer[i]);
+ }
+}
+
+TEST(string_view, substr) {
+ string_view sv1("abcdefghij");
+ string_view sv2 = sv1.substr(2, 3);
+ assert_string_view_eq("cde", sv2);
+}
+
+TEST(string_view, compare0) {
+ ASSERT_TRUE(string_view("meat").compare(string_view("meet")) < 0);
+ ASSERT_TRUE(string_view("rest").compare(string_view("rate")) > 0);
+ ASSERT_TRUE(string_view("equal").compare(string_view("equal")) == 0);
+ ASSERT_TRUE(string_view("star").compare(string_view("start")) < 0);
+ ASSERT_TRUE(string_view("finished").compare(string_view("fin")) > 0);
+}
+
+TEST(string_view, compare1) {
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("ca")) > 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("cd")) == 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("cz")) < 0);
+}
+
+TEST(string_view, compare2) {
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_ca__"), 1, 2) >
+ 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_cd__"), 1, 2) ==
+ 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, string_view("_cz__"), 1, 2) <
+ 0);
+}
+
+TEST(string_view, compare3) {
+ ASSERT_TRUE(string_view("abcdef").compare("aaaa") > 0);
+ ASSERT_TRUE(string_view("abcdef").compare("abcdef") == 0);
+ ASSERT_TRUE(string_view("abcdef").compare("zzzz") < 0);
+}
+
+TEST(string_view, compare4) {
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "ca") > 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cd") == 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cz") < 0);
+}
+
+TEST(string_view, compare5) {
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "ca____", 2) > 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cd___", 2) == 0);
+ ASSERT_TRUE(string_view("abcdef").compare(2, 2, "cz__", 2) < 0);
+}
+
+TEST(string_view, find0) {
+ ASSERT_EQ(0U, string_view("find fins").find(string_view("fin")));
+ ASSERT_EQ(5U, string_view("find fins").find(string_view("fin"), 1));
+ ASSERT_EQ(npos, string_view("find fins").find(string_view("fin"), 6));
+}
+
+TEST(string_view, find1) {
+ ASSERT_EQ(0U, string_view("012340123").find('0'));
+ ASSERT_EQ(5U, string_view("012340123").find('0', 2));
+ ASSERT_EQ(npos, string_view("012340123").find('0', 6));
+}
+
+TEST(string_view, find2) {
+ ASSERT_EQ(1U, string_view("012340123").find("12345", 0, 2));
+ ASSERT_EQ(6U, string_view("012340123").find("12345", 3, 2));
+ ASSERT_EQ(npos, string_view("012340123").find("12345", 10, 2));
+}
+
+TEST(string_view, find3) {
+ ASSERT_EQ(1U, string_view("012340123").find("12"));
+ ASSERT_EQ(6U, string_view("012340123").find("12", 2));
+ ASSERT_EQ(npos, string_view("012340123").find("12", 10));
+}
+
+TEST(string_view, rfind0) {
+ ASSERT_EQ(5U, string_view("find fins").rfind(string_view("fin")));
+ ASSERT_EQ(0U, string_view("find fins").rfind(string_view("fin"), 4));
+ ASSERT_EQ(npos, string_view("find fins").rfind(string_view("no")));
+ ASSERT_EQ(npos, string_view("foo").rfind(string_view("foobar")));
+}
+
+TEST(string_view, rfind1) {
+ ASSERT_EQ(5U, string_view("012340123").rfind('0'));
+ ASSERT_EQ(0U, string_view("012340123").rfind('0', 2));
+ ASSERT_EQ(npos, string_view("012340123").rfind('9'));
+}
+
+TEST(string_view, rfind2) {
+ ASSERT_EQ(6U, string_view("012340123").rfind("12345", npos, 2));
+ ASSERT_EQ(1U, string_view("012340123").rfind("12345", 4, 2));
+ ASSERT_EQ(npos, string_view("012340123").rfind("12345", npos, 5));
+ ASSERT_EQ(npos, string_view("012").rfind("12345", npos, 5));
+}
+
+TEST(string_view, rfind3) {
+ ASSERT_EQ(6U, string_view("012340123").rfind("12"));
+ ASSERT_EQ(1U, string_view("012340123").rfind("12", 2));
+ ASSERT_EQ(npos, string_view("012340123").rfind("12", 0));
+ ASSERT_EQ(npos, string_view("012").rfind("12345"));
+}
+
+TEST(string_view, find_first_of0) {
+ ASSERT_EQ(0U, string_view("0123abc").find_first_of(string_view("0a")));
+ ASSERT_EQ(4U, string_view("0123abc").find_first_of(string_view("0a"), 1));
+ ASSERT_EQ(npos, string_view("0123abc").find_first_of(string_view("xyz")));
+}
+
+TEST(string_view, find_first_of1) {
+ ASSERT_EQ(1U, string_view("ahellohi").find_first_of('h'));
+ ASSERT_EQ(6U, string_view("ahellohi").find_first_of('h', 2));
+ ASSERT_EQ(npos, string_view("ahellohi").find_first_of('z', 2));
+}
+
+TEST(string_view, find_first_of2) {
+ ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a1b", 0, 2));
+ ASSERT_EQ(4U, string_view("0123abc").find_first_of("0a1b", 1, 2));
+ ASSERT_EQ(npos, string_view("0123abc").find_first_of("0a1b", 5, 2));
+}
+
+TEST(string_view, find_first_of3) {
+ ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a"));
+ ASSERT_EQ(0U, string_view("0123abc").find_first_of("0a", 0));
+ ASSERT_EQ(4U, string_view("0123abc").find_first_of("0a", 1));
+ ASSERT_EQ(npos, string_view("0123abc").find_first_of("0a", 5));
+}
+
+TEST(string_view, find_last_of0) {
+ ASSERT_EQ(4U, string_view("0123abc").find_last_of(string_view("0a")));
+ ASSERT_EQ(0U, string_view("0123abc").find_last_of(string_view("0a"), 1));
+ ASSERT_EQ(npos, string_view("0123abc").find_last_of(string_view("xyz")));
+}
+
+TEST(string_view, find_last_of1) {
+ ASSERT_EQ(6U, string_view("ahellohi").find_last_of('h'));
+ ASSERT_EQ(1U, string_view("ahellohi").find_last_of('h', 2));
+ ASSERT_EQ(npos, string_view("ahellohi").find_last_of('z', 2));
+}
+
+TEST(string_view, find_last_of2) {
+ ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a1b", npos, 2));
+ ASSERT_EQ(0U, string_view("0123abc").find_last_of("0a1b", 1, 2));
+ ASSERT_EQ(npos, string_view("0123abc").find_last_of("a1b", 0, 2));
+ ASSERT_EQ(npos, string_view("0123abc").find_last_of("xyz", npos, 0));
+}
+
+TEST(string_view, find_last_of3) {
+ ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a"));
+ ASSERT_EQ(4U, string_view("0123abc").find_last_of("0a", npos));
+ ASSERT_EQ(0U, string_view("0123abc").find_last_of("0a", 1));
+ ASSERT_EQ(npos, string_view("0123abc").find_last_of("a1", 0));
+}
+
+TEST(string_view, operator_equal) {
+ ASSERT_TRUE(string_view("this") == string_view("this"));
+ ASSERT_FALSE(string_view("this") == string_view("that"));
+}
+
+TEST(string_view, operator_not_equal) {
+ ASSERT_FALSE(string_view("here") != string_view("here"));
+ ASSERT_TRUE(string_view("here") != string_view("there"));
+}
+
+TEST(string_view, operator_less_than) {
+ ASSERT_TRUE(string_view("abc") < string_view("xyz"));
+ ASSERT_FALSE(string_view("later") < string_view("earlier"));
+ ASSERT_FALSE(string_view("one") < string_view("one"));
+}
+
+TEST(string_view, operator_greater_than) {
+ ASSERT_TRUE(string_view("much") > string_view("little"));
+ ASSERT_FALSE(string_view("future") > string_view("past"));
+ ASSERT_FALSE(string_view("now") > string_view("now"));
+}
+
+TEST(string_view, operator_less_than_or_equal) {
+ ASSERT_TRUE(string_view("abc") <= string_view("xyz"));
+ ASSERT_FALSE(string_view("later") <= string_view("earlier"));
+ ASSERT_TRUE(string_view("one") <= string_view("one"));
+}
+
+TEST(string_view, operator_greater_than_or_equal) {
+ ASSERT_TRUE(string_view("much") >= string_view("little"));
+ ASSERT_FALSE(string_view("future") >= string_view("past"));
+ ASSERT_TRUE(string_view("now") >= string_view("now"));
+}
+
+TEST(string_view, hash) {
+ std::hash<string_view> hasher;
+
+ ASSERT_NE(hasher(string_view("hello")), hasher(string_view("goodbye")));
+ ASSERT_EQ(hasher(string_view("same")), hasher(string_view("same")));
+}
diff --git a/third_party/wasm2c/src/test-utf8.cc b/third_party/wasm2c/src/test-utf8.cc
new file mode 100644
index 0000000000..8e420628bc
--- /dev/null
+++ b/third_party/wasm2c/src/test-utf8.cc
@@ -0,0 +1,167 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include "src/utf8.h"
+
+using namespace wabt;
+
+namespace {
+
+void assert_is_valid_utf8(bool expected,
+ int length,
+ int cu0 = 0,
+ int cu1 = 0,
+ int cu2 = 0,
+ int cu3 = 0) {
+ assert(length <= 4);
+ char buf[4] = {static_cast<char>(cu0), static_cast<char>(cu1),
+ static_cast<char>(cu2), static_cast<char>(cu3)};
+ if (expected) {
+ // Make sure it fails if there are continuation bytes past the end of the
+ // string.
+ for (int bad_length = 1; bad_length < length; ++bad_length) {
+ ASSERT_FALSE(IsValidUtf8(buf, bad_length))
+ << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3;
+ }
+ }
+
+ ASSERT_TRUE(expected == IsValidUtf8(buf, length))
+ << cu0 << ", " << cu1 << ", " << cu2 << ", " << cu3;
+}
+
+bool is_in_range(int x, int low, int high) {
+ return x >= low && x < high;
+}
+
+} // end anonymous namespace
+
+#define FOR_RANGE(var, low, high) for (int var = low; var < high; var++)
+#define FOR_EACH_BYTE(var) FOR_RANGE(var, 0, 0x100)
+
+TEST(utf8, valid_empty) {
+ assert_is_valid_utf8(true, 0);
+}
+
+TEST(utf8, valid_1_byte) {
+ FOR_RANGE(cu0, 0, 0x80) { assert_is_valid_utf8(true, 1, cu0); }
+}
+
+TEST(utf8, invalid_continuation_bytes) {
+ FOR_RANGE(cu0, 0x80, 0xc0) { assert_is_valid_utf8(false, 1, cu0); }
+}
+
+TEST(utf8, invalid_2_byte) {
+ FOR_RANGE(cu0, 0xc0, 0xc2) { assert_is_valid_utf8(false, 1, cu0); }
+}
+
+TEST(utf8, valid_2_bytes) {
+ FOR_RANGE(cu0, 0xc2, 0xe0) {
+ FOR_EACH_BYTE(cu1) {
+ bool is_valid = is_in_range(cu1, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 2, cu0, cu1);
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes_e0) {
+ int cu0 = 0xe0;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0xa0, 0xc0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes) {
+ FOR_RANGE(cu0, 0xe1, 0xf0) {
+ // Handle 0xed in valid_3_bytes_ed.
+ if (cu0 == 0xed) {
+ continue;
+ }
+
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0x80, 0xc0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_3_bytes_ed) {
+ int cu0 = 0xed;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ bool is_valid =
+ is_in_range(cu1, 0x80, 0xa0) && is_in_range(cu2, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 3, cu0, cu1, cu2);
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes_f0) {
+ int cu0 = 0xf0;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x90, 0xc0) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes) {
+ FOR_RANGE(cu0, 0xf1, 0xf4) {
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x80, 0xc0) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+ }
+}
+
+TEST(utf8, valid_4_bytes_f4) {
+ int cu0 = 0xf4;
+ FOR_EACH_BYTE(cu1) {
+ FOR_EACH_BYTE(cu2) {
+ FOR_EACH_BYTE(cu3) {
+ bool is_valid = is_in_range(cu1, 0x80, 0x90) &&
+ is_in_range(cu2, 0x80, 0xc0) &&
+ is_in_range(cu3, 0x80, 0xc0);
+ assert_is_valid_utf8(is_valid, 4, cu0, cu1, cu2, cu3);
+ }
+ }
+ }
+}
+
+TEST(utf8, invalid_4_bytes) {
+ FOR_RANGE(cu0, 0xf5, 0x100) {
+ assert_is_valid_utf8(false, 4, cu0, 0x80, 0x80, 0x80);
+ }
+}
diff --git a/third_party/wasm2c/src/test-wast-parser.cc b/third_party/wasm2c/src/test-wast-parser.cc
new file mode 100644
index 0000000000..00a27bf56a
--- /dev/null
+++ b/third_party/wasm2c/src/test-wast-parser.cc
@@ -0,0 +1,87 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include <memory>
+
+#include "src/wast-lexer.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+
+namespace {
+
+std::string repeat(std::string s, size_t count) {
+ std::string result;
+ for (size_t i = 0; i < count; ++i) {
+ result += s;
+ }
+ return result;
+}
+
+Errors ParseInvalidModule(std::string text) {
+ auto lexer = WastLexer::CreateBufferLexer("test", text.c_str(), text.size());
+ Errors errors;
+ std::unique_ptr<Module> module;
+ Features features;
+ WastParseOptions options(features);
+ Result result = ParseWatModule(lexer.get(), &module, &errors, &options);
+ EXPECT_EQ(Result::Error, result);
+
+ return errors;
+}
+
+} // end of anonymous namespace
+
+TEST(WastParser, LongToken) {
+ std::string text;
+ text = "(module (memory ";
+ text += repeat("a", 0x5000);
+ text += "))";
+
+ Errors errors = ParseInvalidModule(text);
+ ASSERT_EQ(1u, errors.size());
+
+ ASSERT_EQ(ErrorLevel::Error, errors[0].error_level);
+ ASSERT_EQ(1, errors[0].loc.line);
+ ASSERT_EQ(17, errors[0].loc.first_column);
+ ASSERT_STREQ(
+ R"(unexpected token "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", expected a natural number (e.g. 123).)",
+ errors[0].message.c_str());
+}
+
+TEST(WastParser, LongTokenSpace) {
+ std::string text;
+ text = "notparen";
+ text += repeat(" ", 0x10000);
+ text += "notmodule";
+
+ Errors errors = ParseInvalidModule(text);
+ ASSERT_EQ(2u, errors.size());
+
+ ASSERT_EQ(ErrorLevel::Error, errors[0].error_level);
+ ASSERT_EQ(1, errors[0].loc.line);
+ ASSERT_EQ(1, errors[0].loc.first_column);
+ ASSERT_STREQ(
+ R"(unexpected token "notparen", expected a module field or a module.)",
+ errors[0].message.c_str());
+
+ ASSERT_EQ(1, errors[1].loc.line);
+ ASSERT_EQ(65545, errors[1].loc.first_column);
+ ASSERT_STREQ(R"(unexpected token notmodule, expected EOF.)",
+ errors[1].message.c_str());
+}
diff --git a/third_party/wasm2c/src/token.cc b/third_party/wasm2c/src/token.cc
new file mode 100644
index 0000000000..657d37bed5
--- /dev/null
+++ b/third_party/wasm2c/src/token.cc
@@ -0,0 +1,99 @@
+/*
+ * 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 "src/token.h"
+
+namespace wabt {
+
+const char* GetTokenTypeName(TokenType token_type) {
+ static const char* s_names[] = {
+#define WABT_TOKEN(name, string) string,
+#define WABT_TOKEN_FIRST(name, string)
+#define WABT_TOKEN_LAST(name, string)
+#include "token.def"
+#undef WABT_TOKEN
+#undef WABT_TOKEN_FIRST
+#undef WABT_TOKEN_LAST
+ };
+
+ static_assert(
+ WABT_ARRAY_SIZE(s_names) == WABT_ENUM_COUNT(TokenType),
+ "Expected TokenType names list length to match number of TokenTypes.");
+
+ int x = static_cast<int>(token_type);
+ if (x < WABT_ENUM_COUNT(TokenType)) {
+ return s_names[x];
+ }
+
+ return "Invalid";
+}
+
+Token::Token(Location loc, TokenType token_type)
+ : loc(loc), token_type_(token_type) {
+ assert(IsTokenTypeBare(token_type_));
+}
+
+Token::Token(Location loc, TokenType token_type, Type type)
+ : loc(loc), token_type_(token_type) {
+ assert(HasType());
+ Construct(type_, type);
+}
+
+Token::Token(Location loc, TokenType token_type, string_view text)
+ : loc(loc), token_type_(token_type) {
+ assert(HasText());
+ Construct(text_, text);
+}
+
+Token::Token(Location loc, TokenType token_type, Opcode opcode)
+ : loc(loc), token_type_(token_type) {
+ assert(HasOpcode());
+ Construct(opcode_, opcode);
+}
+
+Token::Token(Location loc, TokenType token_type, const Literal& literal)
+ : loc(loc), token_type_(token_type) {
+ assert(HasLiteral());
+ Construct(literal_, literal);
+}
+
+std::string Token::to_string() const {
+ if (IsTokenTypeBare(token_type_)) {
+ return GetTokenTypeName(token_type_);
+ } else if (HasLiteral()) {
+ return literal_.text.to_string();
+ } else if (HasOpcode()) {
+ return opcode_.GetName();
+ } else if (HasText()) {
+ return text_.to_string();
+ } else if (IsTokenTypeRefKind(token_type_)) {
+ return type_.GetRefKindName();
+ } else {
+ assert(HasType());
+ return type_.GetName();
+ }
+}
+
+std::string Token::to_string_clamp(size_t max_length) const {
+ std::string s = to_string();
+ if (s.length() > max_length) {
+ return s.substr(0, max_length - 3) + "...";
+ } else {
+ return s;
+ }
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/token.def b/third_party/wasm2c/src/token.def
new file mode 100644
index 0000000000..3238675d88
--- /dev/null
+++ b/third_party/wasm2c/src/token.def
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef WABT_TOKEN
+#error "You must define WABT_TOKEN before including this file."
+#endif
+
+/* Tokens with no additional data (i.e. bare). */
+WABT_TOKEN(Invalid, "Invalid")
+WABT_TOKEN(Array, "array")
+WABT_TOKEN(AssertExhaustion, "assert_exhaustion")
+WABT_TOKEN(AssertInvalid, "assert_invalid")
+WABT_TOKEN(AssertMalformed, "assert_malformed")
+WABT_TOKEN(AssertReturn, "assert_return")
+WABT_TOKEN(AssertTrap, "assert_trap")
+WABT_TOKEN(AssertUnlinkable, "assert_unlinkable")
+WABT_TOKEN(Bin, "bin")
+WABT_TOKEN(Item, "item")
+WABT_TOKEN(Data, "data")
+WABT_TOKEN(Declare, "declare")
+WABT_TOKEN(Delegate, "delegate")
+WABT_TOKEN(Do, "do")
+WABT_TOKEN(Elem, "elem")
+WABT_TOKEN(Eof, "EOF")
+WABT_TOKEN(Tag, "tag")
+WABT_TOKEN(Export, "export")
+WABT_TOKEN(Field, "field")
+WABT_TOKEN(Get, "get")
+WABT_TOKEN(Global, "global")
+WABT_TOKEN(Import, "import")
+WABT_TOKEN(Invoke, "invoke")
+WABT_TOKEN(Input, "input")
+WABT_TOKEN(Local, "local")
+WABT_TOKEN(Lpar, "(")
+WABT_TOKEN(Memory, "memory")
+WABT_TOKEN(Module, "module")
+WABT_TOKEN(Mut, "mut")
+WABT_TOKEN(NanArithmetic, "nan:arithmetic")
+WABT_TOKEN(NanCanonical, "nan:canonical")
+WABT_TOKEN(Offset, "offset")
+WABT_TOKEN(Output, "output")
+WABT_TOKEN(Param, "param")
+WABT_TOKEN(Quote, "quote")
+WABT_TOKEN(Register, "register")
+WABT_TOKEN(Result, "result")
+WABT_TOKEN(Rpar, ")")
+WABT_TOKEN(Shared, "shared")
+WABT_TOKEN(Start, "start")
+WABT_TOKEN(Struct, "struct")
+WABT_TOKEN(Table, "table")
+WABT_TOKEN(Then, "then")
+WABT_TOKEN(Type, "type")
+WABT_TOKEN(I8X16, "i8x16")
+WABT_TOKEN(I16X8, "i16x8")
+WABT_TOKEN(I32X4, "i32x4")
+WABT_TOKEN(I64X2, "i64x2")
+WABT_TOKEN(F32X4, "f32x4")
+WABT_TOKEN(F64X2, "f64x2")
+WABT_TOKEN_FIRST(Bare, Invalid)
+WABT_TOKEN_LAST(Bare, F64X2)
+
+/* Tokens with Literal data. */
+WABT_TOKEN(Float, "FLOAT")
+WABT_TOKEN(Int, "INT")
+WABT_TOKEN(Nat, "NAT")
+WABT_TOKEN_FIRST(Literal, Float)
+WABT_TOKEN_LAST(Literal, Nat)
+
+/* Tokens with Opcode data. */
+WABT_TOKEN(AtomicFence, "atomic.fence")
+WABT_TOKEN(AtomicLoad, "ATOMIC_LOAD")
+WABT_TOKEN(AtomicNotify, "ATOMIC_NOTIFY")
+WABT_TOKEN(AtomicRmw, "ATOMIC_RMW")
+WABT_TOKEN(AtomicRmwCmpxchg, "ATOMIC_RMW_CMPXCHG")
+WABT_TOKEN(AtomicStore, "ATOMIC_STORE")
+WABT_TOKEN(AtomicWait, "ATOMIC_WAIT")
+WABT_TOKEN(Binary, "BINARY")
+WABT_TOKEN(Block, "block")
+WABT_TOKEN(Br, "br")
+WABT_TOKEN(BrIf, "br_if")
+WABT_TOKEN(BrTable, "br_table")
+WABT_TOKEN(Call, "call")
+WABT_TOKEN(CallIndirect, "call_indirect")
+WABT_TOKEN(CallRef, "call_ref")
+WABT_TOKEN(Catch, "catch")
+WABT_TOKEN(CatchAll, "catch_all")
+WABT_TOKEN(Compare, "COMPARE")
+WABT_TOKEN(Const, "CONST")
+WABT_TOKEN(Convert, "CONVERT")
+WABT_TOKEN(DataDrop, "data.drop")
+WABT_TOKEN(Drop, "drop")
+WABT_TOKEN(ElemDrop, "elem.drop")
+WABT_TOKEN(Else, "else")
+WABT_TOKEN(End, "end")
+WABT_TOKEN(GlobalGet, "global.get")
+WABT_TOKEN(GlobalSet, "global.set")
+WABT_TOKEN(If, "if")
+WABT_TOKEN(Load, "LOAD")
+WABT_TOKEN(LocalGet, "local.get")
+WABT_TOKEN(LocalSet, "local.set")
+WABT_TOKEN(LocalTee, "local.tee")
+WABT_TOKEN(Loop, "loop")
+WABT_TOKEN(MemoryCopy, "memory.copy")
+WABT_TOKEN(MemoryFill, "memory.fill")
+WABT_TOKEN(MemoryGrow, "memory.grow")
+WABT_TOKEN(MemoryInit, "memory.init")
+WABT_TOKEN(MemorySize, "memory.size")
+WABT_TOKEN(Nop, "nop")
+WABT_TOKEN(RefExtern, "ref.extern")
+WABT_TOKEN(RefFunc, "ref.func")
+WABT_TOKEN(RefIsNull, "ref.is_null")
+WABT_TOKEN(RefNull, "ref.null")
+WABT_TOKEN(Rethrow, "rethrow")
+WABT_TOKEN(ReturnCallIndirect, "return_call_indirect")
+WABT_TOKEN(ReturnCall, "return_call")
+WABT_TOKEN(Return, "return")
+WABT_TOKEN(Select, "select")
+WABT_TOKEN(SimdLaneOp, "SIMDLANEOP")
+WABT_TOKEN(SimdLoadSplat, "SIMDLOADSPLAT")
+WABT_TOKEN(SimdLoadLane, "SIMDLOADLANE")
+WABT_TOKEN(SimdStoreLane, "SIMDSTORELANE")
+WABT_TOKEN(SimdShuffleOp, "i8x16.shuffle")
+WABT_TOKEN(Store, "STORE")
+WABT_TOKEN(TableCopy, "table.copy")
+WABT_TOKEN(TableFill, "table.full")
+WABT_TOKEN(TableGet, "table.get")
+WABT_TOKEN(TableGrow, "table.grow")
+WABT_TOKEN(TableInit, "table.init")
+WABT_TOKEN(TableSet, "table.set")
+WABT_TOKEN(TableSize, "table.size")
+WABT_TOKEN(Ternary, "TERNARY")
+WABT_TOKEN(Throw, "throw")
+WABT_TOKEN(Try, "try")
+WABT_TOKEN(Unary, "UNARY")
+WABT_TOKEN(Unreachable, "unreachable")
+WABT_TOKEN_FIRST(Opcode, AtomicFence)
+WABT_TOKEN_LAST(Opcode, Unreachable)
+
+/* Tokens with string data. */
+WABT_TOKEN(AlignEqNat, "align=")
+WABT_TOKEN(LparAnn, "Annotation")
+WABT_TOKEN(OffsetEqNat, "offset=")
+WABT_TOKEN(Reserved, "Reserved")
+WABT_TOKEN(Text, "TEXT")
+WABT_TOKEN(Var, "VAR")
+WABT_TOKEN_FIRST(String, AlignEqNat)
+WABT_TOKEN_LAST(String, Var)
+
+/* Tokens with Type data. */
+WABT_TOKEN(ValueType, "VALUETYPE")
+WABT_TOKEN_FIRST(Type, ValueType)
+WABT_TOKEN_LAST(Type, ValueType)
+
+/* Tokens with Type data, but are reference kinds. */
+WABT_TOKEN(Func, "func")
+WABT_TOKEN(Extern, "extern")
+WABT_TOKEN(Exn, "exn")
+WABT_TOKEN_FIRST(RefKind, Func)
+WABT_TOKEN_LAST(RefKind, Exn)
diff --git a/third_party/wasm2c/src/token.h b/third_party/wasm2c/src/token.h
new file mode 100644
index 0000000000..719e1aee50
--- /dev/null
+++ b/third_party/wasm2c/src/token.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_TOKEN_H_
+#define WABT_TOKEN_H_
+
+#include "src/literal.h"
+#include "src/opcode.h"
+#include "src/string-view.h"
+
+namespace wabt {
+
+struct Literal {
+ Literal() = default;
+ Literal(LiteralType type, string_view text) : type(type), text(text) {}
+
+ LiteralType type;
+ string_view text;
+};
+
+enum class TokenType {
+#define WABT_TOKEN(name, string) name,
+#define WABT_TOKEN_FIRST(group, first) First_##group = first,
+#define WABT_TOKEN_LAST(group, last) Last_##group = last,
+#include "token.def"
+#undef WABT_TOKEN
+#undef WABT_TOKEN_FIRST
+#undef WABT_TOKEN_LAST
+
+ First = First_Bare,
+ Last = Last_RefKind,
+};
+
+const char* GetTokenTypeName(TokenType);
+
+inline bool IsTokenTypeBare(TokenType token_type) {
+ return token_type >= TokenType::First_Bare &&
+ token_type <= TokenType::Last_Bare;
+}
+
+inline bool IsTokenTypeString(TokenType token_type) {
+ return token_type >= TokenType::First_String &&
+ token_type <= TokenType::Last_String;
+}
+
+inline bool IsTokenTypeType(TokenType token_type) {
+ return token_type == TokenType::ValueType;
+}
+
+inline bool IsTokenTypeOpcode(TokenType token_type) {
+ return token_type >= TokenType::First_Opcode &&
+ token_type <= TokenType::Last_Opcode;
+}
+
+inline bool IsTokenTypeLiteral(TokenType token_type) {
+ return token_type >= TokenType::First_Literal &&
+ token_type <= TokenType::Last_Literal;
+}
+
+inline bool IsTokenTypeRefKind(TokenType token_type) {
+ return token_type >= TokenType::First_RefKind &&
+ token_type <= TokenType::Last_RefKind;
+}
+
+struct Token {
+ Token() : token_type_(TokenType::Invalid) {}
+ Token(Location, TokenType);
+ Token(Location, TokenType, Type);
+ Token(Location, TokenType, string_view);
+ Token(Location, TokenType, Opcode);
+ Token(Location, TokenType, const Literal&);
+
+ Location loc;
+
+ TokenType token_type() const { return token_type_; }
+
+ bool HasText() const { return IsTokenTypeString(token_type_); }
+ bool HasType() const {
+ return IsTokenTypeType(token_type_) || IsTokenTypeRefKind(token_type_);
+ }
+ bool HasOpcode() const { return IsTokenTypeOpcode(token_type_); }
+ bool HasLiteral() const { return IsTokenTypeLiteral(token_type_); }
+
+ string_view text() const {
+ assert(HasText());
+ return text_;
+ }
+
+ Type type() const {
+ assert(HasType());
+ return type_;
+ }
+
+ Opcode opcode() const {
+ assert(HasOpcode());
+ return opcode_;
+ }
+
+ const Literal& literal() const {
+ assert(HasLiteral());
+ return literal_;
+ }
+
+ std::string to_string() const;
+ std::string to_string_clamp(size_t max_length) const;
+
+ private:
+ TokenType token_type_;
+
+ union {
+ string_view text_;
+ Type type_;
+ Opcode opcode_;
+ Literal literal_;
+ };
+};
+
+} // namespace wabt
+
+#endif // WABT_TOKEN_H_
diff --git a/third_party/wasm2c/src/tools/spectest-interp.cc b/third_party/wasm2c/src/tools/spectest-interp.cc
new file mode 100644
index 0000000000..c27e7a9f69
--- /dev/null
+++ b/third_party/wasm2c/src/tools/spectest-interp.cc
@@ -0,0 +1,1852 @@
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/interp/binary-reader-interp.h"
+#include "src/interp/interp-util.h"
+#include "src/interp/interp.h"
+#include "src/literal.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+using namespace wabt::interp;
+
+static int s_verbose;
+static const char* s_infile;
+static Thread::Options s_thread_options;
+static Stream* s_trace_stream;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+static std::unique_ptr<FileStream> s_stdout_stream;
+
+enum class RunVerbosity {
+ Quiet = 0,
+ Verbose = 1,
+};
+
+static const char s_description[] =
+ R"( read a Spectest JSON file, and run its tests in the interpreter.
+
+examples:
+ # parse test.json and run the spec tests
+ $ spectest-interp test.json
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("spectest-interp", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption('V', "value-stack-size", "SIZE",
+ "Size in elements of the value stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.value_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('C', "call-stack-size", "SIZE",
+ "Size in elements of the call stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.call_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('t', "trace", "Trace execution",
+ []() { s_trace_stream = s_stdout_stream.get(); });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+namespace spectest {
+
+class Command;
+typedef std::unique_ptr<Command> CommandPtr;
+typedef std::vector<CommandPtr> CommandPtrVector;
+
+class Script {
+ public:
+ std::string filename;
+ CommandPtrVector commands;
+};
+
+class Command {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Command);
+ Command() = delete;
+ virtual ~Command() = default;
+
+ CommandType type;
+ uint32_t line = 0;
+
+ protected:
+ explicit Command(CommandType type) : type(type) {}
+};
+
+template <CommandType TypeEnum>
+class CommandMixin : public Command {
+ public:
+ static bool classof(const Command* cmd) { return cmd->type == TypeEnum; }
+ CommandMixin() : Command(TypeEnum) {}
+};
+
+enum class ModuleType {
+ Text,
+ Binary,
+};
+
+class ModuleCommand : public CommandMixin<CommandType::Module> {
+ public:
+ ModuleType module = ModuleType::Binary;
+ std::string filename;
+ std::string name;
+};
+
+class Action {
+ public:
+ ActionType type = ActionType::Invoke;
+ std::string module_name;
+ std::string field_name;
+ ValueTypes types;
+ Values args;
+};
+
+template <CommandType TypeEnum>
+class ActionCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ Action action;
+};
+
+typedef ActionCommandBase<CommandType::Action> ActionCommand;
+
+class RegisterCommand : public CommandMixin<CommandType::Register> {
+ public:
+ std::string as;
+ std::string name;
+};
+
+struct ExpectedValue {
+ TypedValue value;
+ Type lane_type; // Only valid if value.type == Type::V128.
+ // Up to 4 NaN values used, depending on |value.type| and |lane_type|:
+ // | type | lane_type | valid |
+ // | f32 | | nan[0] |
+ // | f64 | | nan[0] |
+ // | v128 | f32 | nan[0] through nan[3] |
+ // | v128 | f64 | nan[0],nan[1] |
+ // | * | * | none valid |
+ ExpectedNan nan[4];
+};
+
+int LaneCountFromType(Type type) {
+ switch (type) {
+ case Type::I8: return 16;
+ case Type::I16: return 8;
+ case Type::I32: return 4;
+ case Type::I64: return 2;
+ case Type::F32: return 4;
+ case Type::F64: return 2;
+ default: assert(false); return 0;
+ }
+}
+
+ExpectedValue GetLane(const ExpectedValue& ev, int lane) {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ assert(ev.value.type == Type::V128);
+ assert(lane < lane_count);
+
+ ExpectedValue result;
+ result.value.type = ev.lane_type;
+
+ v128 vec = ev.value.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (ev.lane_type) {
+ case Type::I8:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
+TypedValue GetLane(const TypedValue& tv, Type lane_type, int lane) {
+ int lane_count = LaneCountFromType(lane_type);
+ assert(tv.type == Type::V128);
+ assert(lane < lane_count);
+
+ TypedValue result;
+ result.type = lane_type;
+
+ v128 vec = tv.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (lane_type) {
+ case Type::I8:
+ result.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
+class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> {
+ public:
+ Action action;
+ std::vector<ExpectedValue> expected;
+};
+
+template <CommandType TypeEnum>
+class AssertTrapCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ Action action;
+ std::string text;
+};
+
+typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand;
+typedef AssertTrapCommandBase<CommandType::AssertExhaustion>
+ AssertExhaustionCommand;
+
+template <CommandType TypeEnum>
+class AssertModuleCommand : public CommandMixin<TypeEnum> {
+ public:
+ ModuleType type = ModuleType::Binary;
+ std::string filename;
+ std::string text;
+};
+
+typedef AssertModuleCommand<CommandType::AssertMalformed>
+ AssertMalformedCommand;
+typedef AssertModuleCommand<CommandType::AssertInvalid> AssertInvalidCommand;
+typedef AssertModuleCommand<CommandType::AssertUnlinkable>
+ AssertUnlinkableCommand;
+typedef AssertModuleCommand<CommandType::AssertUninstantiable>
+ AssertUninstantiableCommand;
+
+// An extremely simple JSON parser that only knows how to parse the expected
+// format from wat2wasm.
+class JSONParser {
+ public:
+ JSONParser() {}
+
+ wabt::Result ReadFile(string_view spec_json_filename);
+ wabt::Result ParseScript(Script* out_script);
+
+ private:
+ void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
+
+ // Whether to allow parsing of expectation-only forms (e.g. `nan:canonical`,
+ // `nan:arithmetic`, etc.)
+ enum class AllowExpected { No, Yes };
+
+ void PutbackChar();
+ int ReadChar();
+ void SkipWhitespace();
+ bool Match(const char* s);
+ wabt::Result Expect(const char* s);
+ wabt::Result ExpectKey(const char* key);
+ wabt::Result ParseUint32(uint32_t* out_int);
+ wabt::Result ParseString(std::string* out_string);
+ wabt::Result ParseKeyStringValue(const char* key, std::string* out_string);
+ wabt::Result ParseOptNameStringValue(std::string* out_string);
+ wabt::Result ParseLine(uint32_t* out_line_number);
+ wabt::Result ParseType(Type* out_type);
+ wabt::Result ParseTypeObject(Type* out_type);
+ wabt::Result ParseTypeVector(TypeVector* out_types);
+ wabt::Result ParseConst(TypedValue* out_value);
+ wabt::Result ParseI32Value(uint32_t* out_value, string_view value_str);
+ wabt::Result ParseI64Value(uint64_t* out_value, string_view value_str);
+ wabt::Result ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values);
+ wabt::Result ParseExpectedValue(ExpectedValue* out_value, AllowExpected);
+ wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values);
+ wabt::Result ParseAction(Action* out_action);
+ wabt::Result ParseActionResult();
+ wabt::Result ParseModuleType(ModuleType* out_type);
+
+ std::string CreateModulePath(string_view filename);
+ wabt::Result ParseFilename(std::string* out_filename);
+ wabt::Result ParseCommand(CommandPtr* out_command);
+
+ // Parsing info.
+ std::vector<uint8_t> json_data_;
+ size_t json_offset_ = 0;
+ Location loc_;
+ Location prev_loc_;
+ bool has_prev_loc_ = false;
+};
+
+#define EXPECT(x) CHECK_RESULT(Expect(x))
+#define EXPECT_KEY(x) CHECK_RESULT(ExpectKey(x))
+#define PARSE_KEY_STRING_VALUE(key, value) \
+ CHECK_RESULT(ParseKeyStringValue(key, value))
+
+wabt::Result JSONParser::ReadFile(string_view spec_json_filename) {
+ loc_.filename = spec_json_filename;
+ loc_.line = 1;
+ loc_.first_column = 1;
+
+ return wabt::ReadFile(spec_json_filename, &json_data_);
+}
+
+void JSONParser::PrintError(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ fprintf(stderr, "%s:%d:%d: %s\n", loc_.filename.to_string().c_str(),
+ loc_.line, loc_.first_column, buffer);
+}
+
+void JSONParser::PutbackChar() {
+ assert(has_prev_loc_);
+ json_offset_--;
+ loc_ = prev_loc_;
+ has_prev_loc_ = false;
+}
+
+int JSONParser::ReadChar() {
+ if (json_offset_ >= json_data_.size()) {
+ return -1;
+ }
+ prev_loc_ = loc_;
+ char c = json_data_[json_offset_++];
+ if (c == '\n') {
+ loc_.line++;
+ loc_.first_column = 1;
+ } else {
+ loc_.first_column++;
+ }
+ has_prev_loc_ = true;
+ return c;
+}
+
+void JSONParser::SkipWhitespace() {
+ while (1) {
+ switch (ReadChar()) {
+ case -1:
+ return;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+
+ default:
+ PutbackChar();
+ return;
+ }
+ }
+}
+
+bool JSONParser::Match(const char* s) {
+ SkipWhitespace();
+ Location start_loc = loc_;
+ size_t start_offset = json_offset_;
+ while (*s && *s == ReadChar())
+ s++;
+
+ if (*s == 0) {
+ return true;
+ } else {
+ json_offset_ = start_offset;
+ loc_ = start_loc;
+ return false;
+ }
+}
+
+wabt::Result JSONParser::Expect(const char* s) {
+ if (Match(s)) {
+ return wabt::Result::Ok;
+ } else {
+ PrintError("expected %s", s);
+ return wabt::Result::Error;
+ }
+}
+
+wabt::Result JSONParser::ExpectKey(const char* key) {
+ size_t keylen = strlen(key);
+ size_t quoted_len = keylen + 2 + 1;
+ char* quoted = static_cast<char*>(alloca(quoted_len));
+ snprintf(quoted, quoted_len, "\"%s\"", key);
+ EXPECT(quoted);
+ EXPECT(":");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseUint32(uint32_t* out_int) {
+ uint32_t result = 0;
+ SkipWhitespace();
+ while (1) {
+ int c = ReadChar();
+ if (c >= '0' && c <= '9') {
+ uint32_t last_result = result;
+ result = result * 10 + static_cast<uint32_t>(c - '0');
+ if (result < last_result) {
+ PrintError("uint32 overflow");
+ return wabt::Result::Error;
+ }
+ } else {
+ PutbackChar();
+ break;
+ }
+ }
+ *out_int = result;
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseString(std::string* out_string) {
+ out_string->clear();
+
+ SkipWhitespace();
+ if (ReadChar() != '"') {
+ PrintError("expected string");
+ return wabt::Result::Error;
+ }
+
+ while (1) {
+ int c = ReadChar();
+ if (c == '"') {
+ break;
+ } else if (c == '\\') {
+ /* The only escape supported is \uxxxx. */
+ c = ReadChar();
+ if (c != 'u') {
+ PrintError("expected escape: \\uxxxx");
+ return wabt::Result::Error;
+ }
+ uint16_t code = 0;
+ for (int i = 0; i < 4; ++i) {
+ c = ReadChar();
+ int cval;
+ if (c >= '0' && c <= '9') {
+ cval = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ cval = c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ cval = c - 'A' + 10;
+ } else {
+ PrintError("expected hex char");
+ return wabt::Result::Error;
+ }
+ code = (code << 4) + cval;
+ }
+
+ if (code < 256) {
+ *out_string += code;
+ } else {
+ PrintError("only escape codes < 256 allowed, got %u\n", code);
+ }
+ } else {
+ *out_string += c;
+ }
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseKeyStringValue(const char* key,
+ std::string* out_string) {
+ out_string->clear();
+ EXPECT_KEY(key);
+ return ParseString(out_string);
+}
+
+wabt::Result JSONParser::ParseOptNameStringValue(std::string* out_string) {
+ out_string->clear();
+ if (Match("\"name\"")) {
+ EXPECT(":");
+ CHECK_RESULT(ParseString(out_string));
+ EXPECT(",");
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseLine(uint32_t* out_line_number) {
+ EXPECT_KEY("line");
+ CHECK_RESULT(ParseUint32(out_line_number));
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseType(Type* out_type) {
+ std::string type_str;
+ CHECK_RESULT(ParseString(&type_str));
+
+ if (type_str == "i32") {
+ *out_type = Type::I32;
+ } else if (type_str == "f32") {
+ *out_type = Type::F32;
+ } else if (type_str == "i64") {
+ *out_type = Type::I64;
+ } else if (type_str == "f64") {
+ *out_type = Type::F64;
+ } else if (type_str == "v128") {
+ *out_type = Type::V128;
+ } else if (type_str == "i8") {
+ *out_type = Type::I8;
+ } else if (type_str == "i16") {
+ *out_type = Type::I16;
+ } else if (type_str == "funcref") {
+ *out_type = Type::FuncRef;
+ } else if (type_str == "externref") {
+ *out_type = Type::ExternRef;
+ } else {
+ PrintError("unknown type: \"%s\"", type_str.c_str());
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseTypeObject(Type* out_type) {
+ EXPECT("{");
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(out_type));
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseTypeVector(TypeVector* out_types) {
+ out_types->clear();
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ if (!first) {
+ EXPECT(",");
+ }
+ Type type;
+ CHECK_RESULT(ParseTypeObject(&type));
+ first = false;
+ out_types->push_back(type);
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConst(TypedValue* out_value) {
+ ExpectedValue expected;
+ CHECK_RESULT(ParseExpectedValue(&expected, AllowExpected::No));
+ *out_value = expected.value;
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseI32Value(uint32_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseI64Value(uint64_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
+ }
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
+ }
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ v128 v = out_value->value.value.Get<v128>();
+
+ switch (lane_type) {
+ case Type::I8: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u8(lane, value);
+ break;
+ }
+
+ case Type::I16: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u16(lane, value);
+ break;
+ }
+
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u32(lane, value);
+ break;
+ }
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ v.set_u64(lane, value);
+ break;
+ }
+
+ case Type::F32: {
+ ExpectedNan nan;
+ uint32_t value_bits;
+ CHECK_RESULT(ParseF32Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f32_bits(lane, value_bits);
+ assert(lane < 4);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ case Type::F64: {
+ ExpectedNan nan;
+ uint64_t value_bits;
+ CHECK_RESULT(ParseF64Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f64_bits(lane, value_bits);
+ assert(lane < 2);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", lane_type.GetName());
+ return wabt::Result::Error;
+ }
+
+ out_value->value.value.Set<v128>(v);
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ *out_nan = ExpectedNan::None;
+
+ switch (type) {
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ out_value->Set(value);
+ break;
+ }
+
+ case Type::F32: {
+ uint32_t value_bits;
+ CHECK_RESULT(
+ ParseF32Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f32>(value_bits));
+ break;
+ }
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ out_value->Set(value);
+ break;
+ }
+
+ case Type::F64: {
+ uint64_t value_bits;
+ CHECK_RESULT(
+ ParseF64Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f64>(value_bits));
+ break;
+ }
+
+ case Type::V128:
+ assert(false); // Should use ParseLaneConstValue instead.
+ break;
+
+ case Type::FuncRef:
+ if (value_str == "null") {
+ out_value->Set(Ref::Null);
+ } else {
+ assert(allow_expected == AllowExpected::Yes);
+ out_value->Set(Ref{1});
+ }
+ break;
+
+ case Type::ExternRef:
+ if (value_str == "null") {
+ out_value->Set(Ref::Null);
+ } else {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ // TODO: hack, just whatever ref is at this index; but skip null (which
+ // is always 0).
+ out_value->Set(Ref{value + 1});
+ }
+ break;
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", type.GetName());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value,
+ AllowExpected allow_expected) {
+ Type type;
+ std::string value_str;
+ EXPECT("{");
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(&type));
+ EXPECT(",");
+ if (type == Type::V128) {
+ Type lane_type;
+ EXPECT_KEY("lane_type");
+ CHECK_RESULT(ParseType(&lane_type));
+ EXPECT(",");
+ EXPECT_KEY("value");
+ EXPECT("[");
+
+ int lane_count = LaneCountFromType(lane_type);
+ for (int lane = 0; lane < lane_count; ++lane) {
+ CHECK_RESULT(ParseString(&value_str));
+ CHECK_RESULT(ParseLaneConstValue(lane_type, lane, out_value, value_str,
+ allow_expected));
+ if (lane < lane_count - 1) {
+ EXPECT(",");
+ }
+ }
+ EXPECT("]");
+ out_value->value.type = type;
+ out_value->lane_type = lane_type;
+ } else {
+ PARSE_KEY_STRING_VALUE("value", &value_str);
+ CHECK_RESULT(ParseConstValue(type, &out_value->value.value,
+ &out_value->nan[0], value_str,
+ allow_expected));
+ out_value->value.type = type;
+ }
+ EXPECT("}");
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseExpectedValues(
+ std::vector<ExpectedValue>* out_values) {
+ out_values->clear();
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ if (!first) {
+ EXPECT(",");
+ }
+ ExpectedValue value;
+ CHECK_RESULT(ParseExpectedValue(&value, AllowExpected::Yes));
+ out_values->push_back(value);
+ first = false;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) {
+ out_values->clear();
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ if (!first) {
+ EXPECT(",");
+ }
+ TypedValue tv;
+ CHECK_RESULT(ParseConst(&tv));
+ out_types->push_back(tv.type);
+ out_values->push_back(tv.value);
+ first = false;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseAction(Action* out_action) {
+ EXPECT_KEY("action");
+ EXPECT("{");
+ EXPECT_KEY("type");
+ if (Match("\"invoke\"")) {
+ out_action->type = ActionType::Invoke;
+ } else {
+ EXPECT("\"get\"");
+ out_action->type = ActionType::Get;
+ }
+ EXPECT(",");
+ if (Match("\"module\"")) {
+ EXPECT(":");
+ CHECK_RESULT(ParseString(&out_action->module_name));
+ EXPECT(",");
+ }
+ PARSE_KEY_STRING_VALUE("field", &out_action->field_name);
+ if (out_action->type == ActionType::Invoke) {
+ EXPECT(",");
+ EXPECT_KEY("args");
+ CHECK_RESULT(ParseConstVector(&out_action->types, &out_action->args));
+ }
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseActionResult() {
+ // Not needed for wabt-interp, but useful for other parsers.
+ EXPECT_KEY("expected");
+ TypeVector expected;
+ CHECK_RESULT(ParseTypeVector(&expected));
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseModuleType(ModuleType* out_type) {
+ std::string module_type_str;
+
+ PARSE_KEY_STRING_VALUE("module_type", &module_type_str);
+ if (module_type_str == "text") {
+ *out_type = ModuleType::Text;
+ return wabt::Result::Ok;
+ } else if (module_type_str == "binary") {
+ *out_type = ModuleType::Binary;
+ return wabt::Result::Ok;
+ } else {
+ PrintError("unknown module type: \"%s\"", module_type_str.c_str());
+ return wabt::Result::Error;
+ }
+}
+
+static string_view GetDirname(string_view path) {
+ // Strip everything after and including the last slash (or backslash), e.g.:
+ //
+ // s = "foo/bar/baz", => "foo/bar"
+ // s = "/usr/local/include/stdio.h", => "/usr/local/include"
+ // s = "foo.bar", => ""
+ // s = "some\windows\directory", => "some\windows"
+ size_t last_slash = path.find_last_of('/');
+ size_t last_backslash = path.find_last_of('\\');
+ if (last_slash == string_view::npos) {
+ last_slash = 0;
+ }
+ if (last_backslash == string_view::npos) {
+ last_backslash = 0;
+ }
+
+ return path.substr(0, std::max(last_slash, last_backslash));
+}
+
+std::string JSONParser::CreateModulePath(string_view filename) {
+ string_view spec_json_filename = loc_.filename;
+ string_view dirname = GetDirname(spec_json_filename);
+ std::string path;
+
+ if (dirname.size() == 0) {
+ path = filename.to_string();
+ } else {
+ path = dirname.to_string();
+ path += '/';
+ path += filename.to_string();
+ }
+
+ ConvertBackslashToSlash(&path);
+ return path;
+}
+
+wabt::Result JSONParser::ParseFilename(std::string* out_filename) {
+ PARSE_KEY_STRING_VALUE("filename", out_filename);
+ *out_filename = CreateModulePath(*out_filename);
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) {
+ EXPECT("{");
+ EXPECT_KEY("type");
+ if (Match("\"module\"")) {
+ auto command = MakeUnique<ModuleCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseOptNameStringValue(&command->name));
+ CHECK_RESULT(ParseFilename(&command->filename));
+ *out_command = std::move(command);
+ } else if (Match("\"action\"")) {
+ auto command = MakeUnique<ActionCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *out_command = std::move(command);
+ } else if (Match("\"register\"")) {
+ auto command = MakeUnique<RegisterCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseOptNameStringValue(&command->name));
+ PARSE_KEY_STRING_VALUE("as", &command->as);
+ *out_command = std::move(command);
+ } else if (Match("\"assert_malformed\"")) {
+ auto command = MakeUnique<AssertMalformedCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseFilename(&command->filename));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseModuleType(&command->type));
+ *out_command = std::move(command);
+ } else if (Match("\"assert_invalid\"")) {
+ auto command = MakeUnique<AssertInvalidCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseFilename(&command->filename));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseModuleType(&command->type));
+ *out_command = std::move(command);
+ } else if (Match("\"assert_unlinkable\"")) {
+ auto command = MakeUnique<AssertUnlinkableCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseFilename(&command->filename));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseModuleType(&command->type));
+ *out_command = std::move(command);
+ } else if (Match("\"assert_uninstantiable\"")) {
+ auto command = MakeUnique<AssertUninstantiableCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseFilename(&command->filename));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseModuleType(&command->type));
+ *out_command = std::move(command);
+ } else if (Match("\"assert_return\"")) {
+ auto command = MakeUnique<AssertReturnCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(",");
+ EXPECT_KEY("expected");
+ CHECK_RESULT(ParseExpectedValues(&command->expected));
+ *out_command = std::move(command);
+ } else if (Match("\"assert_trap\"")) {
+ auto command = MakeUnique<AssertTrapCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *out_command = std::move(command);
+ } else if (Match("\"assert_exhaustion\"")) {
+ auto command = MakeUnique<AssertExhaustionCommand>();
+ EXPECT(",");
+ CHECK_RESULT(ParseLine(&command->line));
+ EXPECT(",");
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *out_command = std::move(command);
+ } else {
+ PrintError("unknown command type");
+ return wabt::Result::Error;
+ }
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseScript(Script* out_script) {
+ EXPECT("{");
+ PARSE_KEY_STRING_VALUE("source_filename", &out_script->filename);
+ EXPECT(",");
+ EXPECT_KEY("commands");
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ CommandPtr command;
+ if (!first) {
+ EXPECT(",");
+ }
+ CHECK_RESULT(ParseCommand(&command));
+ out_script->commands.push_back(std::move(command));
+ first = false;
+ }
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+struct ActionResult {
+ ValueTypes types;
+ Values values;
+ Trap::Ptr trap;
+};
+
+class CommandRunner {
+ public:
+ CommandRunner();
+ wabt::Result Run(const Script& script);
+
+ int passed() const { return passed_; }
+ int total() const { return total_; }
+
+ private:
+ using ExportMap = std::map<std::string, Extern::Ptr>;
+ using Registry = std::map<std::string, ExportMap>;
+
+ void WABT_PRINTF_FORMAT(3, 4)
+ PrintError(uint32_t line_number, const char* format, ...);
+ ActionResult RunAction(int line_number,
+ const Action* action,
+ RunVerbosity verbose);
+
+ interp::Module::Ptr ReadModule(string_view module_filename, Errors* errors);
+ Extern::Ptr GetImport(const std::string&, const std::string&);
+ void PopulateImports(const interp::Module::Ptr&, RefVec*);
+ void PopulateExports(const Instance::Ptr&, ExportMap*);
+
+ wabt::Result OnModuleCommand(const ModuleCommand*);
+ wabt::Result OnActionCommand(const ActionCommand*);
+ wabt::Result OnRegisterCommand(const RegisterCommand*);
+ wabt::Result OnAssertMalformedCommand(const AssertMalformedCommand*);
+ wabt::Result OnAssertUnlinkableCommand(const AssertUnlinkableCommand*);
+ wabt::Result OnAssertInvalidCommand(const AssertInvalidCommand*);
+ wabt::Result OnAssertUninstantiableCommand(
+ const AssertUninstantiableCommand*);
+ wabt::Result OnAssertReturnCommand(const AssertReturnCommand*);
+ wabt::Result OnAssertTrapCommand(const AssertTrapCommand*);
+ wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*);
+
+ wabt::Result CheckAssertReturnResult(const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error);
+
+ void TallyCommand(wabt::Result);
+
+ wabt::Result ReadInvalidTextModule(string_view module_filename,
+ const std::string& header);
+ wabt::Result ReadInvalidModule(int line_number,
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
+ wabt::Result ReadUnlinkableModule(int line_number,
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
+
+ Store store_;
+ Registry registry_; // Used when importing.
+ Registry instances_; // Used when referencing module by name in invoke.
+ ExportMap last_instance_;
+ int passed_ = 0;
+ int total_ = 0;
+
+ std::string source_filename_;
+};
+
+CommandRunner::CommandRunner() : store_(s_features) {
+ auto&& spectest = registry_["spectest"];
+
+ // Initialize print functions for the spec test.
+ struct {
+ const char* name;
+ interp::FuncType type;
+ } const print_funcs[] = {
+ {"print", interp::FuncType{{}, {}}},
+ {"print_i32", interp::FuncType{{ValueType::I32}, {}}},
+ {"print_f32", interp::FuncType{{ValueType::F32}, {}}},
+ {"print_f64", interp::FuncType{{ValueType::F64}, {}}},
+ {"print_i32_f32", interp::FuncType{{ValueType::I32, ValueType::F32}, {}}},
+ {"print_f64_f64", interp::FuncType{{ValueType::F64, ValueType::F64}, {}}},
+ };
+
+ for (auto&& print : print_funcs) {
+ auto import_name = StringPrintf("spectest.%s", print.name);
+ spectest[print.name] = HostFunc::New(
+ store_, print.type,
+ [=](Thread& inst, const Values& params, Values& results,
+ Trap::Ptr* trap) -> wabt::Result {
+ printf("called host ");
+ WriteCall(s_stdout_stream.get(), import_name, print.type, params,
+ results, *trap);
+ return wabt::Result::Ok;
+ });
+ }
+
+ spectest["table"] =
+ interp::Table::New(store_, TableType{ValueType::FuncRef, Limits{10, 20}});
+
+ spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}});
+
+ spectest["global_i32"] = interp::Global::New(
+ store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666}));
+ spectest["global_i64"] = interp::Global::New(
+ store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666}));
+ spectest["global_f32"] = interp::Global::New(
+ store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666}));
+ spectest["global_f64"] = interp::Global::New(
+ store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666}));
+}
+
+wabt::Result CommandRunner::Run(const Script& script) {
+ source_filename_ = script.filename;
+
+ for (const CommandPtr& command : script.commands) {
+ switch (command->type) {
+ case CommandType::Module:
+ OnModuleCommand(cast<ModuleCommand>(command.get()));
+ break;
+
+ case CommandType::Action:
+ TallyCommand(OnActionCommand(cast<ActionCommand>(command.get())));
+ break;
+
+ case CommandType::Register:
+ OnRegisterCommand(cast<RegisterCommand>(command.get()));
+ break;
+
+ case CommandType::AssertMalformed:
+ TallyCommand(OnAssertMalformedCommand(
+ cast<AssertMalformedCommand>(command.get())));
+ break;
+
+ case CommandType::AssertInvalid:
+ TallyCommand(
+ OnAssertInvalidCommand(cast<AssertInvalidCommand>(command.get())));
+ break;
+
+ case CommandType::AssertUnlinkable:
+ TallyCommand(OnAssertUnlinkableCommand(
+ cast<AssertUnlinkableCommand>(command.get())));
+ break;
+
+ case CommandType::AssertUninstantiable:
+ TallyCommand(OnAssertUninstantiableCommand(
+ cast<AssertUninstantiableCommand>(command.get())));
+ break;
+
+ case CommandType::AssertReturn:
+ TallyCommand(
+ OnAssertReturnCommand(cast<AssertReturnCommand>(command.get())));
+ break;
+
+ case CommandType::AssertTrap:
+ TallyCommand(
+ OnAssertTrapCommand(cast<AssertTrapCommand>(command.get())));
+ break;
+
+ case CommandType::AssertExhaustion:
+ TallyCommand(OnAssertExhaustionCommand(
+ cast<AssertExhaustionCommand>(command.get())));
+ break;
+ }
+ }
+
+ return wabt::Result::Ok;
+}
+
+void CommandRunner::PrintError(uint32_t line_number, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ printf("%s:%u: %s\n", source_filename_.c_str(), line_number, buffer);
+}
+
+ActionResult CommandRunner::RunAction(int line_number,
+ const Action* action,
+ RunVerbosity verbose) {
+ ExportMap& module = !action->module_name.empty()
+ ? instances_[action->module_name]
+ : last_instance_;
+ Extern::Ptr extern_ = module[action->field_name];
+ if (!extern_) {
+ PrintError(line_number, "unknown invoke \"%s.%s\"",
+ action->module_name.c_str(), action->field_name.c_str());
+ return {};
+ }
+
+ ActionResult result;
+
+ switch (action->type) {
+ case ActionType::Invoke: {
+ auto* func = cast<interp::Func>(extern_.get());
+ func->Call(store_, action->args, result.values, &result.trap,
+ s_trace_stream);
+ result.types = func->type().results;
+ if (verbose == RunVerbosity::Verbose) {
+ WriteCall(s_stdout_stream.get(), action->field_name, func->type(),
+ action->args, result.values, result.trap);
+ }
+ break;
+ }
+
+ case ActionType::Get: {
+ auto* global = cast<interp::Global>(extern_.get());
+ result.values.push_back(global->Get());
+ result.types.push_back(global->type().type);
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ return result;
+}
+
+wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename,
+ const std::string& header) {
+ std::vector<uint8_t> file_data;
+ wabt::Result result = ReadFile(module_filename, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ module_filename, file_data.data(), file_data.size());
+ Errors errors;
+ if (Succeeded(result)) {
+ std::unique_ptr<wabt::Module> module;
+ WastParseOptions options(s_features);
+ result = ParseWatModule(lexer.get(), &module, &errors, &options);
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get(), stdout,
+ header, PrintHeader::Once);
+ return result;
+}
+
+interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename,
+ Errors* errors) {
+ std::vector<uint8_t> file_data;
+
+ if (Failed(ReadFile(module_filename, &file_data))) {
+ return {};
+ }
+
+ const bool kReadDebugNames = true;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+ ModuleDesc module_desc;
+ if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options,
+ errors, &module_desc))) {
+ return {};
+ }
+
+ if (s_verbose) {
+ module_desc.istream.Disassemble(s_stdout_stream.get());
+ }
+
+ return interp::Module::New(store_, module_desc);
+}
+
+wabt::Result CommandRunner::ReadInvalidModule(int line_number,
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc) {
+ std::string header = StringPrintf(
+ "%s:%d: %s passed", source_filename_.c_str(), line_number, desc);
+
+ switch (module_type) {
+ case ModuleType::Text: {
+ return ReadInvalidTextModule(module_filename, header);
+ }
+
+ case ModuleType::Binary: {
+ Errors errors;
+ auto module = ReadModule(module_filename, &errors);
+ if (!module) {
+ FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header,
+ PrintHeader::Once);
+ return wabt::Result::Error;
+ } else {
+ return wabt::Result::Ok;
+ }
+ }
+ }
+
+ WABT_UNREACHABLE;
+}
+
+Extern::Ptr CommandRunner::GetImport(const std::string& module,
+ const std::string& name) {
+ auto mod_iter = registry_.find(module);
+ if (mod_iter != registry_.end()) {
+ auto extern_iter = mod_iter->second.find(name);
+ if (extern_iter != mod_iter->second.end()) {
+ return extern_iter->second;
+ }
+ }
+ return {};
+}
+
+void CommandRunner::PopulateImports(const interp::Module::Ptr& module,
+ RefVec* imports) {
+ for (auto&& import : module->desc().imports) {
+ auto extern_ = GetImport(import.type.module, import.type.name);
+ imports->push_back(extern_ ? extern_.ref() : Ref::Null);
+ }
+}
+
+void CommandRunner::PopulateExports(const Instance::Ptr& instance,
+ ExportMap* map) {
+ map->clear();
+ interp::Module::Ptr module{store_, instance->module()};
+ for (size_t i = 0; i < module->export_types().size(); ++i) {
+ const ExportType& export_type = module->export_types()[i];
+ (*map)[export_type.name] = store_.UnsafeGet<Extern>(instance->exports()[i]);
+ }
+}
+
+wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+ FormatErrorsToFile(errors, Location::Type::Binary);
+
+ if (!module) {
+ PrintError(command->line, "error reading module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (trap) {
+ assert(!instance);
+ PrintError(command->line, "error instantiating module: \"%s\"",
+ trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ PopulateExports(instance, &last_instance_);
+ if (!command->name.empty()) {
+ instances_[command->name] = last_instance_;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Verbose);
+
+ if (result.trap) {
+ PrintError(command->line, "unexpected trap: %s",
+ result.trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertMalformedCommand(
+ const AssertMalformedCommand* command) {
+ wabt::Result result = ReadInvalidModule(command->line, command->filename,
+ command->type, "assert_malformed");
+ if (Succeeded(result)) {
+ PrintError(command->line, "expected module to be malformed: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) {
+ if (!command->name.empty()) {
+ auto instance_iter = instances_.find(command->name);
+ if (instance_iter == instances_.end()) {
+ PrintError(command->line, "unknown module in register");
+ return wabt::Result::Error;
+ }
+ registry_[command->as] = instance_iter->second;
+ } else {
+ registry_[command->as] = last_instance_;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertUnlinkableCommand(
+ const AssertUnlinkableCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+
+ if (!module) {
+ PrintError(command->line, "unable to compile unlinkable module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (!trap) {
+ PrintError(command->line, "expected module to be unlinkable: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: Change to one-line error.
+ PrintError(command->line, "assert_unlinkable passed:\n error: %s",
+ trap->message().c_str());
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertInvalidCommand(
+ const AssertInvalidCommand* command) {
+ wabt::Result result = ReadInvalidModule(command->line, command->filename,
+ command->type, "assert_invalid");
+ if (Succeeded(result)) {
+ PrintError(command->line, "expected module to be invalid: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertUninstantiableCommand(
+ const AssertUninstantiableCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+
+ if (!module) {
+ PrintError(command->line, "unable to compile uninstantiable module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (!trap) {
+ PrintError(command->line, "expected module to be uninstantiable: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: print error when assertion passes.
+#if 0
+ PrintError(command->line, "assert_uninstantiable passed: %s",
+ trap->message().c_str());
+#endif
+ return wabt::Result::Ok;
+}
+
+static bool WABT_VECTORCALL IsCanonicalNan(f32 val) {
+ const u32 kQuietNan = 0x7fc00000U;
+ const u32 kQuietNegNan = 0xffc00000U;
+ u32 bits = Bitcast<u32>(val);
+ return bits == kQuietNan || bits == kQuietNegNan;
+}
+
+static bool WABT_VECTORCALL IsCanonicalNan(f64 val) {
+ const u64 kQuietNan = 0x7ff8000000000000ULL;
+ const u64 kQuietNegNan = 0xfff8000000000000ULL;
+ u64 bits = Bitcast<u64>(val);
+ return bits == kQuietNan || bits == kQuietNegNan;
+}
+
+static bool WABT_VECTORCALL IsArithmeticNan(f32 val) {
+ const u32 kQuietNan = 0x7fc00000U;
+ return (Bitcast<u32>(val) & kQuietNan) == kQuietNan;
+}
+
+static bool WABT_VECTORCALL IsArithmeticNan(f64 val) {
+ const u64 kQuietNan = 0x7ff8000000000000ULL;
+ return (Bitcast<u64>(val) & kQuietNan) == kQuietNan;
+}
+
+static std::string ExpectedValueToString(const ExpectedValue& ev) {
+ // Extend TypedValueToString to print expected nan values too.
+ switch (ev.value.type) {
+ case Type::F32:
+ case Type::F64:
+ switch (ev.nan[0]) {
+ case ExpectedNan::None:
+ return TypedValueToString(ev.value);
+
+ case ExpectedNan::Arithmetic:
+ return StringPrintf("%s:nan:arithmetic", ev.value.type.GetName());
+
+ case ExpectedNan::Canonical:
+ return StringPrintf("%s:nan:canonical", ev.value.type.GetName());
+ }
+ break;
+
+ case Type::V128: {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ std::string result = "v128 ";
+ for (int lane = 0; lane < lane_count; ++lane) {
+ result += ExpectedValueToString(GetLane(ev, lane));
+ }
+ return result;
+ }
+
+ default:
+ break;
+ }
+ return TypedValueToString(ev.value);
+}
+
+wabt::Result CommandRunner::CheckAssertReturnResult(
+ const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error) {
+ assert(expected.value.type == actual.type ||
+ IsReference(expected.value.type));
+ bool ok = true;
+ switch (expected.value.type) {
+ case Type::I8:
+ case Type::I16:
+ case Type::I32:
+ ok = expected.value.value.Get<u32>() == actual.value.Get<u32>();
+ break;
+
+ case Type::I64:
+ ok = expected.value.value.Get<u64>() == actual.value.Get<u64>();
+ break;
+
+ case Type::F32:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u32>(expected.value.value.Get<f32>()) ==
+ Bitcast<u32>(actual.value.Get<f32>());
+ break;
+ }
+ break;
+
+ case Type::F64:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u64>(expected.value.value.Get<f64>()) ==
+ Bitcast<u64>(actual.value.Get<f64>());
+ break;
+ }
+ break;
+
+ case Type::V128: {
+ // Compare each lane as if it were its own value.
+ for (int lane = 0; lane < LaneCountFromType(expected.lane_type); ++lane) {
+ ExpectedValue lane_expected = GetLane(expected, lane);
+ TypedValue lane_actual = GetLane(actual, expected.lane_type, lane);
+
+ if (Failed(CheckAssertReturnResult(command, index, lane_expected,
+ lane_actual, false))) {
+ PrintError(command->line,
+ "mismatch in lane %u of result %u of assert_return: "
+ "expected %s, got %s",
+ lane, index, ExpectedValueToString(lane_expected).c_str(),
+ TypedValueToString(lane_actual).c_str());
+ ok = false;
+ }
+ }
+ break;
+ }
+
+ case Type::FuncRef:
+ // A funcref expectation only requires that the reference be a function,
+ // but it doesn't check the actual index.
+ ok = (actual.type == Type::FuncRef);
+ break;
+
+ case Type::ExternRef:
+ ok = expected.value.value.Get<Ref>() == actual.value.Get<Ref>();
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (!ok && print_error) {
+ PrintError(command->line,
+ "mismatch in result %u of assert_return: expected %s, got %s",
+ index, ExpectedValueToString(expected).c_str(),
+ TypedValueToString(actual).c_str());
+ }
+ return ok ? wabt::Result::Ok : wabt::Result::Error;
+}
+
+wabt::Result CommandRunner::OnAssertReturnCommand(
+ const AssertReturnCommand* command) {
+ ActionResult action_result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+
+ if (action_result.trap) {
+ PrintError(command->line, "unexpected trap: %s",
+ action_result.trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ if (action_result.values.size() != command->expected.size()) {
+ PrintError(command->line,
+ "result length mismatch in assert_return: expected %" PRIzd
+ ", got %" PRIzd,
+ command->expected.size(), action_result.values.size());
+ return wabt::Result::Error;
+ }
+
+ wabt::Result result = wabt::Result::Ok;
+ for (size_t i = 0; i < action_result.values.size(); ++i) {
+ const ExpectedValue& expected = command->expected[i];
+ TypedValue actual{action_result.types[i], action_result.values[i]};
+
+ result |= CheckAssertReturnResult(command, i, expected, actual, true);
+ }
+
+ return result;
+}
+
+wabt::Result CommandRunner::OnAssertTrapCommand(
+ const AssertTrapCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+ if (!result.trap) {
+ PrintError(command->line, "expected trap: \"%s\"", command->text.c_str());
+ return wabt::Result::Error;
+ }
+
+ PrintError(command->line, "assert_trap passed: %s",
+ result.trap->message().c_str());
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertExhaustionCommand(
+ const AssertExhaustionCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+ if (!result.trap || result.trap->message() != "call stack exhausted") {
+ PrintError(command->line, "expected trap: \"%s\"", command->text.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: print message when assertion passes.
+#if 0
+ PrintError(command->line, "assert_exhaustion passed: %s",
+ result.trap->message().c_str());
+#endif
+ return wabt::Result::Ok;
+}
+
+void CommandRunner::TallyCommand(wabt::Result result) {
+ if (Succeeded(result)) {
+ passed_++;
+ }
+ total_++;
+}
+
+static int ReadAndRunSpecJSON(string_view spec_json_filename) {
+ JSONParser parser;
+ if (parser.ReadFile(spec_json_filename) == wabt::Result::Error) {
+ return 1;
+ }
+
+ Script script;
+ if (parser.ParseScript(&script) == wabt::Result::Error) {
+ return 1;
+ }
+
+ CommandRunner runner;
+ if (runner.Run(script) == wabt::Result::Error) {
+ return 1;
+ }
+
+ printf("%d/%d tests passed.\n", runner.passed(), runner.total());
+ const int failed = runner.total() - runner.passed();
+ return failed;
+}
+
+} // namespace spectest
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ s_stdout_stream = FileStream::CreateStdout();
+
+ ParseOptions(argc, argv);
+ return spectest::ReadAndRunSpecJSON(s_infile);
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-decompile.cc b/third_party/wasm2c/src/tools/wasm-decompile.cc
new file mode 100644
index 0000000000..74491e55c3
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-decompile.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/decompiler.h"
+
+using namespace wabt;
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ std::string infile;
+ std::string outfile;
+ Features features;
+ DecompileOptions decompile_options;
+ bool fail_on_custom_section_error = true;
+
+ {
+ const char s_description[] =
+ " Read a file in the WebAssembly binary format, and convert it to\n"
+ " a decompiled text file.\n"
+ "\n"
+ "examples:\n"
+ " # parse binary file test.wasm and write text file test.dcmp\n"
+ " $ wasm-decompile test.wasm -o test.dcmp\n";
+ OptionParser parser("wasm-decompile", s_description);
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the decompiled file, by default use stdout",
+ [&](const char* argument) {
+ outfile = argument;
+ ConvertBackslashToSlash(&outfile);
+ });
+ features.AddOptions(&parser);
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ [&]() { fail_on_custom_section_error = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [&](const char* argument) {
+ infile = argument;
+ ConvertBackslashToSlash(&infile);
+ });
+ parser.Parse(argc, argv);
+ }
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(features, nullptr,
+ true, kStopOnFirstError,
+ fail_on_custom_section_error);
+ result = ReadBinaryIr(infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ ValidateOptions options(features);
+ result = ValidateModule(&module, &errors, options);
+ if (Succeeded(result)) {
+ result = GenerateNames(&module,
+ static_cast<NameOpts>(NameOpts::AlphaNames));
+ }
+ if (Succeeded(result)) {
+ // Must be called after ReadBinaryIr & GenerateNames, and before
+ // ApplyNames, see comments at definition.
+ RenameAll(module);
+ }
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+ if (Succeeded(result)) {
+ auto s = Decompile(module, decompile_options);
+ FileStream stream(!outfile.empty() ? FileStream(outfile)
+ : FileStream(stdout));
+ stream.WriteData(s.data(), s.size());
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-interp.cc b/third_party/wasm2c/src/tools/wasm-interp.cc
new file mode 100644
index 0000000000..e349a9e323
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-interp.cc
@@ -0,0 +1,337 @@
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/interp/binary-reader-interp.h"
+#include "src/interp/interp-util.h"
+#include "src/interp/interp-wasi.h"
+#include "src/interp/interp.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+#ifdef WITH_WASI
+#include "uvwasi.h"
+#endif
+
+using namespace wabt;
+using namespace wabt::interp;
+
+static int s_verbose;
+static const char* s_infile;
+static Thread::Options s_thread_options;
+static Stream* s_trace_stream;
+static bool s_run_all_exports;
+static bool s_host_print;
+static bool s_dummy_import_func;
+static Features s_features;
+static bool s_wasi;
+static std::vector<std::string> s_wasi_env;
+static std::vector<std::string> s_wasi_argv;
+static std::vector<std::string> s_wasi_dirs;
+
+static std::unique_ptr<FileStream> s_log_stream;
+static std::unique_ptr<FileStream> s_stdout_stream;
+static std::unique_ptr<FileStream> s_stderr_stream;
+
+static Store s_store;
+
+static const char s_description[] =
+ R"( read a file in the wasm binary format, and run in it a stack-based
+ interpreter.
+
+examples:
+ # parse binary file test.wasm, and type-check it
+ $ wasm-interp test.wasm
+
+ # parse test.wasm and run all its exported functions
+ $ wasm-interp test.wasm --run-all-exports
+
+ # parse test.wasm, run the exported functions and trace the output
+ $ wasm-interp test.wasm --run-all-exports --trace
+
+ # parse test.wasm and run all its exported functions, setting the
+ # value stack size to 100 elements
+ $ wasm-interp test.wasm -V 100 --run-all-exports
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-interp", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption('V', "value-stack-size", "SIZE",
+ "Size in elements of the value stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.value_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('C', "call-stack-size", "SIZE",
+ "Size in elements of the call stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.call_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('t', "trace", "Trace execution",
+ []() { s_trace_stream = s_stdout_stream.get(); });
+ parser.AddOption("wasi",
+ "Assume input module is WASI compliant (Export "
+ " WASI API the the module and invoke _start function)",
+ []() { s_wasi = true; });
+ parser.AddOption(
+ 'e', "env", "ENV",
+ "Pass the given environment string in the WASI runtime",
+ [](const std::string& argument) { s_wasi_env.push_back(argument); });
+ parser.AddOption(
+ 'd', "dir", "DIR", "Pass the given directory the the WASI runtime",
+ [](const std::string& argument) { s_wasi_dirs.push_back(argument); });
+ parser.AddOption(
+ "run-all-exports",
+ "Run all the exported functions, in order. Useful for testing",
+ []() { s_run_all_exports = true; });
+ parser.AddOption("host-print",
+ "Include an importable function named \"host.print\" for "
+ "printing to stdout",
+ []() { s_host_print = true; });
+ parser.AddOption(
+ "dummy-import-func",
+ "Provide a dummy implementation of all imported functions. The function "
+ "will log the call and return an appropriate zero value.",
+ []() { s_dummy_import_func = true; });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.AddArgument(
+ "arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [](const char* argument) { s_wasi_argv.push_back(argument); });
+ parser.Parse(argc, argv);
+}
+
+Result RunAllExports(const Instance::Ptr& instance, Errors* errors) {
+ Result result = Result::Ok;
+
+ auto module = s_store.UnsafeGet<Module>(instance->module());
+ auto&& module_desc = module->desc();
+
+ for (auto&& export_ : module_desc.exports) {
+ if (export_.type.type->kind != ExternalKind::Func) {
+ continue;
+ }
+ auto* func_type = cast<FuncType>(export_.type.type.get());
+ if (func_type->params.empty()) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef(">>> running export \"%s\":\n",
+ export_.type.name.c_str());
+ }
+ auto func = s_store.UnsafeGet<Func>(instance->funcs()[export_.index]);
+ Values params;
+ Values results;
+ Trap::Ptr trap;
+ result |= func->Call(s_store, params, results, &trap, s_trace_stream);
+ WriteCall(s_stdout_stream.get(), export_.type.name, *func_type, params,
+ results, trap);
+ }
+ }
+
+ return result;
+}
+
+static void BindImports(const Module::Ptr& module, RefVec& imports) {
+ auto* stream = s_stdout_stream.get();
+
+ for (auto&& import : module->desc().imports) {
+ if (import.type.type->kind == ExternKind::Func &&
+ ((s_host_print && import.type.module == "host" &&
+ import.type.name == "print") ||
+ s_dummy_import_func)) {
+ auto func_type = *cast<FuncType>(import.type.type.get());
+ auto import_name = StringPrintf("%s.%s", import.type.module.c_str(),
+ import.type.name.c_str());
+
+ auto host_func =
+ HostFunc::New(s_store, func_type,
+ [=](Thread& thread, const Values& params,
+ Values& results, Trap::Ptr* trap) -> Result {
+ printf("called host ");
+ WriteCall(stream, import_name, func_type, params,
+ results, *trap);
+ return Result::Ok;
+ });
+ imports.push_back(host_func.ref());
+ continue;
+ }
+
+ // By default, just push an null reference. This won't resolve, and
+ // instantiation will fail.
+ imports.push_back(Ref::Null);
+ }
+}
+
+static Result ReadModule(const char* module_filename,
+ Errors* errors,
+ Module::Ptr* out_module) {
+ auto* stream = s_stdout_stream.get();
+ std::vector<uint8_t> file_data;
+ CHECK_RESULT(ReadFile(module_filename, &file_data));
+
+ ModuleDesc module_desc;
+ const bool kReadDebugNames = true;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+ CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options,
+ errors, &module_desc));
+
+ if (s_verbose) {
+ module_desc.istream.Disassemble(stream);
+ }
+
+ *out_module = Module::New(s_store, module_desc);
+ return Result::Ok;
+}
+
+static Result InstantiateModule(RefVec& imports,
+ const Module::Ptr& module,
+ Instance::Ptr* out_instance) {
+ RefPtr<Trap> trap;
+ *out_instance = Instance::Instantiate(s_store, module.ref(), imports, &trap);
+ if (!*out_instance) {
+ WriteTrap(s_stderr_stream.get(), "error initializing module", trap);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+static Result ReadAndRunModule(const char* module_filename) {
+ Errors errors;
+ Module::Ptr module;
+ Result result = ReadModule(module_filename, &errors, &module);
+ if (!Succeeded(result)) {
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ return result;
+ }
+
+ RefVec imports;
+
+#if WITH_WASI
+ uvwasi_t uvwasi;
+#endif
+
+ if (s_wasi) {
+#if WITH_WASI
+ uvwasi_errno_t err;
+ uvwasi_options_t init_options;
+
+ std::vector<const char*> argv;
+ argv.push_back(module_filename);
+ for (auto& s : s_wasi_argv) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: arg: \"%s\"\n", s.c_str());
+ }
+ argv.push_back(s.c_str());
+ }
+ argv.push_back(nullptr);
+
+ std::vector<const char*> envp;
+ for (auto& s : s_wasi_env) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: env: \"%s\"\n", s.c_str());
+ }
+ envp.push_back(s.c_str());
+ }
+ envp.push_back(nullptr);
+
+ std::vector<uvwasi_preopen_t> dirs;
+ for (auto& dir : s_wasi_dirs) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: dir: \"%s\"\n", dir.c_str());
+ }
+ dirs.push_back({dir.c_str(), dir.c_str()});
+ }
+
+ /* Setup the initialization options. */
+ init_options.in = 0;
+ init_options.out = 1;
+ init_options.err = 2;
+ init_options.fd_table_size = 3;
+ init_options.argc = argv.size() - 1;
+ init_options.argv = argv.data();
+ init_options.envp = envp.data();
+ init_options.preopenc = dirs.size();
+ init_options.preopens = dirs.data();
+ init_options.allocator = NULL;
+
+ err = uvwasi_init(&uvwasi, &init_options);
+ if (err != UVWASI_ESUCCESS) {
+ s_stderr_stream.get()->Writef("error initialiazing uvwasi: %d\n", err);
+ return Result::Error;
+ }
+ CHECK_RESULT(WasiBindImports(module, imports, s_stderr_stream.get(),
+ s_trace_stream));
+#else
+ s_stderr_stream.get()->Writef("wasi support not compiled in\n");
+ return Result::Error;
+#endif
+ } else {
+ BindImports(module, imports);
+ }
+ BindImports(module, imports);
+
+ Instance::Ptr instance;
+ CHECK_RESULT(InstantiateModule(imports, module, &instance));
+
+ if (s_run_all_exports) {
+ RunAllExports(instance, &errors);
+ }
+#ifdef WITH_WASI
+ if (s_wasi) {
+ CHECK_RESULT(
+ WasiRunStart(instance, &uvwasi, s_stderr_stream.get(), s_trace_stream));
+ }
+#endif
+
+ return Result::Ok;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ s_stdout_stream = FileStream::CreateStdout();
+ s_stderr_stream = FileStream::CreateStderr();
+
+ ParseOptions(argc, argv);
+
+ wabt::Result result = ReadAndRunModule(s_infile);
+ return result != wabt::Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-objdump.cc b/third_party/wasm2c/src/tools/wasm-objdump.cc
new file mode 100644
index 0000000000..fbfed12cf1
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-objdump.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "src/common.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-objdump.h"
+
+using namespace wabt;
+
+static const char s_description[] =
+R"( Print information about the contents of wasm binaries.
+
+examples:
+ $ wasm-objdump test.wasm
+)";
+
+static ObjdumpOptions s_objdump_options;
+
+static std::vector<const char*> s_infiles;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-objdump", s_description);
+
+ parser.AddOption('h', "headers", "Print headers",
+ []() { s_objdump_options.headers = true; });
+ parser.AddOption(
+ 'j', "section", "SECTION", "Select just one section",
+ [](const char* argument) { s_objdump_options.section_name = argument; });
+ parser.AddOption('s', "full-contents", "Print raw section contents",
+ []() { s_objdump_options.raw = true; });
+ parser.AddOption('d', "disassemble", "Disassemble function bodies",
+ []() { s_objdump_options.disassemble = true; });
+ parser.AddOption("debug", "Print extra debug information", []() {
+ s_objdump_options.debug = true;
+ s_log_stream = FileStream::CreateStderr();
+ s_objdump_options.log_stream = s_log_stream.get();
+ });
+ parser.AddOption('x', "details", "Show section details",
+ []() { s_objdump_options.details = true; });
+ parser.AddOption('r', "reloc", "Show relocations inline with disassembly",
+ []() { s_objdump_options.relocs = true; });
+ parser.AddOption(0, "section-offsets",
+ "Print section offsets instead of file offsets "
+ "in code disassembly",
+ []() { s_objdump_options.section_offsets = true; });
+ parser.AddArgument(
+ "filename", OptionParser::ArgumentCount::OneOrMore,
+ [](const char* argument) { s_infiles.push_back(argument); });
+
+ parser.Parse(argc, argv);
+}
+
+Result dump_file(const char* filename) {
+ std::vector<uint8_t> file_data;
+ CHECK_RESULT(ReadFile(filename, &file_data));
+
+ uint8_t* data = file_data.data();
+ size_t size = file_data.size();
+
+ // Perform serveral passed over the binary in order to print out different
+ // types of information.
+ s_objdump_options.filename = filename;
+ printf("\n");
+
+ ObjdumpState state;
+
+ Result result = Result::Ok;
+
+ // Pass 0: Prepass
+ s_objdump_options.mode = ObjdumpMode::Prepass;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ s_objdump_options.log_stream = nullptr;
+
+ // Pass 1: Print the section headers
+ if (s_objdump_options.headers) {
+ s_objdump_options.mode = ObjdumpMode::Headers;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 2: Print extra information based on section type
+ if (s_objdump_options.details) {
+ s_objdump_options.mode = ObjdumpMode::Details;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 3: Disassemble code section
+ if (s_objdump_options.disassemble) {
+ s_objdump_options.mode = ObjdumpMode::Disassemble;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 4: Dump to raw contents of the sections
+ if (s_objdump_options.raw) {
+ s_objdump_options.mode = ObjdumpMode::RawData;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+ if (!s_objdump_options.headers && !s_objdump_options.details &&
+ !s_objdump_options.disassemble && !s_objdump_options.raw) {
+ fprintf(stderr, "At least one of the following switches must be given:\n");
+ fprintf(stderr, " -d/--disassemble\n");
+ fprintf(stderr, " -h/--headers\n");
+ fprintf(stderr, " -x/--details\n");
+ fprintf(stderr, " -s/--full-contents\n");
+ return 1;
+ }
+
+ for (const char* filename: s_infiles) {
+ if (Failed(dump_file(filename))) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-opcodecnt.cc b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc
new file mode 100644
index 0000000000..5d6225f098
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc
@@ -0,0 +1,188 @@
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/binary-reader-opcnt.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+#define ERROR(fmt, ...) \
+ fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__)
+
+using namespace wabt;
+
+static int s_verbose;
+static const char* s_infile;
+static const char* s_outfile;
+static size_t s_cutoff = 0;
+static const char* s_separator = ": ";
+
+static ReadBinaryOptions s_read_binary_options;
+static std::unique_ptr<FileStream> s_log_stream;
+static Features s_features;
+
+static const char s_description[] =
+R"( Read a file in the wasm binary format, and count opcode usage for
+ instructions.
+
+examples:
+ # parse binary file test.wasm and write pcode dist file test.dist
+ $ wasm-opcodecnt test.wasm -o test.dist
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-opcodecnt", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ s_read_binary_options.log_stream = s_log_stream.get();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILENAME",
+ "Output file for the opcode counts, by default use stdout",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'c', "cutoff", "N", "Cutoff for reporting counts less than N",
+ [](const std::string& argument) { s_cutoff = atol(argument.c_str()); });
+ parser.AddOption(
+ 's', "separator", "SEPARATOR",
+ "Separator text between element and count when reporting counts",
+ [](const char* argument) { s_separator = argument; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::OneOrMore,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+template <typename T>
+struct SortByCountDescending {
+ bool operator()(const T& lhs, const T& rhs) const {
+ return lhs.second > rhs.second;
+ }
+};
+
+template <typename T>
+struct WithinCutoff {
+ bool operator()(const T& pair) const {
+ return pair.second >= s_cutoff;
+ }
+};
+
+static size_t SumCounts(const OpcodeInfoCounts& info_counts) {
+ size_t sum = 0;
+ for (auto& pair : info_counts) {
+ sum += pair.second;
+ }
+ return sum;
+}
+
+void WriteCounts(Stream& stream, const OpcodeInfoCounts& info_counts) {
+ typedef std::pair<Opcode, size_t> OpcodeCountPair;
+
+ std::map<Opcode, size_t> counts;
+ for (auto& info_count_pair: info_counts) {
+ Opcode opcode = info_count_pair.first.opcode();
+ size_t count = info_count_pair.second;
+ counts[opcode] += count;
+ }
+
+ std::vector<OpcodeCountPair> sorted;
+ std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted),
+ WithinCutoff<OpcodeCountPair>());
+
+ // Use a stable sort to keep the elements with the same count in opcode
+ // order (since the Opcode map is sorted).
+ std::stable_sort(sorted.begin(), sorted.end(),
+ SortByCountDescending<OpcodeCountPair>());
+
+ for (auto& pair : sorted) {
+ Opcode opcode = pair.first;
+ size_t count = pair.second;
+ stream.Writef("%s%s%" PRIzd "\n", opcode.GetName(), s_separator, count);
+ }
+}
+
+void WriteCountsWithImmediates(Stream& stream,
+ const OpcodeInfoCounts& counts) {
+ // Remove const from the key type so we can sort below.
+ typedef std::pair<std::remove_const<OpcodeInfoCounts::key_type>::type,
+ OpcodeInfoCounts::mapped_type>
+ OpcodeInfoCountPair;
+
+ std::vector<OpcodeInfoCountPair> sorted;
+ std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted),
+ WithinCutoff<OpcodeInfoCountPair>());
+
+ // Use a stable sort to keep the elements with the same count in opcode info
+ // order (since the OpcodeInfoCounts map is sorted).
+ std::stable_sort(sorted.begin(), sorted.end(),
+ SortByCountDescending<OpcodeInfoCountPair>());
+
+ for (auto& pair : sorted) {
+ auto&& info = pair.first;
+ size_t count = pair.second;
+ info.Write(stream);
+ stream.Writef("%s%" PRIzd "\n", s_separator, count);
+ }
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ if (Failed(result)) {
+ const char* input_name = s_infile ? s_infile : "stdin";
+ ERROR("Unable to parse: %s", input_name);
+ return 1;
+ }
+
+ FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout));
+
+ if (Succeeded(result)) {
+ OpcodeInfoCounts counts;
+ s_read_binary_options.features = s_features;
+ result = ReadBinaryOpcnt(file_data.data(), file_data.size(),
+ s_read_binary_options, &counts);
+ if (Succeeded(result)) {
+ stream.Writef("Total opcodes: %" PRIzd "\n\n", SumCounts(counts));
+
+ stream.Writef("Opcode counts:\n");
+ WriteCounts(stream, counts);
+
+ stream.Writef("\nOpcode counts with immediates:\n");
+ WriteCountsWithImmediates(stream, counts);
+ }
+ }
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-strip.cc b/third_party/wasm2c/src/tools/wasm-strip.cc
new file mode 100644
index 0000000000..f838672064
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-strip.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/binary.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-nop.h"
+#include "src/error-formatter.h"
+#include "src/leb128.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+using namespace wabt;
+
+static std::string s_filename;
+
+static const char s_description[] =
+R"( Remove sections of a WebAssembly binary file.
+
+examples:
+ # Remove all custom sections from test.wasm
+ $ wasm-strip test.wasm
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-strip", s_description);
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_filename = argument;
+ ConvertBackslashToSlash(&s_filename);
+ });
+ parser.Parse(argc, argv);
+}
+
+class BinaryReaderStrip : public BinaryReaderNop {
+ public:
+ explicit BinaryReaderStrip(Errors* errors)
+ : errors_(errors) {
+ stream_.WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
+ stream_.WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
+ }
+
+ bool OnError(const Error& error) override {
+ errors_->push_back(error);
+ return true;
+ }
+
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override {
+ if (section_type == BinarySection::Custom) {
+ return Result::Ok;
+ }
+ stream_.WriteU8Enum(section_type, "section code");
+ WriteU32Leb128(&stream_, size, "section size");
+ stream_.WriteData(state->data + state->offset, size, "section data");
+ return Result::Ok;
+ }
+
+ Result WriteToFile(string_view filename) {
+ return stream_.WriteToFile(filename);
+ }
+
+ private:
+ MemoryStream stream_;
+ Errors* errors_;
+};
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_filename.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Features features;
+ const bool kReadDebugNames = false;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = false;
+ ReadBinaryOptions options(features, nullptr, kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+
+ BinaryReaderStrip reader(&errors);
+ result = ReadBinary(file_data.data(), file_data.size(), &reader, options);
+ FormatErrorsToFile(errors, Location::Type::Binary);
+
+ if (Succeeded(result)) {
+ result = reader.WriteToFile(s_filename);
+ }
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-validate.cc b/third_party/wasm2c/src/tools/wasm-validate.cc
new file mode 100644
index 0000000000..a55c5ed898
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-validate.cc
@@ -0,0 +1,99 @@
+/*
+ * 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static Features s_features;
+static bool s_read_debug_names = true;
+static bool s_fail_on_custom_section_error = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and validate it.
+
+examples:
+ # validate binary file test.wasm
+ $ wasm-validate test.wasm
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-validate", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ []() { s_fail_on_custom_section_error = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ s_fail_on_custom_section_error);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm2c.cc b/third_party/wasm2c/src/tools/wasm2c.cc
new file mode 100644
index 0000000000..680be01045
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2c.cc
@@ -0,0 +1,169 @@
+/*
+ * 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+
+#include "src/c-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static Features s_features;
+static WriteCOptions s_write_c_options;
+static bool s_read_debug_names = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and convert it to
+ a C source file and header.
+
+examples:
+ # parse binary file test.wasm and write test.c and test.h
+ $ wasm2c test.wasm -o test.c
+
+ # parse test.wasm, write test.c and test.h, but ignore the debug names, if any
+ $ wasm2c test.wasm --no-debug-names -o test.c
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2c", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated C source file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ parser.AddOption(
+ 'n', "modname", "MODNAME",
+ "Unique name for the module being generated. Each wasm sandboxed module in a single application should have a unique name.",
+ [](const char* argument) {
+ s_write_c_options.mod_name = argument;
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+
+ // TODO(binji): currently wasm2c doesn't support any non-default feature
+ // flags.
+ bool any_non_default_feature = false;
+#define WABT_FEATURE(variable, flag, default_, help) \
+ any_non_default_feature |= (s_features.variable##_enabled() != default_);
+#include "src/feature.def"
+#undef WABT_FEATURE
+
+ if (any_non_default_feature) {
+ fprintf(stderr, "wasm2c currently support only default feature flags.\n");
+ exit(1);
+ }
+}
+
+// TODO(binji): copied from binary-writer-spec.cc, probably should share.
+static string_view strip_extension(string_view s) {
+ string_view ext = s.substr(s.find_last_of('.'));
+ string_view result = s;
+
+ if (ext == ".c")
+ result.remove_suffix(ext.length());
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ kFailOnCustomSectionError);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ if (Succeeded(result)) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ result |= GenerateNames(&module);
+ }
+
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+
+ if (Succeeded(result)) {
+ if (!s_outfile.empty()) {
+ std::string header_name =
+ strip_extension(s_outfile).to_string() + ".h";
+ FileStream c_stream(s_outfile.c_str());
+ FileStream h_stream(header_name);
+ result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module,
+ s_write_c_options);
+ } else {
+ FileStream stream(stdout);
+ result =
+ WriteC(&stream, &stream, "wasm.h", &module, s_write_c_options);
+ }
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
+
diff --git a/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc
new file mode 100644
index 0000000000..1318ef6284
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 Google LLC
+//
+// 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.
+//
+// This file is copied from the oss-fuzz project:
+//
+// https://github.com/google/oss-fuzz/blob/master/projects/wabt/wasm2wat_fuzzer.cc
+
+#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
+#include "src/common.h"
+#include "src/ir.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ wabt::ReadBinaryOptions options;
+ wabt::Errors errors;
+ wabt::Module module;
+ wabt::ReadBinaryIr("dummy filename", data, size, options, &errors, &module);
+ return 0;
+}
diff --git a/third_party/wasm2c/src/tools/wasm2wat.cc b/third_party/wasm2c/src/tools/wasm2wat.cc
new file mode 100644
index 0000000000..58e89849fe
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2wat.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/wat-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static Features s_features;
+static WriteWatOptions s_write_wat_options;
+static bool s_generate_names = false;
+static bool s_read_debug_names = true;
+static bool s_fail_on_custom_section_error = true;
+static std::unique_ptr<FileStream> s_log_stream;
+static bool s_validate = true;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and convert it to
+ the WebAssembly text format.
+
+examples:
+ # parse binary file test.wasm and write text file test.wast
+ $ wasm2wat test.wasm -o test.wat
+
+ # parse test.wasm, write test.wat, but ignore the debug names, if any
+ $ wasm2wat test.wasm --no-debug-names -o test.wat
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2wat", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated wast file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ parser.AddOption('f', "fold-exprs", "Write folded expressions where possible",
+ []() { s_write_wat_options.fold_exprs = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption("inline-exports", "Write all exports inline",
+ []() { s_write_wat_options.inline_export = true; });
+ parser.AddOption("inline-imports", "Write all imports inline",
+ []() { s_write_wat_options.inline_import = true; });
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ []() { s_fail_on_custom_section_error = false; });
+ parser.AddOption(
+ "generate-names",
+ "Give auto-generated names to non-named functions, types, etc.",
+ []() { s_generate_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ s_fail_on_custom_section_error);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ }
+
+ if (s_generate_names) {
+ result = GenerateNames(&module);
+ }
+
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+
+ if (Succeeded(result)) {
+ FileStream stream(!s_outfile.empty() ? FileStream(s_outfile)
+ : FileStream(stdout));
+ result = WriteWat(&stream, &module, s_write_wat_options);
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wast2json.cc b/third_party/wasm2c/src/tools/wast2json.cc
new file mode 100644
index 0000000000..a5c9a47d1d
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wast2json.cc
@@ -0,0 +1,162 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+
+#include "config.h"
+
+#include "src/binary-writer.h"
+#include "src/binary-writer-spec.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/filenames.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static std::string s_outfile;
+static int s_verbose;
+static WriteBinaryOptions s_write_binary_options;
+static bool s_validate = true;
+static bool s_debug_parsing;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( read a file in the wasm spec test format, check it for errors, and
+ convert it to a JSON file and associated wasm binary files.
+
+examples:
+ # parse spec-test.wast, and write files to spec-test.json. Modules are
+ # written to spec-test.0.wasm, spec-test.1.wasm, etc.
+ $ wast2json spec-test.wast -o spec-test.json
+)";
+
+static void ParseOptions(int argc, char* argv[]) {
+ OptionParser parser("wast2json", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wast files",
+ []() { s_debug_parsing = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILE", "output JSON file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'r', "relocatable",
+ "Create a relocatable wasm binary (suitable for linking with e.g. lld)",
+ []() { s_write_binary_options.relocatable = true; });
+ parser.AddOption(
+ "no-canonicalize-leb128s",
+ "Write all LEB128 sizes as 5-bytes instead of their minimal size",
+ []() { s_write_binary_options.canonicalize_lebs = false; });
+ parser.AddOption("debug-names",
+ "Write debug names to the generated binary file",
+ []() { s_write_binary_options.write_debug_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+
+ parser.Parse(argc, argv);
+}
+
+static std::string DefaultOuputName(string_view input_name) {
+ // Strip existing extension and add .json
+ std::string result(StripExtension(GetBasename(input_name)));
+ result += ".json";
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size());
+ if (Failed(result)) {
+ WABT_FATAL("unable to read file: %s\n", s_infile);
+ }
+
+ Errors errors;
+ std::unique_ptr<Script> script;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options);
+
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateScript(script.get(), &errors, options);
+ }
+
+ if (Succeeded(result)) {
+ if (s_outfile.empty()) {
+ s_outfile = DefaultOuputName(s_infile);
+ }
+
+ std::vector<FilenameMemoryStreamPair> module_streams;
+ MemoryStream json_stream;
+
+ std::string output_basename = StripExtension(s_outfile).to_string();
+ s_write_binary_options.features = s_features;
+ result = WriteBinarySpecScript(&json_stream, script.get(), s_infile,
+ output_basename, s_write_binary_options,
+ &module_streams, s_log_stream.get());
+
+ if (Succeeded(result)) {
+ result = json_stream.WriteToFile(s_outfile);
+ }
+
+ if (Succeeded(result)) {
+ for (auto iter = module_streams.begin(); iter != module_streams.end();
+ ++iter) {
+ result = iter->stream->WriteToFile(iter->filename);
+ if (!Succeeded(result)) {
+ break;
+ }
+ }
+ }
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get());
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wat-desugar.cc b/third_party/wasm2c/src/tools/wat-desugar.cc
new file mode 100644
index 0000000000..cc6a2cba8e
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wat-desugar.cc
@@ -0,0 +1,130 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include "config.h"
+
+#include "src/apply-names.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/wast-parser.h"
+#include "src/wat-writer.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static const char* s_outfile;
+static WriteWatOptions s_write_wat_options;
+static bool s_generate_names;
+static bool s_debug_parsing;
+static Features s_features;
+
+static const char s_description[] =
+R"( read a file in the wasm s-expression format and format it.
+
+examples:
+ # write output to stdout
+ $ wat-desugar test.wat
+
+ # write output to test2.wat
+ $ wat-desugar test.wat -o test2.wat
+
+ # generate names for indexed variables
+ $ wat-desugar --generate-names test.wat
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wat-desugar", s_description);
+
+ parser.AddOption('o', "output", "FILE", "Output file for the formatted file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wat files",
+ []() { s_debug_parsing = true; });
+ parser.AddOption('f', "fold-exprs", "Write folded expressions where possible",
+ []() { s_write_wat_options.fold_exprs = true; });
+ parser.AddOption("inline-exports", "Write all exports inline",
+ []() { s_write_wat_options.inline_export = true; });
+ parser.AddOption("inline-imports", "Write all imports inline",
+ []() { s_write_wat_options.inline_import = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption(
+ "generate-names",
+ "Give auto-generated names to non-named functions, types, etc.",
+ []() { s_generate_names = true; });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ if (Failed(result)) {
+ WABT_FATAL("unable to read %s\n", s_infile);
+ }
+
+ std::unique_ptr<WastLexer> lexer(WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size()));
+
+ Errors errors;
+ std::unique_ptr<Script> script;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options);
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text);
+
+ if (Succeeded(result)) {
+ Module* module = script->GetFirstModule();
+ if (!module) {
+ WABT_FATAL("no module in file.\n");
+ }
+
+ if (s_generate_names) {
+ result = GenerateNames(module);
+ }
+
+ if (Succeeded(result)) {
+ result = ApplyNames(module);
+ }
+
+ if (Succeeded(result)) {
+ FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout));
+ result = WriteWat(&stream, module, s_write_wat_options);
+ }
+ }
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wat2wasm.cc b/third_party/wasm2c/src/tools/wat2wasm.cc
new file mode 100644
index 0000000000..f77dad2778
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wat2wasm.cc
@@ -0,0 +1,170 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+
+#include "config.h"
+
+#include "src/binary-writer.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/filenames.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static std::string s_outfile;
+static bool s_dump_module;
+static int s_verbose;
+static WriteBinaryOptions s_write_binary_options;
+static bool s_validate = true;
+static bool s_debug_parsing;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( read a file in the wasm text format, check it for errors, and
+ convert it to the wasm binary format.
+
+examples:
+ # parse and typecheck test.wat
+ $ wat2wasm test.wat
+
+ # parse test.wat and write to binary file test.wasm
+ $ wat2wasm test.wat -o test.wasm
+
+ # parse spec-test.wast, and write verbose output to stdout (including
+ # the meaning of every byte)
+ $ wat2wasm spec-test.wast -v
+)";
+
+static void ParseOptions(int argc, char* argv[]) {
+ OptionParser parser("wat2wasm", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wat files",
+ []() { s_debug_parsing = true; });
+ parser.AddOption('d', "dump-module",
+ "Print a hexdump of the module to stdout",
+ []() { s_dump_module = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILE", "output wasm binary file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'r', "relocatable",
+ "Create a relocatable wasm binary (suitable for linking with e.g. lld)",
+ []() { s_write_binary_options.relocatable = true; });
+ parser.AddOption(
+ "no-canonicalize-leb128s",
+ "Write all LEB128 sizes as 5-bytes instead of their minimal size",
+ []() { s_write_binary_options.canonicalize_lebs = false; });
+ parser.AddOption("debug-names",
+ "Write debug names to the generated binary file",
+ []() { s_write_binary_options.write_debug_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+
+ parser.Parse(argc, argv);
+}
+
+static void WriteBufferToFile(string_view filename,
+ const OutputBuffer& buffer) {
+ if (s_dump_module) {
+ std::unique_ptr<FileStream> stream = FileStream::CreateStdout();
+ if (s_verbose) {
+ stream->Writef(";; dump\n");
+ }
+ if (!buffer.data.empty()) {
+ stream->WriteMemoryDump(buffer.data.data(), buffer.data.size());
+ }
+ }
+
+ buffer.WriteToFile(filename);
+}
+
+static std::string DefaultOuputName(string_view input_name) {
+ // Strip existing extension and add .wasm
+ std::string result(StripExtension(GetBasename(input_name)));
+ result += kWasmExtension;
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size());
+ if (Failed(result)) {
+ WABT_FATAL("unable to read file: %s\n", s_infile);
+ }
+
+ Errors errors;
+ std::unique_ptr<Module> module;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWatModule(lexer.get(), &module, &errors, &parse_wast_options);
+
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(module.get(), &errors, options);
+ }
+
+ if (Succeeded(result)) {
+ MemoryStream stream(s_log_stream.get());
+ s_write_binary_options.features = s_features;
+ result = WriteBinaryModule(&stream, module.get(), s_write_binary_options);
+
+ if (Succeeded(result)) {
+ if (s_outfile.empty()) {
+ s_outfile = DefaultOuputName(s_infile);
+ }
+ WriteBufferToFile(s_outfile.c_str(), stream.output_buffer());
+ }
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get());
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tracing.cc b/third_party/wasm2c/src/tracing.cc
new file mode 100644
index 0000000000..3208acdff8
--- /dev/null
+++ b/third_party/wasm2c/src/tracing.cc
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#define WABT_TRACING 1
+#include "src/tracing.h"
+
+namespace {
+
+size_t indent = 0;
+const char* indent_text = " ";
+
+void Fill() {
+ for (size_t i = 0; i < indent; ++i)
+ fputs(indent_text, stderr);
+}
+
+void Indent() {
+ Fill();
+ ++indent;
+}
+
+void Dedent() {
+ if (indent) {
+ --indent;
+ }
+ Fill();
+}
+
+} // end of anonymous namespace
+
+namespace wabt {
+
+// static
+
+TraceScope::TraceScope(const char* method) : method_(method) {
+ PrintEnter(method);
+ PrintNewline();
+}
+
+TraceScope::~TraceScope() {
+ Dedent();
+ fputs("<- ", stderr);
+ fputs(method_, stderr);
+ fputc('\n', stderr);
+}
+
+void TraceScope::PrintEnter(const char* method) {
+ Indent();
+ fputs("-> ", stderr);
+ fputs(method, stderr);
+ fputs("(", stderr);
+}
+
+void TraceScope::PrintNewline() {
+ fputs(")\n", stderr);
+}
+
+} // end of namespace wabt
diff --git a/third_party/wasm2c/src/tracing.h b/third_party/wasm2c/src/tracing.h
new file mode 100644
index 0000000000..86daf63cf1
--- /dev/null
+++ b/third_party/wasm2c/src/tracing.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_TRACING_H_
+#define WABT_TRACING_H_
+
+// Provides a simple tracing class that automatically generates enter/exit
+// messages using the scope of the instance.
+//
+// It also assumes that this file is only included in .cc files.
+// Immediately before the inclusion of this file, there is a define of
+// for WABT_TRACING, defining whether tracing should be compiled in for
+// that source file.
+
+#ifndef WABT_TRACING
+#define WABT_TRACING 0
+#endif
+
+#include "src/common.h"
+
+namespace wabt {
+
+#if WABT_TRACING
+
+// Scoped class that automatically prints enter("->") and exit("<-")
+// lines, indented by trace level.
+struct TraceScope {
+ WABT_DISALLOW_COPY_AND_ASSIGN(TraceScope);
+ TraceScope() = delete;
+ TraceScope(const char* method);
+ template <typename... Args>
+ TraceScope(const char* method, const char* format, Args&&... args)
+ : method_(method) {
+ PrintEnter(method);
+ fprintf(stderr, format, std::forward<Args>(args)...);
+ PrintNewline();
+ }
+ ~TraceScope();
+
+ private:
+ const char* method_;
+ void PrintEnter(const char* method);
+ void PrintNewline();
+};
+
+#define WABT_TRACE(method_name) TraceScope _func_(#method_name)
+
+#define WABT_TRACE_ARGS(method_name, format, ...) \
+ TraceScope _func_(#method_name, format, __VA_ARGS__)
+
+#else
+
+#define WABT_TRACE(method)
+#define WABT_TRACE_ARGS(method_name, format, ...)
+
+#endif
+
+} // end namespace wabt
+
+#endif // WABT_TRACING_H_
diff --git a/third_party/wasm2c/src/type-checker.cc b/third_party/wasm2c/src/type-checker.cc
new file mode 100644
index 0000000000..3e875a312f
--- /dev/null
+++ b/third_party/wasm2c/src/type-checker.cc
@@ -0,0 +1,941 @@
+/*
+ * 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 "src/type-checker.h"
+
+#include <cinttypes>
+
+namespace wabt {
+
+namespace {
+
+std::string TypesToString(const TypeVector& types,
+ const char* prefix = nullptr) {
+ std::string result = "[";
+ if (prefix) {
+ result += prefix;
+ }
+
+ for (size_t i = 0; i < types.size(); ++i) {
+ result += types[i].GetName();
+ if (i < types.size() - 1) {
+ result += ", ";
+ }
+ }
+ result += "]";
+ return result;
+}
+
+} // end anonymous namespace
+
+TypeChecker::Label::Label(LabelType label_type,
+ const TypeVector& param_types,
+ const TypeVector& result_types,
+ size_t limit)
+ : label_type(label_type),
+ param_types(param_types),
+ result_types(result_types),
+ type_stack_limit(limit),
+ unreachable(false) {}
+
+void TypeChecker::PrintError(const char* fmt, ...) {
+ if (error_callback_) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, fmt);
+ error_callback_(buffer);
+ }
+}
+
+Result TypeChecker::GetLabel(Index depth, Label** out_label) {
+ if (depth >= label_stack_.size()) {
+ assert(label_stack_.size() > 0);
+ PrintError("invalid depth: %" PRIindex " (max %" PRIzd ")", depth,
+ label_stack_.size() - 1);
+ *out_label = nullptr;
+ return Result::Error;
+ }
+ *out_label = &label_stack_[label_stack_.size() - depth - 1];
+ return Result::Ok;
+}
+
+Result TypeChecker::GetRethrowLabel(Index depth, Label** out_label) {
+ if (Failed(GetLabel(depth, out_label))) {
+ return Result::Error;
+ }
+
+ if ((*out_label)->label_type == LabelType::Catch) {
+ return Result::Ok;
+ }
+
+ std::string candidates;
+ for (Index idx = 0; idx < label_stack_.size(); idx++) {
+ LabelType type = label_stack_[label_stack_.size() - idx - 1].label_type;
+ if (type == LabelType::Catch) {
+ if (!candidates.empty()) {
+ candidates.append(", ");
+ }
+ candidates.append(std::to_string(idx));
+ }
+ }
+
+ if (candidates.empty()) {
+ PrintError("rethrow not in try catch block");
+ } else {
+ PrintError("invalid rethrow depth: %" PRIindex " (catches: %s)", depth,
+ candidates.c_str());
+ }
+ *out_label = nullptr;
+ return Result::Error;
+}
+
+Result TypeChecker::TopLabel(Label** out_label) {
+ return GetLabel(0, out_label);
+}
+
+bool TypeChecker::IsUnreachable() {
+ Label* label;
+ if (Failed(TopLabel(&label))) {
+ return true;
+ }
+ return label->unreachable;
+}
+
+void TypeChecker::ResetTypeStackToLabel(Label* label) {
+ type_stack_.resize(label->type_stack_limit);
+}
+
+Result TypeChecker::SetUnreachable() {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ label->unreachable = true;
+ ResetTypeStackToLabel(label);
+ return Result::Ok;
+}
+
+void TypeChecker::PushLabel(LabelType label_type,
+ const TypeVector& param_types,
+ const TypeVector& result_types) {
+ label_stack_.emplace_back(label_type, param_types, result_types,
+ type_stack_.size());
+}
+
+Result TypeChecker::PopLabel() {
+ label_stack_.pop_back();
+ return Result::Ok;
+}
+
+Result TypeChecker::CheckLabelType(Label* label, LabelType label_type) {
+ return label->label_type == label_type ? Result::Ok : Result::Error;
+}
+
+Result TypeChecker::Check2LabelTypes(Label* label,
+ LabelType label_type1,
+ LabelType label_type2) {
+ return label->label_type == label_type1 ||
+ label->label_type == label_type2 ? Result::Ok : Result::Error;
+}
+
+Result TypeChecker::GetThisFunctionLabel(Label** label) {
+ return GetLabel(label_stack_.size() - 1, label);
+}
+
+Result TypeChecker::PeekType(Index depth, Type* out_type) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+
+ if (label->type_stack_limit + depth >= type_stack_.size()) {
+ *out_type = Type::Any;
+ return label->unreachable ? Result::Ok : Result::Error;
+ }
+ *out_type = type_stack_[type_stack_.size() - depth - 1];
+ return Result::Ok;
+}
+
+Result TypeChecker::PeekAndCheckType(Index depth, Type expected) {
+ Type actual = Type::Any;
+ Result result = PeekType(depth, &actual);
+ return result | CheckType(actual, expected);
+}
+
+Result TypeChecker::DropTypes(size_t drop_count) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ if (label->type_stack_limit + drop_count > type_stack_.size()) {
+ ResetTypeStackToLabel(label);
+ return label->unreachable ? Result::Ok : Result::Error;
+ }
+ type_stack_.erase(type_stack_.end() - drop_count, type_stack_.end());
+ return Result::Ok;
+}
+
+void TypeChecker::PushType(Type type) {
+ if (type != Type::Void) {
+ type_stack_.push_back(type);
+ }
+}
+
+void TypeChecker::PushTypes(const TypeVector& types) {
+ for (Type type : types) {
+ PushType(type);
+ }
+}
+
+Result TypeChecker::CheckTypeStackEnd(const char* desc) {
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ Result result = (type_stack_.size() == label->type_stack_limit)
+ ? Result::Ok
+ : Result::Error;
+ PrintStackIfFailed(result, desc);
+ return result;
+}
+
+Result TypeChecker::CheckType(Type actual, Type expected) {
+ if (expected == Type::Any || actual == Type::Any) {
+ return Result::Ok;
+ }
+ if (actual != expected) {
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result TypeChecker::CheckTypes(const TypeVector& actual,
+ const TypeVector& expected) {
+ if (actual.size() != expected.size()) {
+ return Result::Error;
+ } else {
+ Result result = Result::Ok;
+ for (size_t i = 0; i < actual.size(); i++)
+ result |= CheckType(actual[i], expected[i]);
+ return result;
+ }
+}
+
+Result TypeChecker::CheckSignature(const TypeVector& sig, const char* desc) {
+ Result result = Result::Ok;
+ for (size_t i = 0; i < sig.size(); ++i) {
+ result |= PeekAndCheckType(sig.size() - i - 1, sig[i]);
+ }
+ PrintStackIfFailed(result, desc, sig);
+ return result;
+}
+
+Result TypeChecker::CheckReturnSignature(const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc) {
+ Result result = CheckTypes(actual, expected);
+ if (Failed(result)) {
+ PrintError("return signatures have inconsistent types: expected %s, got %s",
+ TypesToString(expected).c_str(), TypesToString(actual).c_str());
+ }
+ return result;
+}
+
+Result TypeChecker::PopAndCheckSignature(const TypeVector& sig,
+ const char* desc) {
+ Result result = CheckSignature(sig, desc);
+ result |= DropTypes(sig.size());
+ return result;
+}
+
+Result TypeChecker::PopAndCheckCall(const TypeVector& param_types,
+ const TypeVector& result_types,
+ const char* desc) {
+ Result result = CheckSignature(param_types, desc);
+ result |= DropTypes(param_types.size());
+ PushTypes(result_types);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck1Type(Type expected, const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected);
+ PrintStackIfFailed(result, desc, expected);
+ result |= DropTypes(1);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck2Types(Type expected1,
+ Type expected2,
+ const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected2);
+ result |= PeekAndCheckType(1, expected1);
+ PrintStackIfFailed(result, desc, expected1, expected2);
+ result |= DropTypes(2);
+ return result;
+}
+
+Result TypeChecker::PopAndCheck3Types(Type expected1,
+ Type expected2,
+ Type expected3,
+ const char* desc) {
+ Result result = Result::Ok;
+ result |= PeekAndCheckType(0, expected3);
+ result |= PeekAndCheckType(1, expected2);
+ result |= PeekAndCheckType(2, expected1);
+ PrintStackIfFailed(result, desc, expected1, expected2, expected3);
+ result |= DropTypes(3);
+ return result;
+}
+
+Result TypeChecker::CheckOpcode1(Opcode opcode,
+ const Limits* limits,
+ bool has_address_operands) {
+ Result result =
+ PopAndCheck1Type(opcode.GetMemoryParam(
+ opcode.GetParamType1(), limits,
+ has_address_operands || opcode.GetMemorySize() != 0),
+ opcode.GetName());
+ PushType(has_address_operands
+ ? opcode.GetMemoryParam(opcode.GetResultType(), limits, true)
+ : opcode.GetResultType());
+ return result;
+}
+
+Result TypeChecker::CheckOpcode2(Opcode opcode, const Limits* limits) {
+ Result result =
+ PopAndCheck2Types(opcode.GetMemoryParam(opcode.GetParamType1(), limits,
+ opcode.GetMemorySize() != 0),
+ opcode.GetParamType2(), opcode.GetName());
+ PushType(opcode.GetResultType());
+ return result;
+}
+
+Result TypeChecker::CheckOpcode3(Opcode opcode,
+ const Limits* limits1,
+ const Limits* limits2,
+ const Limits* limits3) {
+ bool has_address_operands = limits1 || limits2 || limits3;
+ Result result =
+ PopAndCheck3Types(opcode.GetMemoryParam(opcode.GetParamType1(), limits1,
+ has_address_operands),
+ opcode.GetMemoryParam(opcode.GetParamType2(), limits2,
+ has_address_operands),
+ opcode.GetMemoryParam(opcode.GetParamType3(), limits3,
+ has_address_operands),
+ opcode.GetName());
+ PushType(opcode.GetResultType());
+ return result;
+}
+
+void TypeChecker::PrintStackIfFailed(Result result,
+ const char* desc,
+ const TypeVector& expected) {
+ if (Succeeded(result)) {
+ return;
+ }
+
+ size_t limit = 0;
+ Label* label;
+ if (Succeeded(TopLabel(&label))) {
+ limit = label->type_stack_limit;
+ }
+
+ TypeVector actual;
+ size_t max_depth = type_stack_.size() - limit;
+
+ // In general we want to print as many values of the actual stack as were
+ // expected. However, if the stack was expected to be empty, we should
+ // print some amount of the actual stack.
+ size_t actual_size;
+ if (expected.size() == 0) {
+ // Don't print too many elements if the stack is really deep.
+ const size_t kMaxActualStackToPrint = 4;
+ actual_size = std::min(kMaxActualStackToPrint, max_depth);
+ } else {
+ actual_size = std::min(expected.size(), max_depth);
+ }
+
+ bool incomplete_actual_stack = actual_size != max_depth;
+
+ for (size_t i = 0; i < actual_size; ++i) {
+ Type type;
+ Result result = PeekType(actual_size - i - 1, &type);
+ WABT_USE(result);
+ assert(Succeeded(result));
+ actual.push_back(type);
+ }
+
+ std::string message = "type mismatch in ";
+ message += desc;
+ message += ", expected ";
+ message += TypesToString(expected);
+ message += " but got ";
+ message += TypesToString(actual, incomplete_actual_stack ? "... " : nullptr);
+
+ PrintError("%s", message.c_str());
+}
+
+Result TypeChecker::BeginFunction(const TypeVector& sig) {
+ type_stack_.clear();
+ label_stack_.clear();
+ PushLabel(LabelType::Func, TypeVector(), sig);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnAtomicLoad(Opcode opcode, const Limits& limits) {
+ return CheckOpcode1(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicStore(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicRmw(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicRmwCmpxchg(Opcode opcode, const Limits& limits) {
+ return CheckOpcode3(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicWait(Opcode opcode, const Limits& limits) {
+ return CheckOpcode3(opcode, &limits);
+}
+
+Result TypeChecker::OnAtomicFence(uint32_t consistency_model) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnAtomicNotify(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnBinary(Opcode opcode) {
+ return CheckOpcode2(opcode);
+}
+
+Result TypeChecker::OnBlock(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "block");
+ PushLabel(LabelType::Block, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnBr(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ result |= CheckSignature(label->br_types(), "br");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnBrIf(Index depth) {
+ Result result = PopAndCheck1Type(Type::I32, "br_if");
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ result |= PopAndCheckSignature(label->br_types(), "br_if");
+ PushTypes(label->br_types());
+ return result;
+}
+
+Result TypeChecker::BeginBrTable() {
+ br_table_sig_ = nullptr;
+ return PopAndCheck1Type(Type::I32, "br_table");
+}
+
+Result TypeChecker::OnBrTableTarget(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetLabel(depth, &label));
+ TypeVector& label_sig = label->br_types();
+ result |= CheckSignature(label_sig, "br_table");
+
+ // Make sure this label's signature is consistent with the previous labels'
+ // signatures.
+ if (br_table_sig_ == nullptr) {
+ br_table_sig_ = &label_sig;
+ } else {
+ if (*br_table_sig_ != label_sig) {
+ result |= Result::Error;
+ PrintError("br_table labels have inconsistent types: expected %s, got %s",
+ TypesToString(*br_table_sig_).c_str(),
+ TypesToString(label_sig).c_str());
+ }
+ }
+
+ return result;
+}
+
+Result TypeChecker::EndBrTable() {
+ return SetUnreachable();
+}
+
+Result TypeChecker::OnCall(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ return PopAndCheckCall(param_types, result_types, "call");
+}
+
+Result TypeChecker::OnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "call_indirect");
+ result |= PopAndCheckCall(param_types, result_types, "call_indirect");
+ return result;
+}
+
+Result TypeChecker::OnFuncRef(Index* out_index) {
+ Type type;
+ Result result = PeekType(0, &type);
+ if (!type.IsIndex()) {
+ TypeVector actual;
+ if (Succeeded(result)) {
+ actual.push_back(type);
+ }
+ std::string message =
+ "type mismatch in call_ref, expected reference but got " +
+ TypesToString(actual);
+ PrintError("%s", message.c_str());
+ result = Result::Error;
+ }
+ if (Succeeded(result)) {
+ *out_index = type.GetIndex();
+ }
+ result |= DropTypes(1);
+ return result;
+}
+
+Result TypeChecker::OnReturnCall(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "return_call");
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= CheckReturnSignature(result_types, func_label->result_types,
+ "return_call");
+
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnReturnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "return_call_indirect");
+
+ result |= PopAndCheckSignature(param_types, "return_call_indirect");
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= CheckReturnSignature(result_types, func_label->result_types,
+ "return_call_indirect");
+
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnCompare(Opcode opcode) {
+ return CheckOpcode2(opcode);
+}
+
+Result TypeChecker::OnCatch(const TypeVector& sig) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= Check2LabelTypes(label, LabelType::Try, LabelType::Catch);
+ result |= PopAndCheckSignature(label->result_types, "try block");
+ result |= CheckTypeStackEnd("try block");
+ ResetTypeStackToLabel(label);
+ label->label_type = LabelType::Catch;
+ label->unreachable = false;
+ PushTypes(sig);
+ return result;
+}
+
+Result TypeChecker::OnConst(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnConvert(Opcode opcode) {
+ return CheckOpcode1(opcode);
+}
+
+Result TypeChecker::OnDelegate(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ // Delegate starts counting after the current try, as the delegate
+ // instruction is not actually in the try block.
+ CHECK_RESULT(GetLabel(depth + 1, &label));
+ if (Failed(Check2LabelTypes(label, LabelType::Try, LabelType::Func))) {
+ PrintError("try-delegate must target a try block or function label");
+ result = Result::Error;
+ }
+
+ Label* try_label;
+ CHECK_RESULT(TopLabel(&try_label));
+ result |= CheckLabelType(try_label, LabelType::Try);
+ result |= PopAndCheckSignature(try_label->result_types, "try block");
+ result |= CheckTypeStackEnd("try block");
+ ResetTypeStackToLabel(try_label);
+
+ // Since an end instruction does not follow a delegate, we push
+ // the block results here and pop the label.
+ PushTypes(try_label->result_types);
+ PopLabel();
+ return result;
+}
+
+Result TypeChecker::OnDrop() {
+ Result result = Result::Ok;
+ result |= DropTypes(1);
+ PrintStackIfFailed(result, "drop", Type::Any);
+ return result;
+}
+
+Result TypeChecker::OnElse() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::If);
+ result |= PopAndCheckSignature(label->result_types, "if true branch");
+ result |= CheckTypeStackEnd("if true branch");
+ ResetTypeStackToLabel(label);
+ PushTypes(label->param_types);
+ label->label_type = LabelType::Else;
+ label->unreachable = false;
+ return result;
+}
+
+Result TypeChecker::OnEnd(Label* label,
+ const char* sig_desc,
+ const char* end_desc) {
+ Result result = Result::Ok;
+ result |= PopAndCheckSignature(label->result_types, sig_desc);
+ result |= CheckTypeStackEnd(end_desc);
+ ResetTypeStackToLabel(label);
+ PushTypes(label->result_types);
+ PopLabel();
+ return result;
+}
+
+Result TypeChecker::OnEnd() {
+ Result result = Result::Ok;
+ static const char* s_label_type_name[] = {
+ "function", "block", "loop", "if", "if false branch", "try", "try catch"};
+ WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_label_type_name) == kLabelTypeCount);
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ assert(static_cast<int>(label->label_type) < kLabelTypeCount);
+ if (label->label_type == LabelType::If) {
+ // An if without an else will just pass the params through, so the result
+ // types must be the same as the param types. It has the same behavior as
+ // an empty else block.
+ CHECK_RESULT(OnElse());
+ }
+
+ const char* desc = s_label_type_name[static_cast<int>(label->label_type)];
+ result |= OnEnd(label, desc, desc);
+ return result;
+}
+
+Result TypeChecker::OnIf(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheck1Type(Type::I32, "if");
+ result |= PopAndCheckSignature(param_types, "if");
+ PushLabel(LabelType::If, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnGlobalGet(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnGlobalSet(Type type) {
+ return PopAndCheck1Type(type, "global.set");
+}
+
+Result TypeChecker::OnLoad(Opcode opcode, const Limits& limits) {
+ return CheckOpcode1(opcode, &limits);
+}
+
+Result TypeChecker::OnLocalGet(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnLocalSet(Type type) {
+ return PopAndCheck1Type(type, "local.set");
+}
+
+Result TypeChecker::OnLocalTee(Type type) {
+ Result result = Result::Ok;
+ result |= PopAndCheck1Type(type, "local.tee");
+ PushType(type);
+ return result;
+}
+
+Result TypeChecker::OnLoop(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "loop");
+ PushLabel(LabelType::Loop, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnMemoryCopy(const Limits& limits) {
+ return CheckOpcode3(Opcode::MemoryCopy, &limits, &limits, &limits);
+}
+
+Result TypeChecker::OnDataDrop(uint32_t segment) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnMemoryFill(const Limits& limits) {
+ return CheckOpcode3(Opcode::MemoryFill, &limits, nullptr, &limits);
+}
+
+Result TypeChecker::OnMemoryGrow(const Limits& limits) {
+ return CheckOpcode1(Opcode::MemoryGrow, &limits, true);
+}
+
+Result TypeChecker::OnMemoryInit(uint32_t segment, const Limits& limits) {
+ return CheckOpcode3(Opcode::MemoryInit, &limits);
+}
+
+Result TypeChecker::OnMemorySize(const Limits& limits) {
+ PushType(limits.IndexType());
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableCopy() {
+ return CheckOpcode3(Opcode::TableCopy);
+}
+
+Result TypeChecker::OnElemDrop(uint32_t segment) {
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableInit(uint32_t table, uint32_t segment) {
+ return CheckOpcode3(Opcode::TableInit);
+}
+
+Result TypeChecker::OnTableGet(Type elem_type) {
+ Result result = PopAndCheck1Type(Type::I32, "table.get");
+ PushType(elem_type);
+ return result;
+}
+
+Result TypeChecker::OnTableSet(Type elem_type) {
+ return PopAndCheck2Types(Type::I32, elem_type, "table.set");
+}
+
+Result TypeChecker::OnTableGrow(Type elem_type) {
+ Result result = PopAndCheck2Types(elem_type, Type::I32, "table.grow");
+ PushType(Type::I32);
+ return result;
+}
+
+Result TypeChecker::OnTableSize() {
+ PushType(Type::I32);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnTableFill(Type elem_type) {
+ return PopAndCheck3Types(Type::I32, elem_type, Type::I32, "table.fill");
+}
+
+Result TypeChecker::OnRefFuncExpr(Index func_index) {
+ if (features_.function_references_enabled()) {
+ PushType(Type(func_index));
+ } else {
+ PushType(Type::FuncRef);
+ }
+ return Result::Ok;
+}
+
+Result TypeChecker::OnRefNullExpr(Type type) {
+ PushType(type);
+ return Result::Ok;
+}
+
+Result TypeChecker::OnRefIsNullExpr() {
+ Type type;
+ Result result = PeekType(0, &type);
+ if (!type.IsRef()) {
+ TypeVector actual;
+ if (Succeeded(result)) {
+ actual.push_back(type);
+ }
+ std::string message =
+ "type mismatch in ref.is_null, expected reference but got " +
+ TypesToString(actual);
+ PrintError("%s", message.c_str());
+ result = Result::Error;
+ }
+ result |= DropTypes(1);
+ PushType(Type::I32);
+ return result;
+}
+
+Result TypeChecker::OnRethrow(Index depth) {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(GetRethrowLabel(depth, &label));
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnThrow(const TypeVector& sig) {
+ Result result = Result::Ok;
+ result |= PopAndCheckSignature(sig, "throw");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnReturn() {
+ Result result = Result::Ok;
+ Label* func_label;
+ CHECK_RESULT(GetThisFunctionLabel(&func_label));
+ result |= PopAndCheckSignature(func_label->result_types, "return");
+ CHECK_RESULT(SetUnreachable());
+ return result;
+}
+
+Result TypeChecker::OnSelect(const TypeVector& expected) {
+ Result result = Result::Ok;
+ Type type1 = Type::Any;
+ Type type2 = Type::Any;
+ Type result_type = Type::Any;
+ result |= PeekAndCheckType(0, Type::I32);
+ result |= PeekType(1, &type1);
+ result |= PeekType(2, &type2);
+ if (expected.empty()) {
+ if (type1.IsRef() || type2.IsRef()) {
+ result = Result::Error;
+ } else {
+ result |= CheckType(type1, type2);
+ result_type = type1;
+ }
+ } else {
+ assert(expected.size() == 1);
+ result |= CheckType(type1, expected[0]);
+ result |= CheckType(type2, expected[0]);
+ }
+ PrintStackIfFailed(result, "select", result_type, result_type, Type::I32);
+ result |= DropTypes(3);
+ PushType(result_type);
+ return result;
+}
+
+Result TypeChecker::OnStore(Opcode opcode, const Limits& limits) {
+ return CheckOpcode2(opcode, &limits);
+}
+
+Result TypeChecker::OnTry(const TypeVector& param_types,
+ const TypeVector& result_types) {
+ Result result = PopAndCheckSignature(param_types, "try");
+ PushLabel(LabelType::Try, param_types, result_types);
+ PushTypes(param_types);
+ return result;
+}
+
+Result TypeChecker::OnUnary(Opcode opcode) {
+ return CheckOpcode1(opcode);
+}
+
+Result TypeChecker::OnTernary(Opcode opcode) {
+ return CheckOpcode3(opcode);
+}
+
+Result TypeChecker::OnSimdLaneOp(Opcode opcode, uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+
+ switch (opcode) {
+ case Opcode::I8X16ExtractLaneS:
+ case Opcode::I8X16ExtractLaneU:
+ case Opcode::I16X8ExtractLaneS:
+ case Opcode::I16X8ExtractLaneU:
+ case Opcode::I32X4ExtractLane:
+ case Opcode::F32X4ExtractLane:
+ case Opcode::I64X2ExtractLane:
+ case Opcode::F64X2ExtractLane:
+ result |= CheckOpcode1(opcode);
+ break;
+ case Opcode::I8X16ReplaceLane:
+ case Opcode::I16X8ReplaceLane:
+ case Opcode::I32X4ReplaceLane:
+ case Opcode::F32X4ReplaceLane:
+ case Opcode::I64X2ReplaceLane:
+ case Opcode::F64X2ReplaceLane:
+ result |= CheckOpcode2(opcode);
+ break;
+ default:
+ WABT_UNREACHABLE;
+ }
+ return result;
+}
+
+Result TypeChecker::OnSimdLoadLane(Opcode opcode, const Limits& limits, uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+ result |= CheckOpcode2(opcode, &limits);
+ return result;
+}
+
+Result TypeChecker::OnSimdStoreLane(Opcode opcode, const Limits& limits, uint64_t lane_idx) {
+ Result result = Result::Ok;
+ uint32_t lane_count = opcode.GetSimdLaneCount();
+ if (lane_idx >= lane_count) {
+ PrintError("lane index must be less than %d (got %" PRIu64 ")", lane_count,
+ lane_idx);
+ result = Result::Error;
+ }
+ result |= CheckOpcode2(opcode, &limits);
+ return result;
+}
+
+Result TypeChecker::OnSimdShuffleOp(Opcode opcode, v128 lane_idx) {
+ Result result = Result::Ok;
+ uint8_t simd_data[16];
+ memcpy(simd_data, &lane_idx, 16);
+ for (int i = 0; i < 16; i++) {
+ if (simd_data[i] >= 32) {
+ PrintError("lane index must be less than 32 (got %d)", simd_data[i]);
+ result = Result::Error;
+ }
+ }
+
+ result |= CheckOpcode2(opcode);
+ return result;
+}
+
+Result TypeChecker::OnUnreachable() {
+ return SetUnreachable();
+}
+
+Result TypeChecker::EndFunction() {
+ Result result = Result::Ok;
+ Label* label;
+ CHECK_RESULT(TopLabel(&label));
+ result |= CheckLabelType(label, LabelType::Func);
+ result |= OnEnd(label, "implicit return", "function");
+ return result;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/type-checker.h b/third_party/wasm2c/src/type-checker.h
new file mode 100644
index 0000000000..b7cdf82dd7
--- /dev/null
+++ b/third_party/wasm2c/src/type-checker.h
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_TYPE_CHECKER_H_
+#define WABT_TYPE_CHECKER_H_
+
+#include <functional>
+#include <vector>
+
+#include "src/common.h"
+#include "src/feature.h"
+#include "src/opcode.h"
+
+namespace wabt {
+
+class TypeChecker {
+ public:
+ typedef std::function<void(const char* msg)> ErrorCallback;
+
+ struct Label {
+ Label(LabelType,
+ const TypeVector& param_types,
+ const TypeVector& result_types,
+ size_t limit);
+
+ TypeVector& br_types() {
+ return label_type == LabelType::Loop ? param_types : result_types;
+ }
+
+ LabelType label_type;
+ TypeVector param_types;
+ TypeVector result_types;
+ size_t type_stack_limit;
+ bool unreachable;
+ };
+
+ explicit TypeChecker(const Features& features) : features_(features) {}
+
+ void set_error_callback(const ErrorCallback& error_callback) {
+ error_callback_ = error_callback;
+ }
+
+ size_t type_stack_size() const { return type_stack_.size(); }
+
+ bool IsUnreachable();
+ Result GetLabel(Index depth, Label** out_label);
+ Result GetRethrowLabel(Index depth, Label** out_label);
+
+ Result BeginFunction(const TypeVector& sig);
+ Result OnAtomicFence(uint32_t consistency_model);
+ Result OnAtomicLoad(Opcode, const Limits& limits);
+ Result OnAtomicNotify(Opcode, const Limits& limits);
+ Result OnAtomicStore(Opcode, const Limits& limits);
+ Result OnAtomicRmw(Opcode, const Limits& limits);
+ Result OnAtomicRmwCmpxchg(Opcode, const Limits& limits);
+ Result OnAtomicWait(Opcode, const Limits& limits);
+ Result OnBinary(Opcode);
+ Result OnBlock(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnBr(Index depth);
+ Result OnBrIf(Index depth);
+ Result BeginBrTable();
+ Result OnBrTableTarget(Index depth);
+ Result EndBrTable();
+ Result OnCall(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnCallIndirect(const TypeVector& param_types,
+ const TypeVector& result_types);
+ Result OnFuncRef(Index* out_index);
+ Result OnReturnCall(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnReturnCallIndirect(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnCatch(const TypeVector& sig);
+ Result OnCompare(Opcode);
+ Result OnConst(Type);
+ Result OnConvert(Opcode);
+ Result OnDelegate(Index depth);
+ Result OnDrop();
+ Result OnElse();
+ Result OnEnd();
+ Result OnGlobalGet(Type);
+ Result OnGlobalSet(Type);
+ Result OnIf(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnLoad(Opcode, const Limits& limits);
+ Result OnLocalGet(Type);
+ Result OnLocalSet(Type);
+ Result OnLocalTee(Type);
+ Result OnLoop(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnMemoryCopy(const Limits& limits);
+ Result OnDataDrop(Index);
+ Result OnMemoryFill(const Limits& limits);
+ Result OnMemoryGrow(const Limits& limits);
+ Result OnMemoryInit(Index, const Limits& limits);
+ Result OnMemorySize(const Limits& limits);
+ Result OnTableCopy();
+ Result OnElemDrop(Index);
+ Result OnTableInit(Index, Index);
+ Result OnTableGet(Type elem_type);
+ Result OnTableSet(Type elem_type);
+ Result OnTableGrow(Type elem_type);
+ Result OnTableSize();
+ Result OnTableFill(Type elem_type);
+ Result OnRefFuncExpr(Index func_index);
+ Result OnRefNullExpr(Type type);
+ Result OnRefIsNullExpr();
+ Result OnRethrow(Index depth);
+ Result OnReturn();
+ Result OnSelect(const TypeVector& result_types);
+ Result OnSimdLaneOp(Opcode, uint64_t);
+ Result OnSimdLoadLane(Opcode, const Limits& limits, uint64_t);
+ Result OnSimdStoreLane(Opcode, const Limits& limits, uint64_t);
+ Result OnSimdShuffleOp(Opcode, v128);
+ Result OnStore(Opcode, const Limits& limits);
+ Result OnTernary(Opcode);
+ Result OnThrow(const TypeVector& sig);
+ Result OnTry(const TypeVector& param_types, const TypeVector& result_types);
+ Result OnUnary(Opcode);
+ Result OnUnreachable();
+ Result EndFunction();
+
+ static Result CheckType(Type actual, Type expected);
+
+ private:
+ void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* fmt, ...);
+ Result TopLabel(Label** out_label);
+ void ResetTypeStackToLabel(Label* label);
+ Result SetUnreachable();
+ void PushLabel(LabelType label_type,
+ const TypeVector& param_types,
+ const TypeVector& result_types);
+ Result PopLabel();
+ Result CheckLabelType(Label* label, LabelType label_type);
+ Result Check2LabelTypes(Label* label, LabelType label_type1, LabelType label_type2);
+ Result GetThisFunctionLabel(Label **label);
+ Result PeekType(Index depth, Type* out_type);
+ Result PeekAndCheckType(Index depth, Type expected);
+ Result DropTypes(size_t drop_count);
+ void PushType(Type type);
+ void PushTypes(const TypeVector& types);
+ Result CheckTypeStackEnd(const char* desc);
+ Result CheckTypes(const TypeVector &actual, const TypeVector &expected);
+ Result CheckSignature(const TypeVector& sig, const char* desc);
+ Result CheckReturnSignature(const TypeVector& sig, const TypeVector &expected,const char *desc);
+ Result PopAndCheckSignature(const TypeVector& sig, const char* desc);
+ Result PopAndCheckCall(const TypeVector& param_types,
+ const TypeVector& result_types,
+ const char* desc);
+ Result PopAndCheck1Type(Type expected, const char* desc);
+ Result PopAndCheck2Types(Type expected1, Type expected2, const char* desc);
+ Result PopAndCheck3Types(Type expected1,
+ Type expected2,
+ Type expected3,
+ const char* desc);
+ Result CheckOpcode1(Opcode opcode,
+ const Limits* limits = nullptr,
+ bool has_address_operands = false);
+ Result CheckOpcode2(Opcode opcode, const Limits* limits = nullptr);
+ Result CheckOpcode3(Opcode opcode,
+ const Limits* limits1 = nullptr,
+ const Limits* limits2 = nullptr,
+ const Limits* limits3 = nullptr);
+ Result OnEnd(Label* label, const char* sig_desc, const char* end_desc);
+
+ template <typename... Args>
+ void PrintStackIfFailed(Result result, const char* desc, Args... args) {
+ // Minor optimization, check result before constructing the vector to pass
+ // to the other overload of PrintStackIfFailed.
+ if (Failed(result)) {
+ PrintStackIfFailed(result, desc, {args...});
+ }
+ }
+
+ void PrintStackIfFailed(Result, const char* desc, const TypeVector&);
+
+ ErrorCallback error_callback_;
+ TypeVector type_stack_;
+ std::vector<Label> label_stack_;
+ // Cache the expected br_table signature. It will be initialized to `nullptr`
+ // to represent "any".
+ TypeVector* br_table_sig_ = nullptr;
+ Features features_;
+};
+
+} // namespace wabt
+
+#endif /* WABT_TYPE_CHECKER_H_ */
diff --git a/third_party/wasm2c/src/type.h b/third_party/wasm2c/src/type.h
new file mode 100644
index 0000000000..cd383a16ef
--- /dev/null
+++ b/third_party/wasm2c/src/type.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef WABT_TYPE_H_
+#define WABT_TYPE_H_
+
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+#include "config.h"
+
+namespace wabt {
+
+class Type;
+
+using Index = uint32_t;
+using TypeVector = std::vector<Type>;
+
+class Type {
+ public:
+ // Matches binary format, do not change.
+ enum Enum : int32_t {
+ I32 = -0x01, // 0x7f
+ I64 = -0x02, // 0x7e
+ F32 = -0x03, // 0x7d
+ F64 = -0x04, // 0x7c
+ V128 = -0x05, // 0x7b
+ I8 = -0x06, // 0x7a : packed-type only, used in gc and as v128 lane
+ I16 = -0x07, // 0x79 : packed-type only, used in gc and as v128 lane
+ FuncRef = -0x10, // 0x70
+ ExternRef = -0x11, // 0x6f
+ Func = -0x20, // 0x60
+ Struct = -0x21, // 0x5f
+ Array = -0x22, // 0x5e
+ Void = -0x40, // 0x40
+ ___ = Void, // Convenient for the opcode table in opcode.h
+
+ Any = 0, // Not actually specified, but useful for type-checking
+ I8U = 4, // Not actually specified, but used internally with load/store
+ I16U = 6, // Not actually specified, but used internally with load/store
+ I32U = 7, // Not actually specified, but used internally with load/store
+ };
+
+ Type() = default; // Provided so Type can be member of a union.
+ Type(int32_t code) : enum_(static_cast<Enum>(code)) {}
+ Type(Enum e) : enum_(e) {}
+ operator Enum() const { return enum_; }
+
+ bool IsRef() const {
+ return enum_ == Type::ExternRef || enum_ == Type::FuncRef;
+ }
+
+ bool IsNullableRef() const {
+ // Currently all reftypes are nullable
+ return IsRef();
+ }
+
+ const char* GetName() const {
+ switch (enum_) {
+ case Type::I32: return "i32";
+ case Type::I64: return "i64";
+ case Type::F32: return "f32";
+ case Type::F64: return "f64";
+ case Type::V128: return "v128";
+ case Type::I8: return "i8";
+ case Type::I16: return "i16";
+ case Type::FuncRef: return "funcref";
+ case Type::Func: return "func";
+ case Type::Void: return "void";
+ case Type::Any: return "any";
+ case Type::ExternRef: return "externref";
+ default: return "<type_index>";
+ }
+ }
+
+ const char* GetRefKindName() const {
+ switch (enum_) {
+ case Type::FuncRef: return "func";
+ case Type::ExternRef: return "extern";
+ case Type::Struct: return "struct";
+ case Type::Array: return "array";
+ default: return "<invalid>";
+ }
+ }
+
+ // Functions for handling types that are an index into the type section.
+ // These are always positive integers. They occur in the binary format in
+ // block signatures, e.g.
+ //
+ // (block (result i32 i64) ...)
+ //
+ // is encoded as
+ //
+ // (type $T (func (result i32 i64)))
+ // ...
+ // (block (type $T) ...)
+ //
+ bool IsIndex() const { return static_cast<int32_t>(enum_) >= 0; }
+
+ Index GetIndex() const {
+ assert(IsIndex());
+ return static_cast<Index>(enum_);
+ }
+
+ TypeVector GetInlineVector() const {
+ assert(!IsIndex());
+ switch (enum_) {
+ case Type::Void:
+ return TypeVector();
+
+ case Type::I32:
+ case Type::I64:
+ case Type::F32:
+ case Type::F64:
+ case Type::V128:
+ case Type::FuncRef:
+ case Type::ExternRef:
+ return TypeVector(this, this + 1);
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+
+ private:
+ Enum enum_;
+};
+
+} // namespace wabt
+
+#endif // WABT_TYPE_H_
diff --git a/third_party/wasm2c/src/utf8.cc b/third_party/wasm2c/src/utf8.cc
new file mode 100644
index 0000000000..37a8df0081
--- /dev/null
+++ b/third_party/wasm2c/src/utf8.cc
@@ -0,0 +1,104 @@
+/*
+ * 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 "src/utf8.h"
+
+#include <cstdint>
+
+namespace wabt {
+
+namespace {
+
+const int s_utf8_length[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
+};
+
+// Returns true if this is a valid continuation byte.
+bool IsCont(uint8_t c) {
+ return (c & 0xc0) == 0x80;
+}
+
+} // end anonymous namespace
+
+bool IsValidUtf8(const char* s, size_t s_length) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
+ const uint8_t* end = p + s_length;
+ while (p < end) {
+ uint8_t cu0 = *p;
+ int length = s_utf8_length[cu0];
+ if (p + length > end) {
+ return false;
+ }
+
+ switch (length) {
+ case 0:
+ return false;
+
+ case 1:
+ p++;
+ break;
+
+ case 2:
+ p++;
+ if (!IsCont(*p++)) {
+ return false;
+ }
+ break;
+
+ case 3: {
+ p++;
+ uint8_t cu1 = *p++;
+ uint8_t cu2 = *p++;
+ if (!(IsCont(cu1) && IsCont(cu2)) ||
+ (cu0 == 0xe0 && cu1 < 0xa0) || // Overlong encoding.
+ (cu0 == 0xed && cu1 >= 0xa0)) // UTF-16 surrogate halves.
+ return false;
+ break;
+ }
+
+ case 4: {
+ p++;
+ uint8_t cu1 = *p++;
+ uint8_t cu2 = *p++;
+ uint8_t cu3 = *p++;
+ if (!(IsCont(cu1) && IsCont(cu2) && IsCont(cu3)) ||
+ (cu0 == 0xf0 && cu1 < 0x90) || // Overlong encoding.
+ (cu0 == 0xf4 && cu1 >= 0x90)) // Code point >= 0x11000.
+ return false;
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/utf8.h b/third_party/wasm2c/src/utf8.h
new file mode 100644
index 0000000000..c378035762
--- /dev/null
+++ b/third_party/wasm2c/src/utf8.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_UTF8_H_
+#define WABT_UTF8_H_
+
+#include <stdlib.h>
+
+namespace wabt {
+
+bool IsValidUtf8(const char* s, size_t length);
+
+} // namespace wabt
+
+#endif // WABT_UTF8_H_
diff --git a/third_party/wasm2c/src/validator.cc b/third_party/wasm2c/src/validator.cc
new file mode 100644
index 0000000000..d78219ee1c
--- /dev/null
+++ b/third_party/wasm2c/src/validator.cc
@@ -0,0 +1,1072 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/validator.h"
+
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+
+#include "config.h"
+
+#include "src/binary-reader.h"
+#include "src/cast.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/shared-validator.h"
+
+namespace wabt {
+
+namespace {
+
+class ScriptValidator {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(ScriptValidator);
+ ScriptValidator(Errors*, const Script*, const ValidateOptions& options);
+
+ Result CheckScript();
+
+ private:
+ struct ActionResult {
+ enum class Kind {
+ Error,
+ Types,
+ Type,
+ } kind;
+
+ union {
+ const TypeVector* types;
+ Type type;
+ };
+ };
+
+ void WABT_PRINTF_FORMAT(3, 4)
+ PrintError(const Location* loc, const char* fmt, ...);
+ void CheckTypeIndex(const Location* loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind);
+ void CheckResultTypes(const Location* loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc);
+
+ const TypeVector* CheckInvoke(const InvokeAction* action);
+ Result CheckGet(const GetAction* action, Type* out_type);
+ ActionResult CheckAction(const Action* action);
+ void CheckCommand(const Command* command);
+
+ const ValidateOptions& options_;
+ Errors* errors_ = nullptr;
+ const Script* script_ = nullptr;
+
+ Result result_ = Result::Ok;
+};
+
+class Validator : public ExprVisitor::Delegate {
+ public:
+ Validator(Errors*, const Module* module, const ValidateOptions& options);
+
+ Result CheckModule();
+
+ Result OnBinaryExpr(BinaryExpr*) override;
+ 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 OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnCallRefExpr(CallRefExpr*) override;
+ Result OnCompareExpr(CompareExpr*) override;
+ Result OnConstExpr(ConstExpr*) override;
+ Result OnConvertExpr(ConvertExpr*) override;
+ Result OnDropExpr(DropExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result AfterIfTrueExpr(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 OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) 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 OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnRefNullExpr(RefNullExpr*) override;
+ Result OnRefIsNullExpr(RefIsNullExpr*) override;
+ Result OnNopExpr(NopExpr*) override;
+ Result OnReturnExpr(ReturnExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnSelectExpr(SelectExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result OnUnaryExpr(UnaryExpr*) override;
+ Result OnUnreachableExpr(UnreachableExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnAtomicWaitExpr(AtomicWaitExpr*) override;
+ Result OnAtomicFenceExpr(AtomicFenceExpr*) override;
+ Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override;
+ Result OnAtomicLoadExpr(AtomicLoadExpr*) override;
+ Result OnAtomicStoreExpr(AtomicStoreExpr*) override;
+ Result OnAtomicRmwExpr(AtomicRmwExpr*) override;
+ Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override;
+ Result OnTernaryExpr(TernaryExpr*) override;
+ Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+ Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override;
+ Result OnLoadSplatExpr(LoadSplatExpr*) override;
+ Result OnLoadZeroExpr(LoadZeroExpr*) override;
+
+ private:
+ Type GetDeclarationType(const FuncDeclaration&);
+ Var GetFuncTypeIndex(const Location&, const FuncDeclaration&);
+
+ const ValidateOptions& options_;
+ Errors* errors_ = nullptr;
+ SharedValidator validator_;
+ const Module* current_module_ = nullptr;
+ Result result_ = Result::Ok;
+};
+
+ScriptValidator::ScriptValidator(Errors* errors,
+ const Script* script,
+ const ValidateOptions& options)
+ : options_(options), errors_(errors), script_(script) {}
+
+void ScriptValidator::PrintError(const Location* loc, const char* format, ...) {
+ result_ = Result::Error;
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, *loc, buffer);
+}
+
+void ScriptValidator::CheckTypeIndex(const Location* loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind) {
+ if (Failed(TypeChecker::CheckType(actual, expected))) {
+ PrintError(loc,
+ "type mismatch for %s %" PRIindex " of %s. got %s, expected %s",
+ index_kind, index, desc, actual.GetName(), expected.GetName());
+ }
+}
+
+void ScriptValidator::CheckResultTypes(const Location* loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc) {
+ if (actual.size() == expected.size()) {
+ for (size_t i = 0; i < actual.size(); ++i) {
+ CheckTypeIndex(loc, actual[i], expected[i], desc, i, "result");
+ }
+ } else {
+ PrintError(loc, "expected %" PRIzd " results, got %" PRIzd, expected.size(),
+ actual.size());
+ }
+}
+
+Type Validator::GetDeclarationType(const FuncDeclaration& decl) {
+ if (decl.has_func_type) {
+ return Type(decl.type_var.index());
+ }
+ if (decl.sig.param_types.empty()) {
+ if (decl.sig.result_types.empty()) {
+ return Type::Void;
+ }
+ if (decl.sig.result_types.size() == 1) {
+ return decl.sig.result_types[0];
+ }
+ }
+ return Type(current_module_->GetFuncTypeIndex(decl));
+}
+
+Var Validator::GetFuncTypeIndex(const Location& default_loc,
+ const FuncDeclaration& decl) {
+ if (decl.has_func_type) {
+ return decl.type_var;
+ }
+ return Var(current_module_->GetFuncTypeIndex(decl), default_loc);
+}
+
+Result Validator::OnBinaryExpr(BinaryExpr* expr) {
+ result_ |= validator_.OnBinary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::BeginBlockExpr(BlockExpr* expr) {
+ result_ |=
+ validator_.OnBlock(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::EndBlockExpr(BlockExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnBrExpr(BrExpr* expr) {
+ result_ |= validator_.OnBr(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnBrIfExpr(BrIfExpr* expr) {
+ result_ |= validator_.OnBrIf(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnBrTableExpr(BrTableExpr* expr) {
+ result_ |= validator_.BeginBrTable(expr->loc);
+ for (const Var& var : expr->targets) {
+ result_ |= validator_.OnBrTableTarget(expr->loc, var);
+ }
+ result_ |= validator_.OnBrTableTarget(expr->loc, expr->default_target);
+ result_ |= validator_.EndBrTable(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnCallExpr(CallExpr* expr) {
+ result_ |= validator_.OnCall(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ result_ |= validator_.OnCallIndirect(
+ expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table);
+ return Result::Ok;
+}
+
+Result Validator::OnCallRefExpr(CallRefExpr* expr) {
+ Index function_type_index;
+ result_ |= validator_.OnCallRef(expr->loc, &function_type_index);
+ if (Succeeded(result_)) {
+ expr->function_type_index = Var{function_type_index};
+ return Result::Ok;
+ }
+
+ return Result::Error;
+}
+
+Result Validator::OnCompareExpr(CompareExpr* expr) {
+ result_ |= validator_.OnCompare(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnConstExpr(ConstExpr* expr) {
+ result_ |= validator_.OnConst(expr->loc, expr->const_.type());
+ return Result::Ok;
+}
+
+Result Validator::OnConvertExpr(ConvertExpr* expr) {
+ result_ |= validator_.OnConvert(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnDropExpr(DropExpr* expr) {
+ result_ |= validator_.OnDrop(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ result_ |= validator_.OnGlobalGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ result_ |= validator_.OnGlobalSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::BeginIfExpr(IfExpr* expr) {
+ result_ |= validator_.OnIf(expr->loc, GetDeclarationType(expr->true_.decl));
+ return Result::Ok;
+}
+
+Result Validator::AfterIfTrueExpr(IfExpr* expr) {
+ if (!expr->false_.empty()) {
+ result_ |= validator_.OnElse(expr->true_.end_loc);
+ }
+ return Result::Ok;
+}
+
+Result Validator::EndIfExpr(IfExpr* expr) {
+ result_ |= validator_.OnEnd(expr->false_.empty() ? expr->true_.end_loc
+ : expr->false_end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnLoadExpr(LoadExpr* expr) {
+ result_ |= validator_.OnLoad(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnLocalGetExpr(LocalGetExpr* expr) {
+ result_ |= validator_.OnLocalGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnLocalSetExpr(LocalSetExpr* expr) {
+ result_ |= validator_.OnLocalSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ result_ |= validator_.OnLocalTee(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::BeginLoopExpr(LoopExpr* expr) {
+ result_ |= validator_.OnLoop(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::EndLoopExpr(LoopExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ result_ |= validator_.OnMemoryCopy(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnDataDropExpr(DataDropExpr* expr) {
+ result_ |= validator_.OnDataDrop(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ result_ |= validator_.OnMemoryFill(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ result_ |= validator_.OnMemoryGrow(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ result_ |= validator_.OnMemoryInit(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ result_ |= validator_.OnMemorySize(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnTableCopyExpr(TableCopyExpr* expr) {
+ result_ |=
+ validator_.OnTableCopy(expr->loc, expr->dst_table, expr->src_table);
+ return Result::Ok;
+}
+
+Result Validator::OnElemDropExpr(ElemDropExpr* expr) {
+ result_ |= validator_.OnElemDrop(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableInitExpr(TableInitExpr* expr) {
+ result_ |=
+ validator_.OnTableInit(expr->loc, expr->segment_index, expr->table_index);
+ return Result::Ok;
+}
+
+Result Validator::OnTableGetExpr(TableGetExpr* expr) {
+ result_ |= validator_.OnTableGet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableSetExpr(TableSetExpr* expr) {
+ result_ |= validator_.OnTableSet(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableGrowExpr(TableGrowExpr* expr) {
+ result_ |= validator_.OnTableGrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableSizeExpr(TableSizeExpr* expr) {
+ result_ |= validator_.OnTableSize(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnTableFillExpr(TableFillExpr* expr) {
+ result_ |= validator_.OnTableFill(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRefFuncExpr(RefFuncExpr* expr) {
+ result_ |= validator_.OnRefFunc(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRefNullExpr(RefNullExpr* expr) {
+ result_ |= validator_.OnRefNull(expr->loc, expr->type);
+ return Result::Ok;
+}
+
+Result Validator::OnRefIsNullExpr(RefIsNullExpr* expr) {
+ result_ |= validator_.OnRefIsNull(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnNopExpr(NopExpr* expr) {
+ result_ |= validator_.OnNop(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnExpr(ReturnExpr* expr) {
+ result_ |= validator_.OnReturn(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnCallExpr(ReturnCallExpr* expr) {
+ result_ |= validator_.OnReturnCall(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ result_ |= validator_.OnReturnCallIndirect(
+ expr->loc, GetFuncTypeIndex(expr->loc, expr->decl), expr->table);
+ return Result::Ok;
+}
+
+Result Validator::OnSelectExpr(SelectExpr* expr) {
+ result_ |= validator_.OnSelect(expr->loc, expr->result_type.size(),
+ expr->result_type.data());
+ // TODO: Existing behavior fails when select fails.
+#if 0
+ return Result::Ok;
+#else
+ return result_;
+#endif
+}
+
+Result Validator::OnStoreExpr(StoreExpr* expr) {
+ result_ |= validator_.OnStore(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnUnaryExpr(UnaryExpr* expr) {
+ result_ |= validator_.OnUnary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnUnreachableExpr(UnreachableExpr* expr) {
+ result_ |= validator_.OnUnreachable(expr->loc);
+ return Result::Ok;
+}
+
+Result Validator::BeginTryExpr(TryExpr* expr) {
+ result_ |= validator_.OnTry(expr->loc, GetDeclarationType(expr->block.decl));
+ return Result::Ok;
+}
+
+Result Validator::OnCatchExpr(TryExpr*, Catch* catch_) {
+ result_ |= validator_.OnCatch(catch_->loc, catch_->var,
+ catch_->IsCatchAll());
+ return Result::Ok;
+}
+
+Result Validator::OnDelegateExpr(TryExpr* expr) {
+ result_ |= validator_.OnDelegate(expr->loc, expr->delegate_target);
+ return Result::Ok;
+}
+
+Result Validator::EndTryExpr(TryExpr* expr) {
+ result_ |= validator_.OnEnd(expr->block.end_loc);
+ return Result::Ok;
+}
+
+Result Validator::OnThrowExpr(ThrowExpr* expr) {
+ result_ |= validator_.OnThrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnRethrowExpr(RethrowExpr* expr) {
+ result_ |= validator_.OnRethrow(expr->loc, expr->var);
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicWaitExpr(AtomicWaitExpr* expr) {
+ result_ |= validator_.OnAtomicWait(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicFenceExpr(AtomicFenceExpr* expr) {
+ result_ |= validator_.OnAtomicFence(expr->loc, expr->consistency_model);
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicNotifyExpr(AtomicNotifyExpr* expr) {
+ result_ |= validator_.OnAtomicNotify(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicLoadExpr(AtomicLoadExpr* expr) {
+ result_ |= validator_.OnAtomicLoad(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicStoreExpr(AtomicStoreExpr* expr) {
+ result_ |= validator_.OnAtomicStore(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicRmwExpr(AtomicRmwExpr* expr) {
+ result_ |= validator_.OnAtomicRmw(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr* expr) {
+ result_ |= validator_.OnAtomicRmwCmpxchg(
+ expr->loc, expr->opcode, expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnTernaryExpr(TernaryExpr* expr) {
+ result_ |= validator_.OnTernary(expr->loc, expr->opcode);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) {
+ result_ |= validator_.OnSimdLaneOp(expr->loc, expr->opcode, expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+ result_ |= validator_.OnSimdLoadLane(
+ expr->loc, expr->opcode, expr->opcode.GetAlignment(expr->align),
+ expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) {
+ result_ |= validator_.OnSimdStoreLane(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align),
+ expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnSimdShuffleOpExpr(SimdShuffleOpExpr* expr) {
+ result_ |= validator_.OnSimdShuffleOp(expr->loc, expr->opcode, expr->val);
+ return Result::Ok;
+}
+
+Result Validator::OnLoadSplatExpr(LoadSplatExpr* expr) {
+ result_ |= validator_.OnLoadSplat(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Result Validator::OnLoadZeroExpr(LoadZeroExpr* expr) {
+ result_ |= validator_.OnLoadZero(expr->loc, expr->opcode,
+ expr->opcode.GetAlignment(expr->align));
+ return Result::Ok;
+}
+
+Validator::Validator(Errors* errors,
+ const Module* module,
+ const ValidateOptions& options)
+ : options_(options),
+ errors_(errors),
+ validator_(errors_, options_),
+ current_module_(module) {}
+
+Result Validator::CheckModule() {
+ const Module* module = current_module_;
+
+ // Type section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TypeModuleField>(&field)) {
+ switch (f->type->kind()) {
+ case TypeEntryKind::Func: {
+ FuncType* func_type = cast<FuncType>(f->type.get());
+ result_ |= validator_.OnFuncType(field.loc,
+ func_type->sig.param_types.size(),
+ func_type->sig.param_types.data(),
+ func_type->sig.result_types.size(),
+ func_type->sig.result_types.data());
+ break;
+ }
+
+ case TypeEntryKind::Struct: {
+ StructType* struct_type = cast<StructType>(f->type.get());
+ TypeMutVector type_muts;
+ for (auto&& field : struct_type->fields) {
+ type_muts.push_back(TypeMut{field.type, field.mutable_});
+ }
+ result_ |= validator_.OnStructType(field.loc, type_muts.size(),
+ type_muts.data());
+ break;
+ }
+
+ case TypeEntryKind::Array: {
+ ArrayType* array_type = cast<ArrayType>(f->type.get());
+ result_ |= validator_.OnArrayType(
+ field.loc,
+ TypeMut{array_type->field.type, array_type->field.mutable_});
+ break;
+ }
+ }
+ }
+ }
+
+ // Import section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ImportModuleField>(&field)) {
+ switch (f->import->kind()) {
+ case ExternalKind::Func: {
+ auto&& func = cast<FuncImport>(f->import.get())->func;
+ result_ |= validator_.OnFunction(
+ field.loc, GetFuncTypeIndex(field.loc, func.decl));
+ break;
+ }
+
+ case ExternalKind::Table: {
+ auto&& table = cast<TableImport>(f->import.get())->table;
+ result_ |=
+ validator_.OnTable(field.loc, table.elem_type, table.elem_limits);
+ break;
+ }
+
+ case ExternalKind::Memory: {
+ auto&& memory = cast<MemoryImport>(f->import.get())->memory;
+ result_ |= validator_.OnMemory(field.loc, memory.page_limits);
+ break;
+ }
+
+ case ExternalKind::Global: {
+ auto&& global = cast<GlobalImport>(f->import.get())->global;
+ result_ |= validator_.OnGlobalImport(field.loc, global.type,
+ global.mutable_);
+ break;
+ }
+
+ case ExternalKind::Tag: {
+ auto&& tag = cast<TagImport>(f->import.get())->tag;
+ result_ |= validator_.OnTag(field.loc,
+ GetFuncTypeIndex(field.loc, tag.decl));
+ break;
+ }
+ }
+ }
+ }
+
+ // Func section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<FuncModuleField>(&field)) {
+ result_ |= validator_.OnFunction(
+ field.loc, GetFuncTypeIndex(field.loc, f->func.decl));
+ }
+ }
+
+ // Table section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TableModuleField>(&field)) {
+ result_ |= validator_.OnTable(field.loc, f->table.elem_type,
+ f->table.elem_limits);
+ }
+ }
+
+ // Memory section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<MemoryModuleField>(&field)) {
+ result_ |= validator_.OnMemory(field.loc, f->memory.page_limits);
+ }
+ }
+
+ // Global section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<GlobalModuleField>(&field)) {
+ result_ |=
+ validator_.OnGlobal(field.loc, f->global.type, f->global.mutable_);
+
+ if (f->global.init_expr.size() == 1) {
+ const Expr* expr = &f->global.init_expr.front();
+
+ switch (expr->type()) {
+ case ExprType::Const:
+ result_ |= validator_.OnGlobalInitExpr_Const(
+ expr->loc, cast<ConstExpr>(expr)->const_.type());
+ break;
+
+ case ExprType::GlobalGet: {
+ Var var = cast<GlobalGetExpr>(expr)->var;
+ result_ |= validator_.OnGlobalInitExpr_GlobalGet(expr->loc, var);
+ break;
+ }
+
+ case ExprType::RefFunc:
+ result_ |= validator_.OnGlobalInitExpr_RefFunc(
+ expr->loc, cast<RefFuncExpr>(expr)->var);
+ break;
+
+ case ExprType::RefNull:
+ result_ |= validator_.OnGlobalInitExpr_RefNull(
+ expr->loc, cast<RefNullExpr>(expr)->type);
+ break;
+
+ default:
+ result_ |= validator_.OnGlobalInitExpr_Other(field.loc);
+ break;
+ }
+ } else {
+ result_ |= validator_.OnGlobalInitExpr_Other(field.loc);
+ }
+ }
+ }
+
+ // Tag section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<TagModuleField>(&field)) {
+ result_ |=
+ validator_.OnTag(field.loc, GetFuncTypeIndex(field.loc, f->tag.decl));
+ }
+ }
+
+ // Export section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ExportModuleField>(&field)) {
+ result_ |= validator_.OnExport(field.loc, f->export_.kind, f->export_.var,
+ f->export_.name);
+ }
+ }
+
+ // Start section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<StartModuleField>(&field)) {
+ result_ |= validator_.OnStart(field.loc, f->start);
+ }
+ }
+
+ // Elem segment section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<ElemSegmentModuleField>(&field)) {
+ result_ |= validator_.OnElemSegment(field.loc, f->elem_segment.table_var,
+ f->elem_segment.kind);
+
+ validator_.OnElemSegmentElemType(f->elem_segment.elem_type);
+
+ // Init expr.
+ if (f->elem_segment.offset.size() == 1) {
+ const Expr* expr = &f->elem_segment.offset.front();
+
+ switch (expr->type()) {
+ case ExprType::Const:
+ result_ |= validator_.OnElemSegmentInitExpr_Const(
+ expr->loc, cast<ConstExpr>(expr)->const_.type());
+ break;
+
+ case ExprType::GlobalGet: {
+ Var var = cast<GlobalGetExpr>(expr)->var;
+ result_ |=
+ validator_.OnElemSegmentInitExpr_GlobalGet(expr->loc, var);
+ break;
+ }
+
+ default:
+ result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc);
+ break;
+ }
+ } else if (f->elem_segment.offset.size() > 1) {
+ result_ |= validator_.OnElemSegmentInitExpr_Other(field.loc);
+ }
+
+ // Element expr.
+ for (auto&& elem_expr : f->elem_segment.elem_exprs) {
+ switch (elem_expr.kind) {
+ case ElemExprKind::RefNull:
+ // TODO: better location?
+ result_ |= validator_.OnElemSegmentElemExpr_RefNull(field.loc,
+ elem_expr.type);
+ break;
+
+ case ElemExprKind::RefFunc:
+ result_ |= validator_.OnElemSegmentElemExpr_RefFunc(
+ elem_expr.var.loc, elem_expr.var);
+ break;
+ }
+ }
+ }
+ }
+
+ // DataCount section.
+ validator_.OnDataCount(module->data_segments.size());
+
+ // Code section.
+ Index func_index = module->num_func_imports;
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<FuncModuleField>(&field)) {
+ result_ |= validator_.BeginFunctionBody(field.loc, func_index++);
+
+ for (auto&& decl : f->func.local_types.decls()) {
+ // TODO: Better location?
+ result_ |= validator_.OnLocalDecl(field.loc, decl.second, decl.first);
+ }
+
+ ExprVisitor visitor(this);
+ result_ |= visitor.VisitExprList(const_cast<ExprList&>(f->func.exprs));
+ result_ |= validator_.EndFunctionBody(field.loc);
+ }
+ }
+
+ // Data segment section.
+ for (const ModuleField& field : module->fields) {
+ if (auto* f = dyn_cast<DataSegmentModuleField>(&field)) {
+ result_ |= validator_.OnDataSegment(
+ field.loc, f->data_segment.memory_var, f->data_segment.kind);
+
+ // Init expr.
+ if (f->data_segment.offset.size() == 1) {
+ const Expr* expr = &f->data_segment.offset.front();
+
+ switch (expr->type()) {
+ case ExprType::Const:
+ result_ |= validator_.OnDataSegmentInitExpr_Const(
+ expr->loc, cast<ConstExpr>(expr)->const_.type());
+ break;
+
+ case ExprType::GlobalGet: {
+ Var var = cast<GlobalGetExpr>(expr)->var;
+ result_ |=
+ validator_.OnDataSegmentInitExpr_GlobalGet(expr->loc, var);
+ break;
+ }
+
+ default:
+ result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc);
+ break;
+ }
+ } else if (f->data_segment.offset.size() > 1) {
+ result_ |= validator_.OnDataSegmentInitExpr_Other(field.loc);
+ }
+ }
+ }
+
+ result_ |= validator_.EndModule();
+
+ return result_;
+}
+
+// Returns the result type of the invoked function, checked by the caller;
+// returning nullptr means that another error occured first, so the result type
+// should be ignored.
+const TypeVector* ScriptValidator::CheckInvoke(const InvokeAction* action) {
+ const Module* module = script_->GetModule(action->module_var);
+ if (!module) {
+ PrintError(&action->loc, "unknown module");
+ return nullptr;
+ }
+
+ const Export* export_ = module->GetExport(action->name);
+ if (!export_) {
+ PrintError(&action->loc, "unknown function export \"%s\"",
+ action->name.c_str());
+ return nullptr;
+ }
+
+ const Func* func = module->GetFunc(export_->var);
+ if (!func) {
+ // This error will have already been reported, just skip it.
+ return nullptr;
+ }
+
+ size_t actual_args = action->args.size();
+ size_t expected_args = func->GetNumParams();
+ if (expected_args != actual_args) {
+ PrintError(&action->loc,
+ "too %s parameters to function. got %" PRIzd
+ ", expected %" PRIzd,
+ actual_args > expected_args ? "many" : "few", actual_args,
+ expected_args);
+ return nullptr;
+ }
+ for (size_t i = 0; i < actual_args; ++i) {
+ const Const* const_ = &action->args[i];
+ CheckTypeIndex(&const_->loc, const_->type(), func->GetParamType(i),
+ "invoke", i, "argument");
+ }
+
+ return &func->decl.sig.result_types;
+}
+
+Result ScriptValidator::CheckGet(const GetAction* action, Type* out_type) {
+ const Module* module = script_->GetModule(action->module_var);
+ if (!module) {
+ PrintError(&action->loc, "unknown module");
+ return Result::Error;
+ }
+
+ const Export* export_ = module->GetExport(action->name);
+ if (!export_) {
+ PrintError(&action->loc, "unknown global export \"%s\"",
+ action->name.c_str());
+ return Result::Error;
+ }
+
+ const Global* global = module->GetGlobal(export_->var);
+ if (!global) {
+ // This error will have already been reported, just skip it.
+ return Result::Error;
+ }
+
+ *out_type = global->type;
+ return Result::Ok;
+}
+
+ScriptValidator::ActionResult ScriptValidator::CheckAction(
+ const Action* action) {
+ ActionResult result;
+ ZeroMemory(result);
+
+ switch (action->type()) {
+ case ActionType::Invoke:
+ result.types = CheckInvoke(cast<InvokeAction>(action));
+ result.kind =
+ result.types ? ActionResult::Kind::Types : ActionResult::Kind::Error;
+ break;
+
+ case ActionType::Get:
+ if (Succeeded(CheckGet(cast<GetAction>(action), &result.type))) {
+ result.kind = ActionResult::Kind::Type;
+ } else {
+ result.kind = ActionResult::Kind::Error;
+ }
+ break;
+ }
+
+ return result;
+}
+
+void ScriptValidator::CheckCommand(const Command* command) {
+ switch (command->type) {
+ case CommandType::Module: {
+ Validator module_validator(errors_, &cast<ModuleCommand>(command)->module,
+ options_);
+ module_validator.CheckModule();
+ break;
+ }
+
+ case CommandType::Action:
+ // Ignore result type.
+ CheckAction(cast<ActionCommand>(command)->action.get());
+ break;
+
+ case CommandType::Register:
+ case CommandType::AssertMalformed:
+ case CommandType::AssertInvalid:
+ case CommandType::AssertUnlinkable:
+ case CommandType::AssertUninstantiable:
+ // Ignore.
+ break;
+
+ case CommandType::AssertReturn: {
+ auto* assert_return_command = cast<AssertReturnCommand>(command);
+ const Action* action = assert_return_command->action.get();
+ ActionResult result = CheckAction(action);
+ // Here we take the concrete expected output types verify those actains
+ // the types that are the result of the action.
+ TypeVector actual_types;
+ for (auto ex : assert_return_command->expected) {
+ actual_types.push_back(ex.type());
+ }
+ switch (result.kind) {
+ case ActionResult::Kind::Types:
+ CheckResultTypes(&action->loc, actual_types, *result.types, "action");
+ break;
+
+ case ActionResult::Kind::Type:
+ CheckResultTypes(&action->loc, actual_types, {result.type}, "action");
+ break;
+
+ case ActionResult::Kind::Error:
+ // Error occurred, don't do any further checks.
+ break;
+ }
+ break;
+ }
+
+ case CommandType::AssertTrap:
+ // ignore result type.
+ CheckAction(cast<AssertTrapCommand>(command)->action.get());
+ break;
+ case CommandType::AssertExhaustion:
+ // ignore result type.
+ CheckAction(cast<AssertExhaustionCommand>(command)->action.get());
+ break;
+ }
+}
+
+Result ScriptValidator::CheckScript() {
+ for (const std::unique_ptr<Command>& command : script_->commands)
+ CheckCommand(command.get());
+ return result_;
+}
+
+} // end anonymous namespace
+
+Result ValidateScript(const Script* script,
+ Errors* errors,
+ const ValidateOptions& options) {
+ ScriptValidator validator(errors, script, options);
+
+ return validator.CheckScript();
+}
+
+Result ValidateModule(const Module* module,
+ Errors* errors,
+ const ValidateOptions& options) {
+ Validator validator(errors, module, options);
+
+ return validator.CheckModule();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/validator.h b/third_party/wasm2c/src/validator.h
new file mode 100644
index 0000000000..b84acbc945
--- /dev/null
+++ b/third_party/wasm2c/src/validator.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_VALIDATOR_H_
+#define WABT_VALIDATOR_H_
+
+#include "src/error.h"
+#include "src/feature.h"
+#include "src/shared-validator.h"
+
+namespace wabt {
+
+struct Module;
+struct Script;
+
+// Perform all checks on the script. It is valid if and only if this function
+// succeeds.
+Result ValidateScript(const Script*, Errors*, const ValidateOptions&);
+Result ValidateModule(const Module*, Errors*, const ValidateOptions&);
+
+} // namespace wabt
+
+#endif // WABT_VALIDATOR_H_
diff --git a/third_party/wasm2c/src/wabt.post.js b/third_party/wasm2c/src/wabt.post.js
new file mode 100644
index 0000000000..c71e9d038a
--- /dev/null
+++ b/third_party/wasm2c/src/wabt.post.js
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+var WABT_OK = 0;
+var WABT_ERROR = 1;
+var FEATURES = [
+ 'exceptions',
+ 'mutable_globals',
+ 'sat_float_to_int',
+ 'sign_extension',
+ 'simd',
+ 'threads',
+ 'multi_value',
+ 'tail_call',
+ 'bulk_memory',
+ 'reference_types',
+];
+
+/// If value is not undefined, return it. Otherwise return default_.
+function maybeDefault(value, default_) {
+ if (value === undefined) {
+ return default_;
+ }
+ return value;
+}
+
+/// Coerce value to boolean if not undefined. Otherwise return default_.
+function booleanOrDefault(value, default_) {
+ return !!maybeDefault(value, default_);
+}
+
+/// Allocate memory in the Module.
+function malloc(size) {
+ var addr = Module._malloc(size);
+ if (addr == 0) {
+ throw new Error('out of memory');
+ }
+ return addr;
+}
+
+/// Convert an ArrayBuffer/TypedArray/string into a buffer that can be
+/// used by the Module.
+function allocateBuffer(buf) {
+ var addr;
+ var size;
+ if (buf instanceof ArrayBuffer) {
+ size = buf.byteLength;
+ addr = malloc(size);
+ (new Uint8Array(HEAP8.buffer, addr, size)).set(new Uint8Array(buf))
+ } else if (ArrayBuffer.isView(buf)) {
+ size = buf.buffer.byteLength;
+ addr = malloc(size);
+ (new Uint8Array(HEAP8.buffer, addr, size)).set(buf);
+ } else if (typeof buf == 'string') {
+ size = buf.length;
+ addr = malloc(size);
+ writeAsciiToMemory(buf, addr, true); // don't null-terminate
+ } else {
+ throw new Error('unknown buffer type: ' + buf);
+ }
+ return {addr: addr, size: size};
+}
+
+function allocateCString(s) {
+ var size = s.length;
+ var addr = malloc(size);
+ writeAsciiToMemory(s, addr);
+ return {addr: addr, size: size};
+}
+
+
+/// Features
+function Features(obj) {
+ this.addr = Module._wabt_new_features();
+ for (var i = 0; i < FEATURES.length; ++i) {
+ var feature = FEATURES[i];
+ this[feature] = obj[feature] | 0;
+ }
+}
+Features.prototype = Object.create(Object.prototype);
+
+Features.prototype.destroy = function() {
+ Module._wabt_destroy_features(this.addr);
+};
+
+FEATURES.forEach(function(feature) {
+ Object.defineProperty(Features.prototype, feature, {
+ enumerable: true,
+ get: function() {
+ return Module['_wabt_' + feature + '_enabled'](this.addr);
+ },
+ set: function(newValue) {
+ Module['_wabt_set_' + feature + '_enabled'](this.addr, newValue | 0);
+ }
+ });
+});
+
+
+/// Lexer
+function Lexer(filename, buffer) {
+ this.filenameObj = allocateCString(filename);
+ this.bufferObj = allocateBuffer(buffer);
+ this.addr = Module._wabt_new_wast_buffer_lexer(
+ this.filenameObj.addr, this.bufferObj.addr, this.bufferObj.size);
+}
+Lexer.prototype = Object.create(Object.prototype);
+
+Lexer.prototype.destroy = function() {
+ Module._wabt_destroy_wast_lexer(this.addr);
+ Module._free(this.bufferObj.addr);
+ Module._free(this.filenameObj.addr);
+};
+
+
+/// OutputBuffer
+function OutputBuffer(addr) {
+ this.addr = addr;
+}
+OutputBuffer.prototype = Object.create(Object.prototype);
+
+OutputBuffer.prototype.toTypedArray = function() {
+ if (!this.addr) {
+ return null;
+ }
+
+ var addr = Module._wabt_output_buffer_get_data(this.addr);
+ var size = Module._wabt_output_buffer_get_size(this.addr);
+ var buffer = new Uint8Array(size);
+ buffer.set(new Uint8Array(HEAPU8.buffer, addr, size));
+ return buffer;
+};
+
+OutputBuffer.prototype.toString = function() {
+ if (!this.addr) {
+ return '';
+ }
+
+ var addr = Module._wabt_output_buffer_get_data(this.addr);
+ var size = Module._wabt_output_buffer_get_size(this.addr);
+ return UTF8ToString(addr, size);
+};
+
+OutputBuffer.prototype.destroy = function() {
+ Module._wabt_destroy_output_buffer(this.addr);
+};
+
+
+/// Errors
+function Errors(kind, lexer) {
+ this.kind = kind;
+ this.addr = Module._wabt_new_errors();
+ this.lexer = lexer;
+}
+Errors.prototype = Object.create(Object.prototype);
+
+Errors.prototype.format = function() {
+ var buffer;
+ switch (this.kind) {
+ case 'text':
+ buffer = new OutputBuffer(
+ Module._wabt_format_text_errors(this.addr, this.lexer.addr));
+ break;
+ case 'binary':
+ buffer = new OutputBuffer(Module._wabt_format_binary_errors(this.addr));
+ break;
+ default:
+ throw new Error('Invalid Errors kind: ' + this.kind);
+ }
+ var message = buffer.toString();
+ buffer.destroy();
+ return message;
+};
+
+Errors.prototype.destroy = function() {
+ Module._wabt_destroy_errors(this.addr);
+ if (this.lexer) {
+ this.lexer.destroy();
+ }
+};
+
+
+/// parseWat
+function parseWat(filename, buffer, options) {
+ var lexer = new Lexer(filename, buffer);
+ var errors = new Errors('text', lexer);
+ var features = new Features(options || {});
+
+ try {
+ var parseResult_addr =
+ Module._wabt_parse_wat(lexer.addr, features.addr, errors.addr);
+
+ var result = Module._wabt_parse_wat_result_get_result(parseResult_addr);
+ if (result !== WABT_OK) {
+ throw new Error('parseWat failed:\n' + errors.format());
+ }
+
+ var module_addr =
+ Module._wabt_parse_wat_result_release_module(parseResult_addr);
+ var result = new WasmModule(module_addr, errors);
+ // Clear errors so it isn't destroyed below.
+ errors = null;
+ return result;
+ } finally {
+ Module._wabt_destroy_parse_wat_result(parseResult_addr);
+ features.destroy();
+ if (errors) {
+ errors.destroy();
+ }
+ }
+}
+
+
+// readWasm
+function readWasm(buffer, options) {
+ var bufferObj = allocateBuffer(buffer);
+ var errors = new Errors('binary');
+ var readDebugNames = booleanOrDefault(options.readDebugNames, false);
+ var features = new Features(options);
+
+ try {
+ var readBinaryResult_addr = Module._wabt_read_binary(
+ bufferObj.addr, bufferObj.size, readDebugNames, features.addr,
+ errors.addr);
+
+ var result =
+ Module._wabt_read_binary_result_get_result(readBinaryResult_addr);
+ if (result !== WABT_OK) {
+ throw new Error('readWasm failed:\n' + errors.format());
+ }
+
+ var module_addr =
+ Module._wabt_read_binary_result_release_module(readBinaryResult_addr);
+ var result = new WasmModule(module_addr, errors);
+ // Clear errors so it isn't destroyed below.
+ errors = null;
+ return result;
+ } finally {
+ Module._wabt_destroy_read_binary_result(readBinaryResult_addr);
+ features.destroy();
+ if (errors) {
+ errors.destroy();
+ }
+ Module._free(bufferObj.addr);
+ }
+}
+
+
+// WasmModule (can't call it Module because emscripten has claimed it.)
+function WasmModule(module_addr, errors) {
+ this.module_addr = module_addr;
+ this.errors = errors;
+}
+WasmModule.prototype = Object.create(Object.prototype);
+
+WasmModule.prototype.validate = function(options) {
+ var features = new Features(options || {});
+ try {
+ var result = Module._wabt_validate_module(
+ this.module_addr, features.addr, this.errors.addr);
+ if (result !== WABT_OK) {
+ throw new Error('validate failed:\n' + this.errors.format());
+ }
+ } finally {
+ features.destroy();
+ }
+};
+
+WasmModule.prototype.resolveNames = function() {
+ // No-op, this is now part of text parsing.
+};
+
+WasmModule.prototype.generateNames = function() {
+ var result = Module._wabt_generate_names_module(this.module_addr);
+ if (result !== WABT_OK) {
+ throw new Error('generateNames failed.');
+ }
+};
+
+WasmModule.prototype.applyNames = function() {
+ var result = Module._wabt_apply_names_module(this.module_addr);
+ if (result !== WABT_OK) {
+ throw new Error('applyNames failed.');
+ }
+};
+
+WasmModule.prototype.toText = function(options) {
+ var foldExprs = booleanOrDefault(options.foldExprs, false);
+ var inlineExport = booleanOrDefault(options.inlineExport, false);
+
+ var writeModuleResult_addr =
+ Module._wabt_write_text_module(this.module_addr, foldExprs, inlineExport);
+
+ var result = Module._wabt_write_module_result_get_result(
+ writeModuleResult_addr);
+
+ try {
+ if (result !== WABT_OK) {
+ throw new Error('toText failed.');
+ }
+
+ var outputBuffer = new OutputBuffer(
+ Module._wabt_write_module_result_release_output_buffer(
+ writeModuleResult_addr));
+
+ return outputBuffer.toString();
+
+ } finally {
+ if (outputBuffer) {
+ outputBuffer.destroy();
+ }
+ Module._wabt_destroy_write_module_result(writeModuleResult_addr);
+ }
+};
+
+WasmModule.prototype.toBinary = function(options) {
+ var log = booleanOrDefault(options.log, false);
+ var canonicalize_lebs = booleanOrDefault(options.canonicalize_lebs, true);
+ var relocatable = booleanOrDefault(options.relocatable, false);
+ var write_debug_names = booleanOrDefault(options.write_debug_names, false);
+
+ var writeModuleResult_addr = Module._wabt_write_binary_module(
+ this.module_addr, log, canonicalize_lebs, relocatable, write_debug_names);
+
+ var result =
+ Module._wabt_write_module_result_get_result(writeModuleResult_addr);
+
+ try {
+ if (result !== WABT_OK) {
+ throw new Error('toBinary failed.');
+ }
+
+ var binaryOutputBuffer =
+ new OutputBuffer(Module._wabt_write_module_result_release_output_buffer(
+ writeModuleResult_addr));
+ var logOutputBuffer = new OutputBuffer(
+ Module._wabt_write_module_result_release_log_output_buffer(
+ writeModuleResult_addr));
+
+ return {
+ buffer: binaryOutputBuffer.toTypedArray(),
+ log: logOutputBuffer.toString()
+ };
+
+ } finally {
+ if (binaryOutputBuffer) {
+ binaryOutputBuffer.destroy();
+ }
+ if (logOutputBuffer) {
+ logOutputBuffer.destroy();
+ }
+ Module._wabt_destroy_write_module_result(writeModuleResult_addr);
+ }
+};
+
+WasmModule.prototype.destroy = function() {
+ Module._wabt_destroy_module(this.module_addr);
+ if (this.errors) {
+ this.errors.destroy();
+ }
+};
+
+Module['parseWat'] = parseWat;
+Module['readWasm'] = readWasm;
diff --git a/third_party/wasm2c/src/wasm2c.c.tmpl b/third_party/wasm2c/src/wasm2c.c.tmpl
new file mode 100644
index 0000000000..6539ddc8cc
--- /dev/null
+++ b/third_party/wasm2c/src/wasm2c.c.tmpl
@@ -0,0 +1,433 @@
+%%includes
+/* Automically generated by wasm2c */
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+%%declarations
+#if defined(_MSC_VER)
+# define UNLIKELY(x) (x)
+# define LIKELY(x) (x)
+#else
+# define UNLIKELY(x) __builtin_expect(!!(x), 0)
+# define LIKELY(x) __builtin_expect(!!(x), 1)
+#endif
+
+#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)
+
+#ifndef FUNC_PROLOGUE
+#define FUNC_PROLOGUE
+#endif
+
+#ifndef FUNC_EPILOGUE
+#define FUNC_EPILOGUE
+#endif
+
+#ifdef EXTERNAL_CALLBACK_PROLOGUE
+#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x) \
+ if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \
+ EXTERNAL_CALLBACK_PROLOGUE; \
+ }
+#else
+#define EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x)
+#endif
+
+#ifdef EXTERNAL_CALLBACK_EPILOGUE
+#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x) \
+ if (UNLIKELY(table.data[x].func_class == WASM_RT_EXTERNAL_FUNCTION)) { \
+ EXTERNAL_CALLBACK_EPILOGUE; \
+ }
+#else
+#define EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x)
+#endif
+
+#define UNREACHABLE (void) TRAP(UNREACHABLE)
+
+#define CALL_INDIRECT_VOID(table, t, ft, x, func_types, ...) \
+ if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \
+ EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \
+ ((t)table.data[x].func)(__VA_ARGS__); \
+ EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \
+ } else { \
+ wasm_rt_callback_error_trap(&table, x, func_types[ft]); \
+ }
+
+#define CALL_INDIRECT_RES(res, table, t, ft, x, func_types, ...) \
+ if (LIKELY((x) < table.size && table.data[x].func && table.data[x].func_type == func_types[ft])) { \
+ EXTERNAL_CALLBACK_PROLOGUE_EXEC(table, x); \
+ res = ((t)table.data[x].func)(__VA_ARGS__); \
+ EXTERNAL_CALLBACK_EPILOGUE_EXEC(table, x); \
+ } else { \
+ wasm_rt_callback_error_trap(&table, x, func_types[ft]); \
+ }
+
+#if defined(WASM2C_MALLOC_FAIL_CALLBACK)
+void WASM2C_MALLOC_FAIL_CALLBACK(u32 ptr_size);
+# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size) \
+ if (!ptr) { \
+ WASM2C_MALLOC_FAIL_CALLBACK(ptr_size); \
+ }
+#else
+# define WASM2C_MALLOC_FAIL_CHECK(ptr, ptr_size)
+#endif
+
+#if defined(WASM_CHECK_SHADOW_MEMORY)
+# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_load(mem, func_name, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size) wasm2c_shadow_memory_store(mem, func_name, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size) wasm2c_shadow_memory_reserve(mem, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size) wasm2c_shadow_memory_dlmalloc(mem, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr) wasm2c_shadow_memory_dlfree(mem, ptr)
+# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr) wasm2c_shadow_memory_mark_globals_heap_boundary(mem, ptr)
+#else
+# define WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_STORE(mem, func_name, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_RESERVE(mem, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_DLMALLOC(mem, ptr, ptr_size)
+# define WASM2C_SHADOW_MEMORY_DLFREE(mem, ptr)
+# define WASM2C_SHADOW_MEMORY_MARK_GLOBALS_HEAP_BOUNDARY(mem, ptr)
+#endif
+
+#ifdef WASM_USE_GUARD_PAGES
+# define MEMCHECK(mem, a, t)
+#else
+# define MEMCHECK(mem, a, t) if (UNLIKELY((a) + sizeof(t) > mem->size)) { (void) TRAP(OOB); }
+#endif
+
+#if defined(WASM_USE_GUARD_PAGES) && UINTPTR_MAX == 0xffffffff
+// on 32-bit platforms we have to mask memory access into range
+# define MEM_ACCESS_REF(mem, addr) &mem->data[addr & mem->mem_mask]
+#else
+# define MEM_ACCESS_REF(mem, addr) &mem->data[addr]
+#endif
+
+#if defined(WASM_USING_GLOBAL_HEAP)
+# undef MEM_ACCESS_REF
+# define MEM_ACCESS_REF(mem, addr) (char*) addr
+#endif
+
+#ifdef __GNUC__
+#define wasm_asm __asm__
+#else
+#define wasm_asm(X)
+#endif
+
+#if WABT_BIG_ENDIAN
+static inline void load_data(void *dest, const void *src, size_t n) {
+ size_t i = 0;
+ u8 *dest_chars = dest;
+ memcpy(dest, src, n);
+ for (i = 0; i < (n>>1); i++) {
+ u8 cursor = dest_chars[i];
+ dest_chars[i] = dest_chars[n - i - 1];
+ dest_chars[n - i - 1] = cursor;
+ }
+}
+#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[m.size - o - s]), i, s); \
+ WASM2C_SHADOW_MEMORY_RESERVE(&m, m.size - o - s, s); \
+ WASM2C_SHADOW_MEMORY_STORE(&m, "GlobalDataLoad", m.size - o - s, s); \
+}
+
+#define DEFINE_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ memcpy(&result, MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1)), sizeof(t1)); \
+ WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, mem->size - addr - sizeof(t1), sizeof(t1)); \
+ wasm_asm("" ::"r"(result)); \
+ return (t3)(t2)result; \
+ }
+
+#define DEFINE_STORE(name, t1, t2) \
+ static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 wrapped = (t1)value; \
+ memcpy(MEM_ACCESS_REF(mem, mem->size - addr - sizeof(t1), &wrapped, sizeof(t1)); \
+ WASM2C_SHADOW_MEMORY_STORE(mem, func_name, mem->size - addr - sizeof(t1)), sizeof(t1)); \
+ }
+#else
+static inline void load_data(void *dest, const void *src, size_t n) {
+ memcpy(dest, src, n);
+}
+#define LOAD_DATA(m, o, i, s) { load_data(&(m.data[o]), i, s); \
+ WASM2C_SHADOW_MEMORY_RESERVE(&m, o, s); \
+ WASM2C_SHADOW_MEMORY_STORE(&m, "GlobalDataLoad", o, s); \
+}
+
+#define DEFINE_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr, const char* func_name) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ memcpy(&result, MEM_ACCESS_REF(mem, addr), sizeof(t1)); \
+ WASM2C_SHADOW_MEMORY_LOAD(mem, func_name, addr, sizeof(t1)); \
+ wasm_asm("" ::"r"(result)); \
+ return (t3)(t2)result; \
+ }
+
+#define DEFINE_STORE(name, t1, t2) \
+ static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value, const char* func_name) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 wrapped = (t1)value; \
+ memcpy(MEM_ACCESS_REF(mem, addr), &wrapped, sizeof(t1)); \
+ WASM2C_SHADOW_MEMORY_STORE(mem, func_name, addr, sizeof(t1)); \
+ }
+#endif
+
+DEFINE_LOAD(i32_load, u32, u32, u32);
+DEFINE_LOAD(i64_load, u64, u64, u64);
+DEFINE_LOAD(f32_load, f32, f32, f32);
+DEFINE_LOAD(f64_load, f64, f64, f64);
+DEFINE_LOAD(i32_load8_s, s8, s32, u32);
+DEFINE_LOAD(i64_load8_s, s8, s64, u64);
+DEFINE_LOAD(i32_load8_u, u8, u32, u32);
+DEFINE_LOAD(i64_load8_u, u8, u64, u64);
+DEFINE_LOAD(i32_load16_s, s16, s32, u32);
+DEFINE_LOAD(i64_load16_s, s16, s64, u64);
+DEFINE_LOAD(i32_load16_u, u16, u32, u32);
+DEFINE_LOAD(i64_load16_u, u16, u64, u64);
+DEFINE_LOAD(i64_load32_s, s32, s64, u64);
+DEFINE_LOAD(i64_load32_u, u32, u64, u64);
+DEFINE_STORE(i32_store, u32, u32);
+DEFINE_STORE(i64_store, u64, u64);
+DEFINE_STORE(f32_store, f32, f32);
+DEFINE_STORE(f64_store, f64, f64);
+DEFINE_STORE(i32_store8, u8, u32);
+DEFINE_STORE(i32_store16, u16, u32);
+DEFINE_STORE(i64_store8, u8, u64);
+DEFINE_STORE(i64_store16, u16, u64);
+DEFINE_STORE(i64_store32, u32, u64);
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+
+// Adapted from https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h
+
+static inline int I64_CLZ(unsigned long long v) {
+ unsigned long r = 0;
+#if defined(_M_AMD64) || defined(_M_ARM)
+ if (_BitScanReverse64(&r, v)) {
+ return 63 - r;
+ }
+#else
+ if (_BitScanReverse(&r, (unsigned long) (v >> 32))) {
+ return 31 - r;
+ } else if (_BitScanReverse(&r, (unsigned long) v)) {
+ return 63 - r;
+ }
+#endif
+ return 64;
+}
+
+static inline int I32_CLZ(unsigned long v) {
+ unsigned long r = 0;
+ if (_BitScanReverse(&r, v)) {
+ return 31 - r;
+ }
+ return 32;
+}
+
+static inline int I64_CTZ(unsigned long long v) {
+ if (!v) {
+ return 64;
+ }
+ unsigned long r = 0;
+#if defined(_M_AMD64) || defined(_M_ARM)
+ _BitScanForward64(&r, v);
+ return (int) r;
+#else
+ if (_BitScanForward(&r, (unsigned int) (v))) {
+ return (int) (r);
+ }
+
+ _BitScanForward(&r, (unsigned int) (v >> 32));
+ return (int) (r + 32);
+#endif
+}
+
+static inline int I32_CTZ(unsigned long v) {
+ if (!v) {
+ return 32;
+ }
+ unsigned long r = 0;
+ _BitScanForward(&r, v);
+ return (int) r;
+}
+
+#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \
+ static inline u32 f_n(T x) { \
+ x = x - ((x >> 1) & (T)~(T)0/3); \
+ x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \
+ x = (x + (x >> 4)) & (T)~(T)0/255*15; \
+ return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \
+ }
+
+POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32)
+POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64)
+
+#undef POPCOUNT_DEFINE_PORTABLE
+
+#else
+# define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)
+# define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)
+# define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)
+# define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64)
+# define I32_POPCNT(x) (__builtin_popcount(x))
+# define I64_POPCNT(x) (__builtin_popcountll(x))
+#endif
+
+#define DIV_S(ut, min, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
+ : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \
+ : (ut)((x) / (y)))
+
+#define REM_S(ut, min, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
+ : (UNLIKELY((x) == min && (y) == -1)) ? 0 \
+ : (ut)((x) % (y)))
+
+#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y)
+#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y)
+#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y)
+#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y)
+
+#define DIVREM_U(op, x, y) \
+ ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x) op (y)))
+
+#define DIV_U(x, y) DIVREM_U(/, x, y)
+#define REM_U(x, y) DIVREM_U(%, x, y)
+
+#define ROTL(x, y, mask) \
+ (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask))))
+#define ROTR(x, y, mask) \
+ (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask))))
+
+#define I32_ROTL(x, y) ROTL(x, y, 31)
+#define I64_ROTL(x, y) ROTL(x, y, 63)
+#define I32_ROTR(x, y) ROTR(x, y, 31)
+#define I64_ROTR(x, y) ROTR(x, y, 63)
+
+#define FMIN(x, y) \
+ ((UNLIKELY((x) != (x))) ? NAN \
+ : (UNLIKELY((y) != (y))) ? NAN \
+ : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \
+ : (x < y) ? x : y)
+
+#define FMAX(x, y) \
+ ((UNLIKELY((x) != (x))) ? NAN \
+ : (UNLIKELY((y) != (y))) ? NAN \
+ : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \
+ : (x > y) ? x : y)
+
+#define TRUNC_S(ut, st, ft, min, minop, max, x) \
+ ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
+ : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \
+ : (ut)(st)(x))
+
+#define I32_TRUNC_S_F32(x) TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x)
+#define I64_TRUNC_S_F32(x) TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x)
+#define I32_TRUNC_S_F64(x) TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x)
+#define I64_TRUNC_S_F64(x) TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x)
+
+#define TRUNC_U(ut, ft, max, x) \
+ ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \
+ : (UNLIKELY(!((x) > (ft)-1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \
+ : (ut)(x))
+
+#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x)
+#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x)
+#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x)
+#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x)
+
+#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \
+ ((UNLIKELY((x) != (x))) ? 0 \
+ : (UNLIKELY(!((x)minop(min)))) ? smin \
+ : (UNLIKELY(!((x) < (max)))) ? smax \
+ : (ut)(st)(x))
+
+#define I32_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, INT32_MAX, x)
+#define I64_TRUNC_SAT_S_F32(x) TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, INT64_MAX, x)
+#define I32_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., INT32_MAX, x)
+#define I64_TRUNC_SAT_S_F64(x) TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, INT64_MAX, x)
+
+#define TRUNC_SAT_U(ut, ft, max, smax, x) \
+ ((UNLIKELY((x) != (x))) ? 0 \
+ : (UNLIKELY(!((x) > (ft)-1))) ? 0 \
+ : (UNLIKELY(!((x) < (max)))) ? smax \
+ : (ut)(x))
+
+#define I32_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x)
+#define I64_TRUNC_SAT_U_F32(x) TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x)
+#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x)
+#define I64_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x)
+
+#define DEFINE_REINTERPRET(name, t1, t2) \
+ static inline t2 name(t1 x) { \
+ t2 result; \
+ memcpy(&result, &x, sizeof(result)); \
+ return result; \
+ }
+
+DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32)
+DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32)
+DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64)
+DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64)
+%%sandboxapis
+//test
+
+static u32 add_wasm2c_callback(void* sbx_ptr, u32 func_type_idx, void* func_ptr, wasm_rt_elem_target_class_t func_class) {
+ wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);
+ for (u32 i = 1; i < table->max_size; i++) {
+ if (i >= table->size) {
+ wasm_rt_expand_table(table);
+ }
+ if (table->data[i].func == 0) {
+ table->data[i] = (wasm_rt_elem_t){ func_class, func_type_idx, (wasm_rt_anyfunc_t) func_ptr };
+ return i;
+ }
+ }
+ (void) TRAP(CALL_INDIRECT_TABLE_EXPANSION);
+}
+
+static void remove_wasm2c_callback(void* sbx_ptr, u32 callback_idx) {
+ wasm_rt_table_t* table = get_wasm2c_callback_table(sbx_ptr);
+ table->data[callback_idx].func = 0;
+}
+
+static u32 lookup_wasm2c_func_index(void* sbx_ptr, u32 param_count, u32 result_count, wasm_rt_type_t* types) {
+ wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) sbx_ptr;
+ return wasm_rt_register_func_type(&sbx->func_type_structs, &sbx->func_type_count, param_count, result_count, types);
+}
+
+static void* create_wasm2c_sandbox(uint32_t max_wasm_pages) {
+ wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) calloc(sizeof(wasm2c_sandbox_t), 1);
+ if (!init_memory(sbx, max_wasm_pages)) {
+ free(sbx);
+ return 0;
+ }
+ init_func_types(sbx);
+ init_globals(sbx);
+ init_table(sbx);
+ wasm_rt_init_wasi(&(sbx->wasi_data));
+ init_module_starts();
+ // w2c___wasm_call_ctors(sbx);
+ return sbx;
+}
+
+static void destroy_wasm2c_sandbox(void* aSbx) {
+ wasm2c_sandbox_t* const sbx = (wasm2c_sandbox_t* const) aSbx;
+ cleanup_memory(sbx);
+ cleanup_func_types(sbx);
+ cleanup_table(sbx);
+ wasm_rt_cleanup_wasi(&(sbx->wasi_data));
+ free(sbx);
+}
+
+FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)() {
+ wasm2c_sandbox_funcs_t ret;
+ ret.wasm_rt_sys_init = &wasm_rt_sys_init;
+ ret.create_wasm2c_sandbox = &create_wasm2c_sandbox;
+ ret.destroy_wasm2c_sandbox = &destroy_wasm2c_sandbox;
+ ret.lookup_wasm2c_nonfunc_export = &lookup_wasm2c_nonfunc_export;
+ ret.lookup_wasm2c_func_index = &lookup_wasm2c_func_index;
+ ret.add_wasm2c_callback = &add_wasm2c_callback;
+ ret.remove_wasm2c_callback = &remove_wasm2c_callback;
+ return ret;
+}
diff --git a/third_party/wasm2c/src/wasm2c.h.tmpl b/third_party/wasm2c/src/wasm2c.h.tmpl
new file mode 100644
index 0000000000..a79f924e09
--- /dev/null
+++ b/third_party/wasm2c/src/wasm2c.h.tmpl
@@ -0,0 +1,51 @@
+%%top
+/* Automically generated by wasm2c */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include "wasm-rt.h"
+
+#ifndef WASM_RT_MODULE_PREFIX
+#define WASM_RT_MODULE_PREFIX
+#endif
+
+#define WASM_RT_PASTE_(x, y) x ## y
+#define WASM_RT_PASTE(x, y) WASM_RT_PASTE_(x, y)
+#define WASM_RT_ADD_PREFIX(x) WASM_RT_PASTE(WASM_RT_MODULE_PREFIX, x)
+
+#define WASM_CURR_ADD_PREFIX(x) WASM_RT_PASTE(WASM_CURR_MODULE_PREFIX, x)
+
+/* TODO(binji): only use stdint.h types in header */
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef float f32;
+typedef double f64;
+
+#ifndef WASM_DONT_EXPORT_FUNCS
+# if defined(_WIN32)
+# define FUNC_EXPORT __declspec(dllexport)
+# else
+# define FUNC_EXPORT
+# endif
+#else
+# define FUNC_EXPORT
+#endif
+
+FUNC_EXPORT wasm2c_sandbox_funcs_t WASM_CURR_ADD_PREFIX(get_wasm2c_sandbox_info)();
+
+struct wasm2c_sandbox_t;
+typedef struct wasm2c_sandbox_t wasm2c_sandbox_t;
+%%bottom
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/third_party/wasm2c/src/wasm2c_tmpl.py b/third_party/wasm2c/src/wasm2c_tmpl.py
new file mode 100755
index 0000000000..0da117a8ca
--- /dev/null
+++ b/third_party/wasm2c/src/wasm2c_tmpl.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 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.
+#
+
+import argparse
+import io
+import os
+import sys
+
+
+def EscapeCString(s):
+ out = ''
+ for b in bytearray(s.encode('utf-8')):
+ if b in (34, 92):
+ # " or \
+ out += '\\' + chr(b)
+ elif b == 10:
+ # newline
+ out += '\\n'
+ elif 32 <= b <= 127:
+ # printable char
+ out += chr(b)
+ else:
+ # non-printable; write as \xab
+ out += '\\x%02x' % b
+
+ return out
+
+
+def main(args):
+ arg_parser = argparse.ArgumentParser()
+ arg_parser.add_argument('-o', '--output', metavar='PATH',
+ help='output file.')
+ arg_parser.add_argument('file', help='input file.')
+ options = arg_parser.parse_args(args)
+
+ section_name = None
+ output = io.StringIO()
+
+ output.write('/* Generated from \'%s\' by wasm2c_tmpl.py, do not edit! */\n' %
+ os.path.basename(options.file))
+
+ with open(options.file) as f:
+ for line in f.readlines():
+ if line.startswith('%%'):
+ if section_name is not None:
+ output.write(';\n\n')
+ section_name = line[2:-1]
+ output.write('const char SECTION_NAME(%s)[] =\n' % section_name)
+ else:
+ output.write('"%s"\n' % EscapeCString(line))
+
+ output.write(';\n')
+ if options.output:
+ with open(options.output, 'w') as outf:
+ outf.write(output.getvalue())
+ else:
+ sys.stdout.write(output.getvalue())
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/third_party/wasm2c/src/wast-lexer.cc b/third_party/wasm2c/src/wast-lexer.cc
new file mode 100644
index 0000000000..05ac736a5a
--- /dev/null
+++ b/third_party/wasm2c/src/wast-lexer.cc
@@ -0,0 +1,557 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/wast-lexer.h"
+
+#include <cassert>
+#include <cstdio>
+
+#include "config.h"
+
+#include "src/lexer-source.h"
+#include "src/wast-parser.h"
+
+#define ERROR(...) parser->Error(GetLocation(), __VA_ARGS__)
+
+namespace wabt {
+
+namespace {
+
+#include "src/prebuilt/lexer-keywords.cc"
+
+} // namespace
+
+WastLexer::WastLexer(std::unique_ptr<LexerSource> source, string_view filename)
+ : source_(std::move(source)),
+ filename_(filename),
+ line_(1),
+ buffer_(static_cast<const char*>(source_->data())),
+ buffer_end_(buffer_ + source_->size()),
+ line_start_(buffer_),
+ token_start_(buffer_),
+ cursor_(buffer_) {}
+
+// static
+std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer(string_view filename,
+ const void* data,
+ size_t size) {
+ return MakeUnique<WastLexer>(MakeUnique<LexerSource>(data, size), filename);
+}
+
+Token WastLexer::GetToken(WastParser* parser) {
+ while (true) {
+ token_start_ = cursor_;
+ switch (PeekChar()) {
+ case kEof:
+ return BareToken(TokenType::Eof);
+
+ case '(':
+ if (MatchString("(;")) {
+ if (ReadBlockComment(parser)) {
+ continue;
+ }
+ return BareToken(TokenType::Eof);
+ } else if (MatchString("(@")) {
+ ReadReservedChars();
+ // offset=2 to skip the "(@" prefix
+ return TextToken(TokenType::LparAnn, 2);
+ } else {
+ ReadChar();
+ return BareToken(TokenType::Lpar);
+ }
+ break;
+
+ case ')':
+ ReadChar();
+ return BareToken(TokenType::Rpar);
+
+ case ';':
+ if (MatchString(";;")) {
+ if (ReadLineComment()) {
+ continue;
+ }
+ return BareToken(TokenType::Eof);
+ } else {
+ ReadChar();
+ ERROR("unexpected char");
+ continue;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ ReadWhitespace();
+ continue;
+
+ case '"':
+ return GetStringToken(parser);
+
+ case '+':
+ case '-':
+ ReadChar();
+ switch (PeekChar()) {
+ case 'i':
+ return GetInfToken();
+
+ case 'n':
+ return GetNanToken();
+
+ case '0':
+ return MatchString("0x") ? GetHexNumberToken(TokenType::Int)
+ : GetNumberToken(TokenType::Int);
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return GetNumberToken(TokenType::Int);
+
+ default:
+ return GetReservedToken();
+ }
+ break;
+
+ case '0':
+ return MatchString("0x") ? GetHexNumberToken(TokenType::Nat)
+ : GetNumberToken(TokenType::Nat);
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return GetNumberToken(TokenType::Nat);
+
+ case '$':
+ return GetIdToken();
+
+ case 'a':
+ return GetNameEqNumToken("align=", TokenType::AlignEqNat);
+
+ case 'i':
+ return GetInfToken();
+
+ case 'n':
+ return GetNanToken();
+
+ case 'o':
+ return GetNameEqNumToken("offset=", TokenType::OffsetEqNat);
+
+ default:
+ if (IsKeyword(PeekChar())) {
+ return GetKeywordToken();
+ } else if (IsReserved(PeekChar())) {
+ return GetReservedToken();
+ } else {
+ ReadChar();
+ ERROR("unexpected char");
+ continue;
+ }
+ }
+ }
+}
+
+Location WastLexer::GetLocation() {
+ auto column = [=](const char* p) {
+ return std::max(1, static_cast<int>(p - line_start_ + 1));
+ };
+ return Location(filename_, line_, column(token_start_), column(cursor_));
+}
+
+string_view WastLexer::GetText(size_t offset) {
+ return string_view(token_start_ + offset, (cursor_ - token_start_) - offset);
+}
+
+Token WastLexer::BareToken(TokenType token_type) {
+ return Token(GetLocation(), token_type);
+}
+
+Token WastLexer::LiteralToken(TokenType token_type, LiteralType literal_type) {
+ return Token(GetLocation(), token_type, Literal(literal_type, GetText()));
+}
+
+Token WastLexer::TextToken(TokenType token_type, size_t offset) {
+ return Token(GetLocation(), token_type, GetText(offset));
+}
+
+int WastLexer::PeekChar() {
+ return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_) : kEof;
+}
+
+int WastLexer::ReadChar() {
+ return cursor_ < buffer_end_ ? static_cast<uint8_t>(*cursor_++) : kEof;
+}
+
+bool WastLexer::MatchChar(char c) {
+ if (PeekChar() == c) {
+ ReadChar();
+ return true;
+ }
+ return false;
+}
+
+bool WastLexer::MatchString(string_view s) {
+ const char* saved_cursor = cursor_;
+ for (char c : s) {
+ if (ReadChar() != c) {
+ cursor_ = saved_cursor;
+ return false;
+ }
+ }
+ return true;
+}
+
+void WastLexer::Newline() {
+ line_++;
+ line_start_ = cursor_;
+}
+
+bool WastLexer::ReadBlockComment(WastParser* parser) {
+ int nesting = 1;
+ while (true) {
+ switch (ReadChar()) {
+ case kEof:
+ ERROR("EOF in block comment");
+ return false;
+
+ case ';':
+ if (MatchChar(')') && --nesting == 0) {
+ return true;
+ }
+ break;
+
+ case '(':
+ if (MatchChar(';')) {
+ nesting++;
+ }
+ break;
+
+ case '\n':
+ Newline();
+ break;
+ }
+ }
+}
+
+bool WastLexer::ReadLineComment() {
+ while (true) {
+ switch (ReadChar()) {
+ case kEof:
+ return false;
+
+ case '\n':
+ Newline();
+ return true;
+ }
+ }
+}
+
+void WastLexer::ReadWhitespace() {
+ while (true) {
+ switch (PeekChar()) {
+ case ' ':
+ case '\t':
+ case '\r':
+ ReadChar();
+ break;
+
+ case '\n':
+ ReadChar();
+ Newline();
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+Token WastLexer::GetStringToken(WastParser* parser) {
+ const char* saved_token_start = token_start_;
+ bool has_error = false;
+ bool in_string = true;
+ ReadChar();
+ while (in_string) {
+ switch (ReadChar()) {
+ case kEof:
+ return BareToken(TokenType::Eof);
+
+ case '\n':
+ token_start_ = cursor_ - 1;
+ ERROR("newline in string");
+ has_error = true;
+ Newline();
+ continue;
+
+ case '"':
+ in_string = false;
+ break;
+
+ case '\\': {
+ switch (ReadChar()) {
+ case 't':
+ case 'n':
+ case 'r':
+ case '"':
+ case '\'':
+ case '\\':
+ // Valid escape.
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': // Hex byte escape.
+ if (IsHexDigit(PeekChar())) {
+ ReadChar();
+ } else {
+ token_start_ = cursor_ - 2;
+ goto error;
+ }
+ break;
+
+ default:
+ token_start_ = cursor_ - 2;
+ goto error;
+
+ error:
+ ERROR("bad escape \"%.*s\"",
+ static_cast<int>(cursor_ - token_start_), token_start_);
+ has_error = true;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ token_start_ = saved_token_start;
+ if (has_error) {
+ return Token(GetLocation(), TokenType::Invalid);
+ }
+
+ return TextToken(TokenType::Text);
+}
+
+// static
+bool WastLexer::IsCharClass(int c, CharClass bit) {
+ // Generated by the following python script:
+ //
+ // def Range(c, lo, hi): return lo <= c <= hi
+ // def IsDigit(c): return Range(c, '0', '9')
+ // def IsHexDigit(c): return IsDigit(c) or Range(c.lower(), 'a', 'f')
+ // def IsKeyword(c): return Range(c, 'a', 'z')
+ // def IsReserved(c): return Range(c, '!', '~') and c not in '"(),;[]{}'
+ //
+ // print ([0] + [
+ // (8 if IsDigit(c) else 0) |
+ // (4 if IsHexDigit(c) else 0) |
+ // (2 if IsKeyword(c) else 0) |
+ // (1 if IsReserved(c) else 0)
+ // for c in map(chr, range(0, 127))
+ // ])
+ static const char kCharClasses[257] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 0, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
+ 1, 1, 1, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 0, 1,
+ };
+
+ assert(c >= -1 && c < 256);
+ return (kCharClasses[c + 1] & static_cast<int>(bit)) != 0;
+}
+
+bool WastLexer::ReadNum() {
+ if (IsDigit(PeekChar())) {
+ ReadChar();
+ return MatchChar('_') || IsDigit(PeekChar()) ? ReadNum() : true;
+ }
+ return false;
+}
+
+bool WastLexer::ReadHexNum() {
+ if (IsHexDigit(PeekChar())) {
+ ReadChar();
+ return MatchChar('_') || IsHexDigit(PeekChar()) ? ReadHexNum() : true;
+ }
+ return false;
+}
+
+int WastLexer::ReadReservedChars() {
+ int count = 0;
+ while (IsReserved(PeekChar())) {
+ ReadChar();
+ ++count;
+ }
+ return count;
+}
+
+void WastLexer::ReadSign() {
+ if (PeekChar() == '+' || PeekChar() == '-') {
+ ReadChar();
+ }
+}
+
+Token WastLexer::GetNumberToken(TokenType token_type) {
+ if (ReadNum()) {
+ if (MatchChar('.')) {
+ token_type = TokenType::Float;
+ if (IsDigit(PeekChar()) && !ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (MatchChar('e') || MatchChar('E')) {
+ token_type = TokenType::Float;
+ ReadSign();
+ if (!ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (NoTrailingReservedChars()) {
+ if (token_type == TokenType::Float) {
+ return LiteralToken(token_type, LiteralType::Float);
+ } else {
+ return LiteralToken(token_type, LiteralType::Int);
+ }
+ }
+ }
+ return GetReservedToken();
+}
+
+Token WastLexer::GetHexNumberToken(TokenType token_type) {
+ if (ReadHexNum()) {
+ if (MatchChar('.')) {
+ token_type = TokenType::Float;
+ if (IsHexDigit(PeekChar()) && !ReadHexNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (MatchChar('p') || MatchChar('P')) {
+ token_type = TokenType::Float;
+ ReadSign();
+ if (!ReadNum()) {
+ return GetReservedToken();
+ }
+ }
+ if (NoTrailingReservedChars()) {
+ if (token_type == TokenType::Float) {
+ return LiteralToken(token_type, LiteralType::Hexfloat);
+ } else {
+ return LiteralToken(token_type, LiteralType::Int);
+ }
+ }
+ }
+ return GetReservedToken();
+}
+
+Token WastLexer::GetInfToken() {
+ if (MatchString("inf")) {
+ if (NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Infinity);
+ }
+ return GetReservedToken();
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetNanToken() {
+ if (MatchString("nan")) {
+ if (MatchChar(':')) {
+ if (MatchString("0x") && ReadHexNum() && NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Nan);
+ }
+ } else if (NoTrailingReservedChars()) {
+ return LiteralToken(TokenType::Float, LiteralType::Nan);
+ }
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetNameEqNumToken(string_view name, TokenType token_type) {
+ if (MatchString(name)) {
+ if (MatchString("0x")) {
+ if (ReadHexNum() && NoTrailingReservedChars()) {
+ return TextToken(token_type, name.size());
+ }
+ } else if (ReadNum() && NoTrailingReservedChars()) {
+ return TextToken(token_type, name.size());
+ }
+ }
+ return GetKeywordToken();
+}
+
+Token WastLexer::GetIdToken() {
+ ReadChar();
+ if (NoTrailingReservedChars()) {
+ return TextToken(TokenType::Reserved);
+ }
+ return TextToken(TokenType::Var);
+}
+
+Token WastLexer::GetKeywordToken() {
+ ReadReservedChars();
+ TokenInfo* info =
+ Perfect_Hash::InWordSet(token_start_, cursor_ - token_start_);
+ if (!info) {
+ return TextToken(TokenType::Reserved);
+ }
+ if (IsTokenTypeBare(info->token_type)) {
+ return BareToken(info->token_type);
+ } else if (IsTokenTypeType(info->token_type) ||
+ IsTokenTypeRefKind(info->token_type)) {
+ return Token(GetLocation(), info->token_type, info->value_type);
+ } else {
+ assert(IsTokenTypeOpcode(info->token_type));
+ return Token(GetLocation(), info->token_type, info->opcode);
+ }
+}
+
+Token WastLexer::GetReservedToken() {
+ ReadReservedChars();
+ return TextToken(TokenType::Reserved);
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wast-lexer.h b/third_party/wasm2c/src/wast-lexer.h
new file mode 100644
index 0000000000..985fb8b540
--- /dev/null
+++ b/third_party/wasm2c/src/wast-lexer.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_WAST_LEXER_H_
+#define WABT_WAST_LEXER_H_
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+
+#include "src/common.h"
+#include "src/lexer-source-line-finder.h"
+#include "src/literal.h"
+#include "src/make-unique.h"
+#include "src/opcode.h"
+#include "src/token.h"
+
+namespace wabt {
+
+class ErrorHandler;
+class LexerSource;
+class WastParser;
+
+class WastLexer {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(WastLexer);
+
+ WastLexer(std::unique_ptr<LexerSource> source, string_view filename);
+
+ // Convenience functions.
+ static std::unique_ptr<WastLexer> CreateBufferLexer(string_view filename,
+ const void* data,
+ size_t size);
+
+ Token GetToken(WastParser* parser);
+
+ // TODO(binji): Move this out of the lexer.
+ std::unique_ptr<LexerSourceLineFinder> MakeLineFinder() {
+ return MakeUnique<LexerSourceLineFinder>(source_->Clone());
+ }
+
+ private:
+ static const int kEof = -1;
+ enum class CharClass { Reserved = 1, Keyword = 2, HexDigit = 4, Digit = 8 };
+
+ Location GetLocation();
+ string_view GetText(size_t offset = 0);
+
+ Token BareToken(TokenType);
+ Token LiteralToken(TokenType, LiteralType);
+ Token TextToken(TokenType, size_t offset = 0);
+
+ int PeekChar();
+ int ReadChar();
+ bool MatchChar(char);
+ bool MatchString(string_view);
+ void Newline();
+ bool ReadBlockComment(WastParser*); // Returns false if EOF.
+ bool ReadLineComment(); // Returns false if EOF.
+ void ReadWhitespace();
+
+ static bool IsCharClass(int c, CharClass);
+ static bool IsDigit(int c) { return IsCharClass(c, CharClass::Digit); }
+ static bool IsHexDigit(int c) { return IsCharClass(c, CharClass::HexDigit); }
+ static bool IsKeyword(int c) { return IsCharClass(c, CharClass::Keyword); }
+ static bool IsReserved(int c) { return IsCharClass(c, CharClass::Reserved); }
+
+ bool ReadNum();
+ bool ReadHexNum();
+ int ReadReservedChars();
+ bool NoTrailingReservedChars() { return ReadReservedChars() == 0; }
+ void ReadSign();
+ Token GetStringToken(WastParser*);
+ Token GetNumberToken(TokenType);
+ Token GetHexNumberToken(TokenType);
+ Token GetInfToken();
+ Token GetNanToken();
+ Token GetNameEqNumToken(string_view name, TokenType);
+ Token GetIdToken();
+ Token GetKeywordToken();
+ Token GetReservedToken();
+
+ std::unique_ptr<LexerSource> source_;
+ std::string filename_;
+ int line_;
+ const char* buffer_;
+ const char* buffer_end_;
+ const char* line_start_;
+ const char* token_start_;
+ const char* cursor_;
+};
+
+} // namespace wabt
+
+#endif /* WABT_WAST_LEXER_H_ */
diff --git a/third_party/wasm2c/src/wast-parser.cc b/third_party/wasm2c/src/wast-parser.cc
new file mode 100644
index 0000000000..5954f6510d
--- /dev/null
+++ b/third_party/wasm2c/src/wast-parser.cc
@@ -0,0 +1,3278 @@
+/*
+ * 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 "src/wast-parser.h"
+
+#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
+#include "src/cast.h"
+#include "src/expr-visitor.h"
+#include "src/make-unique.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/utf8.h"
+#include "src/validator.h"
+
+#define WABT_TRACING 0
+#include "src/tracing.h"
+
+#define EXPECT(token_type) CHECK_RESULT(Expect(TokenType::token_type))
+
+namespace wabt {
+
+namespace {
+
+static const size_t kMaxErrorTokenLength = 80;
+
+bool IsPowerOfTwo(uint32_t x) {
+ return x && ((x & (x - 1)) == 0);
+}
+
+template <typename OutputIter>
+void RemoveEscapes(string_view text, OutputIter dest) {
+ // Remove surrounding quotes; if any. This may be empty if the string was
+ // invalid (e.g. if it contained a bad escape sequence).
+ if (text.size() <= 2) {
+ return;
+ }
+
+ text = text.substr(1, text.size() - 2);
+
+ const char* src = text.data();
+ const char* end = text.data() + text.size();
+
+ while (src < end) {
+ if (*src == '\\') {
+ src++;
+ switch (*src) {
+ case 'n':
+ *dest++ = '\n';
+ break;
+ case 'r':
+ *dest++ = '\r';
+ break;
+ case 't':
+ *dest++ = '\t';
+ break;
+ case '\\':
+ *dest++ = '\\';
+ break;
+ case '\'':
+ *dest++ = '\'';
+ break;
+ case '\"':
+ *dest++ = '\"';
+ break;
+ default: {
+ // The string should be validated already, so we know this is a hex
+ // sequence.
+ uint32_t hi;
+ uint32_t lo;
+ if (Succeeded(ParseHexdigit(src[0], &hi)) &&
+ Succeeded(ParseHexdigit(src[1], &lo))) {
+ *dest++ = (hi << 4) | lo;
+ } else {
+ assert(0);
+ }
+ src++;
+ break;
+ }
+ }
+ src++;
+ } else {
+ *dest++ = *src++;
+ }
+ }
+}
+
+typedef std::vector<string_view> TextVector;
+
+template <typename OutputIter>
+void RemoveEscapes(const TextVector& texts, OutputIter out) {
+ for (string_view text : texts)
+ RemoveEscapes(text, out);
+}
+
+bool IsPlainInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Unreachable:
+ case TokenType::Nop:
+ case TokenType::Drop:
+ case TokenType::Select:
+ case TokenType::Br:
+ case TokenType::BrIf:
+ case TokenType::BrTable:
+ case TokenType::Return:
+ case TokenType::ReturnCall:
+ case TokenType::ReturnCallIndirect:
+ case TokenType::Call:
+ case TokenType::CallIndirect:
+ case TokenType::CallRef:
+ case TokenType::LocalGet:
+ case TokenType::LocalSet:
+ case TokenType::LocalTee:
+ case TokenType::GlobalGet:
+ case TokenType::GlobalSet:
+ case TokenType::Load:
+ case TokenType::Store:
+ case TokenType::Const:
+ case TokenType::Unary:
+ case TokenType::Binary:
+ case TokenType::Compare:
+ case TokenType::Convert:
+ case TokenType::MemoryCopy:
+ case TokenType::DataDrop:
+ case TokenType::MemoryFill:
+ case TokenType::MemoryGrow:
+ case TokenType::MemoryInit:
+ case TokenType::MemorySize:
+ case TokenType::TableCopy:
+ case TokenType::ElemDrop:
+ case TokenType::TableInit:
+ case TokenType::TableGet:
+ case TokenType::TableSet:
+ case TokenType::TableGrow:
+ case TokenType::TableSize:
+ case TokenType::TableFill:
+ case TokenType::Throw:
+ case TokenType::Rethrow:
+ case TokenType::RefFunc:
+ case TokenType::RefNull:
+ case TokenType::RefIsNull:
+ case TokenType::AtomicLoad:
+ case TokenType::AtomicStore:
+ case TokenType::AtomicRmw:
+ case TokenType::AtomicRmwCmpxchg:
+ case TokenType::AtomicNotify:
+ case TokenType::AtomicFence:
+ case TokenType::AtomicWait:
+ case TokenType::Ternary:
+ case TokenType::SimdLaneOp:
+ case TokenType::SimdLoadLane:
+ case TokenType::SimdStoreLane:
+ case TokenType::SimdShuffleOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsBlockInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Block:
+ case TokenType::Loop:
+ case TokenType::If:
+ case TokenType::Try:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsPlainOrBlockInstr(TokenType token_type) {
+ return IsPlainInstr(token_type) || IsBlockInstr(token_type);
+}
+
+bool IsExpr(TokenTypePair pair) {
+ return pair[0] == TokenType::Lpar && IsPlainOrBlockInstr(pair[1]);
+}
+
+bool IsInstr(TokenTypePair pair) {
+ return IsPlainOrBlockInstr(pair[0]) || IsExpr(pair);
+}
+
+bool IsCatch(TokenType token_type) {
+ return token_type == TokenType::Catch || token_type == TokenType::CatchAll;
+}
+
+bool IsModuleField(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar) {
+ return false;
+ }
+
+ switch (pair[1]) {
+ case TokenType::Data:
+ case TokenType::Elem:
+ case TokenType::Tag:
+ case TokenType::Export:
+ case TokenType::Func:
+ case TokenType::Type:
+ case TokenType::Global:
+ case TokenType::Import:
+ case TokenType::Memory:
+ case TokenType::Start:
+ case TokenType::Table:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsCommand(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar) {
+ return false;
+ }
+
+ switch (pair[1]) {
+ case TokenType::AssertExhaustion:
+ case TokenType::AssertInvalid:
+ case TokenType::AssertMalformed:
+ case TokenType::AssertReturn:
+ case TokenType::AssertTrap:
+ case TokenType::AssertUnlinkable:
+ case TokenType::Get:
+ case TokenType::Invoke:
+ case TokenType::Input:
+ case TokenType::Module:
+ case TokenType::Output:
+ case TokenType::Register:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsEmptySignature(const FuncSignature& sig) {
+ return sig.result_types.empty() && sig.param_types.empty();
+}
+
+bool ResolveFuncTypeWithEmptySignature(const Module& module,
+ FuncDeclaration* decl) {
+ // Resolve func type variables where the signature was not specified
+ // explicitly, e.g.: (func (type 1) ...)
+ if (decl->has_func_type && IsEmptySignature(decl->sig)) {
+ const FuncType* func_type = module.GetFuncType(decl->type_var);
+ if (func_type) {
+ decl->sig = func_type->sig;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ResolveImplicitlyDefinedFunctionType(const Location& loc,
+ Module* module,
+ const FuncDeclaration& decl) {
+ // Resolve implicitly defined function types, e.g.: (func (param i32) ...)
+ if (!decl.has_func_type) {
+ Index func_type_index = module->GetFuncTypeIndex(decl.sig);
+ if (func_type_index == kInvalidIndex) {
+ auto func_type_field = MakeUnique<TypeModuleField>(loc);
+ auto func_type = MakeUnique<FuncType>();
+ func_type->sig = decl.sig;
+ func_type_field->type = std::move(func_type);
+ module->AppendField(std::move(func_type_field));
+ }
+ }
+}
+
+Result CheckTypeIndex(const Location& loc,
+ Type actual,
+ Type expected,
+ const char* desc,
+ Index index,
+ const char* index_kind,
+ Errors* errors) {
+ // Types must match exactly; no subtyping should be allowed.
+ if (actual != expected) {
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("type mismatch for %s %" PRIindex
+ " of %s. got %s, expected %s",
+ index_kind, index, desc, actual.GetName(),
+ expected.GetName()));
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+Result CheckTypes(const Location& loc,
+ const TypeVector& actual,
+ const TypeVector& expected,
+ const char* desc,
+ const char* index_kind,
+ Errors* errors) {
+ Result result = Result::Ok;
+ if (actual.size() == expected.size()) {
+ for (size_t i = 0; i < actual.size(); ++i) {
+ result |= CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind,
+ errors);
+ }
+ } else {
+ errors->emplace_back(
+ ErrorLevel::Error, loc,
+ StringPrintf("expected %" PRIzd " %ss, got %" PRIzd, expected.size(),
+ index_kind, actual.size()));
+ result = Result::Error;
+ }
+ return result;
+}
+
+Result CheckFuncTypeVarMatchesExplicit(const Location& loc,
+ const Module& module,
+ const FuncDeclaration& decl,
+ Errors* errors) {
+ Result result = Result::Ok;
+ if (decl.has_func_type) {
+ const FuncType* func_type = module.GetFuncType(decl.type_var);
+ if (func_type) {
+ result |=
+ CheckTypes(loc, decl.sig.result_types, func_type->sig.result_types,
+ "function", "result", errors);
+ result |=
+ CheckTypes(loc, decl.sig.param_types, func_type->sig.param_types,
+ "function", "argument", errors);
+ } else if (!(decl.sig.param_types.empty() &&
+ decl.sig.result_types.empty())) {
+ // We want to check whether the function type at the explicit index
+ // matches the given param and result types. If they were omitted then
+ // they'll be resolved automatically (see
+ // ResolveFuncTypeWithEmptySignature), but if they are provided then we
+ // have to check. If we get here then the type var is invalid, so we
+ // can't check whether they match.
+ errors->emplace_back(ErrorLevel::Error, loc,
+ StringPrintf("invalid func type index %" PRIindex,
+ decl.type_var.index()));
+ result = Result::Error;
+ }
+ }
+ return result;
+}
+
+bool IsInlinableFuncSignature(const FuncSignature& sig) {
+ return sig.GetNumParams() == 0 && sig.GetNumResults() <= 1;
+}
+
+class ResolveFuncTypesExprVisitorDelegate : public ExprVisitor::DelegateNop {
+ public:
+ explicit ResolveFuncTypesExprVisitorDelegate(Module* module, Errors* errors)
+ : module_(module), errors_(errors) {}
+
+ void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) {
+ ResolveFuncTypeWithEmptySignature(*module_, decl);
+ if (!IsInlinableFuncSignature(decl->sig)) {
+ ResolveImplicitlyDefinedFunctionType(loc, module_, *decl);
+ }
+ }
+
+ Result BeginBlockExpr(BlockExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result BeginIfExpr(IfExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->true_.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->true_.decl, errors_);
+ }
+
+ Result BeginLoopExpr(LoopExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result BeginTryExpr(TryExpr* expr) override {
+ ResolveBlockDeclaration(expr->loc, &expr->block.decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
+ expr->block.decl, errors_);
+ }
+
+ Result OnCallIndirectExpr(CallIndirectExpr* expr) override {
+ ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
+ ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
+ errors_);
+ }
+
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) override {
+ ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
+ ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
+ return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
+ errors_);
+ }
+
+ private:
+ Module* module_;
+ Errors* errors_;
+};
+
+Result ResolveFuncTypes(Module* module, Errors* errors) {
+ Result result = Result::Ok;
+ for (ModuleField& field : module->fields) {
+ Func* func = nullptr;
+ FuncDeclaration* decl = nullptr;
+ if (auto* func_field = dyn_cast<FuncModuleField>(&field)) {
+ func = &func_field->func;
+ decl = &func->decl;
+ } else if (auto* tag_field = dyn_cast<TagModuleField>(&field)) {
+ decl = &tag_field->tag.decl;
+ } else if (auto* import_field = dyn_cast<ImportModuleField>(&field)) {
+ if (auto* func_import =
+ dyn_cast<FuncImport>(import_field->import.get())) {
+ // Only check the declaration, not the function itself, since it is an
+ // import.
+ decl = &func_import->func.decl;
+ } else if (auto* tag_import =
+ dyn_cast<TagImport>(import_field->import.get())) {
+ decl = &tag_import->tag.decl;
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ bool has_func_type_and_empty_signature = false;
+
+ if (decl) {
+ has_func_type_and_empty_signature =
+ ResolveFuncTypeWithEmptySignature(*module, decl);
+ ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl);
+ result |=
+ CheckFuncTypeVarMatchesExplicit(field.loc, *module, *decl, errors);
+ }
+
+ if (func) {
+ if (has_func_type_and_empty_signature) {
+ // The call to ResolveFuncTypeWithEmptySignature may have updated the
+ // function signature so there are parameters. Since parameters and
+ // local variables share the same index space, we need to increment the
+ // local indexes bound to a given name by the number of parameters in
+ // the function.
+ for (auto& pair: func->bindings) {
+ pair.second.index += func->GetNumParams();
+ }
+ }
+
+ ResolveFuncTypesExprVisitorDelegate delegate(module, errors);
+ ExprVisitor visitor(&delegate);
+ result |= visitor.VisitFunc(func);
+ }
+ }
+ return result;
+}
+
+void AppendInlineExportFields(Module* module,
+ ModuleFieldList* fields,
+ Index index) {
+ Location last_field_loc = module->fields.back().loc;
+
+ for (ModuleField& field : *fields) {
+ auto* export_field = cast<ExportModuleField>(&field);
+ export_field->export_.var = Var(index, last_field_loc);
+ }
+
+ module->AppendFields(fields);
+}
+
+} // End of anonymous namespace
+
+WastParser::WastParser(WastLexer* lexer,
+ Errors* errors,
+ WastParseOptions* options)
+ : lexer_(lexer), errors_(errors), options_(options) {}
+
+void WastParser::Error(Location loc, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, loc, buffer);
+}
+
+Token WastParser::GetToken() {
+ if (tokens_.empty()) {
+ tokens_.push_back(lexer_->GetToken(this));
+ }
+ return tokens_.front();
+}
+
+Location WastParser::GetLocation() {
+ return GetToken().loc;
+}
+
+TokenType WastParser::Peek(size_t n) {
+ while (tokens_.size() <= n) {
+ Token cur = lexer_->GetToken(this);
+ if (cur.token_type() != TokenType::LparAnn) {
+ tokens_.push_back(cur);
+ } else {
+ // Custom annotation. For now, discard until matching Rpar.
+ if (!options_->features.annotations_enabled()) {
+ Error(cur.loc, "annotations not enabled: %s", cur.to_string().c_str());
+ tokens_.push_back(Token(cur.loc, TokenType::Invalid));
+ continue;
+ }
+ int indent = 1;
+ while (indent > 0) {
+ cur = lexer_->GetToken(this);
+ switch (cur.token_type()) {
+ case TokenType::Lpar:
+ case TokenType::LparAnn:
+ indent++;
+ break;
+
+ case TokenType::Rpar:
+ indent--;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return tokens_.at(n).token_type();
+}
+
+TokenTypePair WastParser::PeekPair() {
+ return TokenTypePair{{Peek(), Peek(1)}};
+}
+
+bool WastParser::PeekMatch(TokenType type) {
+ return Peek() == type;
+}
+
+bool WastParser::PeekMatchLpar(TokenType type) {
+ return Peek() == TokenType::Lpar && Peek(1) == type;
+}
+
+bool WastParser::PeekMatchExpr() {
+ return IsExpr(PeekPair());
+}
+
+bool WastParser::Match(TokenType type) {
+ if (PeekMatch(type)) {
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+bool WastParser::MatchLpar(TokenType type) {
+ if (PeekMatchLpar(type)) {
+ Consume();
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+Result WastParser::Expect(TokenType type) {
+ if (!Match(type)) {
+ Token token = Consume();
+ Error(token.loc, "unexpected token %s, expected %s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str(),
+ GetTokenTypeName(type));
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Token WastParser::Consume() {
+ assert(!tokens_.empty());
+ Token token = tokens_.front();
+ tokens_.pop_front();
+ return token;
+}
+
+Result WastParser::Synchronize(SynchronizeFunc func) {
+ static const int kMaxConsumed = 10;
+ for (int i = 0; i < kMaxConsumed; ++i) {
+ if (func(PeekPair())) {
+ return Result::Ok;
+ }
+
+ Token token = Consume();
+ if (token.token_type() == TokenType::Reserved) {
+ Error(token.loc, "unexpected token %s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str());
+ }
+ }
+
+ return Result::Error;
+}
+
+void WastParser::ErrorUnlessOpcodeEnabled(const Token& token) {
+ Opcode opcode = token.opcode();
+ if (!opcode.IsEnabled(options_->features)) {
+ Error(token.loc, "opcode not allowed: %s", opcode.GetName());
+ }
+}
+
+Result WastParser::ErrorExpected(const std::vector<std::string>& expected,
+ const char* example) {
+ Token token = Consume();
+ std::string expected_str;
+ if (!expected.empty()) {
+ expected_str = ", expected ";
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (i != 0) {
+ if (i == expected.size() - 1) {
+ expected_str += " or ";
+ } else {
+ expected_str += ", ";
+ }
+ }
+
+ expected_str += expected[i];
+ }
+
+ if (example) {
+ expected_str += " (e.g. ";
+ expected_str += example;
+ expected_str += ")";
+ }
+ }
+
+ Error(token.loc, "unexpected token \"%s\"%s.",
+ token.to_string_clamp(kMaxErrorTokenLength).c_str(),
+ expected_str.c_str());
+ return Result::Error;
+}
+
+Result WastParser::ErrorIfLpar(const std::vector<std::string>& expected,
+ const char* example) {
+ if (Match(TokenType::Lpar)) {
+ GetToken();
+ return ErrorExpected(expected, example);
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseBindVarOpt(std::string* name) {
+ WABT_TRACE(ParseBindVarOpt);
+ if (!PeekMatch(TokenType::Var)) {
+ return false;
+ }
+ Token token = Consume();
+ *name = token.text().to_string();
+ return true;
+}
+
+Result WastParser::ParseVar(Var* out_var) {
+ WABT_TRACE(ParseVar);
+ if (PeekMatch(TokenType::Nat)) {
+ Token token = Consume();
+ string_view sv = token.literal().text;
+ uint64_t index = kInvalidIndex;
+ if (Failed(ParseUint64(sv.begin(), sv.end(), &index))) {
+ // Print an error, but don't fail parsing.
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ *out_var = Var(index, token.loc);
+ return Result::Ok;
+ } else if (PeekMatch(TokenType::Var)) {
+ Token token = Consume();
+ *out_var = Var(token.text(), token.loc);
+ return Result::Ok;
+ } else {
+ return ErrorExpected({"a numeric index", "a name"}, "12 or $foo");
+ }
+}
+
+bool WastParser::ParseVarOpt(Var* out_var, Var default_var) {
+ WABT_TRACE(ParseVarOpt);
+ if (PeekMatch(TokenType::Nat) || PeekMatch(TokenType::Var)) {
+ Result result = ParseVar(out_var);
+ // Should always succeed, the only way it could fail is if the token
+ // doesn't match.
+ assert(Succeeded(result));
+ WABT_USE(result);
+ return true;
+ } else {
+ *out_var = default_var;
+ return false;
+ }
+}
+
+Result WastParser::ParseOffsetExpr(ExprList* out_expr_list) {
+ WABT_TRACE(ParseOffsetExpr);
+ if (!ParseOffsetExprOpt(out_expr_list)) {
+ return ErrorExpected({"an offset expr"}, "(i32.const 123)");
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseOffsetExprOpt(ExprList* out_expr_list) {
+ WABT_TRACE(ParseOffsetExprOpt);
+ if (MatchLpar(TokenType::Offset)) {
+ CHECK_RESULT(ParseTerminatingInstrList(out_expr_list));
+ EXPECT(Rpar);
+ return true;
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(out_expr_list));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Result WastParser::ParseTextList(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextList);
+ if (!ParseTextListOpt(out_data)) {
+ // TODO(binji): Add error message here.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+bool WastParser::ParseTextListOpt(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextListOpt);
+ TextVector texts;
+ while (PeekMatch(TokenType::Text))
+ texts.push_back(Consume().text());
+
+ RemoveEscapes(texts, std::back_inserter(*out_data));
+ return !texts.empty();
+}
+
+Result WastParser::ParseVarList(VarVector* out_var_list) {
+ WABT_TRACE(ParseVarList);
+ Var var;
+ while (ParseVarOpt(&var)) {
+ out_var_list->emplace_back(var);
+ }
+ if (out_var_list->empty()) {
+ return ErrorExpected({"a var"}, "12 or $foo");
+ } else {
+ return Result::Ok;
+ }
+}
+
+bool WastParser::ParseElemExprOpt(ElemExpr* out_elem_expr) {
+ Location loc = GetLocation();
+ bool item = MatchLpar(TokenType::Item);
+ bool lpar = Match(TokenType::Lpar);
+ if (Match(TokenType::RefNull)) {
+ if (!(options_->features.bulk_memory_enabled() ||
+ options_->features.reference_types_enabled())) {
+ Error(loc, "ref.null not allowed");
+ }
+ Type type;
+ CHECK_RESULT(ParseRefKind(&type));
+ *out_elem_expr = ElemExpr(type);
+ } else if (Match(TokenType::RefFunc)) {
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ *out_elem_expr = ElemExpr(var);
+ } else {
+ return false;
+ }
+ if (lpar) {
+ EXPECT(Rpar);
+ }
+ if (item) {
+ EXPECT(Rpar);
+ }
+ return true;
+}
+
+bool WastParser::ParseElemExprListOpt(ElemExprVector* out_list) {
+ ElemExpr elem_expr;
+ while (ParseElemExprOpt(&elem_expr)) {
+ out_list->push_back(elem_expr);
+ }
+ return !out_list->empty();
+}
+
+bool WastParser::ParseElemExprVarListOpt(ElemExprVector* out_list) {
+ WABT_TRACE(ParseElemExprVarListOpt);
+ Var var;
+ while (ParseVarOpt(&var)) {
+ out_list->emplace_back(var);
+ }
+ return !out_list->empty();
+}
+
+Result WastParser::ParseValueType(Type* out_type) {
+ WABT_TRACE(ParseValueType);
+ if (!PeekMatch(TokenType::ValueType)) {
+ return ErrorExpected({"i32", "i64", "f32", "f64", "v128", "externref"});
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ bool is_enabled;
+ switch (type) {
+ case Type::V128:
+ is_enabled = options_->features.simd_enabled();
+ break;
+ case Type::FuncRef:
+ case Type::ExternRef:
+ is_enabled = options_->features.reference_types_enabled();
+ break;
+ default:
+ is_enabled = true;
+ break;
+ }
+
+ if (!is_enabled) {
+ Error(token.loc, "value type not allowed: %s", type.GetName());
+ return Result::Error;
+ }
+
+ *out_type = type;
+ return Result::Ok;
+}
+
+Result WastParser::ParseValueTypeList(TypeVector* out_type_list) {
+ WABT_TRACE(ParseValueTypeList);
+ while (PeekMatch(TokenType::ValueType))
+ out_type_list->push_back(Consume().type());
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseRefKind(Type* out_type) {
+ WABT_TRACE(ParseRefKind);
+ if (!IsTokenTypeRefKind(Peek())) {
+ return ErrorExpected({"func", "extern", "exn"});
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+
+ if ((type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) ||
+ ((type == Type::Struct || type == Type::Array) &&
+ !options_->features.gc_enabled())) {
+ Error(token.loc, "value type not allowed: %s", type.GetName());
+ return Result::Error;
+ }
+
+ *out_type = type;
+ return Result::Ok;
+}
+
+Result WastParser::ParseRefType(Type* out_type) {
+ WABT_TRACE(ParseRefType);
+ if (!PeekMatch(TokenType::ValueType)) {
+ return ErrorExpected({"funcref", "externref"});
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ if (type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) {
+ Error(token.loc, "value type not allowed: %s", type.GetName());
+ return Result::Error;
+ }
+
+ *out_type = type;
+ return Result::Ok;
+}
+
+bool WastParser::ParseRefTypeOpt(Type* out_type) {
+ WABT_TRACE(ParseRefTypeOpt);
+ if (!PeekMatch(TokenType::ValueType)) {
+ return false;
+ }
+
+ Token token = Consume();
+ Type type = token.type();
+ if (type == Type::ExternRef &&
+ !options_->features.reference_types_enabled()) {
+ return false;
+ }
+
+ *out_type = type;
+ return true;
+}
+
+Result WastParser::ParseQuotedText(std::string* text) {
+ WABT_TRACE(ParseQuotedText);
+ if (!PeekMatch(TokenType::Text)) {
+ return ErrorExpected({"a quoted string"}, "\"foo\"");
+ }
+
+ Token token = Consume();
+ RemoveEscapes(token.text(), std::back_inserter(*text));
+ if (!IsValidUtf8(text->data(), text->length())) {
+ Error(token.loc, "quoted string has an invalid utf-8 encoding");
+ }
+ return Result::Ok;
+}
+
+bool WastParser::ParseOffsetOpt(Address* out_offset) {
+ WABT_TRACE(ParseOffsetOpt);
+ if (PeekMatch(TokenType::OffsetEqNat)) {
+ Token token = Consume();
+ uint64_t offset64;
+ string_view sv = token.text();
+ if (Failed(ParseInt64(sv.begin(), sv.end(), &offset64,
+ ParseIntType::SignedAndUnsigned))) {
+ Error(token.loc, "invalid offset \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+ // FIXME: make this depend on the current memory.
+ if (offset64 > UINT32_MAX) {
+ Error(token.loc, "offset must be less than or equal to 0xffffffff");
+ }
+ *out_offset = offset64;
+ return true;
+ } else {
+ *out_offset = 0;
+ return false;
+ }
+}
+
+bool WastParser::ParseAlignOpt(Address* out_align) {
+ WABT_TRACE(ParseAlignOpt);
+ if (PeekMatch(TokenType::AlignEqNat)) {
+ Token token = Consume();
+ string_view sv = token.text();
+ if (Failed(ParseInt64(sv.begin(), sv.end(), out_align,
+ ParseIntType::UnsignedOnly))) {
+ Error(token.loc, "invalid alignment \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ if (!IsPowerOfTwo(*out_align)) {
+ Error(token.loc, "alignment must be power-of-two");
+ }
+
+ return true;
+ } else {
+ *out_align = WABT_USE_NATURAL_ALIGNMENT;
+ return false;
+ }
+}
+
+Result WastParser::ParseLimitsIndex(Limits* out_limits) {
+ WABT_TRACE(ParseLimitsIndex);
+
+ if (PeekMatch(TokenType::ValueType)) {
+ if (GetToken().type() == Type::I64) {
+ Consume();
+ out_limits->is_64 = true;
+ } else if (GetToken().type() == Type::I32) {
+ Consume();
+ out_limits->is_64 = false;
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseLimits(Limits* out_limits) {
+ WABT_TRACE(ParseLimits);
+
+ CHECK_RESULT(ParseNat(&out_limits->initial, out_limits->is_64));
+ if (PeekMatch(TokenType::Nat)) {
+ CHECK_RESULT(ParseNat(&out_limits->max, out_limits->is_64));
+ out_limits->has_max = true;
+ } else {
+ out_limits->has_max = false;
+ }
+
+ if (Match(TokenType::Shared)) {
+ out_limits->is_shared = true;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseNat(uint64_t* out_nat, bool is_64) {
+ WABT_TRACE(ParseNat);
+ if (!PeekMatch(TokenType::Nat)) {
+ return ErrorExpected({"a natural number"}, "123");
+ }
+
+ Token token = Consume();
+ string_view sv = token.literal().text;
+ if (Failed(ParseUint64(sv.begin(), sv.end(), out_nat)) ||
+ (!is_64 && *out_nat > 0xffffffffu)) {
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseModule(std::unique_ptr<Module>* out_module) {
+ WABT_TRACE(ParseModule);
+ auto module = MakeUnique<Module>();
+
+ if (PeekMatchLpar(TokenType::Module)) {
+ // Starts with "(module". Allow text and binary modules, but no quoted
+ // modules.
+ CommandPtr command;
+ CHECK_RESULT(ParseModuleCommand(nullptr, &command));
+ auto module_command = cast<ModuleCommand>(std::move(command));
+ *module = std::move(module_command->module);
+ } else if (IsModuleField(PeekPair())) {
+ // Parse an inline module (i.e. one with no surrounding (module)).
+ CHECK_RESULT(ParseModuleFieldList(module.get()));
+ } else {
+ ConsumeIfLpar();
+ ErrorExpected({"a module field", "a module"});
+ }
+
+ EXPECT(Eof);
+ if (errors_->size() == 0) {
+ *out_module = std::move(module);
+ return Result::Ok;
+ } else {
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseScript(std::unique_ptr<Script>* out_script) {
+ WABT_TRACE(ParseScript);
+ auto script = MakeUnique<Script>();
+
+ // Don't consume the Lpar yet, even though it is required. This way the
+ // sub-parser functions (e.g. ParseFuncModuleField) can consume it and keep
+ // the parsing structure more regular.
+ if (IsModuleField(PeekPair())) {
+ // Parse an inline module (i.e. one with no surrounding (module)).
+ auto command = MakeUnique<ModuleCommand>();
+ command->module.loc = GetLocation();
+ CHECK_RESULT(ParseModuleFieldList(&command->module));
+ script->commands.emplace_back(std::move(command));
+ } else if (IsCommand(PeekPair())) {
+ CHECK_RESULT(ParseCommandList(script.get(), &script->commands));
+ } else {
+ ConsumeIfLpar();
+ ErrorExpected({"a module field", "a command"});
+ }
+
+ EXPECT(Eof);
+ if (errors_->size() == 0) {
+ *out_script = std::move(script);
+ return Result::Ok;
+ } else {
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseModuleFieldList(Module* module) {
+ WABT_TRACE(ParseModuleFieldList);
+ while (IsModuleField(PeekPair())) {
+ if (Failed(ParseModuleField(module))) {
+ CHECK_RESULT(Synchronize(IsModuleField));
+ }
+ }
+ CHECK_RESULT(ResolveFuncTypes(module, errors_));
+ CHECK_RESULT(ResolveNamesModule(module, errors_));
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleField(Module* module) {
+ WABT_TRACE(ParseModuleField);
+ switch (Peek(1)) {
+ case TokenType::Data: return ParseDataModuleField(module);
+ case TokenType::Elem: return ParseElemModuleField(module);
+ case TokenType::Tag: return ParseTagModuleField(module);
+ case TokenType::Export: return ParseExportModuleField(module);
+ case TokenType::Func: return ParseFuncModuleField(module);
+ case TokenType::Type: return ParseTypeModuleField(module);
+ case TokenType::Global: return ParseGlobalModuleField(module);
+ case TokenType::Import: return ParseImportModuleField(module);
+ case TokenType::Memory: return ParseMemoryModuleField(module);
+ case TokenType::Start: return ParseStartModuleField(module);
+ case TokenType::Table: return ParseTableModuleField(module);
+ default:
+ assert(
+ !"ParseModuleField should only be called if IsModuleField() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseDataModuleField(Module* module) {
+ WABT_TRACE(ParseDataModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Data);
+ std::string name;
+ ParseBindVarOpt(&name);
+ auto field = MakeUnique<DataSegmentModuleField>(loc, name);
+
+ if (PeekMatchLpar(TokenType::Memory)) {
+ EXPECT(Lpar);
+ EXPECT(Memory);
+ CHECK_RESULT(ParseVar(&field->data_segment.memory_var));
+ EXPECT(Rpar);
+ CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
+ } else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) {
+ CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
+ } else if (!ParseOffsetExprOpt(&field->data_segment.offset)) {
+ if (!options_->features.bulk_memory_enabled()) {
+ Error(loc, "passive data segments are not allowed");
+ return Result::Error;
+ }
+
+ field->data_segment.kind = SegmentKind::Passive;
+ }
+
+ ParseTextListOpt(&field->data_segment.data);
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseElemModuleField(Module* module) {
+ WABT_TRACE(ParseElemModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Elem);
+
+ // With MVP text format the name here was intended to refer to the table
+ // that the elem segment was part of, but we never did anything with this name
+ // since there was only one table anyway.
+ // With bulk-memory enabled this introduces a new name for the particular
+ // elem segment.
+ std::string initial_name;
+ bool has_name = ParseBindVarOpt(&initial_name);
+
+ std::string segment_name = initial_name;
+ if (!options_->features.bulk_memory_enabled()) {
+ segment_name = "";
+ }
+ auto field = MakeUnique<ElemSegmentModuleField>(loc, segment_name);
+ if (options_->features.reference_types_enabled() &&
+ Match(TokenType::Declare)) {
+ field->elem_segment.kind = SegmentKind::Declared;
+ }
+
+ // Optional table specifier
+ if (options_->features.bulk_memory_enabled()) {
+ if (PeekMatchLpar(TokenType::Table)) {
+ EXPECT(Lpar);
+ EXPECT(Table);
+ CHECK_RESULT(ParseVar(&field->elem_segment.table_var));
+ EXPECT(Rpar);
+ } else {
+ ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
+ }
+ } else {
+ if (has_name) {
+ field->elem_segment.table_var = Var(initial_name, loc);
+ } else {
+ ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
+ }
+ }
+
+ // Parse offset expression, if not declared/passive segment.
+ if (options_->features.bulk_memory_enabled()) {
+ if (field->elem_segment.kind != SegmentKind::Declared &&
+ !ParseOffsetExprOpt(&field->elem_segment.offset)) {
+ field->elem_segment.kind = SegmentKind::Passive;
+ }
+ } else {
+ CHECK_RESULT(ParseOffsetExpr(&field->elem_segment.offset));
+ }
+
+ if (ParseRefTypeOpt(&field->elem_segment.elem_type)) {
+ ParseElemExprListOpt(&field->elem_segment.elem_exprs);
+ } else {
+ field->elem_segment.elem_type = Type::FuncRef;
+ if (PeekMatch(TokenType::Func)) {
+ EXPECT(Func);
+ }
+ ParseElemExprVarListOpt(&field->elem_segment.elem_exprs);
+ }
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseTagModuleField(Module* module) {
+ WABT_TRACE(ParseTagModuleField);
+ EXPECT(Lpar);
+ auto field = MakeUnique<TagModuleField>(GetLocation());
+ EXPECT(Tag);
+ ParseBindVarOpt(&field->tag.name);
+ CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig));
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportModuleField(Module* module) {
+ WABT_TRACE(ParseExportModuleField);
+ EXPECT(Lpar);
+ auto field = MakeUnique<ExportModuleField>(GetLocation());
+ EXPECT(Export);
+ CHECK_RESULT(ParseQuotedText(&field->export_.name));
+ CHECK_RESULT(ParseExportDesc(&field->export_));
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncModuleField(Module* module) {
+ WABT_TRACE(ParseFuncModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Func);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Func));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = MakeUnique<FuncImport>(name);
+ Func& func = import->func;
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseTypeUseOpt(&func.decl));
+ CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
+ CHECK_RESULT(ErrorIfLpar({"type", "param", "result"}));
+ auto field =
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = MakeUnique<FuncModuleField>(loc, name);
+ Func& func = field->func;
+ CHECK_RESULT(ParseTypeUseOpt(&func.decl));
+ CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
+ TypeVector local_types;
+ CHECK_RESULT(ParseBoundValueTypeList(TokenType::Local, &local_types,
+ &func.bindings, func.GetNumParams()));
+ func.local_types.Set(local_types);
+ CHECK_RESULT(ParseTerminatingInstrList(&func.exprs));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->funcs.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeModuleField(Module* module) {
+ WABT_TRACE(ParseTypeModuleField);
+ EXPECT(Lpar);
+ auto field = MakeUnique<TypeModuleField>(GetLocation());
+ EXPECT(Type);
+
+ std::string name;
+ ParseBindVarOpt(&name);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+
+ if (Match(TokenType::Func)) {
+ auto func_type = MakeUnique<FuncType>(name);
+ BindingHash bindings;
+ CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ field->type = std::move(func_type);
+ } else if (Match(TokenType::Struct)) {
+ if (!options_->features.gc_enabled()) {
+ Error(loc, "struct not allowed");
+ return Result::Error;
+ }
+ auto struct_type = MakeUnique<StructType>(name);
+ CHECK_RESULT(ParseFieldList(&struct_type->fields));
+ field->type = std::move(struct_type);
+ } else if (Match(TokenType::Array)) {
+ if (!options_->features.gc_enabled()) {
+ Error(loc, "array type not allowed");
+ }
+ auto array_type = MakeUnique<ArrayType>(name);
+ CHECK_RESULT(ParseField(&array_type->field));
+ field->type = std::move(array_type);
+ } else {
+ return ErrorExpected({"func", "struct", "array"});
+ }
+
+ EXPECT(Rpar);
+ EXPECT(Rpar);
+ module->AppendField(std::move(field));
+ return Result::Ok;
+}
+
+Result WastParser::ParseField(Field* field) {
+ WABT_TRACE(ParseField);
+ auto parse_mut_valuetype = [&]() -> Result {
+ // TODO: Share with ParseGlobalType?
+ if (MatchLpar(TokenType::Mut)) {
+ field->mutable_ = true;
+ CHECK_RESULT(ParseValueType(&field->type));
+ EXPECT(Rpar);
+ } else {
+ field->mutable_ = false;
+ CHECK_RESULT(ParseValueType(&field->type));
+ }
+ return Result::Ok;
+ };
+
+ if (MatchLpar(TokenType::Field)) {
+ ParseBindVarOpt(&field->name);
+ CHECK_RESULT(parse_mut_valuetype());
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(parse_mut_valuetype());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseFieldList(std::vector<Field>* fields) {
+ WABT_TRACE(ParseFieldList);
+ while (PeekMatch(TokenType::ValueType) || PeekMatch(TokenType::Lpar)) {
+ Field field;
+ CHECK_RESULT(ParseField(&field));
+ fields->push_back(field);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalModuleField(Module* module) {
+ WABT_TRACE(ParseGlobalModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Global);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Global));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = MakeUnique<GlobalImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseGlobalType(&import->global));
+ auto field =
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = MakeUnique<GlobalModuleField>(loc, name);
+ CHECK_RESULT(ParseGlobalType(&field->global));
+ CHECK_RESULT(ParseTerminatingInstrList(&field->global.init_expr));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->globals.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseImportModuleField(Module* module) {
+ WABT_TRACE(ParseImportModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ CheckImportOrdering(module);
+ EXPECT(Import);
+ std::string module_name;
+ std::string field_name;
+ CHECK_RESULT(ParseQuotedText(&module_name));
+ CHECK_RESULT(ParseQuotedText(&field_name));
+ EXPECT(Lpar);
+
+ std::unique_ptr<ImportModuleField> field;
+ std::string name;
+
+ switch (Peek()) {
+ case TokenType::Func: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = MakeUnique<FuncImport>(name);
+ if (PeekMatchLpar(TokenType::Type)) {
+ import->func.decl.has_func_type = true;
+ CHECK_RESULT(ParseTypeUseOpt(&import->func.decl));
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(
+ ParseFuncSignature(&import->func.decl.sig, &import->func.bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ EXPECT(Rpar);
+ }
+ field = MakeUnique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Table: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = MakeUnique<TableImport>(name);
+ CHECK_RESULT(ParseLimits(&import->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&import->table.elem_type));
+ EXPECT(Rpar);
+ field = MakeUnique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Memory: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = MakeUnique<MemoryImport>(name);
+ CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
+ CHECK_RESULT(ParseLimits(&import->memory.page_limits));
+ EXPECT(Rpar);
+ field = MakeUnique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Global: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = MakeUnique<GlobalImport>(name);
+ CHECK_RESULT(ParseGlobalType(&import->global));
+ EXPECT(Rpar);
+ field = MakeUnique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ case TokenType::Tag: {
+ Consume();
+ ParseBindVarOpt(&name);
+ auto import = MakeUnique<TagImport>(name);
+ CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig));
+ EXPECT(Rpar);
+ field = MakeUnique<ImportModuleField>(std::move(import), loc);
+ break;
+ }
+
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+
+ field->import->module_name = module_name;
+ field->import->field_name = field_name;
+
+ module->AppendField(std::move(field));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseMemoryModuleField(Module* module) {
+ WABT_TRACE(ParseMemoryModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Memory);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Memory));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = MakeUnique<MemoryImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
+ CHECK_RESULT(ParseLimits(&import->memory.page_limits));
+ auto field =
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else {
+ auto field = MakeUnique<MemoryModuleField>(loc, name);
+ CHECK_RESULT(ParseLimitsIndex(&field->memory.page_limits));
+ if (MatchLpar(TokenType::Data)) {
+ auto data_segment_field = MakeUnique<DataSegmentModuleField>(loc);
+ DataSegment& data_segment = data_segment_field->data_segment;
+ data_segment.memory_var = Var(module->memories.size());
+ data_segment.offset.push_back(MakeUnique<ConstExpr>(
+ field->memory.page_limits.is_64 ? Const::I64(0) : Const::I32(0)));
+ data_segment.offset.back().loc = loc;
+ ParseTextListOpt(&data_segment.data);
+ EXPECT(Rpar);
+
+ uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE(data_segment.data.size());
+ uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size);
+ field->memory.page_limits.initial = page_size;
+ field->memory.page_limits.max = page_size;
+ field->memory.page_limits.has_max = true;
+
+ module->AppendField(std::move(field));
+ module->AppendField(std::move(data_segment_field));
+ } else {
+ CHECK_RESULT(ParseLimits(&field->memory.page_limits));
+ module->AppendField(std::move(field));
+ }
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->memories.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseStartModuleField(Module* module) {
+ WABT_TRACE(ParseStartModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ if (module->starts.size() > 0) {
+ Error(loc, "multiple start sections");
+ return Result::Error;
+ }
+ EXPECT(Start);
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ EXPECT(Rpar);
+ module->AppendField(MakeUnique<StartModuleField>(var, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseTableModuleField(Module* module) {
+ WABT_TRACE(ParseTableModuleField);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Table);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Table));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import = MakeUnique<TableImport>(name);
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimits(&import->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&import->table.elem_type));
+ auto field =
+ MakeUnique<ImportModuleField>(std::move(import), GetLocation());
+ module->AppendField(std::move(field));
+ } else if (PeekMatch(TokenType::ValueType)) {
+ Type elem_type;
+ CHECK_RESULT(ParseRefType(&elem_type));
+
+ EXPECT(Lpar);
+ EXPECT(Elem);
+
+ auto elem_segment_field = MakeUnique<ElemSegmentModuleField>(loc);
+ ElemSegment& elem_segment = elem_segment_field->elem_segment;
+ elem_segment.table_var = Var(module->tables.size());
+ elem_segment.offset.push_back(MakeUnique<ConstExpr>(Const::I32(0)));
+ elem_segment.offset.back().loc = loc;
+ elem_segment.elem_type = elem_type;
+ // Syntax is either an optional list of var (legacy), or a non-empty list
+ // of elem expr.
+ ElemExpr elem_expr;
+ if (ParseElemExprOpt(&elem_expr)) {
+ elem_segment.elem_exprs.push_back(elem_expr);
+ // Parse the rest.
+ ParseElemExprListOpt(&elem_segment.elem_exprs);
+ } else {
+ ParseElemExprVarListOpt(&elem_segment.elem_exprs);
+ }
+ EXPECT(Rpar);
+
+ auto table_field = MakeUnique<TableModuleField>(loc, name);
+ table_field->table.elem_limits.initial = elem_segment.elem_exprs.size();
+ table_field->table.elem_limits.max = elem_segment.elem_exprs.size();
+ table_field->table.elem_limits.has_max = true;
+ table_field->table.elem_type = elem_type;
+ module->AppendField(std::move(table_field));
+ module->AppendField(std::move(elem_segment_field));
+ } else {
+ auto field = MakeUnique<TableModuleField>(loc, name);
+ CHECK_RESULT(ParseLimits(&field->table.elem_limits));
+ CHECK_RESULT(ParseRefType(&field->table.elem_type));
+ module->AppendField(std::move(field));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->tables.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportDesc(Export* export_) {
+ WABT_TRACE(ParseExportDesc);
+ EXPECT(Lpar);
+ switch (Peek()) {
+ case TokenType::Func: export_->kind = ExternalKind::Func; break;
+ case TokenType::Table: export_->kind = ExternalKind::Table; break;
+ case TokenType::Memory: export_->kind = ExternalKind::Memory; break;
+ case TokenType::Global: export_->kind = ExternalKind::Global; break;
+ case TokenType::Tag: export_->kind = ExternalKind::Tag; break;
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+ Consume();
+ CHECK_RESULT(ParseVar(&export_->var));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineExports(ModuleFieldList* fields,
+ ExternalKind kind) {
+ WABT_TRACE(ParseInlineExports);
+ while (PeekMatchLpar(TokenType::Export)) {
+ EXPECT(Lpar);
+ auto field = MakeUnique<ExportModuleField>(GetLocation());
+ field->export_.kind = kind;
+ EXPECT(Export);
+ CHECK_RESULT(ParseQuotedText(&field->export_.name));
+ EXPECT(Rpar);
+ fields->push_back(std::move(field));
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineImport(Import* import) {
+ WABT_TRACE(ParseInlineImport);
+ EXPECT(Lpar);
+ EXPECT(Import);
+ CHECK_RESULT(ParseQuotedText(&import->module_name));
+ CHECK_RESULT(ParseQuotedText(&import->field_name));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeUseOpt(FuncDeclaration* decl) {
+ WABT_TRACE(ParseTypeUseOpt);
+ if (MatchLpar(TokenType::Type)) {
+ decl->has_func_type = true;
+ CHECK_RESULT(ParseVar(&decl->type_var));
+ EXPECT(Rpar);
+ } else {
+ decl->has_func_type = false;
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncSignature(FuncSignature* sig,
+ BindingHash* param_bindings) {
+ WABT_TRACE(ParseFuncSignature);
+ CHECK_RESULT(ParseBoundValueTypeList(TokenType::Param, &sig->param_types,
+ param_bindings));
+ CHECK_RESULT(ParseResultList(&sig->result_types));
+ return Result::Ok;
+}
+
+Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) {
+ WABT_TRACE(ParseUnboundFuncSignature);
+ CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types));
+ CHECK_RESULT(ParseResultList(&sig->result_types));
+ return Result::Ok;
+}
+
+Result WastParser::ParseBoundValueTypeList(TokenType token,
+ TypeVector* types,
+ BindingHash* bindings,
+ Index binding_index_offset) {
+ WABT_TRACE(ParseBoundValueTypeList);
+ while (MatchLpar(token)) {
+ if (PeekMatch(TokenType::Var)) {
+ std::string name;
+ Type type;
+ Location loc = GetLocation();
+ ParseBindVarOpt(&name);
+ CHECK_RESULT(ParseValueType(&type));
+ bindings->emplace(name,
+ Binding(loc, binding_index_offset + types->size()));
+ types->push_back(type);
+ } else {
+ CHECK_RESULT(ParseValueTypeList(types));
+ }
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseUnboundValueTypeList(TokenType token,
+ TypeVector* types) {
+ WABT_TRACE(ParseUnboundValueTypeList);
+ while (MatchLpar(token)) {
+ CHECK_RESULT(ParseValueTypeList(types));
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseResultList(TypeVector* result_types) {
+ WABT_TRACE(ParseResultList);
+ return ParseUnboundValueTypeList(TokenType::Result, result_types);
+}
+
+Result WastParser::ParseInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseInstrList);
+ ExprList new_exprs;
+ while (IsInstr(PeekPair())) {
+ if (Succeeded(ParseInstr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsInstr));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseTerminatingInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseTerminatingInstrList);
+ Result result = ParseInstrList(exprs);
+ // An InstrList often has no further Lpar following it, because it would have
+ // gobbled it up. So if there is a following Lpar it is an error. If we
+ // handle it here we can produce a nicer error message.
+ CHECK_RESULT(ErrorIfLpar({"an instr"}));
+ return result;
+}
+
+Result WastParser::ParseInstr(ExprList* exprs) {
+ WABT_TRACE(ParseInstr);
+ if (IsPlainInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ exprs->push_back(std::move(expr));
+ return Result::Ok;
+ } else if (IsBlockInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParseBlockInstr(&expr));
+ exprs->push_back(std::move(expr));
+ return Result::Ok;
+ } else if (PeekMatchExpr()) {
+ return ParseExpr(exprs);
+ } else {
+ assert(!"ParseInstr should only be called when IsInstr() is true");
+ return Result::Error;
+ }
+}
+
+template <typename T>
+Result WastParser::ParsePlainInstrVar(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ out_expr->reset(new T(var, loc));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParsePlainLoadStoreInstr(Location loc,
+ Token token,
+ std::unique_ptr<Expr>* out_expr) {
+ Opcode opcode = token.opcode();
+ Address offset;
+ Address align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+ out_expr->reset(new T(opcode, align, offset, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseSimdLane(Location loc, uint64_t* lane_idx) {
+ if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) {
+ return ErrorExpected({"a natural number in range [0, 32)"});
+ }
+
+ Literal literal = Consume().literal();
+
+ Result result = ParseInt64(literal.text.begin(), literal.text.end(),
+ lane_idx, ParseIntType::UnsignedOnly);
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
+
+ // The valid range is only [0, 32), but it's only malformed if it can't
+ // fit in a byte.
+ if (*lane_idx > 255) {
+ Error(loc, "lane index \"" PRIstringview "\" out-of-range [0, 32)",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParsePlainInstr);
+ Location loc = GetLocation();
+ switch (Peek()) {
+ case TokenType::Unreachable:
+ Consume();
+ out_expr->reset(new UnreachableExpr(loc));
+ break;
+
+ case TokenType::Nop:
+ Consume();
+ out_expr->reset(new NopExpr(loc));
+ break;
+
+ case TokenType::Drop:
+ Consume();
+ out_expr->reset(new DropExpr(loc));
+ break;
+
+ case TokenType::Select: {
+ Consume();
+ TypeVector result;
+ if (options_->features.reference_types_enabled() &&
+ MatchLpar(TokenType::Result)) {
+ CHECK_RESULT(ParseValueTypeList(&result));
+ EXPECT(Rpar);
+ }
+ out_expr->reset(new SelectExpr(result, loc));
+ break;
+ }
+
+ case TokenType::Br:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrIf:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrIfExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrTable: {
+ Consume();
+ auto expr = MakeUnique<BrTableExpr>(loc);
+ CHECK_RESULT(ParseVarList(&expr->targets));
+ expr->default_target = expr->targets.back();
+ expr->targets.pop_back();
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Return:
+ Consume();
+ out_expr->reset(new ReturnExpr(loc));
+ break;
+
+ case TokenType::Call:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<CallExpr>(loc, out_expr));
+ break;
+
+ case TokenType::CallIndirect: {
+ Consume();
+ auto expr = MakeUnique<CallIndirectExpr>(loc);
+ ParseVarOpt(&expr->table, Var(0, loc));
+ CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::CallRef: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new CallRefExpr(loc));
+ break;
+ }
+
+ case TokenType::ReturnCall:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ReturnCallExpr>(loc, out_expr));
+ break;
+
+ case TokenType::ReturnCallIndirect: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ auto expr = MakeUnique<ReturnCallIndirectExpr>(loc);
+ CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
+ ParseVarOpt(&expr->table, Var(0, loc));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::LocalGet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalGetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::LocalSet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalSetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::LocalTee:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<LocalTeeExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GlobalGet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GlobalGetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GlobalSet:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GlobalSetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Load:
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr));
+ break;
+
+ case TokenType::Store:
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr));
+ break;
+
+ case TokenType::Const: {
+ Const const_;
+ CHECK_RESULT(ParseConst(&const_, ConstType::Normal));
+ out_expr->reset(new ConstExpr(const_, loc));
+ break;
+ }
+
+ case TokenType::Unary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new UnaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::Binary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new BinaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::Compare:
+ out_expr->reset(new CompareExpr(Consume().opcode(), loc));
+ break;
+
+ case TokenType::Convert: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new ConvertExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::MemoryCopy:
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new MemoryCopyExpr(loc));
+ break;
+
+ case TokenType::MemoryFill:
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new MemoryFillExpr(loc));
+ break;
+
+ case TokenType::DataDrop:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<DataDropExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemoryInit:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<MemoryInitExpr>(loc, out_expr));
+ break;
+
+ case TokenType::MemorySize:
+ Consume();
+ out_expr->reset(new MemorySizeExpr(loc));
+ break;
+
+ case TokenType::MemoryGrow:
+ Consume();
+ out_expr->reset(new MemoryGrowExpr(loc));
+ break;
+
+ case TokenType::TableCopy: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var dst(0, loc);
+ Var src(0, loc);
+ if (options_->features.reference_types_enabled()) {
+ ParseVarOpt(&dst, dst);
+ ParseVarOpt(&src, src);
+ }
+ out_expr->reset(new TableCopyExpr(dst, src, loc));
+ break;
+ }
+
+ case TokenType::ElemDrop:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ElemDropExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableInit: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Var segment_index(0, loc);
+ CHECK_RESULT(ParseVar(&segment_index));
+ Var table_index(0, loc);
+ if (ParseVarOpt(&table_index, table_index)) {
+ // Here are the two forms:
+ //
+ // table.init $elemidx ...
+ // table.init $tableidx $elemidx ...
+ //
+ // So if both indexes are provided, we need to swap them.
+ std::swap(segment_index, table_index);
+ }
+ out_expr->reset(new TableInitExpr(segment_index, table_index, loc));
+ break;
+ }
+
+ case TokenType::TableGet:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<TableGetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableSet:
+ ErrorUnlessOpcodeEnabled(Consume());
+ // TODO: Table index.
+ CHECK_RESULT(ParsePlainInstrVar<TableSetExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableGrow:
+ ErrorUnlessOpcodeEnabled(Consume());
+ // TODO: Table index.
+ CHECK_RESULT(ParsePlainInstrVar<TableGrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableSize:
+ ErrorUnlessOpcodeEnabled(Consume());
+ // TODO: Table index.
+ CHECK_RESULT(ParsePlainInstrVar<TableSizeExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TableFill:
+ ErrorUnlessOpcodeEnabled(Consume());
+ // TODO: Table index.
+ CHECK_RESULT(ParsePlainInstrVar<TableFillExpr>(loc, out_expr));
+ break;
+
+ case TokenType::RefFunc:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<RefFuncExpr>(loc, out_expr));
+ break;
+
+ case TokenType::RefNull: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ Type type;
+ CHECK_RESULT(ParseRefKind(&type));
+ out_expr->reset(new RefNullExpr(type, loc));
+ break;
+ }
+
+ case TokenType::RefIsNull:
+ ErrorUnlessOpcodeEnabled(Consume());
+ out_expr->reset(new RefIsNullExpr(loc));
+ break;
+
+ case TokenType::Throw:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<ThrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Rethrow:
+ ErrorUnlessOpcodeEnabled(Consume());
+ CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::AtomicNotify: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicNotifyExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicFence: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ uint32_t consistency_model = 0x0;
+ out_expr->reset(new AtomicFenceExpr(consistency_model, loc));
+ break;
+ }
+
+ case TokenType::AtomicWait: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicWaitExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicLoad: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicLoadExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicStore: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicStoreExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicRmw: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicRmwExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::AtomicRmwCmpxchg: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ CHECK_RESULT(
+ ParsePlainLoadStoreInstr<AtomicRmwCmpxchgExpr>(loc, token, out_expr));
+ break;
+ }
+
+ case TokenType::Ternary: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ out_expr->reset(new TernaryExpr(token.opcode(), loc));
+ break;
+ }
+
+ case TokenType::SimdLaneOp: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new SimdLaneOpExpr(token.opcode(), lane_idx, loc));
+ break;
+ }
+
+ case TokenType::SimdLoadLane: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+
+ Address offset;
+ Address align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new SimdLoadLaneExpr(token.opcode(), align, offset, lane_idx, loc));
+ break;
+ }
+
+ case TokenType::SimdStoreLane: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+
+ Address offset;
+ Address align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+
+ uint64_t lane_idx = 0;
+ Result result = ParseSimdLane(loc, &lane_idx);
+
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ out_expr->reset(new SimdStoreLaneExpr(token.opcode(), align, offset, lane_idx, loc));
+ break;
+ }
+
+ case TokenType::SimdShuffleOp: {
+ Token token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ v128 values;
+ for (int lane = 0; lane < 16; ++lane) {
+ Location loc = GetLocation();
+ uint64_t lane_idx;
+ Result result = ParseSimdLane(loc, &lane_idx);
+ if (Failed(result)) {
+ return Result::Error;
+ }
+
+ values.set_u8(lane, static_cast<uint8_t>(lane_idx));
+ }
+
+ out_expr->reset(
+ new SimdShuffleOpExpr(token.opcode(), values, loc));
+ break;
+ }
+
+ default:
+ assert(
+ !"ParsePlainInstr should only be called when IsPlainInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseSimdV128Const(Const* const_,
+ TokenType token_type,
+ ConstType const_type) {
+ WABT_TRACE(ParseSimdV128Const);
+
+ uint8_t lane_count = 0;
+ bool integer = true;
+ switch (token_type) {
+ case TokenType::I8X16: { lane_count = 16; break; }
+ case TokenType::I16X8: { lane_count = 8; break; }
+ case TokenType::I32X4: { lane_count = 4; break; }
+ case TokenType::I64X2: { lane_count = 2; break; }
+ case TokenType::F32X4: { lane_count = 4; integer = false; break; }
+ case TokenType::F64X2: { lane_count = 2; integer = false; break; }
+ default: {
+ Error(
+ const_->loc,
+ "Unexpected type at start of simd constant. "
+ "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. "
+ "Found \"%s\".",
+ GetTokenTypeName(token_type)
+ );
+ return Result::Error;
+ }
+ }
+ Consume();
+
+ const_->loc = GetLocation();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ Location loc = GetLocation();
+
+ // Check that the lane literal type matches the element type of the v128:
+ Token token = GetToken();
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ // OK.
+ break;
+
+ case TokenType::Float:
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ if (integer) {
+ goto error;
+ }
+ break;
+
+ error:
+ default:
+ if (integer) {
+ return ErrorExpected({"a Nat or Integer literal"}, "123");
+ } else {
+ return ErrorExpected({"a Float literal"}, "42.0");
+ }
+ }
+
+ Result result;
+
+ // For each type, parse the next literal, bound check it, and write it to
+ // the array of bytes:
+ if (integer) {
+ string_view sv = Consume().literal().text;
+ const char* s = sv.begin();
+ const char* end = sv.end();
+
+ switch (lane_count) {
+ case 16: {
+ uint8_t value = 0;
+ result = ParseInt8(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u8(lane, value);
+ break;
+ }
+ case 8: {
+ uint16_t value = 0;
+ result = ParseInt16(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u16(lane, value);
+ break;
+ }
+ case 4: {
+ uint32_t value = 0;
+ result = ParseInt32(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u32(lane, value);
+ break;
+ }
+ case 2: {
+ uint64_t value = 0;
+ result = ParseInt64(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u64(lane, value);
+ break;
+ }
+ }
+ } else {
+ Const lane_const_;
+ switch (lane_count) {
+ case 4:
+ result = ParseF32(&lane_const_, const_type);
+ const_->set_v128_f32(lane, lane_const_.f32_bits());
+ break;
+
+ case 2:
+ result = ParseF64(&lane_const_, const_type);
+ const_->set_v128_f64(lane, lane_const_.f64_bits());
+ break;
+ }
+
+ const_->set_expected_nan(lane, lane_const_.expected_nan());
+ }
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"%s\"", token.to_string().c_str());
+ return Result::Error;
+ }
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpectedNan(ExpectedNan* expected) {
+ WABT_TRACE(ParseExpectedNan);
+ TokenType token_type = Peek();
+ switch (token_type) {
+ case TokenType::NanArithmetic:
+ *expected = ExpectedNan::Arithmetic;
+ break;
+ case TokenType::NanCanonical:
+ *expected = ExpectedNan::Canonical;
+ break;
+ default:
+ return Result::Error;
+ }
+ Consume();
+ return Result::Ok;
+}
+
+Result WastParser::ParseF32(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f32(expected);
+ return Result::Ok;
+ }
+ auto literal = Consume().literal();
+ uint32_t f32_bits;
+ Result result = ParseFloat(literal.type, literal.text.begin(),
+ literal.text.end(), &f32_bits);
+ const_->set_f32(f32_bits);
+ return result;
+}
+
+Result WastParser::ParseF64(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f64(expected);
+ return Result::Ok;
+ }
+ auto literal = Consume().literal();
+ uint64_t f64_bits;
+ Result result = ParseDouble(literal.type, literal.text.begin(),
+ literal.text.end(), &f64_bits);
+ const_->set_f64(f64_bits);
+ return result;
+}
+
+Result WastParser::ParseConst(Const* const_, ConstType const_type) {
+ WABT_TRACE(ParseConst);
+ Token opcode_token = Consume();
+ Opcode opcode = opcode_token.opcode();
+ const_->loc = GetLocation();
+ Token token = GetToken();
+
+ // V128 is fully handled by ParseSimdV128Const:
+ if (opcode != Opcode::V128Const) {
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ case TokenType::Float:
+ // OK.
+ break;
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ break;
+ default:
+ return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
+ }
+ }
+
+ Result result;
+ switch (opcode) {
+ case Opcode::I32Const: {
+ auto sv = Consume().literal().text;
+ uint32_t u32;
+ result = ParseInt32(sv.begin(), sv.end(), &u32,
+ ParseIntType::SignedAndUnsigned);
+ const_->set_u32(u32);
+ break;
+ }
+
+ case Opcode::I64Const: {
+ auto sv = Consume().literal().text;
+ uint64_t u64;
+ result = ParseInt64(sv.begin(), sv.end(), &u64,
+ ParseIntType::SignedAndUnsigned);
+ const_->set_u64(u64);
+ break;
+ }
+
+ case Opcode::F32Const:
+ result = ParseF32(const_, const_type);
+ break;
+
+ case Opcode::F64Const:
+ result = ParseF64(const_, const_type);
+ break;
+
+ case Opcode::V128Const:
+ ErrorUnlessOpcodeEnabled(opcode_token);
+ // Parse V128 Simd Const (16 bytes).
+ result = ParseSimdV128Const(const_, token.token_type(), const_type);
+ // ParseSimdV128Const report error already, just return here if parser get
+ // errors.
+ if (Failed(result)) {
+ return Result::Error;
+ }
+ break;
+
+ default:
+ assert(!"ParseConst called with invalid opcode");
+ return Result::Error;
+ }
+
+ if (Failed(result)) {
+ Error(const_->loc, "invalid literal \"%s\"", token.to_string().c_str());
+ // Return if parser get errors.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseExternref(Const* const_) {
+ WABT_TRACE(ParseExternref);
+ Token token = Consume();
+ if (!options_->features.reference_types_enabled()) {
+ Error(token.loc, "externref not allowed");
+ return Result::Error;
+ }
+
+ Literal literal;
+ string_view sv;
+ const char* s;
+ const char* end;
+ const_->loc = GetLocation();
+ TokenType token_type = Peek();
+
+ switch (token_type) {
+ case TokenType::Nat:
+ case TokenType::Int: {
+ literal = Consume().literal();
+ sv = literal.text;
+ s = sv.begin();
+ end = sv.end();
+ break;
+ }
+ default:
+ return ErrorExpected({"a numeric literal"}, "123");
+ }
+
+ uint64_t ref_bits;
+ Result result = ParseInt64(s, end, &ref_bits, ParseIntType::UnsignedOnly);
+
+ const_->set_externref(static_cast<uintptr_t>(ref_bits));
+
+ if (Failed(result)) {
+ Error(const_->loc, "invalid literal \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ // Return if parser get errors.
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseConstList(ConstVector* consts, ConstType type) {
+ WABT_TRACE(ParseConstList);
+ while (PeekMatchLpar(TokenType::Const) || PeekMatchLpar(TokenType::RefNull) ||
+ PeekMatchLpar(TokenType::RefExtern) ||
+ PeekMatchLpar(TokenType::RefFunc)) {
+ Consume();
+ Const const_;
+ switch (Peek()) {
+ case TokenType::Const:
+ CHECK_RESULT(ParseConst(&const_, type));
+ break;
+ case TokenType::RefNull: {
+ auto token = Consume();
+ Type type;
+ CHECK_RESULT(ParseRefKind(&type));
+ ErrorUnlessOpcodeEnabled(token);
+ const_.loc = GetLocation();
+ const_.set_null(type);
+ break;
+ }
+ case TokenType::RefFunc: {
+ auto token = Consume();
+ ErrorUnlessOpcodeEnabled(token);
+ const_.loc = GetLocation();
+ const_.set_funcref();
+ break;
+ }
+ case TokenType::RefExtern:
+ CHECK_RESULT(ParseExternref(&const_));
+ break;
+ default:
+ assert(!"unreachable");
+ return Result::Error;
+ }
+ EXPECT(Rpar);
+ consts->push_back(const_);
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlockInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParseBlockInstr);
+ Location loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Block: {
+ Consume();
+ auto expr = MakeUnique<BlockExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ auto expr = MakeUnique<LoopExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ auto expr = MakeUnique<IfExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->true_.label));
+ CHECK_RESULT(ParseBlock(&expr->true_));
+ if (Match(TokenType::Else)) {
+ CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
+ expr->false_end_loc = GetLocation();
+ }
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->true_.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ case TokenType::Try: {
+ ErrorUnlessOpcodeEnabled(Consume());
+ auto expr = MakeUnique<TryExpr>(loc);
+ CatchVector catches;
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ if (IsCatch(Peek())) {
+ CHECK_RESULT(ParseCatchInstrList(&expr->catches));
+ expr->kind = TryKind::Catch;
+ } else if (PeekMatch(TokenType::Delegate)) {
+ Consume();
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ expr->delegate_target = var;
+ expr->kind = TryKind::Delegate;
+ }
+ CHECK_RESULT(ErrorIfLpar({"a valid try clause"}));
+ expr->block.end_loc = GetLocation();
+ if (expr->kind != TryKind::Delegate) {
+ EXPECT(End);
+ }
+ CHECK_RESULT(ParseEndLabelOpt(expr->block.label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ default:
+ assert(
+ !"ParseBlockInstr should only be called when IsBlockInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseLabelOpt(std::string* out_label) {
+ WABT_TRACE(ParseLabelOpt);
+ if (PeekMatch(TokenType::Var)) {
+ *out_label = Consume().text().to_string();
+ } else {
+ out_label->clear();
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseEndLabelOpt(const std::string& begin_label) {
+ WABT_TRACE(ParseEndLabelOpt);
+ Location loc = GetLocation();
+ std::string end_label;
+ CHECK_RESULT(ParseLabelOpt(&end_label));
+ if (!end_label.empty()) {
+ if (begin_label.empty()) {
+ Error(loc, "unexpected label \"%s\"", end_label.c_str());
+ } else if (begin_label != end_label) {
+ Error(loc, "mismatching label \"%s\" != \"%s\"", begin_label.c_str(),
+ end_label.c_str());
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlockDeclaration(BlockDeclaration* decl) {
+ WABT_TRACE(ParseBlockDeclaration);
+ FuncDeclaration func_decl;
+ CHECK_RESULT(ParseTypeUseOpt(&func_decl));
+ CHECK_RESULT(ParseUnboundFuncSignature(&func_decl.sig));
+ decl->has_func_type = func_decl.has_func_type;
+ decl->type_var = func_decl.type_var;
+ decl->sig = func_decl.sig;
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlock(Block* block) {
+ WABT_TRACE(ParseBlock);
+ CHECK_RESULT(ParseBlockDeclaration(&block->decl));
+ CHECK_RESULT(ParseInstrList(&block->exprs));
+ block->end_loc = GetLocation();
+ return Result::Ok;
+}
+
+Result WastParser::ParseExprList(ExprList* exprs) {
+ WABT_TRACE(ParseExprList);
+ ExprList new_exprs;
+ while (PeekMatchExpr()) {
+ if (Succeeded(ParseExpr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsExpr));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpr(ExprList* exprs) {
+ WABT_TRACE(ParseExpr);
+ if (!PeekMatch(TokenType::Lpar)) {
+ return Result::Error;
+ }
+
+ if (IsPlainInstr(Peek(1))) {
+ Consume();
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ CHECK_RESULT(ParseExprList(exprs));
+ CHECK_RESULT(ErrorIfLpar({"an expr"}));
+ exprs->push_back(std::move(expr));
+ } else {
+ Location loc = GetLocation();
+
+ switch (Peek(1)) {
+ case TokenType::Block: {
+ Consume();
+ Consume();
+ auto expr = MakeUnique<BlockExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ Consume();
+ auto expr = MakeUnique<LoopExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlock(&expr->block));
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ Consume();
+ auto expr = MakeUnique<IfExpr>(loc);
+
+ CHECK_RESULT(ParseLabelOpt(&expr->true_.label));
+ CHECK_RESULT(ParseBlockDeclaration(&expr->true_.decl));
+
+ if (PeekMatchExpr()) {
+ ExprList cond;
+ CHECK_RESULT(ParseExpr(&cond));
+ exprs->splice(exprs->end(), cond);
+ }
+
+ if (MatchLpar(TokenType::Then)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->true_.exprs));
+ expr->true_.end_loc = GetLocation();
+ EXPECT(Rpar);
+
+ if (MatchLpar(TokenType::Else)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&expr->false_));
+ EXPECT(Rpar);
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->false_));
+ }
+ expr->false_end_loc = GetLocation();
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->true_.exprs));
+ expr->true_.end_loc = GetLocation();
+ if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&expr->false_));
+ expr->false_end_loc = GetLocation();
+ }
+ } else {
+ ConsumeIfLpar();
+ return ErrorExpected({"then block"}, "(then ...)");
+ }
+
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ case TokenType::Try: {
+ Consume();
+ ErrorUnlessOpcodeEnabled(Consume());
+
+ auto expr = MakeUnique<TryExpr>(loc);
+ CHECK_RESULT(ParseLabelOpt(&expr->block.label));
+ CHECK_RESULT(ParseBlockDeclaration(&expr->block.decl));
+ EXPECT(Lpar);
+ EXPECT(Do);
+ CHECK_RESULT(ParseInstrList(&expr->block.exprs));
+ EXPECT(Rpar);
+ if (PeekMatch(TokenType::Lpar)) {
+ Consume();
+ TokenType type = Peek();
+ switch (type) {
+ case TokenType::Catch:
+ case TokenType::CatchAll:
+ CHECK_RESULT(ParseCatchExprList(&expr->catches));
+ expr->kind = TryKind::Catch;
+ break;
+ case TokenType::Delegate: {
+ Consume();
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ expr->delegate_target = var;
+ expr->kind = TryKind::Delegate;
+ EXPECT(Rpar);
+ break;
+ }
+ default:
+ ErrorExpected({"catch", "catch_all", "delegate"});
+ break;
+ }
+ }
+ CHECK_RESULT(ErrorIfLpar({"a valid try clause"}));
+ expr->block.end_loc = GetLocation();
+ exprs->push_back(std::move(expr));
+ break;
+ }
+
+ default:
+ assert(!"ParseExpr should only be called when IsExpr() is true");
+ return Result::Error;
+ }
+ }
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchInstrList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchInstrList);
+ bool parsedCatch = false;
+ bool parsedCatchAll = false;
+
+ while (IsCatch(Peek())) {
+ Catch catch_(GetLocation());
+
+ auto token = Consume();
+ if (token.token_type() == TokenType::Catch) {
+ CHECK_RESULT(ParseVar(&catch_.var));
+ } else {
+ if (parsedCatchAll) {
+ Error(token.loc, "multiple catch_all clauses not allowed");
+ return Result::Error;
+ }
+ parsedCatchAll = true;
+ }
+
+ CHECK_RESULT(ParseInstrList(&catch_.exprs));
+ catches->push_back(std::move(catch_));
+ parsedCatch = true;
+ }
+
+ if (!parsedCatch) {
+ return ErrorExpected({"catch"});
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchExprList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchExprList);
+ bool parsedCatchAll = false;
+
+ do {
+ Catch catch_(GetLocation());
+
+ auto token = Consume();
+ if (token.token_type() == TokenType::Catch) {
+ CHECK_RESULT(ParseVar(&catch_.var));
+ } else {
+ if (parsedCatchAll) {
+ Error(token.loc, "multiple catch_all clauses not allowed");
+ return Result::Error;
+ }
+ parsedCatchAll = true;
+ }
+
+ CHECK_RESULT(ParseTerminatingInstrList(&catch_.exprs));
+ EXPECT(Rpar);
+ catches->push_back(std::move(catch_));
+ } while (Match(TokenType::Lpar) && IsCatch(Peek()));
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalType(Global* global) {
+ WABT_TRACE(ParseGlobalType);
+ if (MatchLpar(TokenType::Mut)) {
+ global->mutable_ = true;
+ CHECK_RESULT(ParseValueType(&global->type));
+ CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"}));
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(ParseValueType(&global->type));
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommandList(Script* script,
+ CommandPtrVector* commands) {
+ WABT_TRACE(ParseCommandList);
+ while (IsCommand(PeekPair())) {
+ CommandPtr command;
+ if (Succeeded(ParseCommand(script, &command))) {
+ commands->push_back(std::move(command));
+ } else {
+ CHECK_RESULT(Synchronize(IsCommand));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommand(Script* script, CommandPtr* out_command) {
+ WABT_TRACE(ParseCommand);
+ switch (Peek(1)) {
+ case TokenType::AssertExhaustion:
+ return ParseAssertExhaustionCommand(out_command);
+
+ case TokenType::AssertInvalid:
+ return ParseAssertInvalidCommand(out_command);
+
+ case TokenType::AssertMalformed:
+ return ParseAssertMalformedCommand(out_command);
+
+ case TokenType::AssertReturn:
+ return ParseAssertReturnCommand(out_command);
+
+ case TokenType::AssertTrap:
+ return ParseAssertTrapCommand(out_command);
+
+ case TokenType::AssertUnlinkable:
+ return ParseAssertUnlinkableCommand(out_command);
+
+ case TokenType::Get:
+ case TokenType::Invoke:
+ return ParseActionCommand(out_command);
+
+ case TokenType::Module:
+ return ParseModuleCommand(script, out_command);
+
+ case TokenType::Register:
+ return ParseRegisterCommand(out_command);
+
+ case TokenType::Input:
+ return ParseInputCommand(out_command);
+
+ case TokenType::Output:
+ return ParseOutputCommand(out_command);
+
+ default:
+ assert(!"ParseCommand should only be called when IsCommand() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseAssertExhaustionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertExhaustionCommand);
+ return ParseAssertActionTextCommand<AssertExhaustionCommand>(
+ TokenType::AssertExhaustion, out_command);
+}
+
+Result WastParser::ParseAssertInvalidCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertInvalidCommand);
+ return ParseAssertScriptModuleCommand<AssertInvalidCommand>(
+ TokenType::AssertInvalid, out_command);
+}
+
+Result WastParser::ParseAssertMalformedCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertMalformedCommand);
+ return ParseAssertScriptModuleCommand<AssertMalformedCommand>(
+ TokenType::AssertMalformed, out_command);
+}
+
+Result WastParser::ParseAssertReturnCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertReturnCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertReturn);
+ auto command = MakeUnique<AssertReturnCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseConstList(&command->expected, ConstType::Expectation));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertTrapCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertTrapCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertTrap);
+ if (PeekMatchLpar(TokenType::Module)) {
+ auto command = MakeUnique<AssertUninstantiableCommand>();
+ CHECK_RESULT(ParseScriptModule(&command->module));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ *out_command = std::move(command);
+ } else {
+ auto command = MakeUnique<AssertTrapCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ *out_command = std::move(command);
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertUnlinkableCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertUnlinkableCommand);
+ return ParseAssertScriptModuleCommand<AssertUnlinkableCommand>(
+ TokenType::AssertUnlinkable, out_command);
+}
+
+Result WastParser::ParseActionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseActionCommand);
+ auto command = MakeUnique<ActionCommand>();
+ CHECK_RESULT(ParseAction(&command->action));
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleCommand(Script* script, CommandPtr* out_command) {
+ WABT_TRACE(ParseModuleCommand);
+ std::unique_ptr<ScriptModule> script_module;
+ CHECK_RESULT(ParseScriptModule(&script_module));
+
+ auto command = MakeUnique<ModuleCommand>();
+ Module& module = command->module;
+
+ switch (script_module->type()) {
+ case ScriptModuleType::Text:
+ module = std::move(cast<TextScriptModule>(script_module.get())->module);
+ break;
+
+ case ScriptModuleType::Binary: {
+ auto* bsm = cast<BinaryScriptModule>(script_module.get());
+ ReadBinaryOptions options;
+#if WABT_TRACING
+ auto log_stream = FileStream::CreateStdout();
+ options.log_stream = log_stream.get();
+#endif
+ options.features = options_->features;
+ Errors errors;
+ const char* filename = "<text>";
+ ReadBinaryIr(filename, bsm->data.data(), bsm->data.size(), options,
+ &errors, &module);
+ module.name = bsm->name;
+ module.loc = bsm->loc;
+ for (const auto& error: errors) {
+ assert(error.error_level == ErrorLevel::Error);
+ if (error.loc.offset == kInvalidOffset) {
+ Error(bsm->loc, "error in binary module: %s", error.message.c_str());
+ } else {
+ Error(bsm->loc, "error in binary module: @0x%08" PRIzx ": %s",
+ error.loc.offset, error.message.c_str());
+ }
+ }
+ break;
+ }
+
+ case ScriptModuleType::Quoted:
+ return ErrorExpected({"a binary module", "a text module"});
+ }
+
+ // script is nullptr when ParseModuleCommand is called from ParseModule.
+ if (script) {
+ Index command_index = script->commands.size();
+
+ if (!module.name.empty()) {
+ script->module_bindings.emplace(module.name,
+ Binding(module.loc, command_index));
+ }
+
+ last_module_index_ = command_index;
+ }
+
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+Result WastParser::ParseRegisterCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseRegisterCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Register);
+ std::string text;
+ Var var;
+ CHECK_RESULT(ParseQuotedText(&text));
+ ParseVarOpt(&var, Var(last_module_index_, loc));
+ EXPECT(Rpar);
+ out_command->reset(new RegisterCommand(text, var));
+ return Result::Ok;
+}
+
+Result WastParser::ParseInputCommand(CommandPtr*) {
+ // Parse the input command, but always fail since this command is not
+ // actually supported.
+ WABT_TRACE(ParseInputCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Input);
+ Error(loc, "input command is not supported");
+ Var var;
+ std::string text;
+ ParseVarOpt(&var);
+ CHECK_RESULT(ParseQuotedText(&text));
+ EXPECT(Rpar);
+ return Result::Error;
+}
+
+Result WastParser::ParseOutputCommand(CommandPtr*) {
+ // Parse the output command, but always fail since this command is not
+ // actually supported.
+ WABT_TRACE(ParseOutputCommand);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Output);
+ Error(loc, "output command is not supported");
+ Var var;
+ std::string text;
+ ParseVarOpt(&var);
+ if (Peek() == TokenType::Text) {
+ CHECK_RESULT(ParseQuotedText(&text));
+ }
+ EXPECT(Rpar);
+ return Result::Error;
+}
+
+Result WastParser::ParseAction(ActionPtr* out_action) {
+ WABT_TRACE(ParseAction);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Invoke: {
+ Consume();
+ auto action = MakeUnique<InvokeAction>(loc);
+ ParseVarOpt(&action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&action->name));
+ CHECK_RESULT(ParseConstList(&action->args, ConstType::Normal));
+ *out_action = std::move(action);
+ break;
+ }
+
+ case TokenType::Get: {
+ Consume();
+ auto action = MakeUnique<GetAction>(loc);
+ ParseVarOpt(&action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&action->name));
+ *out_action = std::move(action);
+ break;
+ }
+
+ default:
+ return ErrorExpected({"invoke", "get"});
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseScriptModule(
+ std::unique_ptr<ScriptModule>* out_module) {
+ WABT_TRACE(ParseScriptModule);
+ EXPECT(Lpar);
+ Location loc = GetLocation();
+ EXPECT(Module);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ switch (Peek()) {
+ case TokenType::Bin: {
+ Consume();
+ std::vector<uint8_t> data;
+ // TODO(binji): The spec allows this to be empty, switch to
+ // ParseTextListOpt.
+ CHECK_RESULT(ParseTextList(&data));
+
+ auto bsm = MakeUnique<BinaryScriptModule>();
+ bsm->name = name;
+ bsm->loc = loc;
+ bsm->data = std::move(data);
+ *out_module = std::move(bsm);
+ break;
+ }
+
+ case TokenType::Quote: {
+ Consume();
+ std::vector<uint8_t> data;
+ // TODO(binji): The spec allows this to be empty, switch to
+ // ParseTextListOpt.
+ CHECK_RESULT(ParseTextList(&data));
+
+ auto qsm = MakeUnique<QuotedScriptModule>();
+ qsm->name = name;
+ qsm->loc = loc;
+ qsm->data = std::move(data);
+ *out_module = std::move(qsm);
+ break;
+ }
+
+ default: {
+ auto tsm = MakeUnique<TextScriptModule>();
+ tsm->module.name = name;
+ tsm->module.loc = loc;
+ if (IsModuleField(PeekPair())) {
+ CHECK_RESULT(ParseModuleFieldList(&tsm->module));
+ } else if (!PeekMatch(TokenType::Rpar)) {
+ ConsumeIfLpar();
+ return ErrorExpected({"a module field"});
+ }
+ *out_module = std::move(tsm);
+ break;
+ }
+ }
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = MakeUnique<T>();
+ CHECK_RESULT(ParseAction(&command->action));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionTextCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionTextCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = MakeUnique<T>();
+ CHECK_RESULT(ParseAction(&command->action));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertScriptModuleCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertScriptModuleCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto command = MakeUnique<T>();
+ CHECK_RESULT(ParseScriptModule(&command->module));
+ CHECK_RESULT(ParseQuotedText(&command->text));
+ EXPECT(Rpar);
+ *out_command = std::move(command);
+ return Result::Ok;
+}
+
+void WastParser::CheckImportOrdering(Module* module) {
+ if (module->funcs.size() != module->num_func_imports ||
+ module->tables.size() != module->num_table_imports ||
+ module->memories.size() != module->num_memory_imports ||
+ module->globals.size() != module->num_global_imports ||
+ module->tags.size() != module->num_tag_imports) {
+ Error(GetLocation(),
+ "imports must occur before all non-import definitions");
+ }
+}
+
+Result ParseWatModule(WastLexer* lexer,
+ std::unique_ptr<Module>* out_module,
+ Errors* errors,
+ WastParseOptions* options) {
+ assert(out_module != nullptr);
+ assert(options != nullptr);
+ WastParser parser(lexer, errors, options);
+ CHECK_RESULT(parser.ParseModule(out_module));
+ return Result::Ok;
+}
+
+Result ParseWastScript(WastLexer* lexer,
+ std::unique_ptr<Script>* out_script,
+ Errors* errors,
+ WastParseOptions* options) {
+ assert(out_script != nullptr);
+ assert(options != nullptr);
+ WastParser parser(lexer, errors, options);
+ CHECK_RESULT(parser.ParseScript(out_script));
+ CHECK_RESULT(ResolveNamesScript(out_script->get(), errors));
+ return Result::Ok;
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wast-parser.h b/third_party/wasm2c/src/wast-parser.h
new file mode 100644
index 0000000000..ea762357b7
--- /dev/null
+++ b/third_party/wasm2c/src/wast-parser.h
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_WAST_PARSER_H_
+#define WABT_WAST_PARSER_H_
+
+#include <array>
+
+#include "src/circular-array.h"
+#include "src/error.h"
+#include "src/feature.h"
+#include "src/intrusive-list.h"
+#include "src/ir.h"
+#include "src/wast-lexer.h"
+
+namespace wabt {
+
+struct WastParseOptions {
+ WastParseOptions(const Features& features) : features(features) {}
+
+ Features features;
+ bool debug_parsing = false;
+};
+
+typedef std::array<TokenType, 2> TokenTypePair;
+
+class WastParser {
+ public:
+ WastParser(WastLexer*, Errors*, WastParseOptions*);
+
+ void WABT_PRINTF_FORMAT(3, 4) Error(Location, const char* format, ...);
+ Result ParseModule(std::unique_ptr<Module>* out_module);
+ Result ParseScript(std::unique_ptr<Script>* out_script);
+
+ std::unique_ptr<Script> ReleaseScript();
+
+ private:
+ enum class ConstType {
+ Normal,
+ Expectation,
+ };
+
+ void ErrorUnlessOpcodeEnabled(const Token&);
+
+ // Print an error message listing the expected tokens, as well as an example
+ // of expected input.
+ Result ErrorExpected(const std::vector<std::string>& expected,
+ const char* example = nullptr);
+
+ // Print an error message, and and return Result::Error if the next token is
+ // '('. This is commonly used after parsing a sequence of s-expressions -- if
+ // no more can be parsed, we know that a following '(' is invalid. This
+ // function consumes the '(' so a better error message can be provided
+ // (assuming the following token was unexpected).
+ Result ErrorIfLpar(const std::vector<std::string>& expected,
+ const char* example = nullptr);
+
+ // Returns the next token without consuming it.
+ Token GetToken();
+
+ // Returns the location of the next token.
+ Location GetLocation();
+
+ // Returns the type of the next token.
+ TokenType Peek(size_t n = 0);
+
+ // Returns the types of the next two tokens.
+ TokenTypePair PeekPair();
+
+ // Returns true if the next token's type is equal to the parameter.
+ bool PeekMatch(TokenType);
+
+ // Returns true if the next token's type is '(' and the following token is
+ // equal to the parameter.
+ bool PeekMatchLpar(TokenType);
+
+ // Returns true if the next two tokens can start an Expr. This allows for
+ // folded expressions, plain instructions and block instructions.
+ bool PeekMatchExpr();
+
+ // Returns true if the next token's type is equal to the parameter. If so,
+ // then the token is consumed.
+ bool Match(TokenType);
+
+ // Returns true if the next token's type is equal to '(' and the following
+ // token is equal to the parameter. If so, then the token is consumed.
+ bool MatchLpar(TokenType);
+
+ // Like Match(), but prints an error message if the token doesn't match, and
+ // returns Result::Error.
+ Result Expect(TokenType);
+
+ // Consume one token and return it.
+ Token Consume();
+
+ // Give the Match() function a clearer name when used to optionally consume a
+ // token (used for printing better error messages).
+ void ConsumeIfLpar() { Match(TokenType::Lpar); }
+
+ typedef bool SynchronizeFunc(TokenTypePair pair);
+
+ // Attempt to synchronize the token stream by dropping tokens until the
+ // SynchronizeFunc returns true, or until a token limit is reached. This
+ // function returns Result::Error if the stream was not able to be
+ // synchronized.
+ Result Synchronize(SynchronizeFunc);
+
+ bool ParseBindVarOpt(std::string* name);
+ Result ParseVar(Var* out_var);
+ bool ParseVarOpt(Var* out_var, Var default_var = Var());
+ Result ParseOffsetExpr(ExprList* out_expr_list);
+ bool ParseOffsetExprOpt(ExprList* out_expr_list);
+ Result ParseTextList(std::vector<uint8_t>* out_data);
+ bool ParseTextListOpt(std::vector<uint8_t>* out_data);
+ Result ParseVarList(VarVector* out_var_list);
+ bool ParseElemExprOpt(ElemExpr* out_elem_expr);
+ bool ParseElemExprListOpt(ElemExprVector* out_list);
+ bool ParseElemExprVarListOpt(ElemExprVector* out_list);
+ Result ParseValueType(Type* out_type);
+ Result ParseValueTypeList(TypeVector* out_type_list);
+ Result ParseRefKind(Type* out_type);
+ Result ParseRefType(Type* out_type);
+ bool ParseRefTypeOpt(Type* out_type);
+ Result ParseQuotedText(std::string* text);
+ bool ParseOffsetOpt(Address* offset);
+ bool ParseAlignOpt(Address* align);
+ Result ParseLimitsIndex(Limits*);
+ Result ParseLimits(Limits*);
+ Result ParseNat(uint64_t*, bool is_64);
+
+ Result ParseModuleFieldList(Module*);
+ Result ParseModuleField(Module*);
+ Result ParseDataModuleField(Module*);
+ Result ParseElemModuleField(Module*);
+ Result ParseTagModuleField(Module*);
+ Result ParseExportModuleField(Module*);
+ Result ParseFuncModuleField(Module*);
+ Result ParseTypeModuleField(Module*);
+ Result ParseGlobalModuleField(Module*);
+ Result ParseImportModuleField(Module*);
+ Result ParseMemoryModuleField(Module*);
+ Result ParseStartModuleField(Module*);
+ Result ParseTableModuleField(Module*);
+
+ Result ParseExportDesc(Export*);
+ Result ParseInlineExports(ModuleFieldList*, ExternalKind);
+ Result ParseInlineImport(Import*);
+ Result ParseTypeUseOpt(FuncDeclaration*);
+ Result ParseFuncSignature(FuncSignature*, BindingHash* param_bindings);
+ Result ParseUnboundFuncSignature(FuncSignature*);
+ Result ParseBoundValueTypeList(TokenType,
+ TypeVector*,
+ BindingHash*,
+ Index binding_index_offset = 0);
+ Result ParseUnboundValueTypeList(TokenType, TypeVector*);
+ Result ParseResultList(TypeVector*);
+ Result ParseInstrList(ExprList*);
+ Result ParseTerminatingInstrList(ExprList*);
+ Result ParseInstr(ExprList*);
+ Result ParsePlainInstr(std::unique_ptr<Expr>*);
+ Result ParseF32(Const*, ConstType type);
+ Result ParseF64(Const*, ConstType type);
+ Result ParseConst(Const*, ConstType type);
+ Result ParseExternref(Const*);
+ Result ParseExpectedNan(ExpectedNan* expected);
+ Result ParseConstList(ConstVector*, ConstType type);
+ Result ParseBlockInstr(std::unique_ptr<Expr>*);
+ Result ParseLabelOpt(std::string*);
+ Result ParseEndLabelOpt(const std::string&);
+ Result ParseBlockDeclaration(BlockDeclaration*);
+ Result ParseBlock(Block*);
+ Result ParseExprList(ExprList*);
+ Result ParseExpr(ExprList*);
+ Result ParseCatchInstrList(CatchVector* catches);
+ Result ParseCatchExprList(CatchVector* catches);
+ Result ParseGlobalType(Global*);
+ Result ParseField(Field*);
+ Result ParseFieldList(std::vector<Field>*);
+
+ template <typename T>
+ Result ParsePlainInstrVar(Location, std::unique_ptr<Expr>*);
+ template <typename T>
+ Result ParsePlainLoadStoreInstr(Location, Token, std::unique_ptr<Expr>*);
+ Result ParseSimdLane(Location, uint64_t*);
+
+ Result ParseCommandList(Script*, CommandPtrVector*);
+ Result ParseCommand(Script*, CommandPtr*);
+ Result ParseAssertExhaustionCommand(CommandPtr*);
+ Result ParseAssertInvalidCommand(CommandPtr*);
+ Result ParseAssertMalformedCommand(CommandPtr*);
+ Result ParseAssertReturnCommand(CommandPtr*);
+ Result ParseAssertReturnFuncCommand(CommandPtr*);
+ Result ParseAssertTrapCommand(CommandPtr*);
+ Result ParseAssertUnlinkableCommand(CommandPtr*);
+ Result ParseActionCommand(CommandPtr*);
+ Result ParseModuleCommand(Script*, CommandPtr*);
+ Result ParseRegisterCommand(CommandPtr*);
+ Result ParseInputCommand(CommandPtr*);
+ Result ParseOutputCommand(CommandPtr*);
+
+ Result ParseAction(ActionPtr*);
+ Result ParseScriptModule(std::unique_ptr<ScriptModule>*);
+
+ template <typename T>
+ Result ParseActionCommand(TokenType, CommandPtr*);
+ template <typename T>
+ Result ParseAssertActionCommand(TokenType, CommandPtr*);
+ template <typename T>
+ Result ParseAssertActionTextCommand(TokenType, CommandPtr*);
+ template <typename T>
+ Result ParseAssertScriptModuleCommand(TokenType, CommandPtr*);
+
+ Result ParseSimdV128Const(Const*, TokenType, ConstType);
+
+ void CheckImportOrdering(Module*);
+
+ WastLexer* lexer_;
+ Index last_module_index_ = kInvalidIndex;
+ Errors* errors_;
+ WastParseOptions* options_;
+
+ CircularArray<Token, 2> tokens_;
+};
+
+Result ParseWatModule(WastLexer* lexer,
+ std::unique_ptr<Module>* out_module,
+ Errors*,
+ WastParseOptions* options);
+
+Result ParseWastScript(WastLexer* lexer,
+ std::unique_ptr<Script>* out_script,
+ Errors*,
+ WastParseOptions* options);
+
+} // namespace wabt
+
+#endif /* WABT_WAST_PARSER_H_ */
diff --git a/third_party/wasm2c/src/wat-writer.cc b/third_party/wasm2c/src/wat-writer.cc
new file mode 100644
index 0000000000..6483224b7f
--- /dev/null
+++ b/third_party/wasm2c/src/wat-writer.cc
@@ -0,0 +1,1723 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/wat-writer.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/ir-util.h"
+#include "src/literal.h"
+#include "src/stream.h"
+
+#define WABT_TRACING 0
+#include "src/tracing.h"
+
+#define INDENT_SIZE 2
+#define NO_FORCE_NEWLINE 0
+#define FORCE_NEWLINE 1
+
+namespace wabt {
+
+namespace {
+
+static const uint8_t s_is_char_escaped[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+
+// This table matches the characters allowed by wast-lexer.cc for `symbol`.
+// The disallowed printable characters are: "(),;[]{} and <space>.
+static const uint8_t s_valid_name_chars[256] = {
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x20 */ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
+};
+
+enum class NextChar {
+ None,
+ Space,
+ Newline,
+ ForceNewline,
+};
+
+struct ExprTree {
+ explicit ExprTree(const Expr* expr, Index result_count)
+ : expr(expr), result_count(result_count) {}
+
+ const Expr* expr;
+ std::vector<ExprTree> children;
+ Index result_count;
+};
+
+class WatWriter : ModuleContext {
+ public:
+ WatWriter(Stream* stream, const WriteWatOptions& options,
+ const Module &module)
+ : ModuleContext(module), options_(options), stream_(stream) {}
+
+ Result WriteModule();
+
+ private:
+ void Indent();
+ void Dedent();
+ void WriteIndent();
+ void WriteNextChar();
+ void WriteDataWithNextChar(const void* src, size_t size);
+ void Writef(const char* format, ...);
+ void WritePutc(char c);
+ void WritePuts(const char* s, NextChar next_char);
+ void WritePutsSpace(const char* s);
+ void WritePutsNewline(const char* s);
+ void WriteNewline(bool force);
+ void WriteOpen(const char* name, NextChar next_char);
+ void WriteOpenNewline(const char* name);
+ void WriteOpenSpace(const char* name);
+ void WriteClose(NextChar next_char);
+ void WriteCloseNewline();
+ void WriteCloseSpace();
+ void WriteString(const std::string& str, NextChar next_char);
+ void WriteName(string_view str, NextChar next_char);
+ void WriteNameOrIndex(string_view str, Index index, NextChar next_char);
+ void WriteQuotedData(const void* data, size_t length);
+ void WriteQuotedString(string_view str, NextChar next_char);
+ void WriteVar(const Var& var, NextChar next_char);
+ void WriteVarUnlessZero(const Var& var, NextChar next_char);
+ void WriteBrVar(const Var& var, NextChar next_char);
+ void WriteRefKind(Type type, NextChar next_char);
+ void WriteType(Type type, NextChar next_char);
+ void WriteTypes(const TypeVector& types, const char* name);
+ void WriteFuncSigSpace(const FuncSignature& func_sig);
+ void WriteBeginBlock(LabelType label_type,
+ const Block& block,
+ const char* text);
+ void WriteEndBlock();
+ void WriteConst(const Const& const_);
+ void WriteExpr(const Expr* expr);
+ template <typename T>
+ void WriteLoadStoreExpr(const Expr* expr);
+ void WriteExprList(const ExprList& exprs);
+ void WriteInitExpr(const ExprList& expr);
+ template <typename T>
+ void WriteTypeBindings(const char* prefix,
+ const T& types,
+ const std::vector<std::string>& index_to_name,
+ Index binding_index_offset = 0);
+ void WriteBeginFunc(const Func& func);
+ void WriteFunc(const Func& func);
+ void WriteBeginGlobal(const Global& global);
+ void WriteGlobal(const Global& global);
+ void WriteTag(const Tag& tag);
+ void WriteLimits(const Limits& limits);
+ void WriteTable(const Table& table);
+ void WriteElemSegment(const ElemSegment& segment);
+ void WriteMemory(const Memory& memory);
+ void WriteDataSegment(const DataSegment& segment);
+ void WriteImport(const Import& import);
+ void WriteExport(const Export& export_);
+ void WriteTypeEntry(const TypeEntry& type);
+ void WriteField(const Field& field);
+ void WriteStartFunction(const Var& start);
+
+ class ExprVisitorDelegate;
+
+ void PushExpr(const Expr* expr, Index operand_count, Index result_count);
+ void FlushExprTree(const ExprTree& expr_tree);
+ void FlushExprTreeVector(const std::vector<ExprTree>&);
+ void FlushExprTreeStack();
+ void WriteFoldedExpr(const Expr*);
+ void WriteFoldedExprList(const ExprList&);
+
+ void BuildInlineExportMap();
+ void WriteInlineExports(ExternalKind, Index);
+ bool IsInlineExport(const Export& export_);
+ void BuildInlineImportMap();
+ void WriteInlineImport(ExternalKind, Index);
+
+ const WriteWatOptions& options_;
+ Stream* stream_ = nullptr;
+ Result result_ = Result::Ok;
+ int indent_ = 0;
+ NextChar next_char_ = NextChar::None;
+ std::vector<ExprTree> expr_tree_stack_;
+ std::multimap<std::pair<ExternalKind, Index>, const Export*>
+ inline_export_map_;
+ std::vector<const Import*> inline_import_map_[kExternalKindCount];
+
+ Index func_index_ = 0;
+ Index global_index_ = 0;
+ Index table_index_ = 0;
+ Index memory_index_ = 0;
+ Index type_index_ = 0;
+ Index tag_index_ = 0;
+ Index data_segment_index_ = 0;
+ Index elem_segment_index_ = 0;
+};
+
+void WatWriter::Indent() {
+ indent_ += INDENT_SIZE;
+}
+
+void WatWriter::Dedent() {
+ indent_ -= INDENT_SIZE;
+ assert(indent_ >= 0);
+}
+
+void WatWriter::WriteIndent() {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t to_write = indent_;
+ while (to_write >= s_indent_len) {
+ stream_->WriteData(s_indent, s_indent_len);
+ to_write -= s_indent_len;
+ }
+ if (to_write > 0) {
+ stream_->WriteData(s_indent, to_write);
+ }
+}
+
+void WatWriter::WriteNextChar() {
+ switch (next_char_) {
+ case NextChar::Space:
+ stream_->WriteChar(' ');
+ break;
+ case NextChar::Newline:
+ case NextChar::ForceNewline:
+ stream_->WriteChar('\n');
+ WriteIndent();
+ break;
+ case NextChar::None:
+ break;
+ }
+ next_char_ = NextChar::None;
+}
+
+void WatWriter::WriteDataWithNextChar(const void* src, size_t size) {
+ WriteNextChar();
+ stream_->WriteData(src, size);
+}
+
+void WABT_PRINTF_FORMAT(2, 3) WatWriter::Writef(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ /* default to following space */
+ WriteDataWithNextChar(buffer, length);
+ next_char_ = NextChar::Space;
+}
+
+void WatWriter::WritePutc(char c) {
+ stream_->WriteChar(c);
+}
+
+void WatWriter::WritePuts(const char* s, NextChar next_char) {
+ size_t len = strlen(s);
+ WriteDataWithNextChar(s, len);
+ next_char_ = next_char;
+}
+
+void WatWriter::WritePutsSpace(const char* s) {
+ WritePuts(s, NextChar::Space);
+}
+
+void WatWriter::WritePutsNewline(const char* s) {
+ WritePuts(s, NextChar::Newline);
+}
+
+void WatWriter::WriteNewline(bool force) {
+ if (next_char_ == NextChar::ForceNewline) {
+ WriteNextChar();
+ }
+ next_char_ = force ? NextChar::ForceNewline : NextChar::Newline;
+}
+
+void WatWriter::WriteOpen(const char* name, NextChar next_char) {
+ WritePuts("(", NextChar::None);
+ WritePuts(name, next_char);
+ Indent();
+}
+
+void WatWriter::WriteOpenNewline(const char* name) {
+ WriteOpen(name, NextChar::Newline);
+}
+
+void WatWriter::WriteOpenSpace(const char* name) {
+ WriteOpen(name, NextChar::Space);
+}
+
+void WatWriter::WriteClose(NextChar next_char) {
+ if (next_char_ != NextChar::ForceNewline) {
+ next_char_ = NextChar::None;
+ }
+ Dedent();
+ WritePuts(")", next_char);
+}
+
+void WatWriter::WriteCloseNewline() {
+ WriteClose(NextChar::Newline);
+}
+
+void WatWriter::WriteCloseSpace() {
+ WriteClose(NextChar::Space);
+}
+
+void WatWriter::WriteString(const std::string& str, NextChar next_char) {
+ WritePuts(str.c_str(), next_char);
+}
+
+void WatWriter::WriteName(string_view str, NextChar next_char) {
+ // Debug names must begin with a $ for for wast file to be valid
+ assert(!str.empty() && str.front() == '$');
+ bool has_invalid_chars = std::any_of(
+ str.begin(), str.end(), [](uint8_t c) { return !s_valid_name_chars[c]; });
+
+ if (has_invalid_chars) {
+ std::string valid_str;
+ std::transform(str.begin(), str.end(), std::back_inserter(valid_str),
+ [](uint8_t c) { return s_valid_name_chars[c] ? c : '_'; });
+ WriteDataWithNextChar(valid_str.data(), valid_str.length());
+ } else {
+ WriteDataWithNextChar(str.data(), str.length());
+ }
+
+ next_char_ = next_char;
+}
+
+void WatWriter::WriteNameOrIndex(string_view str,
+ Index index,
+ NextChar next_char) {
+ if (!str.empty()) {
+ WriteName(str, next_char);
+ } else {
+ Writef("(;%u;)", index);
+ }
+}
+
+void WatWriter::WriteQuotedData(const void* data, size_t length) {
+ const uint8_t* u8_data = static_cast<const uint8_t*>(data);
+ static const char s_hexdigits[] = "0123456789abcdef";
+ WriteNextChar();
+ WritePutc('\"');
+ for (size_t i = 0; i < length; ++i) {
+ uint8_t c = u8_data[i];
+ if (s_is_char_escaped[c]) {
+ WritePutc('\\');
+ WritePutc(s_hexdigits[c >> 4]);
+ WritePutc(s_hexdigits[c & 0xf]);
+ } else {
+ WritePutc(c);
+ }
+ }
+ WritePutc('\"');
+ next_char_ = NextChar::Space;
+}
+
+void WatWriter::WriteQuotedString(string_view str, NextChar next_char) {
+ WriteQuotedData(str.data(), str.length());
+ next_char_ = next_char;
+}
+
+void WatWriter::WriteVar(const Var& var, NextChar next_char) {
+ if (var.is_index()) {
+ Writef("%" PRIindex, var.index());
+ next_char_ = next_char;
+ } else {
+ WriteName(var.name(), next_char);
+ }
+}
+
+bool VarIsZero(const Var& var) {
+ return var.is_index() && var.index() == 0;
+}
+
+void WatWriter::WriteVarUnlessZero(const Var& var, NextChar next_char) {
+ if (!VarIsZero(var)) {
+ WriteVar(var, next_char);
+ }
+}
+
+void WatWriter::WriteBrVar(const Var& var, NextChar next_char) {
+ if (var.is_index()) {
+ if (var.index() < GetLabelStackSize()) {
+ Writef("%" PRIindex " (;@%" PRIindex ";)", var.index(),
+ GetLabelStackSize() - var.index() - 1);
+ } else {
+ Writef("%" PRIindex " (; INVALID ;)", var.index());
+ }
+ next_char_ = next_char;
+ } else {
+ WriteString(var.name(), next_char);
+ }
+}
+
+void WatWriter::WriteRefKind(Type type, NextChar next_char) {
+ WritePuts(type.GetRefKindName(), next_char);
+}
+
+void WatWriter::WriteType(Type type, NextChar next_char) {
+ const char* type_name = type.GetName();
+ assert(type_name);
+ WritePuts(type_name, next_char);
+}
+
+void WatWriter::WriteTypes(const TypeVector& types, const char* name) {
+ if (types.size()) {
+ if (name) {
+ WriteOpenSpace(name);
+ }
+ for (Type type : types) {
+ WriteType(type, NextChar::Space);
+ }
+ if (name) {
+ WriteCloseSpace();
+ }
+ }
+}
+
+void WatWriter::WriteFuncSigSpace(const FuncSignature& func_sig) {
+ WriteTypes(func_sig.param_types, "param");
+ WriteTypes(func_sig.result_types, "result");
+}
+
+void WatWriter::WriteBeginBlock(LabelType label_type,
+ const Block& block,
+ const char* text) {
+ WritePutsSpace(text);
+ bool has_label = !block.label.empty();
+ if (has_label) {
+ WriteString(block.label, NextChar::Space);
+ }
+ WriteTypes(block.decl.sig.param_types, "param");
+ WriteTypes(block.decl.sig.result_types, "result");
+ if (!has_label) {
+ Writef(" ;; label = @%" PRIindex, GetLabelStackSize());
+ }
+ WriteNewline(FORCE_NEWLINE);
+ BeginBlock(label_type, block);
+ Indent();
+}
+
+void WatWriter::WriteEndBlock() {
+ Dedent();
+ EndBlock();
+ WritePutsNewline(Opcode::End_Opcode.GetName());
+}
+
+void WatWriter::WriteConst(const Const& const_) {
+ switch (const_.type()) {
+ case Type::I32:
+ WritePutsSpace(Opcode::I32Const_Opcode.GetName());
+ Writef("%d", static_cast<int32_t>(const_.u32()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+
+ case Type::I64:
+ WritePutsSpace(Opcode::I64Const_Opcode.GetName());
+ Writef("%" PRId64, static_cast<int64_t>(const_.u64()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+
+ case Type::F32: {
+ WritePutsSpace(Opcode::F32Const_Opcode.GetName());
+ char buffer[128];
+ WriteFloatHex(buffer, 128, const_.f32_bits());
+ WritePutsSpace(buffer);
+ Writef("(;=%g;)", Bitcast<float>(const_.f32_bits()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ case Type::F64: {
+ WritePutsSpace(Opcode::F64Const_Opcode.GetName());
+ char buffer[128];
+ WriteDoubleHex(buffer, 128, const_.f64_bits());
+ WritePutsSpace(buffer);
+ Writef("(;=%g;)", Bitcast<double>(const_.f64_bits()));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ case Type::V128: {
+ WritePutsSpace(Opcode::V128Const_Opcode.GetName());
+ auto vec = const_.vec128();
+ Writef("i32x4 0x%08x 0x%08x 0x%08x 0x%08x", vec.u32(0), vec.u32(1),
+ vec.u32(2), vec.u32(3));
+ WriteNewline(NO_FORCE_NEWLINE);
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+template <typename T>
+void WatWriter::WriteLoadStoreExpr(const Expr* expr) {
+ auto typed_expr = cast<T>(expr);
+ WritePutsSpace(typed_expr->opcode.GetName());
+ if (typed_expr->offset) {
+ Writef("offset=%" PRIaddress, typed_expr->offset);
+ }
+ if (!typed_expr->opcode.IsNaturallyAligned(typed_expr->align)) {
+ Writef("align=%" PRIaddress, typed_expr->align);
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+}
+
+class WatWriter::ExprVisitorDelegate : public ExprVisitor::Delegate {
+ public:
+ explicit ExprVisitorDelegate(WatWriter* writer) : writer_(writer) {}
+
+ Result OnBinaryExpr(BinaryExpr*) override;
+ 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 OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnCallRefExpr(CallRefExpr*) override;
+ Result OnCompareExpr(CompareExpr*) override;
+ Result OnConstExpr(ConstExpr*) override;
+ Result OnConvertExpr(ConvertExpr*) override;
+ Result OnDropExpr(DropExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result AfterIfTrueExpr(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 OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) 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 OnRefFuncExpr(RefFuncExpr*) override;
+ Result OnRefNullExpr(RefNullExpr*) override;
+ Result OnRefIsNullExpr(RefIsNullExpr*) override;
+ Result OnNopExpr(NopExpr*) override;
+ Result OnReturnExpr(ReturnExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr*) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnSelectExpr(SelectExpr*) override;
+ Result OnStoreExpr(StoreExpr*) override;
+ Result OnUnaryExpr(UnaryExpr*) override;
+ Result OnUnreachableExpr(UnreachableExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+ Result OnAtomicWaitExpr(AtomicWaitExpr*) override;
+ Result OnAtomicFenceExpr(AtomicFenceExpr*) override;
+ Result OnAtomicNotifyExpr(AtomicNotifyExpr*) override;
+ Result OnAtomicLoadExpr(AtomicLoadExpr*) override;
+ Result OnAtomicStoreExpr(AtomicStoreExpr*) override;
+ Result OnAtomicRmwExpr(AtomicRmwExpr*) override;
+ Result OnAtomicRmwCmpxchgExpr(AtomicRmwCmpxchgExpr*) override;
+ Result OnTernaryExpr(TernaryExpr*) override;
+ Result OnSimdLaneOpExpr(SimdLaneOpExpr*) override;
+ Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override;
+ Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) override;
+ Result OnSimdShuffleOpExpr(SimdShuffleOpExpr*) override;
+ Result OnLoadSplatExpr(LoadSplatExpr*) override;
+ Result OnLoadZeroExpr(LoadZeroExpr*) override;
+
+ private:
+ WatWriter* writer_;
+};
+
+Result WatWriter::ExprVisitorDelegate::OnBinaryExpr(BinaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginBlockExpr(BlockExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Block, expr->block,
+ Opcode::Block_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndBlockExpr(BlockExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrExpr(BrExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Br_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrIfExpr(BrIfExpr* expr) {
+ writer_->WritePutsSpace(Opcode::BrIf_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnBrTableExpr(BrTableExpr* expr) {
+ writer_->WritePutsSpace(Opcode::BrTable_Opcode.GetName());
+ for (const Var& var : expr->targets) {
+ writer_->WriteBrVar(var, NextChar::Space);
+ }
+ writer_->WriteBrVar(expr->default_target, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallExpr(CallExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Call_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallIndirectExpr(
+ CallIndirectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::CallIndirect_Opcode.GetName());
+ writer_->WriteVarUnlessZero(expr->table, NextChar::Space);
+ writer_->WriteOpenSpace("type");
+ writer_->WriteVar(expr->decl.type_var, NextChar::Newline);
+ writer_->WriteCloseNewline();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCallRefExpr(
+ CallRefExpr* expr) {
+ writer_->WritePutsSpace(Opcode::CallRef_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCompareExpr(CompareExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnConstExpr(ConstExpr* expr) {
+ writer_->WriteConst(expr->const_);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnConvertExpr(ConvertExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDropExpr(DropExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Drop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::GlobalGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::GlobalSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginIfExpr(IfExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::If, expr->true_,
+ Opcode::If_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::AfterIfTrueExpr(IfExpr* expr) {
+ if (!expr->false_.empty()) {
+ writer_->Dedent();
+ writer_->WritePutsSpace(Opcode::Else_Opcode.GetName());
+ writer_->Indent();
+ writer_->WriteNewline(FORCE_NEWLINE);
+ }
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndIfExpr(IfExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadExpr(LoadExpr* expr) {
+ writer_->WriteLoadStoreExpr<LoadExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalGetExpr(LocalGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalSetExpr(LocalSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::LocalTee_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginLoopExpr(LoopExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Loop, expr->block,
+ Opcode::Loop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndLoopExpr(LoopExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryCopyExpr(MemoryCopyExpr* expr) {
+ writer_->WritePutsNewline(Opcode::MemoryCopy_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDataDropExpr(DataDropExpr* expr) {
+ writer_->WritePutsSpace(Opcode::DataDrop_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryFillExpr(MemoryFillExpr* expr) {
+ writer_->WritePutsNewline(Opcode::MemoryFill_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ writer_->WritePutsNewline(Opcode::MemoryGrow_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ writer_->WritePutsNewline(Opcode::MemorySize_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableCopyExpr(TableCopyExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableCopy_Opcode.GetName());
+ if (!(VarIsZero(expr->dst_table) && VarIsZero(expr->src_table))) {
+ writer_->WriteVar(expr->dst_table, NextChar::Space);
+ writer_->WriteVar(expr->src_table, NextChar::Space);
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnElemDropExpr(ElemDropExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ElemDrop_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableInitExpr(TableInitExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableInit_Opcode.GetName());
+ writer_->WriteVarUnlessZero(expr->table_index, NextChar::Space);
+ writer_->WriteVar(expr->segment_index, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableGetExpr(TableGetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableGet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableSetExpr(TableSetExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableSet_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableGrowExpr(TableGrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableGrow_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableSizeExpr(TableSizeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableSize_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTableFillExpr(TableFillExpr* expr) {
+ writer_->WritePutsSpace(Opcode::TableFill_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefFuncExpr(RefFuncExpr* expr) {
+ writer_->WritePutsSpace(Opcode::RefFunc_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefNullExpr(RefNullExpr* expr) {
+ writer_->WritePutsSpace(Opcode::RefNull_Opcode.GetName());
+ writer_->WriteRefKind(expr->type, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRefIsNullExpr(RefIsNullExpr* expr) {
+ writer_->WritePutsNewline(Opcode::RefIsNull_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnNopExpr(NopExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Nop_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnExpr(ReturnExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Return_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnCallExpr(ReturnCallExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ReturnCall_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnReturnCallIndirectExpr(
+ ReturnCallIndirectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::ReturnCallIndirect_Opcode.GetName());
+ writer_->WriteOpenSpace("type");
+ writer_->WriteVar(expr->decl.type_var, NextChar::Space);
+ writer_->WriteCloseNewline();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSelectExpr(SelectExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Select_Opcode.GetName());
+ if (!expr->result_type.empty()) {
+ writer_->WriteTypes(expr->result_type, "result");
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnStoreExpr(StoreExpr* expr) {
+ writer_->WriteLoadStoreExpr<StoreExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnUnaryExpr(UnaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnUnreachableExpr(
+ UnreachableExpr* expr) {
+ writer_->WritePutsNewline(Opcode::Unreachable_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::BeginTryExpr(TryExpr* expr) {
+ writer_->WriteBeginBlock(LabelType::Try, expr->block,
+ Opcode::Try_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnCatchExpr(
+ TryExpr* expr, Catch* catch_) {
+ writer_->Dedent();
+ if (catch_->IsCatchAll()) {
+ writer_->WritePutsNewline(Opcode::CatchAll_Opcode.GetName());
+ } else {
+ writer_->WritePutsSpace(Opcode::Catch_Opcode.GetName());
+ writer_->WriteVar(catch_->var, NextChar::Newline);
+ }
+ writer_->Indent();
+ writer_->SetTopLabelType(LabelType::Catch);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnDelegateExpr(TryExpr* expr) {
+ writer_->Dedent();
+ writer_->EndBlock();
+ writer_->WritePutsSpace(Opcode::Delegate_Opcode.GetName());
+ writer_->WriteVar(expr->delegate_target, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::EndTryExpr(TryExpr* expr) {
+ writer_->WriteEndBlock();
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnThrowExpr(ThrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Throw_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnRethrowExpr(RethrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::Rethrow_Opcode.GetName());
+ writer_->WriteBrVar(expr->var, NextChar::Newline);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicWaitExpr(AtomicWaitExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicWaitExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicFenceExpr(
+ AtomicFenceExpr* expr) {
+ assert(expr->consistency_model == 0);
+ writer_->WritePutsNewline(Opcode::AtomicFence_Opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicNotifyExpr(
+ AtomicNotifyExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicNotifyExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicLoadExpr(AtomicLoadExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicLoadExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicStoreExpr(
+ AtomicStoreExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicStoreExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicRmwExpr(AtomicRmwExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicRmwExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnAtomicRmwCmpxchgExpr(
+ AtomicRmwCmpxchgExpr* expr) {
+ writer_->WriteLoadStoreExpr<AtomicRmwCmpxchgExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnTernaryExpr(TernaryExpr* expr) {
+ writer_->WritePutsNewline(expr->opcode.GetName());
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdLaneOpExpr(SimdLaneOpExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ if (expr->offset) {
+ writer_->Writef("offset=%" PRIaddress, expr->offset);
+ }
+ if (!expr->opcode.IsNaturallyAligned(expr->align)) {
+ writer_->Writef("align=%" PRIaddress, expr->align);
+ }
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdStoreLaneExpr(
+ SimdStoreLaneExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ if (expr->offset) {
+ writer_->Writef("offset=%" PRIaddress, expr->offset);
+ }
+ if (!expr->opcode.IsNaturallyAligned(expr->align)) {
+ writer_->Writef("align=%" PRIaddress, expr->align);
+ }
+ writer_->Writef("%" PRIu64, (expr->val));
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnSimdShuffleOpExpr(
+ SimdShuffleOpExpr* expr) {
+ writer_->WritePutsSpace(expr->opcode.GetName());
+ std::array<uint8_t, 16> values = Bitcast<std::array<uint8_t, 16>>(expr->val);
+ for (int32_t lane = 0; lane < 16; ++lane) {
+#if WABT_BIG_ENDIAN
+ writer_->Writef("%u", values[15 - lane]);
+#else
+ writer_->Writef("%u", values[lane]);
+#endif
+ }
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadSplatExpr(LoadSplatExpr* expr) {
+ writer_->WriteLoadStoreExpr<LoadSplatExpr>(expr);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnLoadZeroExpr(LoadZeroExpr* expr) {
+ writer_->WriteLoadStoreExpr<LoadZeroExpr>(expr);
+ return Result::Ok;
+}
+
+void WatWriter::WriteExpr(const Expr* expr) {
+ WABT_TRACE(WriteExprList);
+ ExprVisitorDelegate delegate(this);
+ ExprVisitor visitor(&delegate);
+ visitor.VisitExpr(const_cast<Expr*>(expr));
+}
+
+void WatWriter::WriteExprList(const ExprList& exprs) {
+ WABT_TRACE(WriteExprList);
+ ExprVisitorDelegate delegate(this);
+ ExprVisitor visitor(&delegate);
+ visitor.VisitExprList(const_cast<ExprList&>(exprs));
+}
+
+void WatWriter::WriteFoldedExpr(const Expr* expr) {
+ WABT_TRACE_ARGS(WriteFoldedExpr, "%s", GetExprTypeName(*expr));
+ auto arity = GetExprArity(*expr);
+ PushExpr(expr, arity.nargs, arity.nreturns);
+}
+
+void WatWriter::WriteFoldedExprList(const ExprList& exprs) {
+ WABT_TRACE(WriteFoldedExprList);
+ for (const Expr& expr : exprs) {
+ WriteFoldedExpr(&expr);
+ }
+}
+
+void WatWriter::PushExpr(const Expr* expr,
+ Index operand_count,
+ Index result_count) {
+ WABT_TRACE_ARGS(PushExpr, "%s, %" PRIindex ", %" PRIindex "",
+ GetExprTypeName(*expr), operand_count, result_count);
+
+ // Try to pop operand off the expr stack to use as this expr's children. One
+ // expr can have multiple return values (with the multi-value extension), so
+ // we have to iterate over each in reverse.
+
+ auto first_operand = expr_tree_stack_.end();
+
+ Index current_count = 0;
+ if (operand_count > 0) {
+ for (auto iter = expr_tree_stack_.rbegin(); iter != expr_tree_stack_.rend();
+ ++iter) {
+ assert(iter->result_count > 0);
+ current_count += iter->result_count;
+
+ if (current_count == operand_count) {
+ first_operand = iter.base() - 1;
+ break;
+ } else if (current_count > operand_count) {
+ // We went over the number of operands this instruction wants; this can
+ // only happen when there are expressions on the stack with a
+ // result_count > 1. When this happens we can't fold, since any result
+ // we produce will not make sense.
+ break;
+ }
+ }
+ }
+
+ ExprTree tree(expr, result_count);
+
+ if (current_count == operand_count && operand_count > 0) {
+ auto last_operand = expr_tree_stack_.end();
+ std::move(first_operand, last_operand, std::back_inserter(tree.children));
+ expr_tree_stack_.erase(first_operand, last_operand);
+ }
+
+ expr_tree_stack_.emplace_back(std::move(tree));
+ if (current_count > operand_count || result_count == 0) {
+ FlushExprTreeStack();
+ }
+}
+
+void WatWriter::FlushExprTree(const ExprTree& expr_tree) {
+ WABT_TRACE_ARGS(FlushExprTree, "%s", GetExprTypeName(*expr_tree.expr));
+ switch (expr_tree.expr->type()) {
+ case ExprType::Block:
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Block, cast<BlockExpr>(expr_tree.expr)->block,
+ Opcode::Block_Opcode.GetName());
+ WriteFoldedExprList(cast<BlockExpr>(expr_tree.expr)->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ EndBlock();
+ break;
+
+ case ExprType::Loop:
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Loop, cast<LoopExpr>(expr_tree.expr)->block,
+ Opcode::Loop_Opcode.GetName());
+ WriteFoldedExprList(cast<LoopExpr>(expr_tree.expr)->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ EndBlock();
+ break;
+
+ case ExprType::If: {
+ auto if_expr = cast<IfExpr>(expr_tree.expr);
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::If, if_expr->true_,
+ Opcode::If_Opcode.GetName());
+ FlushExprTreeVector(expr_tree.children);
+ WriteOpenNewline("then");
+ WriteFoldedExprList(if_expr->true_.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ if (!if_expr->false_.empty()) {
+ WriteOpenNewline("else");
+ WriteFoldedExprList(if_expr->false_);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ }
+ WriteCloseNewline();
+ EndBlock();
+ break;
+ }
+
+ case ExprType::Try: {
+ auto try_expr = cast<TryExpr>(expr_tree.expr);
+ WritePuts("(", NextChar::None);
+ WriteBeginBlock(LabelType::Try, try_expr->block,
+ Opcode::Try_Opcode.GetName());
+ WriteOpenNewline("do");
+ FlushExprTreeVector(expr_tree.children);
+ WriteFoldedExprList(try_expr->block.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ switch (try_expr->kind) {
+ case TryKind::Catch:
+ for (const Catch& catch_ : try_expr->catches) {
+ WritePuts("(", NextChar::None);
+ if (catch_.IsCatchAll()) {
+ WritePutsNewline("catch_all");
+ } else {
+ WritePutsSpace(Opcode::Catch_Opcode.GetName());
+ WriteVar(catch_.var, NextChar::Newline);
+ }
+ Indent();
+ WriteFoldedExprList(catch_.exprs);
+ FlushExprTreeStack();
+ WriteCloseNewline();
+ }
+ break;
+ case TryKind::Delegate:
+ WritePuts("(", NextChar::None);
+ WritePutsSpace(Opcode::Delegate_Opcode.GetName());
+ WriteVar(try_expr->delegate_target, NextChar::None);
+ WritePuts(")", NextChar::Newline);
+ break;
+ case TryKind::Plain:
+ // Nothing to do.
+ break;
+ }
+ WriteCloseNewline();
+ EndBlock();
+ break;
+ }
+
+ default: {
+ WritePuts("(", NextChar::None);
+ WriteExpr(expr_tree.expr);
+ Indent();
+ FlushExprTreeVector(expr_tree.children);
+ WriteCloseNewline();
+ break;
+ }
+ }
+}
+
+void WatWriter::FlushExprTreeVector(const std::vector<ExprTree>& expr_trees) {
+ WABT_TRACE_ARGS(FlushExprTreeVector, "%zu", expr_trees.size());
+ for (auto expr_tree : expr_trees) {
+ FlushExprTree(expr_tree);
+ }
+}
+
+void WatWriter::FlushExprTreeStack() {
+ std::vector<ExprTree> stack_copy(std::move(expr_tree_stack_));
+ expr_tree_stack_.clear();
+ FlushExprTreeVector(stack_copy);
+}
+
+void WatWriter::WriteInitExpr(const ExprList& expr) {
+ if (!expr.empty()) {
+ WritePuts("(", NextChar::None);
+ WriteExprList(expr);
+ /* clear the next char, so we don't write a newline after the expr */
+ next_char_ = NextChar::None;
+ WritePuts(")", NextChar::Space);
+ }
+}
+
+template <typename T>
+void WatWriter::WriteTypeBindings(const char* prefix,
+ const T& types,
+ const std::vector<std::string>& index_to_name,
+ Index binding_index_offset) {
+ /* named params/locals must be specified by themselves, but nameless
+ * params/locals can be compressed, e.g.:
+ * (param $foo i32)
+ * (param i32 i64 f32)
+ */
+ bool first = true;
+ bool prev_has_name = false;
+ size_t index = 0;
+ for (Type type : types) {
+ const std::string& name = index_to_name[binding_index_offset + index];
+ bool has_name = !name.empty();
+ if ((has_name || prev_has_name) && !first) {
+ WriteCloseSpace();
+ }
+ if (has_name || prev_has_name || first) {
+ WriteOpenSpace(prefix);
+ }
+ if (has_name) {
+ WriteString(name, NextChar::Space);
+ }
+ WriteType(type, NextChar::Space);
+ prev_has_name = has_name;
+ first = false;
+ ++index;
+ }
+ if (types.size() != 0) {
+ WriteCloseSpace();
+ }
+}
+
+void WatWriter::WriteBeginFunc(const Func& func) {
+ WriteOpenSpace("func");
+ WriteNameOrIndex(func.name, func_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Func, func_index_);
+ WriteInlineImport(ExternalKind::Func, func_index_);
+ if (func.decl.has_func_type) {
+ WriteOpenSpace("type");
+ WriteVar(func.decl.type_var, NextChar::None);
+ WriteCloseSpace();
+ }
+
+ if (module.IsImport(ExternalKind::Func, Var(func_index_))) {
+ // Imported functions can be written a few ways:
+ //
+ // 1. (import "module" "field" (func (type 0)))
+ // 2. (import "module" "field" (func (param i32) (result i32)))
+ // 3. (func (import "module" "field") (type 0))
+ // 4. (func (import "module" "field") (param i32) (result i32))
+ // 5. (func (import "module" "field") (type 0) (param i32) (result i32))
+ //
+ // Note that the text format does not allow including the param/result
+ // explicitly when using the "(import..." syntax (#1 and #2).
+ if (options_.inline_import || !func.decl.has_func_type) {
+ WriteFuncSigSpace(func.decl.sig);
+ }
+ }
+ func_index_++;
+}
+
+void WatWriter::WriteFunc(const Func& func) {
+ WriteBeginFunc(func);
+ std::vector<std::string> index_to_name;
+ MakeTypeBindingReverseMapping(func.GetNumParamsAndLocals(), func.bindings,
+ &index_to_name);
+ WriteTypeBindings("param", func.decl.sig.param_types, index_to_name);
+ WriteTypes(func.decl.sig.result_types, "result");
+ WriteNewline(NO_FORCE_NEWLINE);
+ if (func.local_types.size()) {
+ WriteTypeBindings("local", func.local_types, index_to_name,
+ func.GetNumParams());
+ }
+ WriteNewline(NO_FORCE_NEWLINE);
+ BeginFunc(func);
+ if (options_.fold_exprs) {
+ WriteFoldedExprList(func.exprs);
+ FlushExprTreeStack();
+ } else {
+ WriteExprList(func.exprs);
+ }
+ EndFunc();
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteBeginGlobal(const Global& global) {
+ WriteOpenSpace("global");
+ WriteNameOrIndex(global.name, global_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Global, global_index_);
+ WriteInlineImport(ExternalKind::Global, global_index_);
+ if (global.mutable_) {
+ WriteOpenSpace("mut");
+ WriteType(global.type, NextChar::Space);
+ WriteCloseSpace();
+ } else {
+ WriteType(global.type, NextChar::Space);
+ }
+ global_index_++;
+}
+
+void WatWriter::WriteGlobal(const Global& global) {
+ WriteBeginGlobal(global);
+ WriteInitExpr(global.init_expr);
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteTag(const Tag& tag) {
+ WriteOpenSpace("tag");
+ WriteNameOrIndex(tag.name, tag_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Tag, tag_index_);
+ WriteInlineImport(ExternalKind::Tag, tag_index_);
+ if (tag.decl.has_func_type) {
+ WriteOpenSpace("type");
+ WriteVar(tag.decl.type_var, NextChar::None);
+ WriteCloseSpace();
+ }
+ WriteTypes(tag.decl.sig.param_types, "param");
+ ++tag_index_;
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteLimits(const Limits& limits) {
+ if (limits.is_64) {
+ Writef("i64");
+ }
+ Writef("%" PRIu64, limits.initial);
+ if (limits.has_max) {
+ Writef("%" PRIu64, limits.max);
+ }
+ if (limits.is_shared) {
+ Writef("shared");
+ }
+}
+
+void WatWriter::WriteTable(const Table& table) {
+ WriteOpenSpace("table");
+ WriteNameOrIndex(table.name, table_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Table, table_index_);
+ WriteInlineImport(ExternalKind::Table, table_index_);
+ WriteLimits(table.elem_limits);
+ WriteType(table.elem_type, NextChar::None);
+ WriteCloseNewline();
+ table_index_++;
+}
+
+void WatWriter::WriteElemSegment(const ElemSegment& segment) {
+ WriteOpenSpace("elem");
+ WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space);
+
+ uint8_t flags = segment.GetFlags(&module);
+
+ if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
+ WriteOpenSpace("table");
+ WriteVar(segment.table_var, NextChar::Space);
+ WriteCloseSpace();
+ }
+
+ if (!(flags & SegPassive)) {
+ WriteInitExpr(segment.offset);
+ }
+
+ if ((flags & SegDeclared) == SegDeclared) {
+ WritePuts("declare", NextChar::Space);
+ }
+
+ if (flags & SegUseElemExprs) {
+ WriteType(segment.elem_type, NextChar::Space);
+ } else {
+ assert(segment.elem_type == Type::FuncRef);
+ WritePuts("func", NextChar::Space);
+ }
+
+ for (const ElemExpr& expr : segment.elem_exprs) {
+ if (flags & SegUseElemExprs) {
+ if (expr.kind == ElemExprKind::RefNull) {
+ WriteOpenSpace("ref.null");
+ WriteRefKind(expr.type, NextChar::Space);
+ WriteCloseSpace();
+ } else {
+ WriteOpenSpace("ref.func");
+ WriteVar(expr.var, NextChar::Space);
+ WriteCloseSpace();
+ }
+ } else {
+ assert(expr.kind == ElemExprKind::RefFunc);
+ WriteVar(expr.var, NextChar::Space);
+ }
+ }
+ WriteCloseNewline();
+ elem_segment_index_++;
+}
+
+void WatWriter::WriteMemory(const Memory& memory) {
+ WriteOpenSpace("memory");
+ WriteNameOrIndex(memory.name, memory_index_, NextChar::Space);
+ WriteInlineExports(ExternalKind::Memory, memory_index_);
+ WriteInlineImport(ExternalKind::Memory, memory_index_);
+ WriteLimits(memory.page_limits);
+ WriteCloseNewline();
+ memory_index_++;
+}
+
+void WatWriter::WriteDataSegment(const DataSegment& segment) {
+ WriteOpenSpace("data");
+ WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space);
+ if (segment.kind != SegmentKind::Passive) {
+ WriteInitExpr(segment.offset);
+ }
+ WriteQuotedData(segment.data.data(), segment.data.size());
+ WriteCloseNewline();
+ data_segment_index_++;
+}
+
+void WatWriter::WriteImport(const Import& import) {
+ if (!options_.inline_import) {
+ WriteOpenSpace("import");
+ WriteQuotedString(import.module_name, NextChar::Space);
+ WriteQuotedString(import.field_name, NextChar::Space);
+ }
+
+ switch (import.kind()) {
+ case ExternalKind::Func:
+ WriteBeginFunc(cast<FuncImport>(&import)->func);
+ WriteCloseSpace();
+ break;
+
+ case ExternalKind::Table:
+ WriteTable(cast<TableImport>(&import)->table);
+ break;
+
+ case ExternalKind::Memory:
+ WriteMemory(cast<MemoryImport>(&import)->memory);
+ break;
+
+ case ExternalKind::Global:
+ WriteBeginGlobal(cast<GlobalImport>(&import)->global);
+ WriteCloseSpace();
+ break;
+
+ case ExternalKind::Tag:
+ WriteTag(cast<TagImport>(&import)->tag);
+ break;
+ }
+
+ if (options_.inline_import) {
+ WriteNewline(NO_FORCE_NEWLINE);
+ } else {
+ WriteCloseNewline();
+ }
+}
+
+void WatWriter::WriteExport(const Export& export_) {
+ if (options_.inline_export && IsInlineExport(export_)) {
+ return;
+ }
+ WriteOpenSpace("export");
+ WriteQuotedString(export_.name, NextChar::Space);
+ WriteOpenSpace(GetKindName(export_.kind));
+ WriteVar(export_.var, NextChar::Space);
+ WriteCloseSpace();
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteTypeEntry(const TypeEntry& type) {
+ WriteOpenSpace("type");
+ WriteNameOrIndex(type.name, type_index_++, NextChar::Space);
+ switch (type.kind()) {
+ case TypeEntryKind::Func:
+ WriteOpenSpace("func");
+ WriteFuncSigSpace(cast<FuncType>(&type)->sig);
+ WriteCloseSpace();
+ break;
+
+ case TypeEntryKind::Struct: {
+ auto* struct_type = cast<StructType>(&type);
+ WriteOpenSpace("struct");
+ Index field_index = 0;
+ for (auto&& field : struct_type->fields) {
+ // TODO: Write shorthand if there is no name.
+ WriteOpenSpace("field");
+ WriteNameOrIndex(field.name, field_index++, NextChar::Space);
+ WriteField(field);
+ WriteCloseSpace();
+ }
+ WriteCloseSpace();
+ break;
+ }
+
+ case TypeEntryKind::Array: {
+ auto* array_type = cast<ArrayType>(&type);
+ WriteOpenSpace("array");
+ WriteField(array_type->field);
+ WriteCloseSpace();
+ break;
+ }
+ }
+ WriteCloseNewline();
+}
+
+void WatWriter::WriteField(const Field& field) {
+ if (field.mutable_) {
+ WriteOpenSpace("mut");
+ }
+ WriteType(field.type, NextChar::Space);
+ if (field.mutable_) {
+ WriteCloseSpace();
+ }
+}
+
+void WatWriter::WriteStartFunction(const Var& start) {
+ WriteOpenSpace("start");
+ WriteVar(start, NextChar::None);
+ WriteCloseNewline();
+}
+
+Result WatWriter::WriteModule() {
+ BuildInlineExportMap();
+ BuildInlineImportMap();
+ WriteOpenSpace("module");
+ if (module.name.empty()) {
+ WriteNewline(NO_FORCE_NEWLINE);
+ } else {
+ WriteName(module.name, NextChar::Newline);
+ }
+ for (const ModuleField& field : module.fields) {
+ switch (field.type()) {
+ case ModuleFieldType::Func:
+ WriteFunc(cast<FuncModuleField>(&field)->func);
+ break;
+ case ModuleFieldType::Global:
+ WriteGlobal(cast<GlobalModuleField>(&field)->global);
+ break;
+ case ModuleFieldType::Import:
+ WriteImport(*cast<ImportModuleField>(&field)->import);
+ break;
+ case ModuleFieldType::Tag:
+ WriteTag(cast<TagModuleField>(&field)->tag);
+ break;
+ case ModuleFieldType::Export:
+ WriteExport(cast<ExportModuleField>(&field)->export_);
+ break;
+ case ModuleFieldType::Table:
+ WriteTable(cast<TableModuleField>(&field)->table);
+ break;
+ case ModuleFieldType::ElemSegment:
+ WriteElemSegment(cast<ElemSegmentModuleField>(&field)->elem_segment);
+ break;
+ case ModuleFieldType::Memory:
+ WriteMemory(cast<MemoryModuleField>(&field)->memory);
+ break;
+ case ModuleFieldType::DataSegment:
+ WriteDataSegment(cast<DataSegmentModuleField>(&field)->data_segment);
+ break;
+ case ModuleFieldType::Type:
+ WriteTypeEntry(*cast<TypeModuleField>(&field)->type);
+ break;
+ case ModuleFieldType::Start:
+ WriteStartFunction(cast<StartModuleField>(&field)->start);
+ break;
+ }
+ }
+ WriteCloseNewline();
+ /* force the newline to be written */
+ WriteNextChar();
+ return result_;
+}
+
+void WatWriter::BuildInlineExportMap() {
+ if (!options_.inline_export) {
+ return;
+ }
+
+ for (Export* export_ : module.exports) {
+ Index index = kInvalidIndex;
+
+ // Exported imports can't be written with inline exports, unless the
+ // imports are also inline. For example, the following is invalid:
+ //
+ // (import "module" "field" (func (export "e")))
+ //
+ // But this is valid:
+ //
+ // (func (export "e") (import "module" "field"))
+ //
+ if (!options_.inline_import && module.IsImport(*export_)) {
+ continue;
+ }
+
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ index = module.GetFuncIndex(export_->var);
+ break;
+
+ case ExternalKind::Table:
+ index = module.GetTableIndex(export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ index = module.GetMemoryIndex(export_->var);
+ break;
+
+ case ExternalKind::Global:
+ index = module.GetGlobalIndex(export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ index = module.GetTagIndex(export_->var);
+ break;
+ }
+
+ if (index != kInvalidIndex) {
+ auto key = std::make_pair(export_->kind, index);
+ inline_export_map_.insert(std::make_pair(key, export_));
+ }
+ }
+}
+
+void WatWriter::WriteInlineExports(ExternalKind kind, Index index) {
+ if (!options_.inline_export) {
+ return;
+ }
+
+ auto iter_pair = inline_export_map_.equal_range(std::make_pair(kind, index));
+ for (auto iter = iter_pair.first; iter != iter_pair.second; ++iter) {
+ const Export* export_ = iter->second;
+ WriteOpenSpace("export");
+ WriteQuotedString(export_->name, NextChar::None);
+ WriteCloseSpace();
+ }
+}
+
+bool WatWriter::IsInlineExport(const Export& export_) {
+ Index index;
+ switch (export_.kind) {
+ case ExternalKind::Func:
+ index = module.GetFuncIndex(export_.var);
+ break;
+
+ case ExternalKind::Table:
+ index = module.GetTableIndex(export_.var);
+ break;
+
+ case ExternalKind::Memory:
+ index = module.GetMemoryIndex(export_.var);
+ break;
+
+ case ExternalKind::Global:
+ index = module.GetGlobalIndex(export_.var);
+ break;
+
+ case ExternalKind::Tag:
+ index = module.GetTagIndex(export_.var);
+ break;
+ }
+
+ return inline_export_map_.find(std::make_pair(export_.kind, index)) !=
+ inline_export_map_.end();
+}
+
+void WatWriter::BuildInlineImportMap() {
+ if (!options_.inline_import) {
+ return;
+ }
+
+ for (const Import* import : module.imports) {
+ inline_import_map_[static_cast<size_t>(import->kind())].push_back(import);
+ }
+}
+
+void WatWriter::WriteInlineImport(ExternalKind kind, Index index) {
+ if (!options_.inline_import) {
+ return;
+ }
+
+ size_t kind_index = static_cast<size_t>(kind);
+
+ if (index >= inline_import_map_[kind_index].size()) {
+ return;
+ }
+
+ const Import* import = inline_import_map_[kind_index][index];
+ WriteOpenSpace("import");
+ WriteQuotedString(import->module_name, NextChar::Space);
+ WriteQuotedString(import->field_name, NextChar::Space);
+ WriteCloseSpace();
+}
+
+} // end anonymous namespace
+
+Result WriteWat(Stream* stream,
+ const Module* module,
+ const WriteWatOptions& options) {
+ WatWriter wat_writer(stream, options, *module);
+ return wat_writer.WriteModule();
+}
+
+} // namespace wabt
diff --git a/third_party/wasm2c/src/wat-writer.h b/third_party/wasm2c/src/wat-writer.h
new file mode 100644
index 0000000000..ec8a3eeb71
--- /dev/null
+++ b/third_party/wasm2c/src/wat-writer.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef WABT_WAT_WRITER_H_
+#define WABT_WAT_WRITER_H_
+
+#include "src/common.h"
+
+namespace wabt {
+
+struct Module;
+class Stream;
+
+struct WriteWatOptions {
+ bool fold_exprs = false; // Write folded expressions.
+ bool inline_export = false;
+ bool inline_import = false;
+};
+
+Result WriteWat(Stream*, const Module*, const WriteWatOptions&);
+
+} // namespace wabt
+
+#endif /* WABT_WAT_WRITER_H_ */