// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2025 Red Hat, Inc.

#include <string>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "abg-dwarf-reader.h"
#include "test-utils.h"

using std::string;
using std::ofstream;
using std::cerr;
using std::cout;

using namespace abigail;

///@file
///
/// This example shows how to walk the Internal Representation (IR)
/// graph of the ABI of a binary (called an ABI Corpus) and perform
/// actions on each node of the graph.
///
/// Basically, one has to define a "visitor" which carries member
/// functions that are called during the traversal of the graph.
///
/// On the visitor, there is potentially one member function pair per
/// type of node traversed.  Each time a given node is visited, the
/// corresponding member function pair is called by the traversal
/// machinery.  In other words, the visitor is notified each time a
/// node is traversed.
///
/// To define a visitor, one has to create a type which implements
/// (inherits) the abigail::ir_node_visitor interface.  The visitor
/// must have a pair of node_begin() and node_end() function per type
/// of node that we wish to be notified for.
///
///  Once the visitor is defined, we can load an elf file and build an
///  ABI corpus out of it by using the
///  libabigail::dwarf_reader::read_corpus_from_elf() function, for
///  instance.
///
///  Then we enumerate the translation units comprised in
///  that ABI corpus and we invoke their "traverse()" method, using
///  and instance of the visitor that we just defined.
///
///  Enjoy!

struct name_printing_visitor : public abigail::ir_node_visitor
{
  unsigned level_;

  name_printing_visitor()
    : level_()
  {
    // Using this visitor, the IR walker will visit each type only
    // once.
    allow_visiting_already_visited_type_node(false);
  }

  void
  build_level_prefix(string& str)
  {
    str.clear();
    for (unsigned i = 0; i < level_; ++i)
      str += ' ';
  }

  string
  build_level_prefix()
  {
    string prefix;
    build_level_prefix(prefix);
    return prefix;
  }

  bool
  visit_begin(abigail::namespace_decl* ns)
  {
    string prefix = build_level_prefix();

    cout << prefix << ns->get_pretty_representation() << "\n"
	 << prefix << "{\n";
    ++level_;
    return true;
  }

  bool
  visit_end(abigail::namespace_decl*)
  {
    string prefix = build_level_prefix();
    cout << prefix << "}\n";
    --level_;
    return true;
  }

  bool
  visit_begin(abigail::class_decl* klass)
  {
    string prefix = build_level_prefix();

    cout << prefix << klass->get_pretty_representation() << "\n"
	 << prefix << "{\n";
    ++level_;
    return true;
  }

  bool
  visit_end(abigail::class_decl*)
  {
    string prefix = build_level_prefix();
    cout << prefix << "}\n";
    --level_;
    return true;
  }

  bool
  visit_begin(abigail::function_decl* f)
  {
    string prefix = build_level_prefix();
    cout << prefix << f->get_pretty_representation() << "\n";
    ++level_;
    return true;
  }

  bool
  visit_end(abigail::function_decl*)
  {
    --level_;
    return true;
  }

  bool
  visit_begin(abigail::var_decl* v)
  {
    string prefix = build_level_prefix();
    cout << prefix << v->get_pretty_representation() << "\n";
    ++level_;
    return true;
  }

  bool
  visit_end(abigail::var_decl*)
  {
    --level_;
    return true;
  }
};

int
main(int argc, char **argv)
{
  if (argc < 2)
    return 0;

  string file_name = argv[1];

  abigail::ir::environment env;
  abigail::corpus_sptr c;
  abigail::fe_iface::status status = abigail::fe_iface::STATUS_OK;
  std::vector<string> di_roots;
  if (!(c = dwarf::read_corpus_from_elf(file_name, di_roots, env,
					/*load_all_type=*/false,
					status)))
    {
      cerr << "failed to read " << file_name << "\n";
      return 1;
    }

  name_printing_visitor v;
  // Now traverse each translation unit of the corpus using our
  // instance of name_printing_visitor
  for (abigail::ir::translation_units::const_iterator tu_iterator =
	 c->get_translation_units().begin();
       tu_iterator != c->get_translation_units().end();
       ++tu_iterator)
    (*tu_iterator)->traverse(v);
}
