| /* |
| * 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); |
| 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(); |
| } |
| |
| 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 |