summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src/wat-writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/wasm2c/src/wat-writer.cc')
-rw-r--r--third_party/wasm2c/src/wat-writer.cc1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/wat-writer.cc b/third_party/wasm2c/src/wat-writer.cc
new file mode 100644
index 0000000000..ea70edb389
--- /dev/null
+++ b/third_party/wasm2c/src/wat-writer.cc
@@ -0,0 +1,1789 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wabt/wat-writer.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <cstdarg>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/expr-visitor.h"
+#include "wabt/ir-util.h"
+#include "wabt/ir.h"
+#include "wabt/literal.h"
+#include "wabt/stream.h"
+
+#define WABT_TRACING 0
+#include "wabt/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(std::string_view str, NextChar next_char);
+ void WriteNameOrIndex(std::string_view str, Index index, NextChar next_char);
+ void WriteQuotedData(const void* data, size_t length);
+ void WriteQuotedString(std::string_view str, NextChar next_char);
+ void WriteVar(const Var& var, NextChar next_char);
+ void WriteVarUnlessZero(const Var& var, NextChar next_char);
+ void WriteMemoryVarUnlessZero(const Var& memidx, NextChar next_char);
+ void WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ 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);
+ template <typename T>
+ void WriteMemoryLoadStoreExpr(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(std::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(std::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(std::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::WriteMemoryVarUnlessZero(const Var& memidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(memidx) != 0) {
+ WriteVar(memidx, next_char);
+ } else {
+ next_char_ = next_char;
+ }
+}
+
+void WatWriter::WriteTwoMemoryVarsUnlessBothZero(const Var& srcmemidx,
+ const Var& destmemidx,
+ NextChar next_char) {
+ if (module.GetMemoryIndex(srcmemidx) != 0 ||
+ module.GetMemoryIndex(destmemidx) != 0) {
+ WriteVar(srcmemidx, NextChar::Space);
+ WriteVar(destmemidx, next_char);
+ } else {
+ next_char_ = 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) {
+ WritePuts(type.GetName().c_str(), 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);
+}
+
+template <typename T>
+void WatWriter::WriteMemoryLoadStoreExpr(const Expr* expr) {
+ auto typed_expr = cast<T>(expr);
+ WritePutsSpace(typed_expr->opcode.GetName());
+ WriteMemoryVarUnlessZero(typed_expr->memidx, NextChar::Space);
+ 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 OnCodeMetadataExpr(CodeMetadataExpr*) 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::OnCodeMetadataExpr(
+ CodeMetadataExpr* expr) {
+ writer_->WriteOpen("@metadata.code.", NextChar::None);
+ writer_->WriteDataWithNextChar(expr->name.data(), expr->name.size());
+ writer_->WritePutc(' ');
+ writer_->WriteQuotedData(expr->data.data(), expr->data.size());
+ writer_->WriteCloseSpace();
+ 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_->WriteMemoryLoadStoreExpr<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_->WritePutsSpace(Opcode::MemoryCopy_Opcode.GetName());
+ writer_->WriteTwoMemoryVarsUnlessBothZero(expr->srcmemidx, expr->destmemidx,
+ NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ 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_->WritePutsSpace(Opcode::MemoryFill_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryGrowExpr(MemoryGrowExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryGrow_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemorySizeExpr(MemorySizeExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemorySize_Opcode.GetName());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_NEWLINE);
+ return Result::Ok;
+}
+
+Result WatWriter::ExprVisitorDelegate::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ writer_->WritePutsSpace(Opcode::MemoryInit_Opcode.GetName());
+ writer_->WriteVar(expr->var, NextChar::Space);
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ writer_->WriteNewline(NO_FORCE_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_->WriteMemoryLoadStoreExpr<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());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ 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());
+ writer_->WriteMemoryVarUnlessZero(expr->memidx, NextChar::Space);
+ 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_, Location()))) {
+ // 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");
+ // The first name we encounter here, pre-bulk-memory, was intended to refer to
+ // the table while segment names were not supported at all. For this reason
+ // we cannot emit a segment name here without bulk-memory enabled, otherwise
+ // the name will be assumed to be the name of a table and parsing will fail.
+ if (options_.features.bulk_memory_enabled()) {
+ WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space);
+ } else {
+ Writef("(;%u;)", elem_segment_index_);
+ }
+
+ 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 ExprList& expr : segment.elem_exprs) {
+ if (flags & SegUseElemExprs) {
+ WriteInitExpr(expr);
+ } else {
+ assert(expr.size() == 1);
+ assert(expr.front().type() == ExprType::RefFunc);
+ WriteVar(cast<const RefFuncExpr>(&expr.front())->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) {
+ WriteMemoryVarUnlessZero(segment.memory_var, NextChar::Space);
+ 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