Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/wasm2c/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 56 kB image not shown  

Quelle  wat-writer.cc   Sprache: C

 
/*
 * 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);
  void WriteCustom(const Custom& custom);

  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");
  const auto type_var =
      expr->decl.has_func_type
          ? expr->decl.type_var
          : Var{writer_->module.GetFuncTypeIndex(expr->decl), expr->loc};
  writer_->WriteVar(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->destmemidx, expr->srcmemidx,
                                            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");
  const auto type_var =
      expr->decl.has_func_type
          ? expr->decl.type_var
          : Var{writer_->module.GetFuncTypeIndex(expr->decl), expr->loc};
  writer_->WriteVar(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);
  if (memory.page_size != WABT_DEFAULT_PAGE_SIZE) {
    WriteOpenSpace("pagesize");
    Writef("%u", memory.page_size);
    WriteCloseSpace();
  }
  WriteCloseNewline();
  memory_index_++;
}

void WatWriter::WriteDataSegment(const DataSegment& segment) {
  WriteOpenSpace("data");
  WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space);
  if (segment.kind != SegmentKind::Passive) {
    if (module.GetMemoryIndex(segment.memory_var) != 0) {
      WriteOpenSpace("memory");
      WriteVar(segment.memory_var, NextChar::Space);
      WriteCloseSpace();
    }
    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();
}

void WatWriter::WriteCustom(const Custom& custom) {
  WriteOpenSpace("@custom");
  WriteQuotedString(custom.name, NextChar::Space);
  WriteQuotedData(custom.data.data(), custom.data.size());
  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;
    }
  }
  if (options_.features.annotations_enabled()) {
    for (const Custom& custom : module.customs) {
      WriteCustom(custom);
    }
  }
  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

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.