WasmResult wasm_init_mem_writer_existing(WasmMemoryWriter* writer,
                                         WasmOutputBuffer* buf) {
  WASM_ZERO_MEMORY(*writer);
  writer->base.user_data = writer;
  writer->base.write_data = write_data_to_output_buffer;
  writer->base.move_data = move_data_in_output_buffer;
  writer->buf = *buf;
  /* Clear buffer, since ownership has passed to the writer. */
  WASM_ZERO_MEMORY(*buf);
  return WASM_OK;
}
static WasmResult init_thread(WasmAllocator* allocator,
                              WasmInterpreterModule* module,
                              WasmInterpreterThread* thread) {
  WASM_ZERO_MEMORY(*thread);
  return wasm_init_interpreter_thread(allocator, module, thread,
                                      &s_thread_options);
}
void wasm_init_file_writer_existing(WasmFileWriter* writer, FILE* file) {
  WASM_ZERO_MEMORY(*writer);
  writer->file = file;
  writer->offset = 0;
  writer->base.user_data = writer;
  writer->base.write_data = write_data_to_file;
  writer->base.move_data = move_data_in_file;
}
WasmResult wasm_init_mem_writer(WasmAllocator* allocator,
                                WasmMemoryWriter* writer) {
  WASM_ZERO_MEMORY(*writer);
  writer->base.user_data = writer;
  writer->base.write_data = write_data_to_output_buffer;
  writer->base.move_data = move_data_in_output_buffer;
  wasm_init_output_buffer(allocator, &writer->buf,
                          INITIAL_OUTPUT_BUFFER_CAPACITY);
  return WASM_OK;
}
static WasmResult read_module(WasmAllocator* allocator,
                              const void* data,
                              size_t size,
                              WasmInterpreterModule* out_module,
                              WasmInterpreterThread* out_thread) {
  WasmAllocator* memory_allocator = &g_wasm_libc_allocator;
  WASM_ZERO_MEMORY(*out_module);
  return wasm_read_binary_interpreter(allocator, memory_allocator, data, size,
                                      &s_read_binary_options, &s_error_handler,
                                      out_module);
}
int main(int argc, char** argv) {
  WasmResult result;
  WasmStackAllocator stack_allocator;
  WasmAllocator* allocator;

  wasm_init_stdio();
  parse_options(argc, argv);

  if (s_use_libc_allocator) {
    allocator = &g_wasm_libc_allocator;
  } else {
    wasm_init_stack_allocator(&stack_allocator, &g_wasm_libc_allocator);
    allocator = &stack_allocator.allocator;
  }

  void* data;
  size_t size;
  result = wasm_read_file(allocator, s_infile, &data, &size);
  if (WASM_SUCCEEDED(result)) {
    WasmModule module;
    WASM_ZERO_MEMORY(module);
    result = wasm_read_binary_ast(allocator, data, size, &s_read_binary_options,
                                  &s_error_handler, &module);
    if (WASM_SUCCEEDED(result)) {
      if (s_generate_names)
        result = wasm_generate_names(allocator, &module);

      if (WASM_SUCCEEDED(result))
        result = wasm_apply_names(allocator, &module);

      if (WASM_SUCCEEDED(result)) {
        WasmFileWriter file_writer;
        if (s_outfile) {
          result = wasm_init_file_writer(&file_writer, s_outfile);
        } else {
          wasm_init_file_writer_existing(&file_writer, stdout);
        }

        if (WASM_SUCCEEDED(result)) {
          result = wasm_write_ast(allocator, &file_writer.base, &module);
          wasm_close_file_writer(&file_writer);
        }
      }
    }

    if (s_use_libc_allocator)
      wasm_destroy_module(allocator, &module);
    wasm_free(allocator, data);
    wasm_print_allocator_stats(allocator);
    wasm_destroy_allocator(allocator);
  }
  return result;
}
static void parse_options(int argc, char** argv) {
  WasmOptionParser parser;
  WASM_ZERO_MEMORY(parser);
  parser.description = s_description;
  parser.options = s_options;
  parser.num_options = WASM_ARRAY_SIZE(s_options);
  parser.on_option = on_option;
  parser.on_argument = on_argument;
  parser.on_error = on_option_error;
  wasm_parse_options(&parser, argc, argv);

  if (!s_infile) {
    wasm_print_help(&parser, PROGRAM_NAME);
    WASM_FATAL("No filename given.\n");
  }
}
WasmResult wasm_init_stack_allocator(WasmStackAllocator* stack_allocator,
                                     WasmAllocator* fallback) {
  WASM_ZERO_MEMORY(*stack_allocator);
  stack_allocator->allocator.alloc = stack_alloc;
  stack_allocator->allocator.realloc = stack_realloc;
  stack_allocator->allocator.free = stack_free;
  stack_allocator->allocator.destroy = stack_destroy;
  stack_allocator->allocator.mark = stack_mark;
  stack_allocator->allocator.reset_to_mark = stack_reset_to_mark;
  stack_allocator->allocator.print_stats = stack_print_stats;
  stack_allocator->allocator.setjmp_handler = stack_setjmp_handler;
  stack_allocator->fallback = fallback;

  WasmStackAllocatorChunk* chunk =
      allocate_chunk(stack_allocator, CHUNK_MAX_AVAIL, __FILE__, __LINE__);
  chunk->prev = NULL;
  stack_allocator->first = stack_allocator->last = chunk;
  return WASM_OK;
}
int main(int argc, char** argv) {
  WasmStackAllocator stack_allocator;
  WasmAllocator* allocator;

  wasm_init_stdio();

  wasm_init_file_writer_existing(&s_log_stream_writer, stdout);
  wasm_init_stream(&s_log_stream, &s_log_stream_writer.base, NULL);
  parse_options(argc, argv);

  if (s_use_libc_allocator) {
    allocator = &g_wasm_libc_allocator;
  } else {
    wasm_init_stack_allocator(&stack_allocator, &g_wasm_libc_allocator);
    allocator = &stack_allocator.allocator;
  }

  WasmAstLexer* lexer = wasm_new_ast_file_lexer(allocator, s_infile);
  if (!lexer)
    WASM_FATAL("unable to read %s\n", s_infile);

  WasmScript script;
  WasmResult result = wasm_parse_ast(lexer, &script, &s_error_handler);

  if (WASM_SUCCEEDED(result)) {
    result =
        wasm_resolve_names_script(allocator, lexer, &script, &s_error_handler);

    if (WASM_SUCCEEDED(result) && s_validate) {
      result =
          wasm_validate_script(allocator, lexer, &script, &s_error_handler);
    }

    if (WASM_SUCCEEDED(result) && s_validate_assert_invalid_and_malformed) {
      WasmDefaultErrorHandlerInfo assert_invalid_info;
      WasmSourceErrorHandler assert_invalid_error_handler;
      init_source_error_handler(&assert_invalid_error_handler,
                                &assert_invalid_info, "assert_invalid error");

      WasmDefaultErrorHandlerInfo assert_malformed_info;
      WasmSourceErrorHandler assert_malformed_error_handler;
      init_source_error_handler(&assert_malformed_error_handler,
                                &assert_malformed_info,
                                "assert_malformed error");

      result = wasm_validate_assert_invalid_and_malformed(
          allocator, lexer, &script, &assert_invalid_error_handler,
          &assert_malformed_error_handler, &s_error_handler);
    }

    if (WASM_SUCCEEDED(result)) {
      if (s_spec) {
        s_write_binary_spec_options.json_filename = s_outfile;
        s_write_binary_spec_options.write_binary_options =
            s_write_binary_options;
        result = wasm_write_binary_spec_script(allocator, &script, s_infile,
                                               &s_write_binary_spec_options);
      } else {
        WasmMemoryWriter writer;
        WASM_ZERO_MEMORY(writer);
        if (WASM_FAILED(wasm_init_mem_writer(allocator, &writer)))
          WASM_FATAL("unable to open memory writer for writing\n");

        result = wasm_write_binary_script(allocator, &writer.base, &script,
                                          &s_write_binary_options);
        if (WASM_SUCCEEDED(result))
          write_buffer_to_file(s_outfile, &writer.buf);
        wasm_close_mem_writer(&writer);
      }
    }
  }

  wasm_destroy_ast_lexer(lexer);

  if (s_use_libc_allocator)
    wasm_destroy_script(&script);
  wasm_print_allocator_stats(allocator);
  wasm_destroy_allocator(allocator);
  return result;
}