summaryrefslogtreecommitdiff
path: root/src/ast-writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ast-writer.cc')
-rw-r--r--src/ast-writer.cc756
1 files changed, 756 insertions, 0 deletions
diff --git a/src/ast-writer.cc b/src/ast-writer.cc
new file mode 100644
index 00000000..58f220f4
--- /dev/null
+++ b/src/ast-writer.cc
@@ -0,0 +1,756 @@
+/*
+ * 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 "ast-writer.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ast.h"
+#include "common.h"
+#include "literal.h"
+#include "stream.h"
+#include "writer.h"
+
+#define INDENT_SIZE 2
+#define NO_FORCE_NEWLINE 0
+#define FORCE_NEWLINE 1
+
+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};
+
+typedef enum NextChar {
+ NEXT_CHAR_NONE,
+ NEXT_CHAR_SPACE,
+ NEXT_CHAR_NEWLINE,
+ NEXT_CHAR_FORCE_NEWLINE,
+} NextChar;
+
+typedef struct Context {
+ WabtStream stream;
+ WabtResult result;
+ int indent;
+ NextChar next_char;
+ int depth;
+ WabtStringSliceVector index_to_name;
+
+ int func_index;
+ int global_index;
+ int export_index;
+ int table_index;
+ int memory_index;
+ int func_type_index;
+} Context;
+
+static void indent(Context* ctx) {
+ ctx->indent += INDENT_SIZE;
+}
+
+static void dedent(Context* ctx) {
+ ctx->indent -= INDENT_SIZE;
+ assert(ctx->indent >= 0);
+}
+
+static void write_indent(Context* ctx) {
+ static char s_indent[] =
+ " "
+ " ";
+ static size_t s_indent_len = sizeof(s_indent) - 1;
+ size_t indent = ctx->indent;
+ while (indent > s_indent_len) {
+ wabt_write_data(&ctx->stream, s_indent, s_indent_len, NULL);
+ indent -= s_indent_len;
+ }
+ if (indent > 0) {
+ wabt_write_data(&ctx->stream, s_indent, indent, NULL);
+ }
+}
+
+static void write_next_char(Context* ctx) {
+ switch (ctx->next_char) {
+ case NEXT_CHAR_SPACE:
+ wabt_write_data(&ctx->stream, " ", 1, NULL);
+ break;
+ case NEXT_CHAR_NEWLINE:
+ case NEXT_CHAR_FORCE_NEWLINE:
+ wabt_write_data(&ctx->stream, "\n", 1, NULL);
+ write_indent(ctx);
+ break;
+
+ default:
+ case NEXT_CHAR_NONE:
+ break;
+ }
+ ctx->next_char = NEXT_CHAR_NONE;
+}
+
+static void write_data_with_next_char(Context* ctx,
+ const void* src,
+ size_t size) {
+ write_next_char(ctx);
+ wabt_write_data(&ctx->stream, src, size, NULL);
+}
+
+static void WABT_PRINTF_FORMAT(2, 3)
+ writef(Context* ctx, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ /* default to following space */
+ write_data_with_next_char(ctx, buffer, length);
+ ctx->next_char = NEXT_CHAR_SPACE;
+}
+
+static void write_putc(Context* ctx, char c) {
+ wabt_write_data(&ctx->stream, &c, 1, NULL);
+}
+
+static void write_puts(Context* ctx, const char* s, NextChar next_char) {
+ size_t len = strlen(s);
+ write_data_with_next_char(ctx, s, len);
+ ctx->next_char = next_char;
+}
+
+static void write_puts_space(Context* ctx, const char* s) {
+ write_puts(ctx, s, NEXT_CHAR_SPACE);
+}
+
+static void write_puts_newline(Context* ctx, const char* s) {
+ write_puts(ctx, s, NEXT_CHAR_NEWLINE);
+}
+
+static void write_newline(Context* ctx, bool force) {
+ if (ctx->next_char == NEXT_CHAR_FORCE_NEWLINE)
+ write_next_char(ctx);
+ ctx->next_char = force ? NEXT_CHAR_FORCE_NEWLINE : NEXT_CHAR_NEWLINE;
+}
+
+static void write_open(Context* ctx, const char* name, NextChar next_char) {
+ write_puts(ctx, "(", NEXT_CHAR_NONE);
+ write_puts(ctx, name, next_char);
+ indent(ctx);
+}
+
+static void write_open_newline(Context* ctx, const char* name) {
+ write_open(ctx, name, NEXT_CHAR_NEWLINE);
+}
+
+static void write_open_space(Context* ctx, const char* name) {
+ write_open(ctx, name, NEXT_CHAR_SPACE);
+}
+
+static void write_close(Context* ctx, NextChar next_char) {
+ if (ctx->next_char != NEXT_CHAR_FORCE_NEWLINE)
+ ctx->next_char = NEXT_CHAR_NONE;
+ dedent(ctx);
+ write_puts(ctx, ")", next_char);
+}
+
+static void write_close_newline(Context* ctx) {
+ write_close(ctx, NEXT_CHAR_NEWLINE);
+}
+
+static void write_close_space(Context* ctx) {
+ write_close(ctx, NEXT_CHAR_SPACE);
+}
+
+static void write_string_slice(Context* ctx,
+ const WabtStringSlice* str,
+ NextChar next_char) {
+ writef(ctx, PRIstringslice, WABT_PRINTF_STRING_SLICE_ARG(*str));
+ ctx->next_char = next_char;
+}
+
+static bool write_string_slice_opt(Context* ctx,
+ const WabtStringSlice* str,
+ NextChar next_char) {
+ if (str->start)
+ write_string_slice(ctx, str, next_char);
+ return str->start != NULL;
+}
+
+static void write_string_slice_or_index(Context* ctx,
+ const WabtStringSlice* str,
+ uint32_t index,
+ NextChar next_char) {
+ if (str->start)
+ write_string_slice(ctx, str, next_char);
+ else
+ writef(ctx, "(;%u;)", index);
+}
+
+static void write_quoted_data(Context* ctx, const void* data, size_t length) {
+ const uint8_t* u8_data = (const uint8_t*)data;
+ static const char s_hexdigits[] = "0123456789abcdef";
+ write_next_char(ctx);
+ write_putc(ctx, '\"');
+ size_t i;
+ for (i = 0; i < length; ++i) {
+ uint8_t c = u8_data[i];
+ if (s_is_char_escaped[c]) {
+ write_putc(ctx, '\\');
+ write_putc(ctx, s_hexdigits[c >> 4]);
+ write_putc(ctx, s_hexdigits[c & 0xf]);
+ } else {
+ write_putc(ctx, c);
+ }
+ }
+ write_putc(ctx, '\"');
+ ctx->next_char = NEXT_CHAR_SPACE;
+}
+
+static void write_quoted_string_slice(Context* ctx,
+ const WabtStringSlice* str,
+ NextChar next_char) {
+ write_quoted_data(ctx, str->start, str->length);
+ ctx->next_char = next_char;
+}
+
+static void write_var(Context* ctx, const WabtVar* var, NextChar next_char) {
+ if (var->type == WABT_VAR_TYPE_INDEX) {
+ writef(ctx, "%" PRId64, var->index);
+ ctx->next_char = next_char;
+ } else {
+ write_string_slice(ctx, &var->name, next_char);
+ }
+}
+
+static void write_br_var(Context* ctx, const WabtVar* var, NextChar next_char) {
+ if (var->type == WABT_VAR_TYPE_INDEX) {
+ writef(ctx, "%" PRId64 " (;@%" PRId64 ";)", var->index,
+ ctx->depth - var->index - 1);
+ ctx->next_char = next_char;
+ } else {
+ write_string_slice(ctx, &var->name, next_char);
+ }
+}
+
+static void write_type(Context* ctx, WabtType type, NextChar next_char) {
+ const char* type_name = wabt_get_type_name(type);
+ assert(type_name != NULL);
+ write_puts(ctx, type_name, next_char);
+}
+
+static void write_types(Context* ctx,
+ const WabtTypeVector* types,
+ const char* name) {
+ if (types->size) {
+ size_t i;
+ if (name)
+ write_open_space(ctx, name);
+ for (i = 0; i < types->size; ++i)
+ write_type(ctx, types->data[i], NEXT_CHAR_SPACE);
+ if (name)
+ write_close_space(ctx);
+ }
+}
+
+static void write_func_sig_space(Context* ctx,
+ const WabtFuncSignature* func_sig) {
+ write_types(ctx, &func_sig->param_types, "param");
+ write_types(ctx, &func_sig->result_types, "result");
+}
+
+static void write_expr_list(Context* ctx, const WabtExpr* first);
+
+static void write_expr(Context* ctx, const WabtExpr* expr);
+
+static void write_begin_block(Context* ctx,
+ const WabtBlock* block,
+ const char* text) {
+ write_puts_space(ctx, text);
+ bool has_label =
+ write_string_slice_opt(ctx, &block->label, NEXT_CHAR_SPACE);
+ write_types(ctx, &block->sig, NULL);
+ if (!has_label)
+ writef(ctx, " ;; label = @%d", ctx->depth);
+ write_newline(ctx, FORCE_NEWLINE);
+ ctx->depth++;
+ indent(ctx);
+}
+
+static void write_end_block(Context* ctx) {
+ dedent(ctx);
+ ctx->depth--;
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_END));
+}
+
+static void write_block(Context* ctx,
+ const WabtBlock* block,
+ const char* start_text) {
+ write_begin_block(ctx, block, start_text);
+ write_expr_list(ctx, block->first);
+ write_end_block(ctx);
+}
+
+static void write_const(Context* ctx, const WabtConst* const_) {
+ switch (const_->type) {
+ case WABT_TYPE_I32:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_I32_CONST));
+ writef(ctx, "%d", (int32_t)const_->u32);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+
+ case WABT_TYPE_I64:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_I64_CONST));
+ writef(ctx, "%" PRId64, (int64_t)const_->u64);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+
+ case WABT_TYPE_F32: {
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_F32_CONST));
+ char buffer[128];
+ wabt_write_float_hex(buffer, 128, const_->f32_bits);
+ write_puts_space(ctx, buffer);
+ float f32;
+ memcpy(&f32, &const_->f32_bits, sizeof(f32));
+ writef(ctx, "(;=%g;)", f32);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+ }
+
+ case WABT_TYPE_F64: {
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_F64_CONST));
+ char buffer[128];
+ wabt_write_double_hex(buffer, 128, const_->f64_bits);
+ write_puts_space(ctx, buffer);
+ double f64;
+ memcpy(&f64, &const_->f64_bits, sizeof(f64));
+ writef(ctx, "(;=%g;)", f64);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static void write_expr(Context* ctx, const WabtExpr* expr) {
+ switch (expr->type) {
+ case WABT_EXPR_TYPE_BINARY:
+ write_puts_newline(ctx, wabt_get_opcode_name(expr->binary.opcode));
+ break;
+
+ case WABT_EXPR_TYPE_BLOCK:
+ write_block(ctx, &expr->block, wabt_get_opcode_name(WABT_OPCODE_BLOCK));
+ break;
+
+ case WABT_EXPR_TYPE_BR:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_BR));
+ write_br_var(ctx, &expr->br.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_BR_IF:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_BR_IF));
+ write_br_var(ctx, &expr->br_if.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_BR_TABLE: {
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_BR_TABLE));
+ size_t i;
+ for (i = 0; i < expr->br_table.targets.size; ++i)
+ write_br_var(ctx, &expr->br_table.targets.data[i], NEXT_CHAR_SPACE);
+ write_br_var(ctx, &expr->br_table.default_target, NEXT_CHAR_NEWLINE);
+ break;
+ }
+
+ case WABT_EXPR_TYPE_CALL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_CALL));
+ write_var(ctx, &expr->call.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_CALL_INDIRECT:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_CALL_INDIRECT));
+ write_var(ctx, &expr->call_indirect.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_COMPARE:
+ write_puts_newline(ctx, wabt_get_opcode_name(expr->compare.opcode));
+ break;
+
+ case WABT_EXPR_TYPE_CONST:
+ write_const(ctx, &expr->const_);
+ break;
+
+ case WABT_EXPR_TYPE_CONVERT:
+ write_puts_newline(ctx, wabt_get_opcode_name(expr->convert.opcode));
+ break;
+
+ case WABT_EXPR_TYPE_DROP:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_DROP));
+ break;
+
+ case WABT_EXPR_TYPE_GET_GLOBAL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_GET_GLOBAL));
+ write_var(ctx, &expr->get_global.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_GET_LOCAL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_GET_LOCAL));
+ write_var(ctx, &expr->get_local.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_GROW_MEMORY:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_GROW_MEMORY));
+ break;
+
+ case WABT_EXPR_TYPE_IF:
+ write_begin_block(ctx, &expr->if_.true_,
+ wabt_get_opcode_name(WABT_OPCODE_IF));
+ write_expr_list(ctx, expr->if_.true_.first);
+ if (expr->if_.false_) {
+ dedent(ctx);
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_ELSE));
+ indent(ctx);
+ write_newline(ctx, FORCE_NEWLINE);
+ write_expr_list(ctx, expr->if_.false_);
+ }
+ write_end_block(ctx);
+ break;
+
+ case WABT_EXPR_TYPE_LOAD:
+ write_puts_space(ctx, wabt_get_opcode_name(expr->load.opcode));
+ if (expr->load.offset)
+ writef(ctx, "offset=%" PRIu64, expr->load.offset);
+ if (!wabt_is_naturally_aligned(expr->load.opcode, expr->load.align))
+ writef(ctx, "align=%u", expr->load.align);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_LOOP:
+ write_block(ctx, &expr->loop, wabt_get_opcode_name(WABT_OPCODE_LOOP));
+ break;
+
+ case WABT_EXPR_TYPE_CURRENT_MEMORY:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_CURRENT_MEMORY));
+ break;
+
+ case WABT_EXPR_TYPE_NOP:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_NOP));
+ break;
+
+ case WABT_EXPR_TYPE_RETURN:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_RETURN));
+ break;
+
+ case WABT_EXPR_TYPE_SELECT:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_SELECT));
+ break;
+
+ case WABT_EXPR_TYPE_SET_GLOBAL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_SET_GLOBAL));
+ write_var(ctx, &expr->set_global.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_SET_LOCAL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_SET_LOCAL));
+ write_var(ctx, &expr->set_local.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_STORE:
+ write_puts_space(ctx, wabt_get_opcode_name(expr->store.opcode));
+ if (expr->store.offset)
+ writef(ctx, "offset=%" PRIu64, expr->store.offset);
+ if (!wabt_is_naturally_aligned(expr->store.opcode, expr->store.align))
+ writef(ctx, "align=%u", expr->store.align);
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_TEE_LOCAL:
+ write_puts_space(ctx, wabt_get_opcode_name(WABT_OPCODE_TEE_LOCAL));
+ write_var(ctx, &expr->tee_local.var, NEXT_CHAR_NEWLINE);
+ break;
+
+ case WABT_EXPR_TYPE_UNARY:
+ write_puts_newline(ctx, wabt_get_opcode_name(expr->unary.opcode));
+ break;
+
+ case WABT_EXPR_TYPE_UNREACHABLE:
+ write_puts_newline(ctx, wabt_get_opcode_name(WABT_OPCODE_UNREACHABLE));
+ break;
+
+ default:
+ fprintf(stderr, "bad expr type: %d\n", expr->type);
+ assert(0);
+ break;
+ }
+}
+
+static void write_expr_list(Context* ctx, const WabtExpr* first) {
+ const WabtExpr* expr;
+ for (expr = first; expr; expr = expr->next)
+ write_expr(ctx, expr);
+}
+
+static void write_init_expr(Context* ctx, const WabtExpr* expr) {
+ if (expr) {
+ write_puts(ctx, "(", NEXT_CHAR_NONE);
+ write_expr(ctx, expr);
+ /* clear the next char, so we don't write a newline after the expr */
+ ctx->next_char = NEXT_CHAR_NONE;
+ write_puts(ctx, ")", NEXT_CHAR_SPACE);
+ }
+}
+
+static void write_type_bindings(Context* ctx,
+ const char* prefix,
+ const WabtFunc* func,
+ const WabtTypeVector* types,
+ const WabtBindingHash* bindings) {
+ wabt_make_type_binding_reverse_mapping(types, bindings, &ctx->index_to_name);
+
+ /* 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 is_open = false;
+ size_t i;
+ for (i = 0; i < types->size; ++i) {
+ if (!is_open) {
+ write_open_space(ctx, prefix);
+ is_open = true;
+ }
+
+ const WabtStringSlice* name = &ctx->index_to_name.data[i];
+ bool has_name = name->start != NULL;
+ if (has_name)
+ write_string_slice(ctx, name, NEXT_CHAR_SPACE);
+ write_type(ctx, types->data[i], NEXT_CHAR_SPACE);
+ if (has_name) {
+ write_close_space(ctx);
+ is_open = false;
+ }
+ }
+ if (is_open)
+ write_close_space(ctx);
+}
+
+static void write_func(Context* ctx,
+ const WabtModule* module,
+ const WabtFunc* func) {
+ write_open_space(ctx, "func");
+ write_string_slice_or_index(ctx, &func->name, ctx->func_index++,
+ NEXT_CHAR_SPACE);
+ if (wabt_decl_has_func_type(&func->decl)) {
+ write_open_space(ctx, "type");
+ write_var(ctx, &func->decl.type_var, NEXT_CHAR_NONE);
+ write_close_space(ctx);
+ }
+ write_type_bindings(ctx, "param", func, &func->decl.sig.param_types,
+ &func->param_bindings);
+ write_types(ctx, &func->decl.sig.result_types, "result");
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ if (func->local_types.size) {
+ write_type_bindings(ctx, "local", func, &func->local_types,
+ &func->local_bindings);
+ }
+ write_newline(ctx, NO_FORCE_NEWLINE);
+ ctx->depth = 1; /* for the implicit "return" label */
+ write_expr_list(ctx, func->first_expr);
+ write_close_newline(ctx);
+}
+
+static void write_begin_global(Context* ctx, const WabtGlobal* global) {
+ write_open_space(ctx, "global");
+ write_string_slice_or_index(ctx, &global->name, ctx->global_index++,
+ NEXT_CHAR_SPACE);
+ if (global->mutable_) {
+ write_open_space(ctx, "mut");
+ write_type(ctx, global->type, NEXT_CHAR_SPACE);
+ write_close_space(ctx);
+ } else {
+ write_type(ctx, global->type, NEXT_CHAR_SPACE);
+ }
+}
+
+static void write_global(Context* ctx, const WabtGlobal* global) {
+ write_begin_global(ctx, global);
+ write_init_expr(ctx, global->init_expr);
+ write_close_newline(ctx);
+}
+
+static void write_limits(Context* ctx, const WabtLimits* limits) {
+ writef(ctx, "%" PRIu64, limits->initial);
+ if (limits->has_max)
+ writef(ctx, "%" PRIu64, limits->max);
+}
+
+static void write_table(Context* ctx, const WabtTable* table) {
+ write_open_space(ctx, "table");
+ write_string_slice_or_index(ctx, &table->name, ctx->table_index++,
+ NEXT_CHAR_SPACE);
+ write_limits(ctx, &table->elem_limits);
+ write_puts_space(ctx, "anyfunc");
+ write_close_newline(ctx);
+}
+
+static void write_elem_segment(Context* ctx, const WabtElemSegment* segment) {
+ write_open_space(ctx, "elem");
+ write_init_expr(ctx, segment->offset);
+ size_t i;
+ for (i = 0; i < segment->vars.size; ++i)
+ write_var(ctx, &segment->vars.data[i], NEXT_CHAR_SPACE);
+ write_close_newline(ctx);
+}
+
+static void write_memory(Context* ctx, const WabtMemory* memory) {
+ write_open_space(ctx, "memory");
+ write_string_slice_or_index(ctx, &memory->name, ctx->memory_index++,
+ NEXT_CHAR_SPACE);
+ write_limits(ctx, &memory->page_limits);
+ write_close_newline(ctx);
+}
+
+static void write_data_segment(Context* ctx, const WabtDataSegment* segment) {
+ write_open_space(ctx, "data");
+ write_init_expr(ctx, segment->offset);
+ write_quoted_data(ctx, segment->data, segment->size);
+ write_close_newline(ctx);
+}
+
+static void write_import(Context* ctx, const WabtImport* import) {
+ write_open_space(ctx, "import");
+ write_quoted_string_slice(ctx, &import->module_name, NEXT_CHAR_SPACE);
+ write_quoted_string_slice(ctx, &import->field_name, NEXT_CHAR_SPACE);
+ switch (import->kind) {
+ case WABT_EXTERNAL_KIND_FUNC:
+ write_open_space(ctx, "func");
+ write_string_slice_or_index(ctx, &import->func.name, ctx->func_index++,
+ NEXT_CHAR_SPACE);
+ if (wabt_decl_has_func_type(&import->func.decl)) {
+ write_open_space(ctx, "type");
+ write_var(ctx, &import->func.decl.type_var, NEXT_CHAR_NONE);
+ write_close_space(ctx);
+ } else {
+ write_func_sig_space(ctx, &import->func.decl.sig);
+ }
+ write_close_space(ctx);
+ break;
+
+ case WABT_EXTERNAL_KIND_TABLE:
+ write_table(ctx, &import->table);
+ break;
+
+ case WABT_EXTERNAL_KIND_MEMORY:
+ write_memory(ctx, &import->memory);
+ break;
+
+ case WABT_EXTERNAL_KIND_GLOBAL:
+ write_begin_global(ctx, &import->global);
+ write_close_space(ctx);
+ break;
+
+ case WABT_NUM_EXTERNAL_KINDS:
+ assert(0);
+ break;
+ }
+ write_close_newline(ctx);
+}
+
+static void write_export(Context* ctx, const WabtExport* export_) {
+ static const char* s_kind_names[] = {"func", "table", "memory", "global"};
+ WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_kind_names) == WABT_NUM_EXTERNAL_KINDS);
+ write_open_space(ctx, "export");
+ write_quoted_string_slice(ctx, &export_->name, NEXT_CHAR_SPACE);
+ assert(export_->kind < WABT_ARRAY_SIZE(s_kind_names));
+ write_open_space(ctx, s_kind_names[export_->kind]);
+ write_var(ctx, &export_->var, NEXT_CHAR_SPACE);
+ write_close_space(ctx);
+ write_close_newline(ctx);
+}
+
+static void write_func_type(Context* ctx, const WabtFuncType* func_type) {
+ write_open_space(ctx, "type");
+ write_string_slice_or_index(ctx, &func_type->name, ctx->func_type_index++,
+ NEXT_CHAR_SPACE);
+ write_open_space(ctx, "func");
+ write_func_sig_space(ctx, &func_type->sig);
+ write_close_space(ctx);
+ write_close_newline(ctx);
+}
+
+static void write_start_function(Context* ctx, const WabtVar* start) {
+ write_open_space(ctx, "start");
+ write_var(ctx, start, NEXT_CHAR_NONE);
+ write_close_newline(ctx);
+}
+
+static void write_module(Context* ctx, const WabtModule* module) {
+ write_open_newline(ctx, "module");
+ const WabtModuleField* field;
+ for (field = module->first_field; field != NULL; field = field->next) {
+ switch (field->type) {
+ case WABT_MODULE_FIELD_TYPE_FUNC:
+ write_func(ctx, module, &field->func);
+ break;
+ case WABT_MODULE_FIELD_TYPE_GLOBAL:
+ write_global(ctx, &field->global);
+ break;
+ case WABT_MODULE_FIELD_TYPE_IMPORT:
+ write_import(ctx, &field->import);
+ break;
+ case WABT_MODULE_FIELD_TYPE_EXPORT:
+ write_export(ctx, &field->export_);
+ break;
+ case WABT_MODULE_FIELD_TYPE_TABLE:
+ write_table(ctx, &field->table);
+ break;
+ case WABT_MODULE_FIELD_TYPE_ELEM_SEGMENT:
+ write_elem_segment(ctx, &field->elem_segment);
+ break;
+ case WABT_MODULE_FIELD_TYPE_MEMORY:
+ write_memory(ctx, &field->memory);
+ break;
+ case WABT_MODULE_FIELD_TYPE_DATA_SEGMENT:
+ write_data_segment(ctx, &field->data_segment);
+ break;
+ case WABT_MODULE_FIELD_TYPE_FUNC_TYPE:
+ write_func_type(ctx, &field->func_type);
+ break;
+ case WABT_MODULE_FIELD_TYPE_START:
+ write_start_function(ctx, &field->start);
+ break;
+ }
+ }
+ write_close_newline(ctx);
+ /* force the newline to be written */
+ write_next_char(ctx);
+}
+
+WabtResult wabt_write_ast(WabtWriter* writer, const WabtModule* module) {
+ Context ctx;
+ WABT_ZERO_MEMORY(ctx);
+ ctx.result = WABT_OK;
+ wabt_init_stream(&ctx.stream, writer, NULL);
+ write_module(&ctx, module);
+ /* the memory for the actual string slice is shared with the module, so we
+ * only need to free the vector */
+ wabt_destroy_string_slice_vector(&ctx.index_to_name);
+ return ctx.result;
+}