libabigail/tests/test-ir-walker.cc
Dodji Seketeli 2bb45265d3 Use environment by reference.
This patch simplifies how the environment is created and passed around
the functions that create front ends.  With this change, the
environment can simply be allocated on the stack and passed by
reference to the libabigail pipeline.

At the core of this change, type_or_decl_base::priv now carries a
const reference to an environment rather than a pointer.  The side
effect is that type_or_decl_base can no longer be copied.  This is not
a problem because throughout the years I could see that the use case
to copy ABI artifacts is just not there.  Similarly, elf_symbol::priv
carries a const reference to environment now, no more a pointer.
Getters, setters and code that use the environment from the ABI
artifacts are updated accordingly.

The DWARF front-end can now be created by code that looks like this,
for instance:

    vector<char**> debug_info_paths;
    abigail::ir::environment env;

    abigail::ctf_reader::read_context_sptr reader =
      abigail::dwarf_reader::create_read_context("elf/file/to/analyze",
						 debug_info_paths, env);

    elf_reader::status reading_status;
    corpus_sptr abi_corpus =
      abigail::dwarf_reader::read_corpus_from_elf(reader, reading_status);

	/* then do something with the resulting abi_corpus*/

Note how, you don't need to use the "new" operator to instantiate the
"env" object of type environment.  It can sit on the stack and it's
passed to the read_corpus_from_elf function by reference.

In other words, the internal representation types have been changed to
refer to the environment by reference, rather than requiring a pointer
to it.

	* include/abg-corpus.h (corpus::corpus)
	(corpus_group::corpus_group): Take environment&, not environment*
	as parameter.
	(corpus::{get_environment, set_environment}): Take or return
	environment&, not environment*.
	* src/abg-corpus.cc (corpus::corpus): Likewise.
	(corpus::{get_environment, set_environment}): Likewise.
	(corpus::add): Don't update the environment of the translation
	unit.
	(corpus::{record_type_as_reachable_from_public_interfaces,
	type_is_reachable_from_public_interfaces, init_format_version,
	add_corpus}): Adjust for accessing a reference to environment,
	rather than a pointer.
	* include/abg-ctf-reader.h (create_read_context): Take or return
	environment&, not environment*.
	* src/abg-ctf-reader.cc (read_context::ir_env): Make this a
	reference to environment, not pointer anymore.
	(read_context::read_context): Initialize the reference to
	environment.
	(read_context::initialize): Do not re-set the environment.
	(process_ctf_base_type)
	(build_ir_node_for_variadic_parameter_type)
	(process_ctf_enum_type, read_corpus): Adjust for accessing a
	reference to environment, rather than a pointer.
	(create_read_context, reset_read_context): Take environment&, not
	environment*.
	* include/abg-dwarf-reader.h (create_read_context)
	(reset_read_context, read_corpus_from_elf)
	(lookup_symbol_from_elf, lookup_public_function_symbol_from_elf):
	Likewise.
	* src/abg-dwarf-reader.cc (lookup_symbol_from_sysv_hash_tab)
	(lookup_symbol_from_gnu_hash_tab)
	(lookup_symbol_from_elf_hash_tab, lookup_symbol_from_symtab)
	(lookup_symbol_from_elf, lookup_public_function_symbol_from_elf):
	Likewise.
	(read_context::options_type::env): Make this be a reference to
	environment, not a pointer.
	(read_context::options::options): Remove the default constructor.
	Add a new one to initialize the environment data member.
	(read_context::read_context): Take environment&, not environment*.
	Initialize the options_ data member.
	(read_context::initialize): Do not take or initialize an
	environment anymore.
	(read_context::env): Return or take environment&, not
	environment*.
	(read_context::{get_die_qualified_name,
	get_die_qualified_type_name, get_die_pretty_type_representation,
	get_die_pretty_representation, compare_before_canonicalisation})
	(build_translation_unit_and_add_to_ir, build_function_type)
	(build_typedef_type, read_debug_info_into_corpus)
	(read_debug_info_into_corpus, build_ir_node_from_die)
	(build_ir_node_for_variadic_parameter_type, has_alt_debug_info):
	Adjust to use an environment&, not a pointer.
	(create_default_fn_sym, create_read_context)
	(read_corpus_from_elf, lookup_symbol_from_elf)
	(lookup_public_function_symbol_from_elf): Take environment&, not
	environment*.
	(reset_read_context): Do not take or reset environment* anymore.
	* include/abg-fwd.h (type_or_void): Likewise.
	* include/abg-ir.h (translation_unit::translation_unit): Likewise.
	(translation_unit::{get_environment, set_environment}): Likewise.
	(elf_symbol::elf_symbol): Likewise.
	(elf_symbol::create): Remove the overload that takes no
	parameter.  Then for overload that take parameters then take
	environment&, not environment*.
	(elf_symbol::get_environment): Take environment&, not
	environment*.
	(type_or_decl_base::type_or_decl_base): Make the copy constructor
	and assignment operator private.
	(type_or_decl_base::{s,g}et_environment): Take or return
	environment& not environment*.
	(type_or_decl_base::set_environment_for_artifact): Erase these
	methods.
	(decl_base::decl_base): Make copy constructor private.  Take or
	return environment&, not environment* for the other constructors.
	(scope_decl::scope_decl): Take or return environment&, not
	environment*.
	(type_base::type_base): Likewise.
	(scope_type_decl::scope_type_decl): Likewise.
	(namespace_decl::namespace_decl): Likewise.
	(qualified_type_def::qualified_type_def): Likewise.
	(pointer_type_def::pointer_type_def): Likewise.
	(reference_type_def::reference_type_def): Likewise.
	(array_type_def::subrange_type::subrange_type): Likewise.
	(enum_type_def::enumerator::enumerator): Likewise.
	(enum_type_def::enumerator::{get_name, get_qualified_name}):
	Return a string&, no more interned_string&.  As the enumerator
	don't have an enumerator anymore, there is no way to intern the
	string anymore.  Hopefully this won't incur a performance loss.
	(typedef_decl::typedef_decl, function_type::function_type)
	(method_type::method_type, template_decl::template_decl)
	(function_tdecl::function_tdecl, class_tdecl::class_tdecl)
	(class_or_union::class_or_union, class_decl::class_decl)
	(union_decl::union_decl): Take or return environment&, not
	environment*.
	* include/abg-reader.h (read_translation_unit_from_file)
	(read_translation_unit_from_buffer)
	(read_translation_unit_from_istream)
	(create_native_xml_read_context, create_native_xml_read_context)
	(read_corpus_from_native_xml, read_corpus_from_native_xml_file)
	(read_corpus_group_from_native_xml)
	(read_corpus_group_from_native_xml_file): Likewise.
	* include/abg-tools-utils.h
	(build_corpus_group_from_kernel_dist_under): Likewise.
	* src/abg-tools-utils.cc (maybe_load_vmlinux_dwarf_corpus)
	(maybe_load_vmlinux_ctf_corpus)
	(build_corpus_group_from_kernel_dist_under): Likewise.
	* include/abg-writer.h (create_write_context): Likewise.
	* src/abg-writer.cc (id_manager::m_env, id_manager::id_manager)
	(id_manager::get_environment, id_manager::get_id)
	(id_manager::get_id_with_prefix): Adjust.
	(write_context::m_env, write_context::write_context)
	(write_context::get_environment, write_context::get_config)
	(write_context::get_id_for_type, write_context::decl_is_emitted)
	(write_context::record_decl_as_emitted, create_write_context)
	(write_class_decl): Likewise.
	* src/abg-comparison.cc (compute_diff): Stop ensuring that the two
	artifacts being compare are in the same environment.  Now that the
	environment is passed by reference, the potential for
	accendentally comparing artifacts coming from different
	environments is very low, given how the API is used in practice.
	This is in the overloads for decl_base_sptr, type_base_sptr,
	var_decl_sptr, pointer_type_def_sptr, array_type_def_sptr,
	reference_type_def_sptr, qualified_type_def_sptr,
	enum_type_decl_sptr, class_decl_sptr, class_decl::base_spec_sptr,
	union_decl_sptr, scope_decl_sptr, function_decl::parameter_sptr,
	function_type_sptr, function_decl_sptr, type_decl_sptr,
	typedef_decl_sptr, translation_unit_sptr, corpus_sptr.
	* src/abg-corpus-priv.h (corpus::priv::env): Make this be a
	reference to environments, not a pointer.
	(corpus::priv::priv): Pass environment&, not environment*.
	* src/abg-ir-priv.h (translation_unit::priv::env_): Make this an
	environment&, not an environment* anymore.
	(translation_unit::priv::priv): Take an environment&, not an
	environment*.
	(environment::priv::{confirm_ct_propagation_for_types_dependant_on,
	confirm_ct_propagation,
	cancel_ct_propagation_for_types_dependant_on,
	mark_as_being_compared, unmark_as_being_compared,
	comparison_started, mark_as_being_compared, comparison_started}):
	Adjust to use an environment&, not a pointer.
	* src/abg-ir.cc (class environment_setter): Remove this class.
	(push_composite_type_comparison_operands)
	(pop_composite_type_comparison_operands, try_canonical_compare)
	(return_comparison_result, translation_unit::{get_global_scope,
	bind_function_type_life_time}): Adjust.
	(translation_unit::{translation_unit, get_environment}): Take or
	get an environment&, not an environment*.  Remove the getter that
	returns an environment*.
	(elf_symbol::priv::env_): Make this an environment&, not an
	environment*.
	(elf_symbol::priv::priv): Adjust.
	(elf_symbol::elf_symbol): Remove the default constructor.  Change
	the one that takes an environment.
	(elf_symbol::create): Remove the default one.  Adjust the one that
	takes an environment.
	(elf_symbol::get_environment): Adjust.
	(elf_symbol::set_environment_for_artifact): Remove.
	(environment::{get_void_type, get_variadic_parameter_type}):
	Adjust.
	(type_or_decl_base::priv::env_): Make this be a const
	environment&, not a const environment*.
	(type_or_decl_base::priv::priv): Adjust.
	(type_or_decl_base::type_or_decl_base): Remove the default and
	copy constructor.
	(type_or_decl_base::{set_environment, operator=})
	(set_environment_for_artifact): Remove.
	(type_or_decl_base::get_environment): Adjust.
	(decl_base::{decl_base, set_name, set_naming_typedef,
	set_linkage_name}): Adjust.
	(get_decl_name_for_comparison, strip_typedef)
	(strip_useless_const_qualification): Adjust.
	(scope_decl::{scope_decl, add_member_decl, insert_member_decl}):
	Adjust.
	(get_generic_anonymous_internal_type_name, get_type_name)
	(get_name_of_pointer_to_type, get_name_of_reference_to_type)
	(get_name_of_qualified_type, get_function_type_name)
	(get_method_type_name, is_void_pointer_type, lookup_basic_type)
	(lookup_union_type, lookup_union_type_per_location)
	(lookup_enum_type, lookup_typedef_type, lookup_pointer_type)
	(lookup_type, lookup_basic_type_per_location)
	(lookup_basic_type_per_location, lookup_basic_type)
	(lookup_class_type, lookup_class_types)
	(lookup_class_type_per_location, lookup_union_type)
	(lookup_enum_type, lookup_enum_types)
	(lookup_enum_type_per_location, lookup_typedef_type)
	(lookup_typedef_type_per_location, maybe_update_types_lookup_map)
	(maybe_update_types_lookup_map)
	(synthesize_type_from_translation_unit)
	(synthesize_function_type_from_translation_unit)
	(demangle_cplus_mangled_name, type_or_void)
	(types_defined_same_linux_kernel_corpus_public)
	(compare_types_during_canonicalization)
	(type_base::get_canonical_type_for, type_base::type_base)
	(type_base::get_cached_pretty_representation)
	(type_decl::type_decl, type_decl::get_qualified_name): Adjust.
	(scope_type_decl::scope_type_decl)
	(namespace_decl::namespace_decl, qualified_type_def::build_name)
	(qualified_type_def::qualified_type_def)
	(qualified_type_def::get_qualified_name)
	(qualified_type_def::set_underlying_type)
	(pointer_type_def::pointer_type_def)
	(pointer_type_def::set_pointed_to_type)
	(reference_type_def::reference_type_def)
	(reference_type_def::set_pointed_to_type)
	(array_type_def::subrange_type::subrange_type)
	(array_type_def::array_type_def, array_type_def::update_size)
	(array_type_def::set_element_type)
	(array_type_def::append_subranges)
	(array_type_def::get_qualified_name, enum_has_non_name_change):
	Adjust.
	(enum_type_decl::enumerator::priv::env_): Remove this pointer to
	env.  This is because the enumerator must be copy-able.  As the
	enumerator doesn't have an env anymore, it can't intern strings.
	So the enumerator name and qualified name is not going to be
	interned.  If that incurs a performance hit, we'll reconsider this
	decision.  For now, it seems to work OK.  As it simplifies things,
	I am keeping this for now.
	(enum_type_decl::enumerator::priv::{name, qualified_name}): Make
	this be string, not interned_string.
	(enum_type_decl::enumerator::get_environment): Remove.
	(enum_type_decl::enumerator::priv::priv): Adjust.
	(enum_type_decl::enumerator::enumerator)
	(enum_type_decl::enumerator::operator=)
	(enum_type_decl::enumerator::get_name)
	(enum_type_decl::enumerator::get_qualified_name)
	(enum_type_decl::enumerator::set_name): Likewise.
	(typedef_decl::typedef_decl): Adjust.
	(var_decl::get_id, var_decl::get_qualified_name): Adjust.
	(function_type::function_type, method_type::method_type)
	(function_decl::get_pretty_representation_of_declarator)
	(function_decl::set_symbol): Likewise.
	(function_decl::get_id, function_decl::parameter::get_type)
	(function_decl::parameter::get_type_name)
	(function_decl::parameter::get_type_pretty_representation)
	(function_decl::parameter::get_name_id)
	(class_or_union::class_or_union, class_decl::class_decl)
	(class_decl::add_base_specifier, union_decl::union_decl)
	(union_decl::union_decl, template_decl::template_decl)
	(class_tdecl::class_tdecl)
	(maybe_cancel_propagated_canonical_type)
	(dump_classes_being_compared)
	(dump_fn_types_being_compared, copy_member_function)
	(maybe_propagate_canonical_type, keep_type_alive)
	(is_non_canonicalized_type, qualified_name_setter::do_update):
	Likewise.
	(equals): Adjust the overloads for var_decl, function_type,
	class_or_union, class_decl, union_decl.
	* src/abg-reader.cc (read_context::m_env): Make this be an
	environment&, not an environment*.
	(read_context::read_context): Adjust
	(read_context::set_environment): Remove.
	(read_context::{get_environment,
	maybe_check_abixml_canonical_type_stability}): Adjust.
	(read_corpus_from_input, read_corpus_group_from_native_xml)
	(read_corpus_group_from_native_xml_file)
	(read_translation_unit_from_file)
	(read_translation_unit_from_buffer, read_translation_unit)
	(maybe_map_type_with_type_id, build_namespace_decl)
	(build_elf_symbol, build_function_parameter, build_function_decl)
	(build_function_type, build_enum_type_decl, build_class_decl)
	(build_union_decl, build_function_tdecl, build_class_tdecl)
	(build_type_tparameter, read_translation_unit_from_istream)
	(create_native_xml_read_context, read_corpus_from_native_xml):
	Likewise.
	* src/abg-symtab-reader.h (symtab::load): Likewise.
	* src/abg-symtab-reader.cc (symtab::load): Likewise.
	* tests/print-diff-tree.cc (main): Likewise.
	* tests/test-abidiff.cc (main): Likewise.
	* tests/test-diff-dwarf.cc (main): Likewise.
	* tests/test-ir-walker.cc (main): Likewise.
	* tests/test-read-ctf.cc (test_task_ctf::perform): Likewise.
	* tests/test-symtab.cc (read_corpus): Likewise.
	* tools/abicompat.cc (read_corpus, main): Likewise.
	* tools/abidiff.cc (main): Likewise.
	* tools/abidw.cc (load_corpus_and_write_abixml)
	(load_kernel_corpus_group_and_write_abixml, main): Likewise.
	* tools/abilint.cc (main): Likewise.
	* tools/abipkgdiff.cc (compare, compare_to_self)
	(compare_prepared_linux_kernel_packages,  compare_task::perform):
	Likewise.
	* tools/abisym.cc (main): Likewise.
	* tools/kmidiff.cc (main): Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2022-11-18 15:22:49 +01:00

180 lines
4.1 KiB
C++

// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2022 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;
///@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::elf_reader::status status = abigail::elf_reader::STATUS_OK;
std::vector<char**> di_roots;
if (!(c = abigail::dwarf_reader::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);
}