static WasmStackAllocatorChunk* allocate_chunk(
    WasmStackAllocator* stack_allocator,
    size_t max_avail,
    const char* file,
    int line) {
  assert(max_avail < SIZE_MAX - sizeof(WasmStackAllocatorChunk));
  size_t real_size = max_avail + sizeof(WasmStackAllocatorChunk);
  /* common case of allocating a chunk of exactly CHUNK_SIZE */
  if (real_size == CHUNK_SIZE) {
    if (stack_allocator->next_free) {
      WasmStackAllocatorChunk* chunk = stack_allocator->next_free;
      stack_allocator->next_free = stack_allocator->next_free->next_free;
      return chunk;
    }
  }

  WasmStackAllocatorChunk* chunk =
      wasm_alloc(stack_allocator->fallback, real_size, CHUNK_ALIGN);
  if (!chunk) {
    if (stack_allocator->has_jmpbuf)
      longjmp(stack_allocator->jmpbuf, 1);
    WASM_FATAL("%s:%d: memory allocation failed\n", file, line);
  }
  /* use the same allocation for the WasmStackAllocatorChunk and its data. + 1
   * skips over the WasmStackAllocatorChunk */
  chunk->start = chunk + 1;
  chunk->current = chunk->start;
  chunk->end = (void*)((intptr_t)chunk->start + max_avail);
  chunk->prev = NULL;
#if WASM_STACK_ALLOCATOR_STATS
  stack_allocator->chunk_alloc_count++;
  stack_allocator->total_chunk_bytes += CHUNK_SIZE;
#endif /* WASM_STACK_ALLOCATOR_STATS */
  return chunk;
}
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");
  }
}
static void on_option_error(struct WasmOptionParser* parser,
                            const char* message) {
  WASM_FATAL("%s\n", message);
}
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;
}