blob: 02047d1f2c06ff31efcb7acb77391c45ecf4c9ae [file] [edit]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright (C) 2013-2020 Red Hat, Inc.
//
//Author: Dodji Seketeli
/// @file
///
/// Definitions for the Internal Representation artifacts of libabigail.
#include <cxxabi.h>
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <sstream>
#include <typeinfo>
#include <unordered_map>
#include <tuple>
#include <utility>
#include <vector>
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS
#include "abg-interned-str.h"
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-corpus-priv.h"
ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>
#include "abg-cxx-compat.h" // for abg_compat::optional
#include "abg-tools-utils.h"
#include "abg-comp-filter.h"
#include "abg-ir-priv.h"
namespace
{
/// This internal type is a tree walker that walks the sub-tree of a
/// type and sets the environment of the type (including its sub-type)
/// to a new environment.
class environment_setter : public abigail::ir::ir_node_visitor
{
const abigail::ir::environment* env_;
public:
environment_setter(const abigail::ir::environment* env)
: env_(env)
{}
/// This function is called on each sub-tree node that is a
/// declaration. Note that it's also called on some types because
/// most types that have a declarations also inherit the type @ref
/// decl_base.
///
/// @param d the declaration being visited.
bool
visit_begin(abigail::ir::decl_base* d)
{
if (const abigail::ir::environment* env = d->get_environment())
{
ABG_ASSERT(env == env_);
return false;
}
else
d->set_environment(env_);
return true;
}
/// This function is called on each sub-tree node that is a type.
///
/// @param t the type being visited.
bool
visit_begin(abigail::ir::type_base* t)
{
if (abigail::ir::environment* env = t->get_environment())
{
ABG_ASSERT(env == env_);
return false;
}
else
{
ABG_ASSERT(!t->get_environment());
t->set_environment(env_);
}
return true;
}
};
/// This internal type is a tree walking that is used to set the
/// qualified name of a tree of decls and types. It used by the
/// function update_qualified_name().
class qualified_name_setter : public abigail::ir::ir_node_visitor
{
public:
bool
do_update(abigail::ir::decl_base* d);
bool
visit_begin(abigail::ir::decl_base* d);
bool
visit_begin(abigail::ir::type_base* d);
}; // end class qualified_name_setter
}// end anon namespace
namespace abigail
{
// Inject.
using std::string;
using std::list;
using std::vector;
using std::unordered_map;
using std::dynamic_pointer_cast;
using std::static_pointer_cast;
/// Convenience typedef for a map of string -> string*.
typedef unordered_map<string, string*> pool_map_type;
/// The type of the private data structure of type @ref
/// intered_string_pool.
struct interned_string_pool::priv
{
pool_map_type map;
}; //end struc struct interned_string_pool::priv
/// Default constructor.
interned_string_pool::interned_string_pool()
: priv_(new priv)
{
priv_->map[""] = 0;
}
/// Test if the interned string pool already contains a string with a
/// given value.
///
/// @param s the string to test for.
///
/// @return true if the pool contains a string with the value @p s.
bool
interned_string_pool::has_string(const char* s) const
{return priv_->map.find(s) != priv_->map.end();}
/// Get a pointer to the interned string which has a given value.
///
/// @param s the value of the interned string to look for.
///
/// @return a pointer to the raw string of characters which has the
/// value of @p s. Or null if no string with value @p s was interned.
const char*
interned_string_pool::get_string(const char* s) const
{
unordered_map<string, string*>::const_iterator i =
priv_->map.find(s);
if (i == priv_->map.end())
return 0;
if (i->second)
return i->second->c_str();
return "";
}
/// Create an interned string with a given value.
///
/// @param str_value the value of the interned string to create.
///
/// @return the new created instance of @ref interned_string created.
interned_string
interned_string_pool::create_string(const std::string& str_value)
{
string*& result = priv_->map[str_value];
if (!result && !str_value.empty())
result = new string(str_value);
return interned_string(result);
}
/// Destructor.
interned_string_pool::~interned_string_pool()
{
for (pool_map_type::iterator i = priv_->map.begin();
i != priv_->map.end();
++i)
if (i->second)
delete i->second;
}
/// Equality operator.
///
/// @param l the instance of std::string on the left-hand-side of the
/// equality operator.
///
/// @param r the instance of @ref interned_string on the
/// right-hand-side of the equality operator.
///
/// @return true iff the two string are equal.
bool
operator==(const std::string& l, const interned_string& r)
{return r.operator==(l);}
bool
operator!=(const std::string& l, const interned_string& r)
{return !(l == r);}
/// Streaming operator.
///
/// Streams an instance of @ref interned_string to an output stream.
///
/// @param o the destination output stream.
///
/// @param s the instance of @ref interned_string to stream out.
///
/// @return the output stream this function just streamed to.
std::ostream&
operator<<(std::ostream& o, const interned_string& s)
{
o << static_cast<std::string>(s);
return o;
}
/// Concatenation operator.
///
/// Concatenate two instances of @ref interned_string, builds an
/// instance of std::string with the resulting string and return it.
///
/// @param s1 the first string to consider.
///
/// @param s2 the second string to consider.
///
/// @return the resuting concatenated string.
std::string
operator+(const interned_string& s1,const std::string& s2)
{return static_cast<std::string>(s1) + s2;}
/// Concatenation operator.
///
/// Concatenate two instances of @ref interned_string, builds an
/// instance of std::string with the resulting string and return it.
///
/// @param s1 the first string to consider.
///
/// @param s2 the second string to consider.
///
/// @return the resuting concatenated string.
std::string
operator+(const std::string& s1, const interned_string& s2)
{return s1 + static_cast<std::string>(s2);}
namespace ir
{
static size_t
hash_as_canonical_type_or_constant(const type_base *t);
static bool
has_generic_anonymous_internal_type_name(const decl_base *d);
static interned_string
get_generic_anonymous_internal_type_name(const decl_base *d);
static void
update_qualified_name(decl_base * d);
static void
update_qualified_name(decl_base_sptr d);
void
push_composite_type_comparison_operands(const type_base& left,
const type_base& right);
void
pop_composite_type_comparison_operands(const type_base& left,
const type_base& right);
bool
mark_dependant_types_compared_until(const type_base &r);
/// Push a pair of operands on the stack of operands of the current
/// type comparison, during type canonicalization.
///
/// For more information on this, please look at the description of
/// the environment::priv::right_type_comp_operands_ data member.
///
/// @param left the left-hand-side comparison operand to push.
///
/// @param right the right-hand-side comparison operand to push.
void
push_composite_type_comparison_operands(const type_base& left,
const type_base& right)
{
const environment * env = left.get_environment();
env->priv_->push_composite_type_comparison_operands(&left, &right);
}
/// Pop a pair of operands from the stack of operands to the current
/// type comparison.
///
/// For more information on this, please look at the description of
/// the environment::privright_type_comp_operands_ data member.
///
/// @param left the left-hand-side comparison operand we expect to
/// pop from the top of the stack. If this doesn't match the
/// operand found on the top of the stack, the function aborts.
///
/// @param right the right-hand-side comparison operand we expect to
/// pop from the bottom of the stack. If this doesn't match the
/// operand found on the top of the stack, the function aborts.
void
pop_composite_type_comparison_operands(const type_base& left,
const type_base& right)
{
const environment * env = left.get_environment();
env->priv_->pop_composite_type_comparison_operands(&left, &right);
}
/// In the stack of the current types being compared (as part of type
/// canonicalization), mark all the types that comes after a certain
/// one as NOT being eligible to the canonical type propagation
/// optimization.
///
/// For a starter, please read about the @ref
/// OnTheFlyCanonicalization, aka, "canonical type propagation
/// optimization".
///
/// To implement that optimization, we need, among other things to
/// maintain stack of the types (and their sub-types) being
/// currently compared as part of type canonicalization.
///
/// Note that we only consider the type that is the right-hand-side
/// operand of the comparison because it's that one that is being
/// canonicalized and thus, that is not yet canonicalized.
///
/// The reason why a type is deemed NON-eligible to the canonical
/// type propagation optimization is that it "depends" on
/// recursively present type. Let me explain.
///
/// Suppose we have a type T that has sub-types named ST0 and ST1.
/// Suppose ST1 itself has a sub-type that is T itself. In this
/// case, we say that T is a recursive type, because it has T
/// (itself) as one of its sub-types:
///
/// T
/// +-- ST0
/// |
/// +-- ST1
/// +
/// |
/// +-- T
///
/// ST1 is said to "depend" on T because it has T as a sub-type.
/// But because T is recursive, then ST1 is said to depend on a
/// recursive type. Notice however that ST0 does not depend on any
/// recursive type.
///
/// When we are at the point of comparing the sub-type T of ST1
/// against its counterpart, the stack of the right-hand-side
/// operands of the type canonicalization is going to look like
/// this:
///
/// | T | ST1 |
///
/// We don't add the type T to the stack as we detect that T was
/// already in there (recursive cycle).
///
/// So, this function will basically mark ST1 as being NON-eligible
/// to being the target of canonical type propagation, by marking ST1
/// as being dependant on T.
///
/// @param right the right-hand-side operand of the type comparison.
///
/// @return true iff the operation was successful.
bool
mark_dependant_types_compared_until(const type_base &r)
{
const environment * env = r.get_environment();
return env->priv_->mark_dependant_types_compared_until(&r);
}
/// @brief the location of a token represented in its simplest form.
/// Instances of this type are to be stored in a sorted vector, so the
/// type must have proper relational operators.
class expanded_location
{
string path_;
unsigned line_;
unsigned column_;
expanded_location();
public:
friend class location_manager;
expanded_location(const string& path, unsigned line, unsigned column)
: path_(path), line_(line), column_(column)
{}
bool
operator==(const expanded_location& l) const
{
return (path_ == l.path_
&& line_ == l.line_
&& column_ && l.column_);
}
bool
operator<(const expanded_location& l) const
{
if (path_ < l.path_)
return true;
else if (path_ > l.path_)
return false;
if (line_ < l.line_)
return true;
else if (line_ > l.line_)
return false;
return column_ < l.column_;
}
};
/// Expand the location into a tripplet path, line and column number.
///
/// @param path the output parameter where this function sets the
/// expanded path.
///
/// @param line the output parameter where this function sets the
/// expanded line.
///
/// @param column the ouptut parameter where this function sets the
/// expanded column.
void
location::expand(std::string& path, unsigned& line, unsigned& column) const
{
if (!get_location_manager())
{
// We don't have a location manager maybe because this location
// was just freshly instanciated. We still want to be able to
// expand to default values.
path = "";
line = 0;
column = 0;
return;
}
get_location_manager()->expand_location(*this, path, line, column);
}
/// Expand the location into a string.
///
/// @return the string representing the location.
string
location::expand(void) const
{
string path, result;
unsigned line = 0, column = 0;
expand(path, line, column);
std::ostringstream o;
o << path << ":" << line << ":" << column;
return o.str();
}
struct location_manager::priv
{
/// This sorted vector contains the expanded locations of the tokens
/// coming from a given ABI Corpus. The index of a given expanded
/// location in the table gives us an integer that is used to build
/// instance of location types.
std::vector<expanded_location> locs;
};
location_manager::location_manager()
: priv_(new location_manager::priv)
{}
location_manager::~location_manager() = default;
/// Insert the triplet representing a source locus into our internal
/// vector of location triplet. Return an instance of location type,
/// built from an integral type that represents the index of the
/// source locus triplet into our source locus table.
///
/// @param file_path the file path of the source locus
/// @param line the line number of the source location
/// @param col the column number of the source location
location
location_manager::create_new_location(const std::string& file_path,
size_t line,
size_t col)
{
expanded_location l(file_path, line, col);
// Just append the new expanded location to the end of the vector
// and return its index. Note that indexes start at 1.
priv_->locs.push_back(l);
return location(priv_->locs.size(), this);
}
/// Given an instance of location type, return the triplet
/// {path,line,column} that represents the source locus. Note that
/// the location must have been previously created from the function
/// location_manager::create_new_location, otherwise this function yields
/// unexpected results, including possibly a crash.
///
/// @param location the instance of location type to expand
/// @param path the resulting path of the source locus
/// @param line the resulting line of the source locus
/// @param column the resulting colum of the source locus
void
location_manager::expand_location(const location& location,
std::string& path,
unsigned& line,
unsigned& column) const
{
if (location.value_ == 0)
return;
expanded_location &l = priv_->locs[location.value_ - 1];
path = l.path_;
line = l.line_;
column = l.column_;
}
typedef unordered_map<function_type_sptr,
bool,
function_type::hash,
type_shared_ptr_equal> fn_type_ptr_map;
// <type_maps stuff>
struct type_maps::priv
{
mutable istring_type_base_wptrs_map_type basic_types_;
mutable istring_type_base_wptrs_map_type class_types_;
mutable istring_type_base_wptrs_map_type union_types_;
mutable istring_type_base_wptrs_map_type enum_types_;
mutable istring_type_base_wptrs_map_type typedef_types_;
mutable istring_type_base_wptrs_map_type qualified_types_;
mutable istring_type_base_wptrs_map_type pointer_types_;
mutable istring_type_base_wptrs_map_type reference_types_;
mutable istring_type_base_wptrs_map_type array_types_;
mutable istring_type_base_wptrs_map_type subrange_types_;
mutable istring_type_base_wptrs_map_type function_types_;
mutable vector<type_base_wptr> sorted_types_;
}; // end struct type_maps::priv
type_maps::type_maps()
: priv_(new priv)
{}
type_maps::~type_maps() = default;
/// Test if the type_maps is empty.
///
/// @return true iff the type_maps is empty.
bool
type_maps::empty() const
{
return (basic_types().empty()
&& class_types().empty()
&& union_types().empty()
&& enum_types().empty()
&& typedef_types().empty()
&& qualified_types().empty()
&& pointer_types().empty()
&& reference_types().empty()
&& array_types().empty()
&& subrange_types().empty()
&& function_types().empty());
}
/// Getter for the map that associates the name of a basic type to the
/// vector instances of type_decl_sptr that represents that type.
const istring_type_base_wptrs_map_type&
type_maps::basic_types() const
{return priv_->basic_types_;}
/// Getter for the map that associates the name of a basic type to the
/// vector of instances of @ref type_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::basic_types()
{return priv_->basic_types_;}
/// Getter for the map that associates the name of a class type to the
/// vector of instances of @ref class_decl_sptr that represents that
/// type.
const istring_type_base_wptrs_map_type&
type_maps::class_types() const
{return priv_->class_types_;}
/// Getter for the map that associates the name of a class type to the
/// vector of instances of @ref class_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::class_types()
{return priv_->class_types_;}
/// Getter for the map that associates the name of a union type to the
/// vector of instances of @ref union_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::union_types()
{return priv_->union_types_;}
/// Getter for the map that associates the name of a union type to the
/// vector of instances of @ref union_decl_sptr that represents that
/// type.
const istring_type_base_wptrs_map_type&
type_maps::union_types() const
{return priv_->union_types_;}
/// Getter for the map that associates the name of an enum type to the
/// vector of instances of @ref enum_type_decl_sptr that represents
/// that type.
istring_type_base_wptrs_map_type&
type_maps::enum_types()
{return priv_->enum_types_;}
/// Getter for the map that associates the name of an enum type to the
/// vector of instances of @ref enum_type_decl_sptr that represents
/// that type.
const istring_type_base_wptrs_map_type&
type_maps::enum_types() const
{return priv_->enum_types_;}
/// Getter for the map that associates the name of a typedef to the
/// vector of instances of @ref typedef_decl_sptr that represents tha
/// type.
istring_type_base_wptrs_map_type&
type_maps::typedef_types()
{return priv_->typedef_types_;}
/// Getter for the map that associates the name of a typedef to the
/// vector of instances of @ref typedef_decl_sptr that represents tha
/// type.
const istring_type_base_wptrs_map_type&
type_maps::typedef_types() const
{return priv_->typedef_types_;}
/// Getter for the map that associates the name of a qualified type to
/// the vector of instances of @ref qualified_type_def_sptr.
istring_type_base_wptrs_map_type&
type_maps::qualified_types()
{return priv_->qualified_types_;}
/// Getter for the map that associates the name of a qualified type to
/// the vector of instances of @ref qualified_type_def_sptr.
const istring_type_base_wptrs_map_type&
type_maps::qualified_types() const
{return priv_->qualified_types_;}
/// Getter for the map that associates the name of a pointer type to
/// the vector of instances of @ref pointer_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::pointer_types()
{return priv_->pointer_types_;}
/// Getter for the map that associates the name of a pointer type to
/// the vector of instances of @ref pointer_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::pointer_types() const
{return priv_->pointer_types_;}
/// Getter for the map that associates the name of a reference type to
/// the vector of instances of @ref reference_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::reference_types()
{return priv_->reference_types_;}
/// Getter for the map that associates the name of a reference type to
/// the vector of instances of @ref reference_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::reference_types() const
{return priv_->reference_types_;}
/// Getter for the map that associates the name of an array type to
/// the vector of instances of @ref array_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::array_types()
{return priv_->array_types_;}
/// Getter for the map that associates the name of an array type to
/// the vector of instances of @ref array_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::array_types() const
{return priv_->array_types_;}
/// Getter for the map that associates the name of a subrange type to
/// the vector of instances of @ref array_type_def::subrange_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::subrange_types()
{return priv_->subrange_types_;}
/// Getter for the map that associates the name of a subrange type to
/// the vector of instances of @ref array_type_def::subrange_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::subrange_types() const
{return priv_->subrange_types_;}
/// Getter for the map that associates the name of a function type to
/// the vector of instances of @ref function_type_sptr that represents
/// that type.
const istring_type_base_wptrs_map_type&
type_maps::function_types() const
{return priv_->function_types_;}
/// Getter for the map that associates the name of a function type to
/// the vector of instances of @ref function_type_sptr that represents
/// that type.
istring_type_base_wptrs_map_type&
type_maps::function_types()
{return priv_->function_types_;}
/// A comparison functor to compare/sort types based on their pretty
/// representations.
struct type_name_comp
{
/// Comparison operator for two instances of @ref type_base.
///
/// This compares the two types by lexicographically comparing their
/// pretty representation.
///
/// @param l the left-most type to compare.
///
/// @param r the right-most type to compare.
///
/// @return true iff @p l < @p r.
bool
operator()(type_base *l, type_base *r) const
{
if (l == 0 && r == 0)
return false;
string l_repr = get_pretty_representation(l);
string r_repr = get_pretty_representation(r);
return l_repr < r_repr;
}
/// Comparison operator for two instances of @ref type_base.
///
/// This compares the two types by lexicographically comparing their
/// pretty representation.
///
/// @param l the left-most type to compare.
///
/// @param r the right-most type to compare.
///
/// @return true iff @p l < @p r.
bool
operator()(const type_base_sptr &l, const type_base_sptr &r) const
{return operator()(l.get(), r.get());}
/// Comparison operator for two instances of @ref type_base.
///
/// This compares the two types by lexicographically comparing their
/// pretty representation.
///
/// @param l the left-most type to compare.
///
/// @param r the right-most type to compare.
///
/// @return true iff @p l < @p r.
bool
operator()(const type_base_wptr &l, const type_base_wptr &r) const
{return operator()(type_base_sptr(l), type_base_sptr(r));}
}; // end struct type_name_comp
#ifdef WITH_DEBUG_SELF_COMPARISON
/// This is a function called when the ABG_RETURN* macros defined
/// below return false.
///
/// The purpose of this function is to ease debugging. To know where
/// the equality functions first compare non-equal, we can just set a
/// breakpoint on this notify_equality_failed function and run the
/// equality functions. Because all the equality functions use the
/// ABG_RETURN* macros to return their values, this function is always
/// called when any of those equality function return false.
///
/// @param l the first operand of the equality.
///
/// @param r the second operand of the equality.
static void
notify_equality_failed(const type_or_decl_base &l __attribute__((unused)),
const type_or_decl_base &r __attribute__((unused)))
{}
/// This is a function called when the ABG_RETURN* macros defined
/// below return false.
///
/// The purpose of this function is to ease debugging. To know where
/// the equality functions first compare non-equal, we can just set a
/// breakpoint on this notify_equality_failed function and run the
/// equality functions. Because all the equality functions use the
/// ABG_RETURN* macros to return their values, this function is always
/// called when any of those equality function return false.
///
/// @param l the first operand of the equality.
///
/// @param r the second operand of the equality.
static void
notify_equality_failed(const type_or_decl_base *l __attribute__((unused)),
const type_or_decl_base *r __attribute__((unused)))
{}
#define ABG_RETURN_EQUAL(l, r) \
do \
{ \
if (l != r) \
notify_equality_failed(l, r); \
return (l == r); \
} \
while(false)
#define ABG_RETURN_FALSE \
do \
{ \
notify_equality_failed(l, r); \
return false; \
} while(false)
#define ABG_RETURN(value) \
do \
{ \
if (value == false) \
notify_equality_failed(l, r); \
return value; \
} while (false)
#else // WITH_DEBUG_SELF_COMPARISON
#define ABG_RETURN_FALSE return false
#define ABG_RETURN(value) return (value)
#define ABG_RETURN_EQUAL(l, r) return ((l) == (r));
#endif
/// Compare two types by comparing their canonical types if present.
///
/// If the canonical types are not present (because the types have not
/// yet been canonicalized, for instance) then the types are compared
/// structurally.
///
/// @param l the first type to take into account in the comparison.
///
/// @param r the second type to take into account in the comparison.
template<typename T>
bool
try_canonical_compare(const T *l, const T *r)
{
#if WITH_DEBUG_TYPE_CANONICALIZATION
// We are debugging the canonicalization of a type down the stack.
// 'l' is a subtype of a canonical type and 'r' is a subtype of the
// type being canonicalized. We are at a point where we can compare
// 'l' and 'r' either using canonical comparison (if 'l' and 'r'
// have canonical types) or structural comparison.
//
// Because we are debugging the process of type canonicalization, we
// want to compare 'l' and 'r' canonically *AND* structurally. Both
// kinds of comparison should yield the same result, otherwise type
// canonicalization just failed for the subtype 'r' of the type
// being canonicalized.
//
// In concrete terms, this function is going to be called twice with
// the same pair {'l', 'r'} to compare: The first time with
// environment::priv_->use_canonical_type_comparison_ set to true,
// instructing us to compare them canonically, and the second time
// with that boolean set to false, instructing us to compare them
// structurally.
const environment *env = l->get_environment();
if (env->priv_->use_canonical_type_comparison_)
{
if (const type_base *lc = l->get_naked_canonical_type())
if (const type_base *rc = r->get_naked_canonical_type())
ABG_RETURN_EQUAL(lc, rc);
}
return equals(*l, *r, 0);
#else
if (const type_base *lc = l->get_naked_canonical_type())
if (const type_base *rc = r->get_naked_canonical_type())
ABG_RETURN_EQUAL(lc, rc);
return equals(*l, *r, 0);
#endif
}
/// Detect if a recursive comparison cycle is detected while
/// structurally comparing two types (a.k.a member-wise comparison).
///
/// @param l the left-hand-side operand of the current comparison.
///
/// @param r the right-hand-side operand of the current comparison.
///
/// @return true iff a comparison cycle is detected.
template<typename T>
bool
is_comparison_cycle_detected(T& l, T& r)
{
bool result = (l.priv_->comparison_started(l)
|| l.priv_->comparison_started(r));
return result ;
}
/// This macro is to be used while comparing composite types that
/// might recursively refer to themselves. Comparing two such types
/// might get us into a cyle.
///
/// Practically, if we detect that we are already into comparing 'l'
/// and 'r'; then, this is a cycle.
//
/// To break the cycle, we assume the result of the comparison is true
/// for now. Comparing the other sub-types of l & r will tell us later
/// if l & r are actually different or not.
///
/// In the mean time, returning true from this macro should not be
/// used to propagate the canonical type of 'l' onto 'r' as we don't
/// know yet if l equals r. All the types that depend on l and r
/// can't (and that are in the comparison stack currently) can't have
/// their canonical type propagated either. So this macro disallows
/// canonical type propagation for those types that depend on a
/// recursively defined sub-type for now.
///
/// @param l the left-hand-side operand of the comparison.
#define RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r) \
do \
{ \
if (is_comparison_cycle_detected(l, r)) \
{ \
mark_dependant_types_compared_until(r); \
return true; \
} \
} \
while(false)
/// Mark a pair of types as being compared.
///
/// This is helpful to later detect recursive cycles in the comparison
/// stack.
///
/// @param l the left-hand-side operand of the comparison.
///
/// @parm r the right-hand-side operand of the comparison.
template<typename T>
void
mark_types_as_being_compared(T& l, T&r)
{
l.priv_->mark_as_being_compared(l);
l.priv_->mark_as_being_compared(r);
push_composite_type_comparison_operands(l, r);
}
/// Mark a pair of types as being not compared anymore.
///
/// This is helpful to later detect recursive cycles in the comparison
/// stack.
///
/// Note that the types must have been passed to
/// mark_types_as_being_compared prior to calling this function.
///
/// @param l the left-hand-side operand of the comparison.
///
/// @parm r the right-hand-side operand of the comparison.
template<typename T>
void
unmark_types_as_being_compared(T& l, T&r)
{
l.priv_->unmark_as_being_compared(l);
l.priv_->unmark_as_being_compared(r);
pop_composite_type_comparison_operands(l, r);
}
/// Return the result of the comparison of two (sub) types.
///
/// The function does the necessary book keeping before returning the
/// result of the comparison of two (sub) types.
///
/// The book-keeping done is in the following
/// areas:
///
/// * Management of the Canonical Type Propagation optimization
/// * type comparison cycle detection
///
/// @param l the left-hand-side operand of the type comparison
///
/// @param r the right-hand-side operand of the type comparison
///
/// @param propagate_canonical_type if true, it means the function
/// performs the @ref OnTheFlyCanonicalization, aka, "canonical type
/// propagation optimization".
///
/// @param value the result of the comparison of @p l and @p r.
///
/// @return the value @p value.
template<typename T>
bool
return_comparison_result(T& l, T& r, bool value,
bool propagate_canonical_type = true)
{
if (propagate_canonical_type && (value == true))
maybe_propagate_canonical_type(l, r);
unmark_types_as_being_compared(l, r);
const environment* env = l.get_environment();
if (propagate_canonical_type && env->do_on_the_fly_canonicalization())
// We are instructed to perform the "canonical type propagation"
// optimization, making 'r' to possibly get the canonical type of
// 'l' if it has one. This mostly means that we are currently
// canonicalizing the type that contain the subtype provided in
// the 'r' argument.
{
if (value == true
&& is_type(&r)->priv_->depends_on_recursive_type()
&& !env->priv_->right_type_comp_operands_.empty()
&& is_type(&r)->priv_->canonical_type_propagated())
{
// Track the object 'r' for which the propagated canonical
// type might be re-initialized if the current comparison
// eventually fails.
env->priv_->types_with_non_confirmed_propagated_ct_.insert
(reinterpret_cast<uintptr_t>(is_type(&r)));
}
else if (value == true && env->priv_->right_type_comp_operands_.empty())
{
// The type provided in the 'r' argument is the type that is
// being canonicalized; 'r' is not a mere subtype being
// compared, it's the whole type being canonicalized. And
// its canonicalization has just succeeded. So let's
// confirm the "canonical type propagation" of all the
// sub-types that were compared during the comparison of
// 'r'.
env->priv_->confirm_ct_propagation(&r);
if (is_type(&r)->priv_->depends_on_recursive_type())
{
is_type(&r)->priv_->set_does_not_depend_on_recursive_type();
env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&r);
}
}
else if (value == false)
{
// The comparison of the current sub-type failed. So all
// the types in
// env->prix_->types_with_non_confirmed_propagated_ct_
// should see their tentatively propagated canonical type
// cancelled.
env->priv_->cancel_ct_propagation(&r);
if (is_type(&r)->priv_->depends_on_recursive_type())
{
// The right-hand-side operand cannot carry any tentative
// canonical type at this point.
is_type(&r)->priv_->clear_propagated_canonical_type();
// Reset the marking of the right-hand-side operand as it no
// longer carries a tentative canonical type that might be
// later cancelled.
is_type(&r)->priv_->set_does_not_depend_on_recursive_type();
env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&r);
}
}
}
ABG_RETURN(value);
}
/// Getter of all types types sorted by their pretty representation.
///
/// @return a sorted vector of all types sorted by their pretty
/// representation.
const vector<type_base_wptr>&
type_maps::get_types_sorted_by_name() const
{
if (priv_->sorted_types_.empty())
{
istring_type_base_wptrs_map_type::const_iterator i;
vector<type_base_wptr>::const_iterator j;
for (i = basic_types().begin(); i != basic_types().end(); ++i)
for (j = i->second.begin(); j != i->second.end(); ++j)
priv_->sorted_types_.push_back(*j);
for (i = class_types().begin(); i != class_types().end(); ++i)
for (j = i->second.begin(); j != i->second.end(); ++j)
priv_->sorted_types_.push_back(*j);
for (i = union_types().begin(); i != union_types().end(); ++i)
for (j = i->second.begin(); j != i->second.end(); ++j)
priv_->sorted_types_.push_back(*j);
for (i = enum_types().begin(); i != enum_types().end(); ++i)
for (j = i->second.begin(); j != i->second.end(); ++j)
priv_->sorted_types_.push_back(*j);
for (i = typedef_types().begin(); i != typedef_types().end(); ++i)
for (j = i->second.begin(); j != i->second.end(); ++j)
priv_->sorted_types_.push_back(*j);
type_name_comp comp;
sort(priv_->sorted_types_.begin(), priv_->sorted_types_.end(), comp);
}
return priv_->sorted_types_;
}
// </type_maps stuff>
// <translation_unit stuff>
/// Constructor of translation_unit.
///
/// @param env the environment of this translation unit. Please note
/// that the life time of the environment must be greater than the
/// life time of the translation unit because the translation uses
/// resources that are allocated in the environment.
///
/// @param path the location of the translation unit.
///
/// @param address_size the size of addresses in the translation unit,
/// in bits.
translation_unit::translation_unit(const environment* env,
const std::string& path,
char address_size)
: priv_(new priv(env))
{
priv_->path_ = path;
priv_->address_size_ = address_size;
}
/// Getter of the the global scope of the translation unit.
///
/// @return the global scope of the current translation unit. If
/// there is not global scope allocated yet, this function creates one
/// and returns it.
const scope_decl_sptr&
translation_unit::get_global_scope() const
{
return const_cast<translation_unit*>(this)->get_global_scope();
}
/// Getter of the the global scope of the translation unit.
///
/// @return the global scope of the current translation unit. If
/// there is not global scope allocated yet, this function creates one
/// and returns it.
scope_decl_sptr&
translation_unit::get_global_scope()
{
if (!priv_->global_scope_)
{
priv_->global_scope_.reset
(new global_scope(const_cast<translation_unit*>(this)));
// The global scope must be out of the same environment as its
// translation unit.
priv_->global_scope_->
set_environment(const_cast<environment*>(get_environment()));
priv_->global_scope_->set_translation_unit
(const_cast<translation_unit*>(this));
}
return priv_->global_scope_;
}
/// Getter of the types of the current @ref translation_unit.
///
/// @return the maps of the types of the translation unit.
const type_maps&
translation_unit::get_types() const
{return priv_->types_;}
/// Getter of the types of the current @ref translation_unit.
///
/// @return the maps of the types of the translation unit.
type_maps&
translation_unit::get_types()
{return priv_->types_;}
/// Get the vector of function types that are used in the current
/// translation unit.
///
/// @return the vector of function types that are used in the current
/// translation unit.
const vector<function_type_sptr>&
translation_unit::get_live_fn_types() const
{return priv_->live_fn_types_;}
/// Getter of the environment of the current @ref translation_unit.
///
/// @return the translation unit of the current translation unit.
const environment*
translation_unit::get_environment() const
{return priv_->env_;}
/// Getter of the environment of the current @ref translation_unit.
///
/// @return the translation unit of the current translation unit.
environment*
translation_unit::get_environment()
{return const_cast<environment*>(priv_->env_);}
/// Setter of the environment of the current @ref translation_unit.
///
/// @param env the environment.
void
translation_unit::set_environment(const environment* env)
{priv_->env_ = env;}
/// Getter of the language of the source code of the translation unit.
///
/// @return the language of the source code.
translation_unit::language
translation_unit::get_language() const
{return priv_->language_;}
/// Setter of the language of the source code of the translation unit.
///
/// @param l the new language.
void
translation_unit::set_language(language l)
{priv_->language_ = l;}
/// Get the path of the current translation unit.
///
/// This path is relative to the build directory of the translation
/// unit as returned by translation_unit::get_compilation_dir_path.
///
/// @return the relative path of the compilation unit associated to
/// the current instance of translation_unit.
//
const std::string&
translation_unit::get_path() const
{return priv_->path_;}
/// Set the path associated to the current instance of
/// translation_unit.
///
/// This path is relative to the build directory of the translation
/// unit as returned by translation_unit::get_compilation_dir_path.
///
/// @param a_path the new relative path to set.
void
translation_unit::set_path(const string& a_path)
{priv_->path_ = a_path;}
/// Get the path of the directory that was 'current' when the
/// translation unit was compiled.
///
/// Note that the path returned by translation_unit::get_path is
/// relative to the path returned by this function.
///
/// @return the compilation directory for the current translation
/// unit.
const std::string&
translation_unit::get_compilation_dir_path() const
{return priv_->comp_dir_path_;}
/// Set the path of the directory that was 'current' when the
/// translation unit was compiled.
///
/// Note that the path returned by translation_unit::get_path is
/// relative to the path returned by this function.
///
/// @param the compilation directory for the current translation unit.
void
translation_unit::set_compilation_dir_path(const std::string& d)
{priv_->comp_dir_path_ = d;}
/// Get the concatenation of the build directory and the relative path
/// of the translation unit.
///
/// @return the absolute path of the translation unit.
const std::string&
translation_unit::get_absolute_path() const
{
if (priv_->abs_path_.empty())
{
string path;
if (!priv_->path_.empty())
{
if (!priv_->comp_dir_path_.empty())
{
path = priv_->comp_dir_path_;
path += "/";
}
path += priv_->path_;
}
priv_->abs_path_ = path;
}
return priv_->abs_path_;
}
/// Set the corpus this translation unit is a member of.
///
/// Note that adding a translation unit to a @ref corpus automatically
/// triggers a call to this member function.
///
/// @param corpus the corpus.
void
translation_unit::set_corpus(corpus* c)
{priv_->corp = c;}
/// Get the corpus this translation unit is a member of.
///
/// @return the parent corpus, or nil if this doesn't belong to any
/// corpus yet.
corpus*
translation_unit::get_corpus()
{return priv_->corp;}
/// Get the corpus this translation unit is a member of.
///
/// @return the parent corpus, or nil if this doesn't belong to any
/// corpus yet.
const corpus*
translation_unit::get_corpus() const
{return const_cast<translation_unit*>(this)->get_corpus();}
/// Getter of the location manager for the current translation unit.
///
/// @return a reference to the location manager for the current
/// translation unit.
location_manager&
translation_unit::get_loc_mgr()
{return priv_->loc_mgr_;}
/// const Getter of the location manager.
///
/// @return a const reference to the location manager for the current
/// translation unit.
const location_manager&
translation_unit::get_loc_mgr() const
{return priv_->loc_mgr_;}
/// Tests whether if the current translation unit contains ABI
/// artifacts or not.
///
/// @return true iff the current translation unit is empty.
bool
translation_unit::is_empty() const
{return get_global_scope()->is_empty();}
/// Getter of the address size in this translation unit.
///
/// @return the address size, in bits.
char
translation_unit::get_address_size() const
{return priv_->address_size_;}
/// Setter of the address size in this translation unit.
///
/// @param a the new address size in bits.
void
translation_unit::set_address_size(char a)
{priv_->address_size_= a;}
/// Getter of the 'is_constructed" flag. It says if the translation
/// unit is fully constructed or not.
///
/// This flag is important for cases when comparison might depend on
/// if the translation unit is fully built or not. For instance, when
/// reading types from DWARF, the virtual methods of a class are not
/// necessarily fully constructed until we have reached the end of the
/// translation unit. In that case, before we've reached the end of
/// the translation unit, we might not take virtual functions into
/// account when comparing classes.
///
/// @return true if the translation unit is constructed.
bool
translation_unit::is_constructed() const
{return priv_->is_constructed_;}
/// Setter of the 'is_constructed" flag. It says if the translation
/// unit is fully constructed or not.
///
/// This flag is important for cases when comparison might depend on
/// if the translation unit is fully built or not. For instance, when
/// reading types from DWARF, the virtual methods of a class are not
/// necessarily fully constructed until we have reached the end of the
/// translation unit. In that case, before we've reached the end of
/// the translation unit, we might not take virtual functions into
/// account when comparing classes.
///
/// @param f true if the translation unit is constructed.
void
translation_unit::set_is_constructed(bool f)
{priv_->is_constructed_ = f;}
/// Compare the current translation unit against another one.
///
/// @param other the other tu to compare against.
///
/// @return true if the two translation units are equal, false
/// otherwise.
bool
translation_unit::operator==(const translation_unit& other)const
{
if (get_address_size() != other.get_address_size())
return false;
return *get_global_scope() == *other.get_global_scope();
}
/// Inequality operator.
///
/// @param o the instance of @ref translation_unit to compare the
/// current instance against.
///
/// @return true iff the current instance is different from @p o.
bool
translation_unit::operator!=(const translation_unit& o) const
{return ! operator==(o);}
/// Ensure that the life time of a function type is bound to the life
/// time of the current translation unit.
///
/// @param ftype the function time which life time to bind to the life
/// time of the current instance of @ref translation_unit. That is,
/// it's onlyh when the translation unit is destroyed that the
/// function type can be destroyed to.
void
translation_unit::bind_function_type_life_time(function_type_sptr ftype) const
{
const environment* env = get_environment();
ABG_ASSERT(env);
const_cast<translation_unit*>(this)->priv_->live_fn_types_.push_back(ftype);
interned_string repr = get_type_name(ftype);
const_cast<translation_unit*>(this)->get_types().function_types()[repr].
push_back(ftype);
// The function type must be out of the same environment as its
// translation unit.
if (const environment* e = ftype->get_environment())
ABG_ASSERT(env == e);
ftype->set_environment(const_cast<environment*>(env));
if (const translation_unit* existing_tu = ftype->get_translation_unit())
ABG_ASSERT(existing_tu == this);
else
ftype->set_translation_unit(const_cast<translation_unit*>(this));
}
/// This implements the ir_traversable_base::traverse virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
/// unit during the traversal.
///
/// @return true if the entire type IR tree got traversed, false
/// otherwise.
bool
translation_unit::traverse(ir_node_visitor& v)
{return get_global_scope()->traverse(v);}
translation_unit::~translation_unit()
{}
/// Converts a translation_unit::language enumerator into a string.
///
/// @param l the language enumerator to translate.
///
/// @return the resulting string.
string
translation_unit_language_to_string(translation_unit::language l)
{
switch (l)
{
case translation_unit::LANG_UNKNOWN:
return "LANG_UNKNOWN";
case translation_unit::LANG_Cobol74:
return "LANG_Cobol74";
case translation_unit::LANG_Cobol85:
return "LANG_Cobol85";
case translation_unit::LANG_C89:
return "LANG_C89";
case translation_unit::LANG_C99:
return "LANG_C99";
case translation_unit::LANG_C11:
return "LANG_C11";
case translation_unit::LANG_C:
return "LANG_C";
case translation_unit::LANG_C_plus_plus_11:
return "LANG_C_plus_plus_11";
case translation_unit::LANG_C_plus_plus_14:
return "LANG_C_plus_plus_14";
case translation_unit::LANG_C_plus_plus:
return "LANG_C_plus_plus";
case translation_unit::LANG_ObjC:
return "LANG_ObjC";
case translation_unit::LANG_ObjC_plus_plus:
return "LANG_ObjC_plus_plus";
case translation_unit::LANG_Fortran77:
return "LANG_Fortran77";
case translation_unit::LANG_Fortran90:
return "LANG_Fortran90";
case translation_unit::LANG_Fortran95:
return "LANG_Fortran95";
case translation_unit::LANG_Ada83:
return "LANG_Ada83";
case translation_unit::LANG_Ada95:
return "LANG_Ada95";
case translation_unit::LANG_Pascal83:
return "LANG_Pascal83";
case translation_unit::LANG_Modula2:
return "LANG_Modula2";
case translation_unit::LANG_Java:
return "LANG_Java";
case translation_unit::LANG_PLI:
return "LANG_PLI";
case translation_unit::LANG_UPC:
return "LANG_UPC";
case translation_unit::LANG_D:
return "LANG_D";
case translation_unit::LANG_Python:
return "LANG_Python";
case translation_unit::LANG_Go:
return "LANG_Go";
case translation_unit::LANG_Mips_Assembler:
return "LANG_Mips_Assembler";
default:
return "LANG_UNKNOWN";
}
return "LANG_UNKNOWN";
}
/// Parse a string representing a language into a
/// translation_unit::language enumerator into a string.
///
/// @param l the string representing the language.
///
/// @return the resulting translation_unit::language enumerator.
translation_unit::language
string_to_translation_unit_language(const string& l)
{
if (l == "LANG_Cobol74")
return translation_unit::LANG_Cobol74;
else if (l == "LANG_Cobol85")
return translation_unit::LANG_Cobol85;
else if (l == "LANG_C89")
return translation_unit::LANG_C89;
else if (l == "LANG_C99")
return translation_unit::LANG_C99;
else if (l == "LANG_C11")
return translation_unit::LANG_C11;
else if (l == "LANG_C")
return translation_unit::LANG_C;
else if (l == "LANG_C_plus_plus_11")
return translation_unit::LANG_C_plus_plus_11;
else if (l == "LANG_C_plus_plus_14")
return translation_unit::LANG_C_plus_plus_14;
else if (l == "LANG_C_plus_plus")
return translation_unit::LANG_C_plus_plus;
else if (l == "LANG_ObjC")
return translation_unit::LANG_ObjC;
else if (l == "LANG_ObjC_plus_plus")
return translation_unit::LANG_ObjC_plus_plus;
else if (l == "LANG_Fortran77")
return translation_unit::LANG_Fortran77;
else if (l == "LANG_Fortran90")
return translation_unit::LANG_Fortran90;
else if (l == "LANG_Fortran95")
return translation_unit::LANG_Fortran95;
else if (l == "LANG_Ada83")
return translation_unit::LANG_Ada83;
else if (l == "LANG_Ada95")
return translation_unit::LANG_Ada95;
else if (l == "LANG_Pascal83")
return translation_unit::LANG_Pascal83;
else if (l == "LANG_Modula2")
return translation_unit::LANG_Modula2;
else if (l == "LANG_Java")
return translation_unit::LANG_Java;
else if (l == "LANG_PLI")
return translation_unit::LANG_PLI;
else if (l == "LANG_UPC")
return translation_unit::LANG_UPC;
else if (l == "LANG_D")
return translation_unit::LANG_D;
else if (l == "LANG_Python")
return translation_unit::LANG_Python;
else if (l == "LANG_Go")
return translation_unit::LANG_Go;
else if (l == "LANG_Mips_Assembler")
return translation_unit::LANG_Mips_Assembler;
return translation_unit::LANG_UNKNOWN;
}
/// Test if a language enumerator designates the C language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the C language.
bool
is_c_language(translation_unit::language l)
{
return (l == translation_unit::LANG_C89
|| l == translation_unit::LANG_C99
|| l == translation_unit::LANG_C11
|| l == translation_unit::LANG_C);
}
/// Test if a language enumerator designates the C++ language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the C++ language.
bool
is_cplus_plus_language(translation_unit::language l)
{
return (l == translation_unit::LANG_C_plus_plus_03
|| l == translation_unit::LANG_C_plus_plus_11
|| l == translation_unit::LANG_C_plus_plus_14
|| l == translation_unit::LANG_C_plus_plus);
}
/// Test if a language enumerator designates the Java language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the Java language.
bool
is_java_language(translation_unit::language l)
{return l == translation_unit::LANG_Java;}
/// Test if a language enumerator designates the Ada language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the Ada language.
bool
is_ada_language(translation_unit::language l)
{
return (l == translation_unit::LANG_Ada83
|| l == translation_unit::LANG_Ada95);
}
/// A deep comparison operator for pointers to translation units.
///
/// @param l the first translation unit to consider for the comparison.
///
/// @param r the second translation unit to consider for the comparison.
///
/// @return true if the two translation units are equal, false otherwise.
bool
operator==(const translation_unit_sptr& l, const translation_unit_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// A deep inequality operator for pointers to translation units.
///
/// @param l the first translation unit to consider for the comparison.
///
/// @param r the second translation unit to consider for the comparison.
///
/// @return true iff the two translation units are different.
bool
operator!=(const translation_unit_sptr& l, const translation_unit_sptr& r)
{return !operator==(l, r);}
// </translation_unit stuff>
// <elf_symbol stuff>
struct elf_symbol::priv
{
const environment* env_;
size_t index_;
size_t size_;
string name_;
elf_symbol::type type_;
elf_symbol::binding binding_;
elf_symbol::version version_;
elf_symbol::visibility visibility_;
bool is_defined_;
// This flag below says if the symbol is a common elf symbol. In
// relocatable files, a common symbol is a symbol defined in a
// section of kind SHN_COMMON.
//
// Note that a symbol of kind STT_COMMON is also considered a common
// symbol. Here is what the gABI says about STT_COMMON and
// SHN_COMMON:
//
// Symbols with type STT_COMMON label uninitialized common
// blocks. In relocatable objects, these symbols are not
// allocated and must have the special section index SHN_COMMON
// (see below). In shared objects and executables these symbols
// must be allocated to some section in the defining object.
//
// In relocatable objects, symbols with type STT_COMMON are
// treated just as other symbols with index SHN_COMMON. If the
// link-editor allocates space for the SHN_COMMON symbol in an
// output section of the object it is producing, it must
// preserve the type of the output symbol as STT_COMMON.
//
// When the dynamic linker encounters a reference to a symbol
// that resolves to a definition of type STT_COMMON, it may (but
// is not required to) change its symbol resolution rules as
// follows: instead of binding the reference to the first symbol
// found with the given name, the dynamic linker searches for
// the first symbol with that name with type other than
// STT_COMMON. If no such symbol is found, it looks for the
// STT_COMMON definition of that name that has the largest size.
bool is_common_;
bool is_in_ksymtab_;
abg_compat::optional<uint32_t> crc_;
abg_compat::optional<std::string> namespace_;
bool is_suppressed_;
elf_symbol_wptr main_symbol_;
elf_symbol_wptr next_alias_;
elf_symbol_wptr next_common_instance_;
string id_string_;
priv()
: env_(),
index_(),
size_(),
type_(elf_symbol::NOTYPE_TYPE),
binding_(elf_symbol::GLOBAL_BINDING),
visibility_(elf_symbol::DEFAULT_VISIBILITY),
is_defined_(false),
is_common_(false),
is_in_ksymtab_(false),
crc_(),
namespace_(),
is_suppressed_(false)
{}
priv(const environment* e,
size_t i,
size_t s,
const string& n,
elf_symbol::type t,
elf_symbol::binding b,
bool d,
bool c,
const elf_symbol::version& ve,
elf_symbol::visibility vi,
bool is_in_ksymtab,
const abg_compat::optional<uint32_t>& crc,
const abg_compat::optional<std::string>& ns,
bool is_suppressed)
: env_(e),
index_(i),
size_(s),
name_(n),
type_(t),
binding_(b),
version_(ve),
visibility_(vi),
is_defined_(d),
is_common_(c),
is_in_ksymtab_(is_in_ksymtab),
crc_(crc),
namespace_(ns),
is_suppressed_(is_suppressed)
{
if (!is_common_)
is_common_ = type_ == COMMON_TYPE;
}
}; // end struct elf_symbol::priv
/// Default constructor of the @ref elf_symbol type.
///
/// Note that this constructor is private, so client code cannot use
/// it to create instances of @ref elf_symbol. Rather, client code
/// should use the @ref elf_symbol::create() function to create
/// instances of @ref elf_symbol instead.
elf_symbol::elf_symbol()
: priv_(new priv)
{}
/// Constructor of the @ref elf_symbol type.
///
/// Note that this constructor is private, so client code cannot use
/// it to create instances of @ref elf_symbol. Rather, client code
/// should use the @ref elf_symbol::create() function to create
/// instances of @ref elf_symbol instead.
///
/// @param e the environment we are operating from.
///
/// @param i the index of the symbol in the (ELF) symbol table.
///
/// @param s the size of the symbol.
///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
///
/// @param b the binding of the symbol.
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol, false otherwise.
///
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
///
/// @param crc the CRC (modversions) value of Linux Kernel symbols
///
/// @param ns the namespace of Linux Kernel symbols, if any
elf_symbol::elf_symbol(const environment* e,
size_t i,
size_t s,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
visibility vi,
bool is_in_ksymtab,
const abg_compat::optional<uint32_t>& crc,
const abg_compat::optional<std::string>& ns,
bool is_suppressed)
: priv_(new priv(e,
i,
s,
n,
t,
b,
d,
c,
ve,
vi,
is_in_ksymtab,
crc,
ns,
is_suppressed))
{}
/// Factory of instances of @ref elf_symbol.
///
/// This is the function to use to create instances of @ref elf_symbol.
///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
elf_symbol::create()
{
elf_symbol_sptr e(new elf_symbol());
e->priv_->main_symbol_ = e;
return e;
}
/// Factory of instances of @ref elf_symbol.
///
/// This is the function to use to create instances of @ref elf_symbol.
///
/// @param e the environment we are operating from.
///
/// @param i the index of the symbol in the (ELF) symbol table.
///
/// @param s the size of the symbol.
///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
///
/// @param b the binding of the symbol.
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol.
///
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
///
/// @param crc the CRC (modversions) value of Linux Kernel symbols
///
/// @param ns the namespace of Linux Kernel symbols, if any
///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
elf_symbol::create(const environment* e,
size_t i,
size_t s,
const string& n,
type t,
binding b,
bool d,
bool c,
const version& ve,
visibility vi,
bool is_in_ksymtab,
const abg_compat::optional<uint32_t>& crc,
const abg_compat::optional<std::string>& ns,
bool is_suppressed)
{
elf_symbol_sptr sym(new elf_symbol(e, i, s, n, t, b, d, c, ve, vi,
is_in_ksymtab, crc, ns, is_suppressed));
sym->priv_->main_symbol_ = sym;
return sym;
}
/// Test textual equality between two symbols.
///
/// Textual equality means that the aliases of the compared symbols
/// are not taken into account. Only the name, type, and version of
/// the symbols are compared.
///
/// @return true iff the two symbols are textually equal.
static bool
textually_equals(const elf_symbol&l,
const elf_symbol&r)
{
bool equals = (l.get_name() == r.get_name()
&& l.get_type() == r.get_type()
&& l.is_public() == r.is_public()
&& l.is_defined() == r.is_defined()
&& l.is_common_symbol() == r.is_common_symbol()
&& l.get_version() == r.get_version()
&& l.get_crc() == r.get_crc()
&& l.get_namespace() == r.get_namespace());
if (equals && l.is_variable())
// These are variable symbols. Let's compare their symbol size.
// The symbol size in this case is the size taken by the storage
// of the variable. If that size changes, then it's an ABI
// change.
equals = l.get_size() == r.get_size();
return equals;
}
/// Getter of the environment used by the current instance of @ref
/// elf_symbol.
///
/// @return the enviroment used by the current instance of @ref elf_symbol.
const environment*
elf_symbol::get_environment() const
{return priv_->env_;}
/// Setter of the environment used by the current instance of @ref
/// elf_symbol.
///
/// @param The new enviroment used by the current instance of @ref
/// elf_symbol.
void
elf_symbol::set_environment(const environment* e) const
{priv_->env_ = e;}
/// Getter for the index
///
/// @return the index of the symbol.
size_t
elf_symbol::get_index() const
{return priv_->index_;}
/// Setter for the index.
///
/// @param s the new index.
void
elf_symbol::set_index(size_t s)
{priv_->index_ = s;}
/// Getter for the name of the @ref elf_symbol.
///
/// @return a reference to the name of the @ref symbol.
const string&
elf_symbol::get_name() const
{return priv_->name_;}
/// Setter for the name of the current intance of @ref elf_symbol.
///
/// @param n the new name.
void
elf_symbol::set_name(const string& n)
{
priv_->name_ = n;
priv_->id_string_.clear();
}
/// Getter for the type of the current instance of @ref elf_symbol.
///
/// @return the type of the elf symbol.
elf_symbol::type
elf_symbol::get_type() const
{return priv_->type_;}
/// Setter for the type of the current instance of @ref elf_symbol.
///
/// @param t the new symbol type.
void
elf_symbol::set_type(type t)
{priv_->type_ = t;}
/// Getter of the size of the symbol.
///
/// @return the size of the symbol, in bytes.
size_t
elf_symbol::get_size() const
{return priv_->size_;}
/// Setter of the size of the symbol.
///
/// @param size the new size of the symbol, in bytes.
void
elf_symbol::set_size(size_t size)
{priv_->size_ = size;}
/// Getter for the binding of the current instance of @ref elf_symbol.
///
/// @return the binding of the symbol.
elf_symbol::binding
elf_symbol::get_binding() const
{return priv_->binding_;}
/// Setter for the binding of the current instance of @ref elf_symbol.
///
/// @param b the new binding.
void
elf_symbol::set_binding(binding b)
{priv_->binding_ = b;}
/// Getter for the version of the current instanc of @ref elf_symbol.
///
/// @return the version of the elf symbol.
elf_symbol::version&
elf_symbol::get_version() const
{return priv_->version_;}
/// Setter for the version of the current instance of @ref elf_symbol.
///
/// @param v the new version of the elf symbol.
void
elf_symbol::set_version(const version& v)
{
priv_->version_ = v;
priv_->id_string_.clear();
}
/// Setter of the visibility of the current instance of @ref
/// elf_symbol.
///
/// @param v the new visibility of the elf symbol.
void
elf_symbol::set_visibility(visibility v)
{priv_->visibility_ = v;}
/// Getter of the visibility of the current instance of @ref
/// elf_symbol.
///
/// @return the visibility of the elf symbol.
elf_symbol::visibility
elf_symbol::get_visibility() const
{return priv_->visibility_;}
/// Test if the current instance of @ref elf_symbol is defined or not.
///
/// @return true if the current instance of @ref elf_symbol is
/// defined, false otherwise.
bool
elf_symbol::is_defined() const
{return priv_->is_defined_;}
/// Sets a flag saying if the current instance of @ref elf_symbol is
/// defined
///
/// @param b the new value of the flag.
void
elf_symbol::is_defined(bool d)
{priv_->is_defined_ = d;}
/// Test if the current instance of @ref elf_symbol is public or not.
///
/// This tests if the symbol is defined, has default or protected
///visibility, and either:
/// - has global binding
/// - has weak binding
/// - or has a GNU_UNIQUE binding.
///
/// return true if the current instance of @ref elf_symbol is public,
/// false otherwise.
bool
elf_symbol::is_public() const
{
return (is_defined()
&& (get_binding() == GLOBAL_BINDING
|| get_binding() == WEAK_BINDING
|| get_binding() == GNU_UNIQUE_BINDING)
&& (get_visibility() == DEFAULT_VISIBILITY
|| get_visibility() == PROTECTED_VISIBILITY));
}
/// Test if the current instance of @ref elf_symbol is a function
/// symbol or not.
///
/// @return true if the current instance of @ref elf_symbol is a
/// function symbol, false otherwise.
bool
elf_symbol::is_function() const
{return get_type() == FUNC_TYPE || get_type() == GNU_IFUNC_TYPE;}
/// Test if the current instance of @ref elf_symbol is a variable
/// symbol or not.
///
/// @return true if the current instance of @ref elf_symbol is a
/// variable symbol, false otherwise.
bool
elf_symbol::is_variable() const
{return get_type() == OBJECT_TYPE || get_type() == TLS_TYPE;}
/// Getter of the 'is-in-ksymtab' property.
///
/// @return true iff the current symbol is in the Linux Kernel
/// specific 'ksymtab' symbol table.
bool
elf_symbol::is_in_ksymtab() const
{return priv_->is_in_ksymtab_;}
/// Setter of the 'is-in-ksymtab' property.
///
/// @param is_in_ksymtab this is true iff the current symbol is in the
/// Linux Kernel specific 'ksymtab' symbol table.
void
elf_symbol::set_is_in_ksymtab(bool is_in_ksymtab)
{priv_->is_in_ksymtab_ = is_in_ksymtab;}
/// Getter of the 'crc' property.
///
/// @return the CRC (modversions) value for Linux Kernel symbols, if any
const abg_compat::optional<uint32_t>&
elf_symbol::get_crc() const
{return priv_->crc_;}
/// Setter of the 'crc' property.
///
/// @param crc the new CRC (modversions) value for Linux Kernel symbols
void
elf_symbol::set_crc(const abg_compat::optional<uint32_t>& crc)
{priv_->crc_ = crc;}
/// Getter of the 'namespace' property.
///
/// @return the namespace for Linux Kernel symbols, if any
const abg_compat::optional<std::string>&
elf_symbol::get_namespace() const
{return priv_->namespace_;}
/// Setter of the 'namespace' property.
///
/// @param ns the new namespace for Linux Kernel symbols, if any
void
elf_symbol::set_namespace(const abg_compat::optional<std::string>& ns)
{priv_->namespace_ = ns;}
/// Getter for the 'is-suppressed' property.
///
/// @return true iff the current symbol has been suppressed by a
/// suppression specification that was provided in the context that
/// led to the creation of the corpus this ELF symbol belongs to.
bool
elf_symbol::is_suppressed() const
{return priv_->is_suppressed_;}
/// Setter for the 'is-suppressed' property.
///
/// @param true iff the current symbol has been suppressed by a
/// suppression specification that was provided in the context that
/// led to the creation of the corpus this ELF symbol belongs to.
void
elf_symbol::set_is_suppressed(bool is_suppressed)
{priv_->is_suppressed_ = is_suppressed;}
/// @name Elf symbol aliases
///
/// An alias A for an elf symbol S is a symbol that is defined at the
/// same address as S. S is chained to A through the
/// elf_symbol::get_next_alias() method.
///
/// When there are several aliases to a symbol, the main symbol is the
/// the first symbol found in the symbol table for a given address.
///
/// The alias chain is circular. That means if S is the main symbol
/// and A is the alias, S is chained to A and A
/// is chained back to the main symbol S. The last alias in an alias
///chain is always chained to the main symbol.
///
/// Thus, when looping over the aliases of an elf_symbol A, detecting
/// an alias that is equal to the main symbol should logically be a
/// loop exit condition.
///
/// Accessing and adding aliases for instances of elf_symbol is done
/// through the member functions below.
/// @{
/// Get the main symbol of an alias chain.
///
///@return the main symbol.
const elf_symbol_sptr
elf_symbol::get_main_symbol() const
{return priv_->main_symbol_.lock();}
/// Get the main symbol of an alias chain.
///
///@return the main symbol.
elf_symbol_sptr
elf_symbol::get_main_symbol()
{return priv_->main_symbol_.lock();}
/// Tests whether this symbol is the main symbol.
///
/// @return true iff this symbol is the main symbol.
bool
elf_symbol::is_main_symbol() const
{return get_main_symbol().get() == this;}
/// Get the next alias of the current symbol.
///
///@return the alias, or NULL if there is no alias.
elf_symbol_sptr
elf_symbol::get_next_alias() const
{return priv_->next_alias_.lock();}
/// Check if the current elf_symbol has an alias.
///
///@return true iff the current elf_symbol has an alias.
bool
elf_symbol::has_aliases() const
{return bool(get_next_alias());}
/// Get the number of aliases to this elf symbol
///
/// @return the number of aliases to this elf symbol.
int
elf_symbol::get_number_of_aliases() const
{
int result = 0;
for (elf_symbol_sptr a = get_next_alias();
a && a.get() != get_main_symbol().get();
a = a->get_next_alias())
++result;
return result;
}
/// Add an alias to the current elf symbol.
///
/// @param alias the new alias. Note that this elf_symbol should *NOT*
/// have aliases prior to the invocation of this function.
void
elf_symbol::add_alias(const elf_symbol_sptr& alias)
{
if (!alias)
return;
ABG_ASSERT(!alias->has_aliases());
ABG_ASSERT(is_main_symbol());
if (has_aliases())
{
elf_symbol_sptr last_alias;
for (elf_symbol_sptr a = get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
{
if (a->get_next_alias()->is_main_symbol())
{
ABG_ASSERT(last_alias == 0);
last_alias = a;
}
}
ABG_ASSERT(last_alias);
last_alias->priv_->next_alias_ = alias;
}
else
priv_->next_alias_ = alias;
alias->priv_->next_alias_ = get_main_symbol();
alias->priv_->main_symbol_ = get_main_symbol();
}
/// Update the main symbol for a group of aliased symbols
///
/// If after the construction of the symbols (in order of discovery), the
/// actual main symbol can be identified (e.g. as the symbol that actually is
/// defined in the code), this method offers a way of updating the main symbol
/// through one of the aliased symbols.
///
/// For that, locate the new main symbol by name and update all references to
/// the main symbol among the group of aliased symbols.
///
/// @param name the name of the main symbol
///
/// @return the new main elf_symbol
elf_symbol_sptr
elf_symbol::update_main_symbol(const std::string& name)
{
ABG_ASSERT(is_main_symbol());
if (!has_aliases() || get_name() == name)
return get_main_symbol();
// find the new main symbol
elf_symbol_sptr new_main;
// we've already checked this; check the rest of the aliases
for (elf_symbol_sptr a = get_next_alias(); a.get() != this;
a = a->get_next_alias())
if (a->get_name() == name)
{
new_main = a;
break;
}
if (!new_main)
return get_main_symbol();
// now update all main symbol references
priv_->main_symbol_ = new_main;
for (elf_symbol_sptr a = get_next_alias(); a.get() != this;
a = a->get_next_alias())
a->priv_->main_symbol_ = new_main;
return new_main;
}
/// Return true if the symbol is a common one.
///
/// @return true iff the symbol is common.
bool
elf_symbol::is_common_symbol() const
{return priv_->is_common_;}
/// Return true if this common common symbol has other common instances.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name. Those exist in relocatable files. The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// Note that the current object must be a common symbol, otherwise,
/// this function aborts.
///
/// @return true iff the current common symbol has other common
/// instances.
bool
elf_symbol::has_other_common_instances() const
{
ABG_ASSERT(is_common_symbol());
return bool(get_next_common_instance());
}
/// Get the next common instance of the current common symbol.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name. Those exist in relocatable files. The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// @return the next common instance, or nil if there is not any.
elf_symbol_sptr
elf_symbol::get_next_common_instance() const
{return priv_->next_common_instance_.lock();}
/// Add a common instance to the current common elf symbol.
///
/// Note that this symbol must be the main symbol. Being the main
/// symbol means being the first common symbol to appear in the symbol
/// table.
///
/// @param common the other common instance to add.
void
elf_symbol::add_common_instance(const elf_symbol_sptr& common)
{
if (!common)
return;
ABG_ASSERT(!common->has_other_common_instances());
ABG_ASSERT(is_common_symbol());
ABG_ASSERT(is_main_symbol());
if (has_other_common_instances())
{
elf_symbol_sptr last_common_instance;
for (elf_symbol_sptr c = get_next_common_instance();
c && (c.get() != get_main_symbol().get());
c = c->get_next_common_instance())
{
if (c->get_next_common_instance().get() == get_main_symbol().get())
{
ABG_ASSERT(last_common_instance == 0);
last_common_instance = c;
}
}
ABG_ASSERT(last_common_instance);
last_common_instance->priv_->next_common_instance_ = common;
}
else
priv_->next_common_instance_ = common;
common->priv_->next_common_instance_ = get_main_symbol();
common->priv_->main_symbol_ = get_main_symbol();
}
/// Get a string that is representative of a given elf_symbol.
///
/// If the symbol has a version, then the ID string is the
/// concatenation of the name of the symbol, the '@' character, and
/// the version of the symbol. If the version is the default version
/// of the symbol then the '@' character is replaced by a "@@" string.
///
/// Otherwise, if the symbol does not have any version, this function
/// returns the name of the symbol.
///
/// @return a the ID string.
const string&
elf_symbol::get_id_string() const
{
if (priv_->id_string_.empty())
{
string s = get_name ();
if (!get_version().is_empty())
{
if (get_version().is_default())
s += "@@";
else
s += "@";
s += get_version().str();
}
priv_->id_string_ = s;
}
return priv_->id_string_;
}
/// From the aliases of the current symbol, lookup one with a given name.
///
/// @param name the name of symbol alias we are looking for.
///
/// @return the symbol alias that has the name @p name, or nil if none
/// has been found.
elf_symbol_sptr
elf_symbol::get_alias_from_name(const string& name) const
{
if (name == get_name())
return elf_symbol_sptr(priv_->main_symbol_);
for (elf_symbol_sptr a = get_next_alias();
a && a.get() != get_main_symbol().get();
a = a->get_next_alias())
if (a->get_name() == name)
return a;
return elf_symbol_sptr();
}
/// In the list of aliases of a given elf symbol, get the alias that
/// equals this current symbol.
///
/// @param other the elf symbol to get the potential aliases from.
///
/// @return the alias of @p other that texually equals the current
/// symbol, or nil if no alias textually equals the current symbol.
elf_symbol_sptr
elf_symbol::get_alias_which_equals(const elf_symbol& other) const
{
for (elf_symbol_sptr a = other.get_next_alias();
a && a.get() != a->get_main_symbol().get();
a = a->get_next_alias())
if (textually_equals(*this, *a))
return a;
return elf_symbol_sptr();
}
/// Return a comma separated list of the id of the current symbol as
/// well as the id string of its aliases.
///
/// @param syms a map of all the symbols of the corpus the current
/// symbol belongs to.
///
/// @param include_symbol_itself if set to true, then the name of the
/// current symbol is included in the list of alias names that is emitted.
///
/// @return the string.
string
elf_symbol::get_aliases_id_string(const string_elf_symbols_map_type& syms,
bool include_symbol_itself) const
{
string result;
if (include_symbol_itself)
result = get_id_string();
vector<elf_symbol_sptr> aliases;
compute_aliases_for_elf_symbol(*this, syms, aliases);
if (!aliases.empty() && include_symbol_itself)
result += ", ";
for (vector<elf_symbol_sptr>::const_iterator i = aliases.begin();
i != aliases.end();
++i)
{
if (i != aliases.begin())
result += ", ";
result += (*i)->get_id_string();
}
return result;
}
/// Return a comma separated list of the id of the current symbol as
/// well as the id string of its aliases.
///
/// @param include_symbol_itself if set to true, then the name of the
/// current symbol is included in the list of alias names that is emitted.
///
/// @return the string.
string
elf_symbol::get_aliases_id_string(bool include_symbol_itself) const
{
vector<elf_symbol_sptr> aliases;
if (include_symbol_itself)
aliases.push_back(get_main_symbol());
for (elf_symbol_sptr a = get_next_alias();
a && a.get() != get_main_symbol().get();
a = a->get_next_alias())
aliases.push_back(a);
string result;
for (vector<elf_symbol_sptr>::const_iterator i = aliases.begin();
i != aliases.end();
++i)
{
if (i != aliases.begin())
result += ", ";
result += (*i)->get_id_string();
}
return result;
}
/// Given the ID of a symbol, get the name and the version of said
/// symbol.
///
/// @param id the symbol ID to consider.
///
/// @param name the symbol name extracted from the ID. This is set
/// only if the function returned true.
///
/// @param ver the symbol version extracted from the ID.
bool
elf_symbol::get_name_and_version_from_id(const string& id,
string& name,
string& ver)
{
name.clear(), ver.clear();
string::size_type i = id.find('@');
if (i == string::npos)
{
name = id;
return true;
}
name = id.substr(0, i);
++i;
if (i >= id.size())
return true;
string::size_type j = id.find('@', i);
if (j == string::npos)
j = i;
else
++j;
if (j >= id.size())
{
ver = "";
return true;
}
ver = id.substr(j);
return true;
}
///@}
/// Test if two main symbols are textually equal, or, if they have
/// aliases that are textually equal.
///
/// @param other the symbol to compare against.
///
/// @return true iff the current instance of elf symbol equals the @p
/// other.
bool
elf_symbol::operator==(const elf_symbol& other) const
{
bool are_equal = textually_equals(*this, other);
if (!are_equal)
are_equal = bool(get_alias_which_equals(other));
return are_equal;
}
/// Test if the current symbol aliases another one.
///
/// @param o the other symbol to test against.
///
/// @return true iff the current symbol aliases @p o.
bool
elf_symbol::does_alias(const elf_symbol& o) const
{
if (*this == o)
return true;
if (get_main_symbol() == o.get_main_symbol())
return true;
for (elf_symbol_sptr a = get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
{
if (o == *a)
return true;
}
return false;
}
/// Equality operator for smart pointers to elf_symbol.
///
/// @param lhs the first elf symbol to consider.
///
/// @param rhs the second elf symbol to consider.
///
/// @return true iff @p lhs equals @p rhs.
bool
operator==(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs)
{
if (!!lhs != !!rhs)
return false;
if (!lhs)
return true;
return *lhs == *rhs;
}
/// Inequality operator for smart pointers to elf_symbol.
///
/// @param lhs the first elf symbol to consider.
///
/// @param rhs the second elf symbol to consider.
///
/// @return true iff @p lhs is different from @p rhs.
bool
operator!=(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs)
{return !operator==(lhs, rhs);}
/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol& s1, const elf_symbol& s2)
{return s1.does_alias(s2) || s2.does_alias(s1);}
void
compute_aliases_for_elf_symbol(const elf_symbol& sym,
const string_elf_symbols_map_type& symtab,
vector<elf_symbol_sptr>& aliases)
{
if (elf_symbol_sptr a = sym.get_next_alias())
for (; a && !a->is_main_symbol(); a = a->get_next_alias())
aliases.push_back(a);
else
for (string_elf_symbols_map_type::const_iterator i = symtab.begin();
i != symtab.end();
++i)
for (elf_symbols::const_iterator j = i->second.begin();
j != i->second.end();
++j)
{
if (**j == sym)
for (elf_symbol_sptr s = (*j)->get_next_alias();
s && !s->is_main_symbol();
s = s->get_next_alias())
aliases.push_back(s);
else
for (elf_symbol_sptr s = (*j)->get_next_alias();
s && !s->is_main_symbol();
s = s->get_next_alias())
if (*s == sym)
aliases.push_back(*j);
}
}
/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol* s1, const elf_symbol* s2)
{
if (!!s1 != !!s2)
return false;
if (s1 == s2)
return true;
return elf_symbols_alias(*s1, *s2);
}
/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol_sptr s1, const elf_symbol_sptr s2)
{return elf_symbols_alias(s1.get(), s2.get());}
/// Serialize an instance of @ref symbol_type and stream it to a given
/// output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param t the symbol type to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::type t)
{
string repr;
switch (t)
{
case elf_symbol::NOTYPE_TYPE:
repr = "unspecified symbol type";
break;
case elf_symbol::OBJECT_TYPE:
repr = "variable symbol type";
break;
case elf_symbol::FUNC_TYPE:
repr = "function symbol type";
break;
case elf_symbol::SECTION_TYPE:
repr = "section symbol type";
break;
case elf_symbol::FILE_TYPE:
repr = "file symbol type";
break;
case elf_symbol::COMMON_TYPE:
repr = "common data object symbol type";
break;
case elf_symbol::TLS_TYPE:
repr = "thread local data object symbol type";
break;
case elf_symbol::GNU_IFUNC_TYPE:
repr = "indirect function symbol type";
break;
default:
{
std::ostringstream s;
s << "unknown symbol type (" << (char)t << ')';
repr = s.str();
}
break;
}
o << repr;
return o;
}
/// Serialize an instance of @ref symbol_binding and stream it to a
/// given output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param b the symbol binding to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::binding b)
{
string repr;
switch (b)
{
case elf_symbol::LOCAL_BINDING:
repr = "local binding";
break;
case elf_symbol::GLOBAL_BINDING:
repr = "global binding";
break;
case elf_symbol::WEAK_BINDING:
repr = "weak binding";
break;
case elf_symbol::GNU_UNIQUE_BINDING:
repr = "GNU unique binding";
break;
default:
{
std::ostringstream s;
s << "unknown binding (" << (unsigned char) b << ")";
repr = s.str();
}
break;
}
o << repr;
return o;
}
/// Serialize an instance of @ref elf_symbol::visibility and stream it
/// to a given output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param v the symbol visibility to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::visibility v)
{
string repr;
switch (v)
{
case elf_symbol::DEFAULT_VISIBILITY:
repr = "default visibility";
break;
case elf_symbol::PROTECTED_VISIBILITY:
repr = "protected visibility";
break;
case elf_symbol::HIDDEN_VISIBILITY:
repr = "hidden visibility";
break;
case elf_symbol::INTERNAL_VISIBILITY:
repr = "internal visibility";
break;
default:
{
std::ostringstream s;
s << "unknown visibility (" << (unsigned char) v << ")";
repr = s.str();
}
break;
}
o << repr;
return o;
}
/// Convert a string representing a symbol type into an
/// elf_symbol::type.
///
///@param s the string to convert.
///
///@param t the resulting elf_symbol::type.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_type(const string& s, elf_symbol::type& t)
{
if (s == "no-type")
t = elf_symbol::NOTYPE_TYPE;
else if (s == "object-type")
t = elf_symbol::OBJECT_TYPE;
else if (s == "func-type")
t = elf_symbol::FUNC_TYPE;
else if (s == "section-type")
t = elf_symbol::SECTION_TYPE;
else if (s == "file-type")
t = elf_symbol::FILE_TYPE;
else if (s == "common-type")
t = elf_symbol::COMMON_TYPE;
else if (s == "tls-type")
t = elf_symbol::TLS_TYPE;
else if (s == "gnu-ifunc-type")
t = elf_symbol::GNU_IFUNC_TYPE;
else
return false;
return true;
}
/// Convert a string representing a an elf symbol binding into an
/// elf_symbol::binding.
///
/// @param s the string to convert.
///
/// @param b the resulting elf_symbol::binding.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_binding(const string& s, elf_symbol::binding& b)
{
if (s == "local-binding")
b = elf_symbol::LOCAL_BINDING;
else if (s == "global-binding")
b = elf_symbol::GLOBAL_BINDING;
else if (s == "weak-binding")
b = elf_symbol::WEAK_BINDING;
else if (s == "gnu-unique-binding")
b = elf_symbol::GNU_UNIQUE_BINDING;
else
return false;
return true;
}
/// Convert a string representing a an elf symbol visibility into an
/// elf_symbol::visibility.
///
/// @param s the string to convert.
///
/// @param b the resulting elf_symbol::visibility.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_visibility(const string& s, elf_symbol::visibility& v)
{
if (s == "default-visibility")
v = elf_symbol::DEFAULT_VISIBILITY;
else if (s == "protected-visibility")
v = elf_symbol::PROTECTED_VISIBILITY;
else if (s == "hidden-visibility")
v = elf_symbol::HIDDEN_VISIBILITY;
else if (s == "internal-visibility")
v = elf_symbol::INTERNAL_VISIBILITY;
else
return false;
return true;
}
/// Test if the type of an ELF symbol denotes a function symbol.
///
/// @param t the type of the ELF symbol.
///
/// @return true iff elf symbol type @p t denotes a function symbol
/// type.
bool
elf_symbol_is_function(elf_symbol::type t)
{return t == elf_symbol::FUNC_TYPE;}
/// Test if the type of an ELF symbol denotes a function symbol.
///
/// @param t the type of the ELF symbol.
///
/// @return true iff elf symbol type @p t denotes a function symbol
/// type.
bool
elf_symbol_is_variable(elf_symbol::type t)
{return t == elf_symbol::OBJECT_TYPE;}
// <elf_symbol::version stuff>
struct elf_symbol::version::priv
{
string version_;
bool is_default_;
priv()
: is_default_(false)
{}
priv(const string& v,
bool d)
: version_(v),
is_default_(d)
{}
}; // end struct elf_symbol::version::priv
elf_symbol::version::version()
: priv_(new priv)
{}
/// @param v the name of the version.
///
/// @param is_default true if this is a default version.
elf_symbol::version::version(const string& v,
bool is_default)
: priv_(new priv(v, is_default))
{}
elf_symbol::version::version(const elf_symbol::version& v)
: priv_(new priv(v.str(), v.is_default()))
{
}
elf_symbol::version::~version() = default;
/// Cast the version_type into a string that is its name.
///
/// @return the name of the version.
elf_symbol::version::operator const string&() const
{return priv_->version_;}
/// Getter for the version name.
///
/// @return the version name.
const string&
elf_symbol::version::str() const
{return priv_->version_;}
/// Setter for the version name.
///
/// @param s the version name.
void
elf_symbol::version::str(const string& s)
{priv_->version_ = s;}
/// Getter for the 'is_default' property of the version.
///
/// @return true iff this is a default version.
bool
elf_symbol::version::is_default() const
{return priv_->is_default_;}
/// Setter for the 'is_default' property of the version.
///
/// @param f true if this is the default version.
void
elf_symbol::version::is_default(bool f)
{priv_->is_default_ = f;}
bool
elf_symbol::version::is_empty() const
{return str().empty();}
/// Compares the current version against another one.
///
/// @param o the other version to compare the current one to.
///
/// @return true iff the current version equals @p o.
bool
elf_symbol::version::operator==(const elf_symbol::version& o) const
{return str() == o.str();}
/// Inequality operator.
///
/// @param o the version to compare against the current one.
///
/// @return true iff both versions are different.
bool
elf_symbol::version::operator!=(const version& o) const
{return !operator==(o);}
/// Assign a version to the current one.
///
/// @param o the other version to assign to this one.
///
/// @return a reference to the assigned version.
elf_symbol::version&
elf_symbol::version::operator=(const elf_symbol::version& o)
{
str(o.str());
is_default(o.is_default());
return *this;
}
// </elf_symbol::version stuff>
// </elf_symbol stuff>
// <class dm_context_rel stuff>
struct dm_context_rel::priv
{
bool is_laid_out_;
size_t offset_in_bits_;
var_decl* anonymous_data_member_;
priv(bool is_static = false)
: is_laid_out_(!is_static),
offset_in_bits_(0),
anonymous_data_member_()
{}
priv(bool is_laid_out, size_t offset_in_bits)
: is_laid_out_(is_laid_out),
offset_in_bits_(offset_in_bits),
anonymous_data_member_()
{}
}; //end struct dm_context_rel::priv
dm_context_rel::dm_context_rel()
: context_rel(),
priv_(new priv)
{}
dm_context_rel::dm_context_rel(scope_decl* s,
bool is_laid_out,
size_t offset_in_bits,
access_specifier a,
bool is_static)
: context_rel(s, a, is_static),
priv_(new priv(is_laid_out, offset_in_bits))
{}
dm_context_rel::dm_context_rel(scope_decl* s)
: context_rel(s),
priv_(new priv())
{}
bool
dm_context_rel::get_is_laid_out() const
{return priv_->is_laid_out_;}
void
dm_context_rel::set_is_laid_out(bool f)
{priv_->is_laid_out_ = f;}
size_t
dm_context_rel::get_offset_in_bits() const
{return priv_->offset_in_bits_;}
void
dm_context_rel::set_offset_in_bits(size_t o)
{priv_->offset_in_bits_ = o;}
bool
dm_context_rel::operator==(const dm_context_rel& o) const
{
if (!context_rel::operator==(o))
return false;
return (priv_->is_laid_out_ == o.priv_->is_laid_out_
&& priv_->offset_in_bits_ == o.priv_->offset_in_bits_);
}
bool
dm_context_rel::operator!=(const dm_context_rel& o) const
{return !operator==(o);}
/// Return a non-nil value if this data member context relationship
/// has an anonymous data member. That means, if the data member this
/// relation belongs to is part of an anonymous data member.
///
/// @return the containing anonymous data member of this data member
/// relationship. Nil if there is none.
const var_decl*
dm_context_rel::get_anonymous_data_member() const
{return priv_->anonymous_data_member_;}
/// Set the containing anonymous data member of this data member
/// context relationship. That means that the data member this
/// relation belongs to is part of an anonymous data member.
///
/// @param anon_dm the containing anonymous data member of this data
/// member relationship. Nil if there is none.
void
dm_context_rel::set_anonymous_data_member(var_decl* anon_dm)
{priv_->anonymous_data_member_ = anon_dm;}
dm_context_rel::~dm_context_rel()
{}
// </class dm_context_rel stuff>
// <environment stuff>
/// Convenience typedef for a map of interned_string -> bool.
typedef unordered_map<interned_string,
bool, hash_interned_string> interned_string_bool_map_type;
/// Default constructor of the @ref environment type.
environment::environment()
:priv_(new priv)
{}
/// Destructor for the @ref environment type.
environment::~environment()
{}
/// Getter the map of canonical types.
///
/// @return the map of canonical types. The key of the map is the
/// hash of the canonical type and its value if the canonical type.
environment::canonical_types_map_type&
environment::get_canonical_types_map()
{return priv_->canonical_types_;}
/// Getter the map of canonical types.
///
/// @return the map of canonical types. The key of the map is the
/// hash of the canonical type and its value if the canonical type.
const environment::canonical_types_map_type&
environment::get_canonical_types_map() const
{return const_cast<environment*>(this)->get_canonical_types_map();}
/// Helper to detect if a type is either a reference, a pointer, or a
/// qualified type.
static bool
is_ptr_ref_or_qual_type(const type_base *t)
{
if (is_pointer_type(t)
|| is_reference_type(t)
|| is_qualified_type(t))
return true;
return false;
}
/// A functor to sort decls somewhat topologically. That is, types
/// are sorted in a way that makes the ones that are defined "first"
/// to come first.
///
/// The topological criteria is a lexicographic sort of the definition
/// location of the type. For types that have no location (or the
/// same location), it's their qualified name that is used for the
/// lexicographic sort.
struct decl_topo_comp
{
/// The "Less Than" comparison operator of this functor.
///
/// @param f the first decl to be considered for the comparison.
///
/// @param s the second decl to be considered for the comparison.
///
/// @return true iff @p f is less than @p s.
bool
operator()(const decl_base *f,
const decl_base *s)
{
if (!!f != !!s)
return f && !s;
if (!f)
return false;
// If a decl has artificial location, then use that one over the
// natural one.
location fl = get_artificial_or_natural_location(f);
location sl = get_artificial_or_natural_location(s);
if (fl.get_value() && sl.get_value())
{
if (fl.get_is_artificial() == sl.get_is_artificial())
{
// The locations of the two artfifacts have the same
// artificial-ness so they can be compared.
string p1, p2;
unsigned l1 = 0, l2 = 0, c1 = 0, c2 = 0;
fl.expand(p1, l1, c1);
sl.expand(p2, l2, c2);
if (p1 != p2)
return p1 < p2;
if (l1 != l2)
return l1 < l2;
if (c1 != c2)
return c1 < c2;
}
}
else if (!!fl != !!sl)
// So one of the decls doesn't have location data.
// The first decl is less than the second if it's the one not
// having location data.
return !fl && sl;
// We reach this point if location data is useless.
return (get_pretty_representation(f, true)
< get_pretty_representation(s, true));
}
/// The "Less Than" comparison operator of this functor.
///
/// @param f the first decl to be considered for the comparison.
///
/// @param s the second decl to be considered for the comparison.
///
/// @return true iff @p f is less than @p s.
bool
operator()(const decl_base_sptr &f,
const decl_base_sptr &s)
{return operator()(f.get(), s.get());}
}; // end struct decl_topo_comp
/// A functor to sort types somewhat topologically. That is, types
/// are sorted in a way that makes the ones that are defined "first"
/// to come first.
///
/// The topological criteria is a lexicographic sort of the definition
/// location of the type. For types that have no location, it's their
/// qualified name that is used for the lexicographic sort.
struct type_topo_comp
{
/// The "Less Than" comparison operator of this functor.
///
/// @param f the first type to be considered for the comparison.
///
/// @param s the second type to be considered for the comparison.
///
/// @return true iff @p f is less than @p s.
bool
operator()(const type_base_sptr &f,
const type_base_sptr &s)
{return operator()(f.get(), s.get());}
/// The "Less Than" comparison operator of this functor.
///
/// @param f the first type to be considered for the comparison.
///
/// @param s the second type to be considered for the comparison.
///
/// @return true iff @p f is less than @p s.
bool
operator()(const type_base *f,
const type_base *s)
{
bool f_is_ptr_ref_or_qual = is_ptr_ref_or_qual_type(f);
bool s_is_ptr_ref_or_qual = is_ptr_ref_or_qual_type(s);
if (f_is_ptr_ref_or_qual != s_is_ptr_ref_or_qual)
return !f_is_ptr_ref_or_qual && s_is_ptr_ref_or_qual;
if (f_is_ptr_ref_or_qual && s_is_ptr_ref_or_qual
&& !is_decl(f)->get_location() && !is_decl(s)->get_location())
{
string s1 = get_pretty_representation(f, true);
string s2 = get_pretty_representation(s, true);
if (s1 == s2)
if (qualified_type_def * q = is_qualified_type(f))
if (q->get_cv_quals() == qualified_type_def::CV_NONE)
if (!is_qualified_type(s))
// We are looking at two types that are the result of
// an optimization that happens during the IR
// construction. Namely, type f is a cv-qualified
// type with no qualifier (no const, no volatile, no
// nothing, we call it an empty-qualified type).
// These are the result of an optimization which
// removes "redundant qualifiers" from some types.
// For instance, consider a "const reference". The
// const there is redundant because a reference is
// always const. So as a result of the optimizaton
// that type is going to be transformed into an
// empty-qualified reference. If we don't make that
// optimization, then we risk having spurious change
// reports down the road. But then, as a consequence
// of that optimization, we need to sort the
// empty-qualified type and its non-qualified variant
// e.g, to ensure stability in the abixml output; both
// types are logically equal, but here, we decide that
// the empty-qualified one is topologically "less
// than" the non-qualified counterpart.
//
// So here, type f is an empty-qualified type and type
// s is its non-qualified variant. We decide that f
// is topologically less than s.
return true;
return (s1 < s2);
}
decl_base *fd = is_decl(f);
decl_base *sd = is_decl(s);
if (!!fd != !!sd)
return fd && !sd;
if (!fd)
{
type_base *peeled_f = peel_pointer_or_reference_type(f);
type_base *peeled_s = peel_pointer_or_reference_type(s);
fd = is_decl(peeled_f);
sd = is_decl(peeled_s);
if (!!fd != !!sd)
return fd && !sd;
if (!fd)
return (get_pretty_representation(f, true)
< get_pretty_representation(s, true));
}
// From this point, fd and sd should be non-nil
decl_topo_comp decl_comp;
return decl_comp(fd, sd);
}
}; //end struct type_topo_comp
/// Get a @ref type_decl that represents a "void" type for the current
/// environment.
///
/// @return the @ref type_decl that represents a "void" type.
const type_base_sptr&
environment::get_void_type() const
{
if (!priv_->void_type_)
priv_->void_type_.reset(new type_decl(const_cast<environment*>(this),
intern("void"),
0, 0, location()));
return priv_->void_type_;
}
/// Get a @ref type_decl instance that represents a the type of a
/// variadic function parameter.
///
/// @return the Get a @ref type_decl instance that represents a the
/// type of a variadic function parameter.
const type_base_sptr&
environment::get_variadic_parameter_type() const
{
if (!priv_->variadic_marker_type_)
priv_->variadic_marker_type_.
reset(new type_decl(const_cast<environment*>(this),
intern("variadic parameter type"),
0, 0, location()));
return priv_->variadic_marker_type_;
}
/// Test if the canonicalization of types created out of the current
/// environment is done.
///
/// @return true iff the canonicalization of types created out of the current
/// environment is done.
bool
environment::canonicalization_is_done() const
{return priv_->canonicalization_is_done_;}
/// Set a flag saying if the canonicalization of types created out of
/// the current environment is done or not.
///
/// Note that this function must only be called by internal code of
/// the library that creates ABI artifacts (e.g, read an abi corpus
/// from elf or from our own xml format and creates representations of
/// types out of it) and thus needs to canonicalize types to speed-up
/// further type comparison.
///
/// @param f the new value of the flag.
void
environment::canonicalization_is_done(bool f)
{priv_->canonicalization_is_done_ = f;}
/// Getter for the "on-the-fly-canonicalization" flag.
///
/// @return true iff @ref OnTheFlyCanonicalization
/// "on-the-fly-canonicalization" is to be performed during
/// comparison.
bool
environment::do_on_the_fly_canonicalization() const
{return priv_->do_on_the_fly_canonicalization_;}
/// Setter for the "on-the-fly-canonicalization" flag.
///
/// @param f If this is true then @ref OnTheFlyCanonicalization
/// "on-the-fly-canonicalization" is to be performed during
/// comparison.
void
environment::do_on_the_fly_canonicalization(bool f)
{priv_->do_on_the_fly_canonicalization_ = f;}
/// Getter of the "decl-only-class-equals-definition" flag.
///
/// Usually, a declaration-only class named 'struct foo' compares
/// equal to any class definition named "struct foo'. This is at
/// least true for C++.
///
/// In C, though, because there can be multiple definitions of 'struct
/// foo' in the binary, a declaration-only "struct foo" might be
/// considered to *NOT* resolve to any of the struct foo defined. In
/// that case, the declaration-only "struct foo" is considered
/// different from the definitions.
///
/// This flag controls the behaviour of the comparison of an
/// unresolved decl-only class against a definition of the same name.
///
/// If set to false, the the declaration equals the definition. If
/// set to false, then the decalration is considered different from
/// the declaration.
///
/// @return the value of the "decl-only-class-equals-definition" flag.
bool
environment::decl_only_class_equals_definition() const
{return priv_->decl_only_class_equals_definition_;}
/// Setter of the "decl-only-class-equals-definition" flag.
///
/// Usually, a declaration-only class named 'struct foo' compares
/// equal to any class definition named "struct foo'. This is at
/// least true for C++.
///
/// In C, though, because there can be multiple definitions of 'struct
/// foo' in the binary, a declaration-only "struct foo" might be
/// considered to *NOT* resolve to any of the struct foo defined. In
/// that case, the declaration-only "struct foo" is considered
/// different from the definitions.
///
/// This flag controls the behaviour of the comparison of an
/// unresolved decl-only class against a definition of the same name.
///
/// If set to false, the the declaration equals the definition. If
/// set to false, then the decalration is considered different from
/// the declaration.
///
/// @param the new value of the "decl-only-class-equals-definition"
/// flag.
void
environment::decl_only_class_equals_definition(bool f) const
{priv_->decl_only_class_equals_definition_ = f;}
/// Test if a given type is a void type as defined in the current
/// environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a void type as defined in the current
/// environment.
bool
environment::is_void_type(const type_base_sptr& t) const
{
if (!t)
return false;
return t.get() == get_void_type().get();
}
/// Test if a given type is a void type as defined in the current
/// environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a void type as defined in the current
/// environment.
bool
environment::is_void_type(const type_base* t) const
{
if (!t)
return false;
return t == get_void_type().get();
}
/// Test if a type is a variadic parameter type as defined in the
/// current environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a variadic parameter type as defined in
/// the current environment.
bool
environment::is_variadic_parameter_type(const type_base* t) const
{
if (!t)
return false;
return t == get_variadic_parameter_type().get();
}
/// Test if a type is a variadic parameter type as defined in the
/// current environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a variadic parameter type as defined in
/// the current environment.
bool
environment::is_variadic_parameter_type(const type_base_sptr& t) const
{return is_variadic_parameter_type(t.get());}
/// Do intern a string.
///
/// If a value of this string already exists in the interned string
/// pool of the current environment, then this function returns a new
/// interned_string pointing to that already existing string.
/// Otherwise, a new string is created, stored in the interned string
/// pool and a new interned_string instance is created to point to
/// that new intrerned string, and it's return.
///
/// @param s the value of the string to intern.
///
/// @return the interned string.
interned_string
environment::intern(const string& s) const
{return const_cast<environment*>(this)->priv_->string_pool_.create_string(s);}
/// Getter of the general configuration object.
///
/// @return the configuration object.
const config&
environment::get_config() const
{return priv_->config_;}
#ifdef WITH_DEBUG_SELF_COMPARISON
/// Setter of the corpus of the input corpus of the self comparison
/// that takes place when doing "abidw --debug-abidiff <binary>".
///
/// The first invocation of this function sets the first corpus of the
/// self comparison. The second invocation of this very same function
/// sets the second corpus of the self comparison. That second corpus
/// is supposed to come from the abixml serialization of the first
/// corpus.
///
/// @param c the corpus of the input binary or the corpus of the
/// abixml serialization of the initial binary input.
void
environment::set_self_comparison_debug_input(const corpus_sptr& c)
{
self_comparison_debug_is_on(true);
if (priv_->first_self_comparison_corpus_.expired())
priv_->first_self_comparison_corpus_ = c;
else if (priv_->second_self_comparison_corpus_.expired()
&& c.get() != corpus_sptr(priv_->first_self_comparison_corpus_).get())
priv_->second_self_comparison_corpus_ = c;
}
/// Getter for the corpora of the input binary and the intermediate
/// abixml of the self comparison that takes place when doing
/// 'abidw --debug-abidiff <binary>'.
///
/// @param first_corpus output parameter that is set to the corpus of
/// the input corpus.
///
/// @param second_corpus output parameter that is set to the corpus of
/// the second corpus.
void
environment::get_self_comparison_debug_inputs(corpus_sptr& first_corpus,
corpus_sptr& second_corpus)
{
first_corpus = priv_->first_self_comparison_corpus_.lock();
second_corpus = priv_->second_self_comparison_corpus_.lock();
}
/// Turn on/off the self comparison debug mode.
///
/// @param f true iff the self comparison debug mode is turned on.
void
environment::self_comparison_debug_is_on(bool f)
{priv_->self_comparison_debug_on_ = f;}
/// Test if we are in the process of the 'self-comparison
/// debugging' as triggered by 'abidw --debug-abidiff' command.
///
/// @return true if self comparison debug is on.
bool
environment::self_comparison_debug_is_on() const
{return priv_->self_comparison_debug_on_;}
#endif
#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
/// Set the "type canonicalization debugging" mode, triggered by using
/// the command: "abidw --debug-tc".
///
/// @param flag if true then the type canonicalization debugging mode
/// is enabled.
void
environment::debug_type_canonicalization_is_on(bool flag)
{priv_->debug_type_canonicalization_ = flag;}
/// Getter of the "type canonicalization debugging" mode, triggered by
/// using the command: "abidw --debug-tc".
///
/// @return true iff the type canonicalization debugging mode is
/// enabled.
bool
environment::debug_type_canonicalization_is_on() const
{return priv_->debug_type_canonicalization_;}
#endif // WITH_DEBUG_TYPE_CANONICALIZATION
/// Get the vector of canonical types which have a given "string
/// representation".
///
/// @param 'name', the textual representation of the type as returned
/// by type_or_decl_base::get_pretty_representation(/*internal=*/true,
/// /*qualified=*/true)
///
/// This is useful to for debugging purposes as it's handy to use from
/// inside a debugger like GDB.
///
/// @return a pointer to the vector of canonical types having the
/// representation @p name, or nullptr if no type with that
/// representation exists.
vector<type_base_sptr>*
environment::get_canonical_types(const char* name)
{
auto ti = get_canonical_types_map().find(name);
if (ti == get_canonical_types_map().end())
return nullptr;
return &ti->second;
}
/// Get a given canonical type which has a given "string
/// representation".
///
/// @param 'name', the textual representation of the type as returned
/// by type_or_decl_base::get_pretty_representation(/*internal=*/true,
/// /*qualified=*/true).
///
/// @param index, the index of the type in the vector of types that
/// all have the same textual representation @p 'name'. That vector
/// is returned by the function environment::get_canonical_types().
///
/// @return the canonical type which has the representation @p name,
/// and which is at index @p index in the vector of canonical types
/// having that same textual representation.
type_base*
environment::get_canonical_type(const char* name, unsigned index)
{
vector<type_base_sptr> *types = get_canonical_types(name);
if (!types ||index >= types->size())
return nullptr;
return (*types)[index].get();
}
#ifdef WITH_DEBUG_SELF_COMPARISON
/// Get the set of abixml type-id and the pointer value of the
/// (canonical) type it's associated to.
///
/// This is useful for debugging purposes, especially in the context
/// of the use of the command:
/// 'abidw --debug-abidiff <binary>'.
///
/// @return the set of abixml type-id and the pointer value of the
/// (canonical) type it's associated to.
unordered_map<string, uintptr_t>&
environment::get_type_id_canonical_type_map() const
{return priv_->type_id_canonical_type_map_;}
/// Getter of the map that associates the values of type pointers to
/// their type-id strings.
///
/// Note that this map is populated at abixml reading time, (by
/// build_type()) when a given XML element representing a type is
/// read into a corresponding abigail::ir::type_base.
///
/// This is used only for the purpose of debugging the
/// self-comparison process. That is, when invoking "abidw
/// --debug-abidiff".
///
/// @return the map that associates the values of type pointers to
/// their type-id strings.
unordered_map<uintptr_t, string>&
environment::get_pointer_type_id_map()
{return priv_->pointer_type_id_map_;}
/// Getter of the type-id that corresponds to the value of a pointer
/// to abigail::ir::type_base that was created from the abixml reader.
///
/// That value is retrieved from the map returned from
/// environment::get_pointer_type_id_map().
///
/// That map is populated at abixml reading time, (by build_type())
/// when a given XML element representing a type is read into a
/// corresponding abigail::ir::type_base.
///
/// This is used only for the purpose of debugging the
/// self-comparison process. That is, when invoking "abidw
/// --debug-abidiff".
///
/// @return the type-id strings that corresponds
string
environment::get_type_id_from_pointer(uintptr_t ptr)
{
auto it = get_pointer_type_id_map().find(ptr);
if (it != get_pointer_type_id_map().end())
return it->second;
return "";
}
/// Getter of the canonical type of the artifact designated by a
/// type-id.
///
/// That type-id was generated by the abixml writer at the emitting
/// time of the abixml file. The corresponding canonical type was
/// stored in the map returned by
/// environment::get_type_id_canonical_type_map().
///
/// This is useful for debugging purposes, especially in the context
/// of the use of the command:
/// 'abidw --debug-abidiff <binary>'.
///
/// @return the set of abixml type-id and the pointer value of the
/// (canonical) type it's associated to.
uintptr_t
environment::get_canonical_type_from_type_id(const char* type_id)
{
if (!type_id)
return 0;
auto it = get_type_id_canonical_type_map().find(type_id);
if (it != get_type_id_canonical_type_map().end())
return it->second;
return 0;
}
#endif
// </environment stuff>
// <type_or_decl_base stuff>
/// The private data of @ref type_or_decl_base.
struct type_or_decl_base::priv
{
// This holds the kind of dynamic type of particular instance.
// Yes, this is part of the implementation of a "poor man" runtime
// type identification. We are doing this because profiling shows
// that using dynamic_cast in some places is really to slow and is
// constituting a hotspot. This poor man's implementation made
// things be much faster.
enum type_or_decl_kind kind_;
// This holds the runtime type instance pointer of particular
// instance. In other words, this is the "this pointer" of the
// dynamic type of a particular instance.
void* rtti_;
// This holds a pointer to either the type_base sub-object (if the
// current instance is a type) or the decl_base sub-object (if the
// current instance is a decl). This is used by the is_decl() and
// is_type() functions, which also show up during profiling as
// hotspots, due to their use of dynamic_cast.
void* type_or_decl_ptr_;
bool hashing_started_;
const environment* env_;
translation_unit* translation_unit_;
// The location of an artifact as seen from its input by the
// artifact reader. This might be different from the source
// location advertised by the original emitter of the artifact
// emitter.
location artificial_location_;
// Flags if the current ABI artifact is artificial (i.e, *NOT*
// generated from the initial source code, but rather either
// artificially by the compiler or by libabigail itself).
bool is_artificial_;
/// Constructor of the type_or_decl_base::priv private type.
///
/// @param e the environment in which the ABI artifact was created.
///
/// @param k the identifier of the runtime type of the current
/// instance of ABI artifact.
priv(const environment* e = 0,
enum type_or_decl_kind k = ABSTRACT_TYPE_OR_DECL)
: kind_(k),
rtti_(),
type_or_decl_ptr_(),
hashing_started_(),
env_(e),
translation_unit_(),
is_artificial_()
{}
enum type_or_decl_kind
kind() const
{return kind_;}
void
kind (enum type_or_decl_kind k)
{kind_ |= k;}
}; // end struct type_or_decl_base::priv
/// bitwise "OR" operator for the type_or_decl_base::type_or_decl_kind
/// bitmap type.
type_or_decl_base::type_or_decl_kind
operator|(type_or_decl_base::type_or_decl_kind l,
type_or_decl_base::type_or_decl_kind r)
{
return static_cast<type_or_decl_base::type_or_decl_kind>
(static_cast<unsigned>(l) | static_cast<unsigned>(r));
}
/// bitwise "|=" operator for the type_or_decl_base::type_or_decl_kind
/// bitmap type.
type_or_decl_base::type_or_decl_kind&
operator|=(type_or_decl_base::type_or_decl_kind& l,
type_or_decl_base::type_or_decl_kind r)
{
l = l | r;
return l;
}
/// bitwise "AND" operator for the
/// type_or_decl_base::type_or_decl_kind bitmap type.
type_or_decl_base::type_or_decl_kind
operator&(type_or_decl_base::type_or_decl_kind l,
type_or_decl_base::type_or_decl_kind r)
{
return static_cast<type_or_decl_base::type_or_decl_kind>
(static_cast<unsigned>(l) & static_cast<unsigned>(r));
}
/// bitwise "A&=" operator for the
/// type_or_decl_base::type_or_decl_kind bitmap type.
type_or_decl_base::type_or_decl_kind&
operator&=(type_or_decl_base::type_or_decl_kind& l,
type_or_decl_base::type_or_decl_kind r)
{
l = l & r;
return l;
}
/// Default constructor of @ref type_or_decl_base.
type_or_decl_base::type_or_decl_base()
:priv_(new priv)
{}
/// Constructor of @ref type_or_decl_base.
///
/// @param the environment the current ABI artifact is constructed
/// from.
///
/// @param k the runtime identifier bitmap of the type being built.
type_or_decl_base::type_or_decl_base(const environment* e,
enum type_or_decl_kind k)
:priv_(new priv(e, k))
{}
/// Copy constructor of @ref type_or_decl_base.
type_or_decl_base::type_or_decl_base(const type_or_decl_base& o)
{*priv_ = *o.priv_;}
/// The destructor of the @ref type_or_decl_base type.
type_or_decl_base::~type_or_decl_base()
{}
/// Getter of the flag that says if the artefact is artificial.
///
/// Being artificial means it was not explicitely mentionned in the
/// source code, but was rather artificially created by the compiler
/// or libabigail.
///
/// @return true iff the declaration is artificial.
bool
type_or_decl_base::get_is_artificial() const
{return priv_->is_artificial_;}
/// Setter of the flag that says if the artefact is artificial.
///
/// Being artificial means the artefact was not explicitely
/// mentionned in the source code, but was rather artificially created
/// by the compiler or by libabigail.
///
/// @param f the new value of the flag that says if the artefact is
/// artificial.
void
type_or_decl_base::set_is_artificial(bool f)
{priv_->is_artificial_ = f;}
/// Getter for the "kind" property of @ref type_or_decl_base type.
///
/// This property holds the identifier bitmap of the runtime type of
/// an ABI artifact.
///
/// @return the runtime type identifier bitmap of the current ABI
/// artifact.
enum type_or_decl_base::type_or_decl_kind
type_or_decl_base::kind() const
{return priv_->kind();}
/// Setter for the "kind" property of @ref type_or_decl_base type.
///
/// This property holds the identifier bitmap of the runtime type of
/// an ABI artifact.
///
/// @param the runtime type identifier bitmap of the current ABI
/// artifact.
void
type_or_decl_base::kind(enum type_or_decl_kind k)
{priv_->kind(k);}
/// Getter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @return the pointer to the runtime type sub-object of the current
/// instance.
const void*
type_or_decl_base::runtime_type_instance() const
{return priv_->rtti_;}
/// Getter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @return the pointer to the runtime type sub-object of the current
/// instance.
void*
type_or_decl_base::runtime_type_instance()
{return priv_->rtti_;}
/// Setter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @param i the new pointer to the runtime type sub-object of the
/// current instance.
void
type_or_decl_base::runtime_type_instance(void* i)
{
priv_->rtti_ = i;
if (type_base* t = dynamic_cast<type_base*>(this))
priv_->type_or_decl_ptr_ = t;
else if (decl_base *d = dynamic_cast<decl_base*>(this))
priv_->type_or_decl_ptr_ = d;
}
/// Getter of the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
///
/// @return the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
const void*
type_or_decl_base::type_or_decl_base_pointer() const
{return const_cast<type_or_decl_base*>(this)->type_or_decl_base_pointer();}
/// Getter of the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
///
/// @return the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
void*
type_or_decl_base::type_or_decl_base_pointer()
{return priv_->type_or_decl_ptr_;}
/// Getter for the 'hashing_started' property.
///
/// @return the 'hashing_started' property.
bool
type_or_decl_base::hashing_started() const
{return priv_->hashing_started_;}
/// Setter for the 'hashing_started' property.
///
/// @param b the value to set the 'hashing_property' to.
void
type_or_decl_base::hashing_started(bool b) const
{priv_->hashing_started_ = b;}
/// Setter of the environment of the current ABI artifact.
///
/// This just sets the environment artifact of the current ABI
/// artifact, not on its sub-trees. If you want to set the
/// environment of an ABI artifact including its sub-tree, use the
/// abigail::ir::set_environment_for_artifact() function.
///
/// @param env the new environment.
void
type_or_decl_base::set_environment(const environment* env)
{priv_->env_ = env;}
/// Getter of the environment of the current ABI artifact.
///
/// @return the environment of the artifact.
const environment*
type_or_decl_base::get_environment() const
{return priv_->env_;}
/// Setter of the artificial location of the artificat.
///
/// The artificial location is a location that was artificially
/// generated by libabigail, not generated by the original emitter of
/// the ABI meta-data. For instance, when reading an XML element from
/// an abixml file, the artificial location is the source location of
/// the XML element within the file, not the value of the
/// 'location'property that might be carried by the element.
///
/// Artificial locations might be useful to ensure that abixml emitted
/// by the abixml writer are sorted the same way as the input abixml
/// read by the reader.
///
/// @param l the new artificial location.
void
type_or_decl_base::set_artificial_location(const location &l)
{priv_->artificial_location_ = l;}
/// Getter of the artificial location of the artifact.
///
/// The artificial location is a location that was artificially
/// generated by libabigail, not generated by the original emitter of
/// the ABI meta-data. For instance, when reading an XML element from
/// an abixml file, the artificial location is the source location of
/// the XML element within the file, not the value of the
/// 'location'property that might be carried by the element.
///
/// Artificial locations might be useful to ensure that the abixml
/// emitted by the abixml writer is sorted the same way as the input
/// abixml read by the reader.
///
/// @return the new artificial location.
location&
type_or_decl_base::get_artificial_location() const
{return priv_->artificial_location_;}
/// Test if the current ABI artifact carries an artificial location.
///
/// @return true iff the current ABI artifact carries an artificial location.
bool
type_or_decl_base::has_artificial_location() const
{
return (priv_->artificial_location_
&& priv_->artificial_location_.get_is_artificial());
}
/// Getter of the environment of the current ABI artifact.
///
/// @return the environment of the artifact.
environment*
type_or_decl_base::get_environment()
{return const_cast<environment*>(priv_->env_);}
/// Get the @ref corpus this ABI artifact belongs to.
///
/// @return the corpus this ABI artifact belongs to, or nil if it
/// belongs to none for now.
corpus*
type_or_decl_base::get_corpus()
{
translation_unit* tu = get_translation_unit();
if (!tu)
return 0;
return tu->get_corpus();
}
/// Get the @ref corpus this ABI artifact belongs to.
///
/// @return the corpus this ABI artifact belongs to, or nil if it
/// belongs to none for now.
const corpus*
type_or_decl_base::get_corpus() const
{return const_cast<type_or_decl_base*>(this)->get_corpus();}
/// Set the @ref translation_unit this ABI artifact belongs to.
///
/// Note that adding an ABI artifact to a containining on should
/// invoke this member function.
void
type_or_decl_base::set_translation_unit(translation_unit* tu)
{priv_->translation_unit_ = tu;}
/// Get the @ref translation_unit this ABI artifact belongs to.
///
/// @return the translation unit this ABI artifact belongs to, or nil
/// if belongs to none for now.
translation_unit*
type_or_decl_base::get_translation_unit()
{return priv_->translation_unit_;}
/// Get the @ref translation_unit this ABI artifact belongs to.
///
/// @return the translation unit this ABI artifact belongs to, or nil
/// if belongs to none for now.
const translation_unit*
type_or_decl_base::get_translation_unit() const
{return const_cast<type_or_decl_base*>(this)->get_translation_unit();}
/// Assignment operator for @ref type_or_decl_base.
///
/// @param o the other instance to assign the current instance to.
///
/// return a reference to the assigned instance of @ref
/// type_or_decl_base.
type_or_decl_base&
type_or_decl_base::operator=(const type_or_decl_base& o)
{
*priv_ = *o.priv_;
return *this;
}
/// Traverse the the ABI artifact.
///
/// @param v the visitor used to traverse the sub-tree nodes of the
/// artifact.
bool
type_or_decl_base::traverse(ir_node_visitor&)
{return true;}
/// Set the environment of a given ABI artifact, including recursively
/// setting the environment on the sub-trees of the artifact.
///
/// @param artifact the artifact to set the environment for.
///
/// @param env the new environment.
void
set_environment_for_artifact(type_or_decl_base* artifact,
const environment* env)
{
ABG_ASSERT(artifact && env);
::environment_setter s(env);
artifact->traverse(s);
}
/// Set the environment of a given ABI artifact, including recursively
/// setting the environment on the sub-trees of the artifact.
///
/// @param artifact the artifact to set the environment for.
///
/// @param env the new environment.
void
set_environment_for_artifact(type_or_decl_base_sptr artifact,
const environment* env)
{set_environment_for_artifact(artifact.get(), env);}
/// Non-member equality operator for the @type_or_decl_base type.
///
/// @param lr the left-hand operand of the equality.
///
/// @param rr the right-hand operatnr of the equality.
///
/// @return true iff @p lr equals @p rr.
bool
operator==(const type_or_decl_base& lr, const type_or_decl_base& rr)
{
const type_or_decl_base* l = &lr;
const type_or_decl_base* r = &rr;
const decl_base* dl = dynamic_cast<const decl_base*>(l),
*dr = dynamic_cast<const decl_base*>(r);
if (!!dl != !!dr)
return false;
if (dl && dr)
return *dl == *dr;
const type_base* tl = dynamic_cast<const type_base*>(l),
*tr = dynamic_cast<const type_base*>(r);
if (!!tl != !!tr)
return false;
if (tl && tr)
return *tl == *tr;
return false;
}
/// Non-member equality operator for the @type_or_decl_base type.
///
/// @param l the left-hand operand of the equality.
///
/// @param r the right-hand operatnr of the equality.
///
/// @return true iff @p l equals @p r.
bool
operator==(const type_or_decl_base_sptr& l, const type_or_decl_base_sptr& r)
{
if (!! l != !!r)
return false;
if (!l)
return true;
return *r == *l;
}
/// Non-member inequality operator for the @type_or_decl_base type.
///
/// @param l the left-hand operand of the equality.
///
/// @param r the right-hand operator of the equality.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const type_or_decl_base_sptr& l, const type_or_decl_base_sptr& r)
{return !operator==(l, r);}
// </type_or_decl_base stuff>
// <Decl definition>
struct decl_base::priv
{
bool in_pub_sym_tab_;
bool is_anonymous_;
location location_;
context_rel *context_;
interned_string name_;
interned_string qualified_parent_name_;
// This temporary qualified name is the cache used for the qualified
// name before the type associated to this decl (if applicable) is
// canonicalized. Once the type is canonicalized, the cached use is
// the data member qualified_parent_name_ above.
interned_string temporary_qualified_name_;
// This is the fully qualified name of the decl. It contains the
// name of the decl and the qualified name of its scope. So if in
// the parent scopes of the decl, there is one anonymous struct,
// somewhere in the name, there is going to by an
// __anonymous_struct__ string, even if the anonymous struct is not
// the direct containing scope of this decl.
interned_string qualified_name_;
// Unline qualified_name_, scoped_name_ contains the name of the
// decl and the name of its scope; not the qualified name of the
// scope.
interned_string scoped_name_;
interned_string linkage_name_;
visibility visibility_;
decl_base_sptr declaration_;
decl_base_wptr definition_of_declaration_;
decl_base* naked_definition_of_declaration_;
bool is_declaration_only_;
typedef_decl_sptr naming_typedef_;
priv()
: in_pub_sym_tab_(false),
is_anonymous_(true),
context_(),
visibility_(VISIBILITY_DEFAULT),
naked_definition_of_declaration_(),
is_declaration_only_(false)
{}
priv(interned_string name, interned_string linkage_name, visibility vis)
: in_pub_sym_tab_(false),
context_(),
name_(name),
qualified_name_(name),
linkage_name_(linkage_name),
visibility_(vis),
naked_definition_of_declaration_(),
is_declaration_only_(false)
{
is_anonymous_ = name_.empty();
}
~priv()
{
delete context_;
}
};// end struct decl_base::priv
/// Constructor for the @ref decl_base type.
///
/// @param e the environment the current @ref decl_base is being
/// created in.
///
/// @param name the name of the declaration.
///
/// @param locus the location where to find the declaration in the
/// source code.
///
/// @param linkage_name the linkage name of the declaration.
///
/// @param vis the visibility of the declaration.
decl_base::decl_base(const environment* e,
const string& name,
const location& locus,
const string& linkage_name,
visibility vis)
: type_or_decl_base(e, ABSTRACT_DECL_BASE),
priv_(new priv(e->intern(name), e->intern(linkage_name), vis))
{
set_location(locus);
}
/// Constructor.
///
/// @param e the environment this instance of @ref decl_base is
/// created in.
///
/// @param name the name of the declaration being constructed.
///
/// @param locus the source location of the declaration being constructed.
///
/// @param linkage_name the linkage name of the declaration being
/// constructed.
///
/// @param vis the visibility of the declaration being constructed.
decl_base::decl_base(const environment* e,
const interned_string& name,
const location& locus,
const interned_string& linkage_name,
visibility vis)
: type_or_decl_base(e, ABSTRACT_DECL_BASE),
priv_(new priv(name, linkage_name, vis))
{
set_location(locus);
}
/// Constructor for the @ref decl_base type.
///
///@param environment the environment this instance of @ref decl_base
/// is being constructed in.
///
/// @param l the location where to find the declaration in the source
/// code.
decl_base::decl_base(const environment* e, const location& l)
: type_or_decl_base(e, ABSTRACT_DECL_BASE),
priv_(new priv())
{
set_location(l);
}
decl_base::decl_base(const decl_base& d)
: type_or_decl_base(d)
{
priv_->in_pub_sym_tab_ = d.priv_->in_pub_sym_tab_;
priv_->location_ = d.priv_->location_;
priv_->name_ = d.priv_->name_;
priv_->qualified_parent_name_ = d.priv_->qualified_parent_name_;
priv_->qualified_name_ = d.priv_->qualified_name_;
priv_->linkage_name_ = d.priv_->linkage_name_;
priv_->context_ = d.priv_->context_;
priv_->visibility_ = d.priv_->visibility_;
}
/// Getter for the qualified name.
///
/// Unlike decl_base::get_qualified_name() this doesn't try to update
/// the qualified name.
///
/// @return the qualified name.
const interned_string&
decl_base::peek_qualified_name() const
{return priv_->qualified_name_;}
/// Clear the qualified name of this decl.
///
/// This is useful to ensure that the cache for the qualified name of
/// the decl is refreshed right after type canonicalization, for
/// instance.
void
decl_base::clear_qualified_name()
{priv_->qualified_name_.clear();}
/// Setter for the qualified name.
///
/// @param n the new qualified name.
void
decl_base::set_qualified_name(const interned_string& n) const
{priv_->qualified_name_ = n;}
/// Getter of the temporary qualified name of the current declaration.
///
/// This temporary qualified name is used as a qualified name cache by
/// the type for which this is the declaration (when applicable)
/// before the type is canonicalized. Once the type is canonicalized,
/// it's the result of decl_base::peek_qualified_name() that becomes
/// the qualified name cached.
///
/// @return the temporary qualified name.
const interned_string&
decl_base::peek_temporary_qualified_name() const
{return priv_->temporary_qualified_name_;}
/// Setter for the temporary qualified name of the current
/// declaration.
///
///@param n the new temporary qualified name.
///
/// This temporary qualified name is used as a qualified name cache by
/// the type for which this is the declaration (when applicable)
/// before the type is canonicalized. Once the type is canonicalized,
/// it's the result of decl_base::peek_qualified_name() that becomes
/// the qualified name cached.
void
decl_base::set_temporary_qualified_name(const interned_string& n) const
{priv_->temporary_qualified_name_ = n;}
///Getter for the context relationship.
///
///@return the context relationship for the current decl_base.
const context_rel*
decl_base::get_context_rel() const
{return priv_->context_;}
///Getter for the context relationship.
///
///@return the context relationship for the current decl_base.
context_rel*
decl_base::get_context_rel()
{return priv_->context_;}
void
decl_base::set_context_rel(context_rel *c)
{priv_->context_ = c;}
/// Get the hash of a decl. If the hash hasn't been computed yet,
/// compute it ans store its value; otherwise, just return the hash.
///
/// @return the hash of the decl.
size_t
decl_base::get_hash() const
{
size_t result = 0;
if (const type_base* t = dynamic_cast<const type_base*>(this))
{
type_base::dynamic_hash hash;
result = hash(t);
}
else
// If we reach this point, it mean we are missing a virtual
// overload for decl_base::get_hash. Add it!
abort();
return result;
}
/// Test if the decl is defined in a ELF symbol table as a public
/// symbol.
///
/// @return true iff the decl is defined in a ELF symbol table as a
/// public symbol.
bool
decl_base::get_is_in_public_symbol_table() const
{return priv_->in_pub_sym_tab_;}
/// Set the flag saying if this decl is from a symbol that is in
/// a public symbols table, defined as public (global or weak).
///
/// @param f the new flag value.
void
decl_base::set_is_in_public_symbol_table(bool f)
{priv_->in_pub_sym_tab_ = f;}
/// Get the location of a given declaration.
///
/// The location is an abstraction for the tripplet {file path,
/// line, column} that defines where the declaration appeared in the
/// source code.
///
/// To get the value of the tripplet {file path, line, column} from
/// the @ref location, you need to use the
/// location_manager::expand_location() method.
///
/// The instance of @ref location_manager that you want is
/// accessible from the instance of @ref translation_unit that the
/// current instance of @ref decl_base belongs to, via a call to
/// translation_unit::get_loc_mgr().
///
/// @return the location of the current instance of @ref decl_base.
const location&
decl_base::get_location() const
{return priv_->location_;}
/// Set the location for a given declaration.
///
/// The location is an abstraction for the tripplet {file path,
/// line, column} that defines where the declaration appeared in the
/// source code.
///
/// To create a location from a tripplet {file path, line, column},
/// you need to use the method @ref
/// location_manager::create_new_location().
///
/// Note that there can be two kinds of location. An artificial
/// location and a non-artificial one. The non-artificial location is
/// the one emitted by the original emitter of the ABI artifact, for
/// instance, if the ABI artifact comes from debug info, then the
/// source location that is present in the debug info represent a
/// non-artificial location. When looking at an abixml file on the
/// other hand, the value of the 'location' attribute of an XML
/// element describing an artifact is the non-artificial location.
/// The artificial location is the location (line number from the
/// beginning of the file) of the XML element within the abixml file.
///
/// So, if the location that is being set is artificial, note that the
/// type_or_decl_base::has_artificial_location() method of this decl will
/// subsequently return true and that artificial location will have to
/// be retrieved using type_or_decl_base::get_artificial_location().
/// If the location is non-artificial however,
/// type_or_decl_base::has_artificial_location() will subsequently
/// return false and the non-artificial location will have to be
/// retrieved using decl_base::get_location().
///
/// The instance of @ref location_manager that you want is
/// accessible from the instance of @ref translation_unit that the
/// current instance of @ref decl_base belongs to, via a call to
/// translation_unit::get_loc_mgr().
void
decl_base::set_location(const location& l)
{
if (l.get_is_artificial())
set_artificial_location(l);
else
priv_->location_ = l;
}
/// Setter for the name of the decl.
///
/// @param n the new name to set.
void
decl_base::set_name(const string& n)
{
priv_->name_ = get_environment()->intern(n);
priv_->is_anonymous_ = n.empty();
}
/// Test if the current declaration is anonymous.
///
/// Being anonymous means that the declaration was created without a
/// name. This can usually happen for enum or struct types.
///
/// @return true iff the type is anonymous.
bool
decl_base::get_is_anonymous() const
{return priv_->is_anonymous_;}
/// Set the "is_anonymous" flag of the current declaration.
///
/// Being anonymous means that the declaration was created without a
/// name. This can usually happen for enum or struct types.
///
/// @param f the new value of the flag.
void
decl_base::set_is_anonymous(bool f)
{priv_->is_anonymous_ = f;}
/// Get the "has_anonymous_parent" flag of the current declaration.
///
/// Having an anoymous parent means having a anonymous parent scope
/// (containing type or namespace) which is either direct or indirect.
///
/// @return true iff the current decl has a direct or indirect scope
/// which is anonymous.
bool
decl_base::get_has_anonymous_parent() const
{
scope_decl *scope = get_scope();
if (!scope)
return false;
return scope->get_is_anonymous();
}
/// @return the logical "OR" of decl_base::get_is_anonymous() and
/// decl_base::get_has_anonymous_parent().
bool
decl_base::get_is_anonymous_or_has_anonymous_parent() const
{return get_is_anonymous() || get_has_anonymous_parent();}
/// Getter for the naming typedef of the current decl.
///
/// Consider the C idiom:
///
/// typedef struct {int member;} foo_type;
///
/// In that idiom, foo_type is the naming typedef of the anonymous
/// struct that is declared.
///
/// @return the naming typedef, if any. Otherwise, returns nil.
typedef_decl_sptr
decl_base::get_naming_typedef() const
{return priv_->naming_typedef_;}
/// Set the naming typedef of the current instance of @ref decl_base.
///
/// Consider the C idiom:
///
/// typedef struct {int member;} foo_type;
///
/// In that idiom, foo_type is the naming typedef of the anonymous
/// struct that is declared.
///
/// After completion of this function, the decl will not be considered
/// anonymous anymore. It's name is going to be the name of the
/// naming typedef.
///
/// @param typedef_type the new naming typedef.
void
decl_base::set_naming_typedef(const typedef_decl_sptr& t)
{
// A naming typedef is usually for an anonymous type.
ABG_ASSERT(get_is_anonymous()
// Whe the typedef-named decl is saved into abixml, it's
// not anonymous anymore. Its name is the typedef name.
// So when we read it back, we must still be able to
// apply the naming typedef to the decl.
|| t->get_name() == get_name());
// Only non canonicalized types can be edited this way.
ABG_ASSERT(is_type(this)
&& is_type(this)->get_naked_canonical_type() == nullptr);
priv_->naming_typedef_ = t;
set_name(t->get_name());
set_qualified_name(t->get_qualified_name());
set_is_anonymous(false);
// Now that the qualified type of the decl has changed, let's update
// the qualified names of the member types of this decls.
update_qualified_name(this);
}
/// Getter for the mangled name.
///
/// @return the new mangled name.
const interned_string&
decl_base::get_linkage_name() const
{return priv_->linkage_name_;}
/// Setter for the linkage name.
///
/// @param m the new linkage name.
void
decl_base::set_linkage_name(const string& m)
{
const environment* env = get_environment();
ABG_ASSERT(env);
priv_->linkage_name_ = env->intern(m);
}
/// Getter for the visibility of the decl.
///
/// @return the new visibility.
decl_base::visibility
decl_base::get_visibility() const
{return priv_->visibility_;}
/// Setter for the visibility of the decl.
///
/// @param v the new visibility.
void
decl_base::set_visibility(visibility v)
{priv_->visibility_ = v;}
/// Return the type containing the current decl, if any.
///
/// @return the type that contains the current decl, or NULL if there
/// is none.
scope_decl*
decl_base::get_scope() const
{
if (priv_->context_)
return priv_->context_->get_scope();
return 0;
}
/// Return a copy of the qualified name of the parent of the current
/// decl.
///
/// @return the newly-built qualified name of the of the current decl.
const interned_string&
decl_base::get_qualified_parent_name() const
{return priv_->qualified_parent_name_;}
/// Getter for the name of the current decl.
///
/// @return the name of the current decl.
const interned_string&
decl_base::get_name() const
{return priv_->name_;}
/// Compute the qualified name of the decl.
///
/// @param qn the resulting qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
void
decl_base::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}
/// Get the pretty representatin of the current declaration.
///
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the default pretty representation for a decl. This is
/// basically the fully qualified name of the decl optionally prefixed
/// with a meaningful string to add context for the user.
string
decl_base::get_pretty_representation(bool internal,
bool qualified_name) const
{
if (internal
&& get_is_anonymous()
&& has_generic_anonymous_internal_type_name(this))
{
// We are looking at an anonymous enum, union or class and we
// want an *internal* pretty representation for it. All
// anonymous types of this kind in the same namespace must have
// the same internal representation for type canonicalization to
// work properly.
//
// OK, in practise, we are certainly looking at an enum because
// classes and unions should have their own overloaded virtual
// member function for this.
string name = get_generic_anonymous_internal_type_name(this);
if (qualified_name && !get_qualified_parent_name().empty())
name = get_qualified_parent_name() + "::" + name;
return name;
}
if (qualified_name)
return get_qualified_name(internal);
return get_name();
}
/// Return the qualified name of the decl.
///
/// This is the fully qualified name of the decl. It's made of the
/// concatenation of the name of the decl with the qualified name of
/// its scope.
///
/// Note that the value returned by this function is computed by @ref
/// update_qualified_name when the decl is added to its scope.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
decl_base::get_qualified_name(bool /*internal*/) const
{return priv_->qualified_name_;}
/// Return the scoped name of the decl.
///
/// This is made of the concatenation of the name of the decl with the
/// name of its scope. It doesn't contain the qualified name of its
/// scope, unlike what is returned by decl_base::get_qualified_name.
///
/// Note that the value returned by this function is computed by @ref
/// update_qualified_name when the decl is added to its scope.
///
/// @return the scoped name of the decl.
const interned_string&
decl_base::get_scoped_name() const
{return priv_->scoped_name_;}
/// If this @ref decl_base is a definition, get its earlier
/// declaration.
///
/// @return the earlier declaration of the class, if any.
const decl_base_sptr
decl_base::get_earlier_declaration() const
{return priv_->declaration_;}
/// set the earlier declaration of this @ref decl_base definition.
///
/// @param d the earlier declaration to set. Note that it's set only
/// if it's a pure declaration.
void
decl_base::set_earlier_declaration(const decl_base_sptr& d)
{
if (d && d->get_is_declaration_only())
priv_->declaration_ = d;
}
/// If this @ref decl_base is declaration-only, get its definition, if
/// any.
///
/// @return the definition of this decl-only @ref decl_base.
const decl_base_sptr
decl_base::get_definition_of_declaration() const
{return priv_->definition_of_declaration_.lock();}
/// If this @ref decl_base is declaration-only, get its definition,
/// if any.
///
/// Note that this function doesn't return a smart pointer, but rather
/// the underlying pointer managed by the smart pointer. So it's as
/// fast as possible. This getter is to be used in code paths that
/// are proven to be performance hot spots; especially, when comparing
/// sensitive types like enums, classes or unions. Those are compared
/// extremely frequently and thus, their access to the definition of
/// declaration must be fast.
///
/// @return the definition of the declaration.
const decl_base*
decl_base::get_naked_definition_of_declaration() const
{return priv_->naked_definition_of_declaration_;}
/// Test if a @ref decl_base is a declaration-only decl.
///
/// @return true iff the current @ref decl_base is declaration-only.
bool
decl_base::get_is_declaration_only() const
{return priv_->is_declaration_only_;}
/// Set a flag saying if the @ref enum_type_decl is a declaration-only
/// @ref enum_type_decl.
///
/// @param f true if the @ref enum_type_decl is a declaration-only
/// @ref enum_type_decl.
void
decl_base::set_is_declaration_only(bool f)
{
bool update_types_lookup_map = !f && priv_->is_declaration_only_;
priv_->is_declaration_only_ = f;
if (update_types_lookup_map)
if (scope_decl* s = get_scope())
{
scope_decl::declarations::iterator i;
if (s->find_iterator_for_member(this, i))
maybe_update_types_lookup_map(*i);
else
ABG_ASSERT_NOT_REACHED;
}
}
change_kind
operator|(change_kind l, change_kind r)
{
return static_cast<change_kind>(static_cast<unsigned>(l)
| static_cast<unsigned>(r));
}
change_kind
operator&(change_kind l, change_kind r)
{
return static_cast<change_kind>(static_cast<unsigned>(l)
& static_cast<unsigned>(r));
}
change_kind&
operator|=(change_kind& l, change_kind r)
{
l = l | r;
return l;
}
change_kind&
operator&=(change_kind& l, change_kind r)
{
l = l & r;
return l;
}
/// Compare the properties that belong to the "is-a-member-relation"
/// of a decl.
///
/// For instance, access specifiers are part of the
/// "is-a-member-relation" of a decl.
///
/// This comparison however doesn't take decl names into account. So
/// typedefs for instance are decls that we want to compare with this
/// function.
///
/// This function is a sub-routine of the more general 'equals'
/// overload for instances of decl_base.
///
/// @param l the left-hand side operand of the comparison.
///
/// @param r the right-hand side operand of the comparison.
///
/// @return true iff @p l compare equals, as a member decl, to @p r.
bool
maybe_compare_as_member_decls(const decl_base& l,
const decl_base& r,
change_kind* k)
{
bool result = true;
if (is_member_decl(l) && is_member_decl(r))
{
context_rel* r1 = const_cast<context_rel*>(l.get_context_rel());
context_rel *r2 = const_cast<context_rel*>(r.get_context_rel());
access_specifier la = no_access, ra = no_access;
bool member_types_or_functions =
((is_type(l) && is_type(r))
|| (is_function_decl(l) && is_function_decl(r)));
if (member_types_or_functions)
{
// Access specifiers on member types in DWARF is not
// reliable; in the same DSO, the same struct can be either
// a class or a struct, and the access specifiers of its
// member types are not necessarily given, so they
// effectively can be considered differently, again, in the
// same DSO. So, here, let's avoid considering those!
// during comparison.
la = r1->get_access_specifier();
ra = r2->get_access_specifier();
r1->set_access_specifier(no_access);
r2->set_access_specifier(no_access);
}
bool rels_are_different = *r1 != *r2;
if (member_types_or_functions)
{
// restore the access specifiers.
r1->set_access_specifier(la);
r2->set_access_specifier(ra);
}
if (rels_are_different)
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
}
}
ABG_RETURN(result);
}
/// Get the name of a decl for the purpose of comparing two decl
/// names.
///
/// This is a sub-routine of the 'equal' overload for decl_base.
///
/// This function takes into account the fact that all anonymous names
/// shall have the same name for the purpose of comparison.
///
/// For decls that are part of an anonymous scope, only the
/// non-qualified name should be taken into account.
static interned_string
get_decl_name_for_comparison(const decl_base &d)
{
if (has_generic_anonymous_internal_type_name(&d)
&& d.get_is_anonymous())
{
// The decl is anonymous. It should have the same name ass the
// other anymous types of the same kind.
string r;
r += get_generic_anonymous_internal_type_name(&d);
return d.get_environment()->intern(r);
}
interned_string n = (is_anonymous_or_typedef_named(d)
|| scope_anonymous_or_typedef_named(d))
? d.get_name()
: d.get_qualified_name(/*internal=*/true);
return n;
}
/// Compares two instances of @ref decl_base.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff it's non-null and if the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const decl_base& l, const decl_base& r, change_kind* k)
{
bool result = true;
const interned_string &l_linkage_name = l.get_linkage_name();
const interned_string &r_linkage_name = r.get_linkage_name();
if (!l_linkage_name.empty() && !r_linkage_name.empty())
{
if (l_linkage_name != r_linkage_name)
{
// Linkage names are different. That usually means the two
// decls are different, unless we are looking at two
// function declarations which have two different symbols
// that are aliases of each other.
const function_decl *f1 = is_function_decl(&l),
*f2 = is_function_decl(&r);
if (f1 && f2 && function_decls_alias(*f1, *f2))
;// The two functions are aliases, so they are not
// different.
else
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
}
}
// This is the qualified name of the decls that we want to compare.
// We want to use the "internal" version of the qualified name as
// that one is stable even for anonymous decls.
interned_string ln = get_decl_name_for_comparison(l);
interned_string rn = get_decl_name_for_comparison(r);
/// If both of the current decls have an anonymous scope then let's
/// compare their name component by component by properly handling
/// anonymous scopes. That's the slow path.
///
/// Otherwise, let's just compare their name, the obvious way.
/// That's the fast path because in that case the names are
/// interned_string and comparing them is much faster.
bool decls_are_same = (ln == rn);
if (!decls_are_same
&& l.get_is_anonymous()
&& !l.get_has_anonymous_parent()
&& r.get_is_anonymous()
&& !r.get_has_anonymous_parent())
// Both decls are anonymous and their scope are *NOT* anonymous.
// So we consider the decls to have equivalent names (both
// anonymous, remember). We are still in the fast path here.
decls_are_same = true;
if (!decls_are_same
&& l.get_has_anonymous_parent()
&& r.get_has_anonymous_parent())
// This is the slow path as we are comparing the decl qualified
// names component by component, properly handling anonymous
// scopes.
decls_are_same = tools_utils::decl_names_equal(ln, rn);
if (!decls_are_same)
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
result &= maybe_compare_as_member_decls(l, r, k);
ABG_RETURN(result);
}
/// Return true iff the two decls have the same name.
///
/// This function doesn't test if the scopes of the the two decls are
/// equal.
///
/// Note that this virtual function is to be implemented by classes
/// that extend the \p decl_base class.
bool
decl_base::operator==(const decl_base& other) const
{return equals(*this, other, 0);}
/// Inequality operator.
///
/// @param other to other instance of @ref decl_base to compare the
/// current instance to.
///
/// @return true iff the current instance of @ref decl_base is
/// different from @p other.
bool
decl_base::operator!=(const decl_base& other) const
{return !operator==(other);}
/// Destructor of the @ref decl_base type.
decl_base::~decl_base()
{delete priv_;}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
/// unit during the traversal.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
decl_base::traverse(ir_node_visitor&)
{
// Do nothing in the base class.
return true;
}
/// Setter of the scope of the current decl.
///
/// Note that the decl won't hold a reference on the scope. It's
/// rather the scope that holds a reference on its members.
void
decl_base::set_scope(scope_decl* scope)
{
if (!priv_->context_)
priv_->context_ = new context_rel(scope);
else
priv_->context_->set_scope(scope);
}
// </decl_base definition>
/// Streaming operator for the decl_base::visibility.
///
/// @param o the output stream to serialize the visibility to.
///
/// @param v the visibility to serialize.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& o, decl_base::visibility v)
{
string r;
switch (v)
{
case decl_base::VISIBILITY_NONE:
r = "none";
break;
case decl_base::VISIBILITY_DEFAULT:
r = "default";
break;
case decl_base::VISIBILITY_PROTECTED:
r = "protected";
break;
case decl_base::VISIBILITY_HIDDEN:
r = "hidden";
break;
case decl_base::VISIBILITY_INTERNAL:
r = "internal";
break;
}
return o;
}
/// Streaming operator for decl_base::binding.
///
/// @param o the output stream to serialize the visibility to.
///
/// @param b the binding to serialize.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& o, decl_base::binding b)
{
string r;
switch (b)
{
case decl_base::BINDING_NONE:
r = "none";
break;
case decl_base::BINDING_LOCAL:
r = "local";
break;
case decl_base::BINDING_GLOBAL:
r = "global";
break;
case decl_base::BINDING_WEAK:
r = "weak";
break;
}
o << r;
return o;
}
/// Turn equality of shared_ptr of decl_base into a deep equality;
/// that is, make it compare the pointed to objects, not just the
/// pointers.
///
/// @param l the shared_ptr of decl_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of decl_base on right-hand-side of the
/// equality.
///
/// @return true if the decl_base pointed to by the shared_ptrs are
/// equal, false otherwise.
bool
operator==(const decl_base_sptr& l, const decl_base_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// Inequality operator of shared_ptr of @ref decl_base.
///
/// This is a deep equality operator, that is, it compares the
/// pointed-to objects, rather than just the pointers.
///
/// @param l the left-hand-side operand.
///
/// @param r the right-hand-side operand.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const decl_base_sptr& l, const decl_base_sptr& r)
{return !operator==(l, r);}
/// Turn equality of shared_ptr of type_base into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of type_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of type_base on right-hand-side of the
/// equality.
///
/// @return true if the type_base pointed to by the shared_ptrs are
/// equal, false otherwise.
bool
operator==(const type_base_sptr& l, const type_base_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// Turn inequality of shared_ptr of type_base into a deep equality;
/// that is, make it compare the pointed to objects..
///
/// @param l the shared_ptr of type_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of type_base on right-hand-side of the
/// equality.
///
/// @return true iff the type_base pointed to by the shared_ptrs are
/// different.
bool
operator!=(const type_base_sptr& l, const type_base_sptr& r)
{return !operator==(l, r);}
/// Tests if a declaration has got a scope.
///
/// @param d the declaration to consider.
///
/// @return true if the declaration has got a scope, false otherwise.
bool
has_scope(const decl_base& d)
{return (d.get_scope());}
/// Tests if a declaration has got a scope.
///
/// @param d the declaration to consider.
///
/// @return true if the declaration has got a scope, false otherwise.
bool
has_scope(const decl_base_sptr d)
{return has_scope(*d.get());}
/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base_sptr d)
{return is_at_class_scope(d) || is_method_decl(d);}
/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base* d)
{return is_at_class_scope(d) || is_method_decl(d);}
/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base& d)
{return is_at_class_scope(d) || is_method_decl(d);}
/// Test if a declaration is a @ref scope_decl.
///
/// @param d the declaration to take in account.
///
/// @return the a pointer to the @ref scope_decl sub-object of @p d,
/// if d is a @ref scope_decl.
scope_decl*
is_scope_decl(decl_base* d)
{return dynamic_cast<scope_decl*>(d);}
/// Test if a declaration is a @ref scope_decl.
///
/// @param d the declaration to take in account.
///
/// @return the a pointer to the @ref scope_decl sub-object of @p d,
/// if d is a @ref scope_decl.
scope_decl_sptr
is_scope_decl(const decl_base_sptr& d)
{return dynamic_pointer_cast<scope_decl>(d);}
/// Tests if a type is a class member.
///
/// @param t the type to consider.
///
/// @return true if @p t is a class member type, false otherwise.
bool
is_member_type(const type_base_sptr& t)
{
decl_base_sptr d = get_type_declaration(t);
return is_member_decl(d);
}
/// Test if a type is user-defined.
///
/// A type is considered user-defined if it's a
/// struct/class/union/enum that is *NOT* artificial.
///
/// @param t the type to consider.
///
/// @return true iff the type @p t is user-defined.
bool
is_user_defined_type(const type_base* t)
{
if (t == 0)
return false;
t = peel_qualified_or_typedef_type(t);
decl_base *d = is_decl(t);
if ((is_class_or_union_type(t) || is_enum_type(t))
&& d && !d->get_is_artificial())
return true;
return false;
}
/// Test if a type is user-defined.
///
/// A type is considered user-defined if it's a
/// struct/class/union/enum.
///
///
/// @param t the type to consider.
///
/// @return true iff the type @p t is user-defined.
bool
is_user_defined_type(const type_base_sptr& t)
{return is_user_defined_type(t.get());}
/// Gets the access specifier for a class member.
///
/// @param d the declaration of the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return the access specifier for the class member @p d.
access_specifier
get_member_access_specifier(const decl_base& d)
{
ABG_ASSERT(is_member_decl(d));
const context_rel* c = d.get_context_rel();
ABG_ASSERT(c);
return c->get_access_specifier();
}
/// Gets the access specifier for a class member.
///
/// @param d the declaration of the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return the access specifier for the class member @p d.
access_specifier
get_member_access_specifier(const decl_base_sptr& d)
{return get_member_access_specifier(*d);}
/// Sets the access specifier for a class member.
///
/// @param d the class member to set the access specifier for. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @param a the new access specifier to set the class member to.
void
set_member_access_specifier(decl_base& d,
access_specifier a)
{
ABG_ASSERT(is_member_decl(d));
context_rel* c = d.get_context_rel();
ABG_ASSERT(c);
c->set_access_specifier(a);
}
/// Sets the access specifier for a class member.
///
/// @param d the class member to set the access specifier for. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @param a the new access specifier to set the class member to.
void
set_member_access_specifier(const decl_base_sptr& d,
access_specifier a)
{set_member_access_specifier(*d, a);}
/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base&d)
{
ABG_ASSERT(is_member_decl(d));
const context_rel* c = d.get_context_rel();
ABG_ASSERT(c);
return c->get_is_static();
}
/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base* d)
{return get_member_is_static(*d);}
/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base_sptr& d)
{return get_member_is_static(*d);}
/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl& v)
{return is_at_class_scope(v);}
/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl* v)
{return is_data_member(*v);}
/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl_sptr d)
{return is_at_class_scope(d);}
/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl_sptr
is_data_member(const decl_base_sptr& d)
{
if (var_decl_sptr v = is_var_decl(d))
{
if (is_data_member(v))
return v;
}
return var_decl_sptr();
}
/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl_sptr
is_data_member(const type_or_decl_base_sptr& d)
{
if (var_decl_sptr v = is_var_decl(d))
{
if (is_data_member(v))
return v;
}
return var_decl_sptr();
}
/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl*
is_data_member(const type_or_decl_base* d)
{
if (var_decl *v = is_var_decl(d))
if (is_data_member(v))
return v;
return 0;
}
/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl*
is_data_member(const decl_base *d)
{
if (var_decl *v = is_var_decl(d))
if (is_data_member(v))
return v;
return 0;
}
/// Get the first non-anonymous data member of a given anonymous data
/// member.
///
/// E.g:
///
/// struct S
/// {
/// union // <-- for this anonymous data member, the function
/// // returns a.
/// {
/// int a;
/// charb;
/// };
/// };
///
/// @return anon_dm the anonymous data member to consider.
///
/// @return the first non-anonymous data member of @p anon_dm. If no
/// data member was found then this function returns @p anon_dm.
const var_decl_sptr
get_first_non_anonymous_data_member(const var_decl_sptr anon_dm)
{
if (!anon_dm || !is_anonymous_data_member(anon_dm))
return anon_dm;
class_or_union_sptr klass = anonymous_data_member_to_class_or_union(anon_dm);
var_decl_sptr first = *klass->get_non_static_data_members().begin();
if (is_anonymous_data_member(first))
return get_first_non_anonymous_data_member(first);
return first;
}
/// In the context of a given class or union, this function returns
/// the data member that is located after a given data member.
///
/// @param klass the class or union to consider.
///
/// @param the data member to consider.
///
/// @return the data member that is located right after @p
/// data_member.
const var_decl_sptr
get_next_data_member(const class_or_union_sptr &klass,
const var_decl_sptr &data_member)
{
if (!klass ||!data_member)
return var_decl_sptr();
for (class_or_union::data_members::const_iterator it =
klass->get_non_static_data_members().begin();
it != klass->get_non_static_data_members().end();
++it)
if (**it == *data_member)
{
++it;
if (it != klass->get_non_static_data_members().end())
return get_first_non_anonymous_data_member(*it);
break;
}
return var_decl_sptr();
}
/// Get the last data member of a class type.
///
/// @param klass the class type to consider.
var_decl_sptr
get_last_data_member(const class_or_union_sptr &klass)
{return klass->get_non_static_data_members().back();}
/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return true iff @p d is an anonymous data member.
bool
is_anonymous_data_member(const decl_base& d)
{return is_anonymous_data_member(&d);}
/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return the var_decl representing the data member iff @p d is an
/// anonymous data member.
const var_decl*
is_anonymous_data_member(const type_or_decl_base* d)
{
if (const var_decl* v = is_data_member(d))
{
if (is_anonymous_data_member(v))
return v;
}
return 0;
}
/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member. Otherwise returns a nil pointer.
const var_decl*
is_anonymous_data_member(const decl_base* d)
{
if (const var_decl* v = is_data_member(d))
{
if (is_anonymous_data_member(v))
return v;
}
return 0;
}
/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member. Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const type_or_decl_base_sptr& d)
{
if (var_decl_sptr v = is_data_member(d))
{
if (is_anonymous_data_member(v))
return v;
}
return var_decl_sptr();
}
/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member. Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const decl_base_sptr& d)
{
if (var_decl_sptr v = is_data_member(d))
return is_anonymous_data_member(v);
return var_decl_sptr();
}
/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member. Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const var_decl_sptr& d)
{
if (is_anonymous_data_member(d.get()))
return d;
return var_decl_sptr();
}
/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member. Otherwise returns a nil pointer.
const var_decl*
is_anonymous_data_member(const var_decl* d)
{
if (d && is_anonymous_data_member(*d))
return d;
return 0;
}
/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return true iff @p d is an anonymous data member.
bool
is_anonymous_data_member(const var_decl& d)
{
return (is_data_member(d)
&& d.get_is_anonymous()
&& d.get_name().empty()
&& is_class_or_union_type(d.get_type()));
}
/// Get the @ref class_or_union type of a given anonymous data member.
///
/// @param d the anonymous data member to consider.
///
/// @return the @ref class_or_union type of the anonymous data member
/// @p d.
class_or_union*
anonymous_data_member_to_class_or_union(const var_decl* d)
{
if ((d = is_anonymous_data_member(d)))
return is_class_or_union_type(d->get_type().get());
return 0;
}
/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type. Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl& d)
{
if (is_data_member(d))
if (const class_or_union_sptr cou = is_class_or_union_type(d.get_type()))
if (cou->get_is_anonymous())
return cou;
return class_or_union_sptr();
}
/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type. Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl* d)
{
if (d)
return data_member_has_anonymous_type(*d);
return class_or_union_sptr();
}
/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type. Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl_sptr& d)
{return data_member_has_anonymous_type(d.get());}
/// Get the @ref class_or_union type of a given anonymous data member.
///
/// @param d the anonymous data member to consider.
///
/// @return the @ref class_or_union type of the anonymous data member
/// @p d.
class_or_union_sptr
anonymous_data_member_to_class_or_union(const var_decl_sptr &d)
{
if (var_decl_sptr v = is_anonymous_data_member(d))
return is_class_or_union_type(v->get_type());
return class_or_union_sptr();
}
/// Test if the scope of a given decl is anonymous or anonymous with a
/// naming typedef.
///
/// @param d the decl consider.
///
/// @return true iff the scope of @p d is anonymous or anonymous with
/// a naming typedef.
bool
scope_anonymous_or_typedef_named(const decl_base& d)
{
if (d.get_has_anonymous_parent()
|| (d.get_scope() && d.get_scope()->get_naming_typedef()))
return true;
return false;
}
/// Test if a given decl is anonymous or has a naming typedef.
///
/// @param d the decl to consider.
///
/// @return true iff @p d is anonymous or has a naming typedef.
bool
is_anonymous_or_typedef_named(const decl_base& d)
{
if (d.get_is_anonymous() || d.get_naming_typedef())
return true;
return false;
}
/// Set the offset of a data member into its containing class.
///
/// @param m the data member to consider.
///
/// @param o the offset, in bits.
void
set_data_member_offset(var_decl_sptr m, uint64_t o)
{
ABG_ASSERT(is_data_member(m));
dm_context_rel* ctxt_rel =
dynamic_cast<dm_context_rel*>(m->get_context_rel());
ABG_ASSERT(ctxt_rel);
ctxt_rel->set_offset_in_bits(o);
}
/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const var_decl& m)
{
ABG_ASSERT(is_data_member(m));
const dm_context_rel* ctxt_rel =
dynamic_cast<const dm_context_rel*>(m.get_context_rel());
ABG_ASSERT(ctxt_rel);
return ctxt_rel->get_offset_in_bits();
}
/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const var_decl_sptr m)
{return get_data_member_offset(*m);}
/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const decl_base_sptr d)
{return get_data_member_offset(dynamic_pointer_cast<var_decl>(d));}
/// Get the offset of the non-static data member that comes after a
/// given one.
///
/// If there is no data member after after the one given to this
/// function (maybe because the given one is the last data member of
/// the class type) then the function return false.
///
/// @param klass the class to consider.
///
/// @param dm the data member before the one we want to retrieve.
///
/// @param offset out parameter. This parameter is set by the
/// function to the offset of the data member that comes right after
/// the data member @p dm, iff the function returns true.
///
/// @return true iff the data member coming right after @p dm was
/// found.
bool
get_next_data_member_offset(const class_or_union_sptr& klass,
const var_decl_sptr& dm,
uint64_t& offset)
{
var_decl_sptr next_dm = get_next_data_member(klass, dm);
if (!next_dm)
return false;
offset = get_data_member_offset(next_dm);
return true;
}
/// Get the absolute offset of a data member.
///
/// If the data member is part of an anonymous data member then this
/// returns the absolute offset -- relative to the beginning of the
/// containing class of the anonymous data member.
///
/// @param m the data member to consider.
///
/// @return the aboslute offset of the data member @p m.
uint64_t
get_absolute_data_member_offset(const var_decl& m)
{
ABG_ASSERT(is_data_member(m));
const dm_context_rel* ctxt_rel =
dynamic_cast<const dm_context_rel*>(m.get_context_rel());
ABG_ASSERT(ctxt_rel);
const var_decl *containing_anonymous_data_member =
ctxt_rel->get_anonymous_data_member();
uint64_t containing_anonymous_data_member_offset = 0;
if (containing_anonymous_data_member)
containing_anonymous_data_member_offset =
get_absolute_data_member_offset(*containing_anonymous_data_member);
return (ctxt_rel->get_offset_in_bits()
+
containing_anonymous_data_member_offset);
}
/// Get the absolute offset of a data member.
///
/// If the data member is part of an anonymous data member then this
/// returns the absolute offset -- relative to the beginning of the
/// containing class of the anonymous data member.
///
/// @param m the data member to consider.
///
/// @return the aboslute offset of the data member @p m.
uint64_t
get_absolute_data_member_offset(const var_decl_sptr& m)
{
if (!m)
return 0;
return get_absolute_data_member_offset(*m);
}
/// Get the size of a given variable.
///
/// @param v the variable to consider.
///
/// @return the size of variable @p v.
uint64_t
get_var_size_in_bits(const var_decl_sptr& v)
{
type_base_sptr t = v->get_type();
ABG_ASSERT(t);
return t->get_size_in_bits();
}
/// Set a flag saying if a data member is laid out.
///
/// @param m the data member to consider.
///
/// @param l true if @p m is to be considered as laid out.
void
set_data_member_is_laid_out(var_decl_sptr m, bool l)
{
ABG_ASSERT(is_data_member(m));
dm_context_rel* ctxt_rel =
dynamic_cast<dm_context_rel*>(m->get_context_rel());
ctxt_rel->set_is_laid_out(l);
}
/// Test whether a data member is laid out.
///
/// @param m the data member to consider.
///
/// @return true if @p m is laid out, false otherwise.
bool
get_data_member_is_laid_out(const var_decl& m)
{
ABG_ASSERT(is_data_member(m));
const dm_context_rel* ctxt_rel =
dynamic_cast<const dm_context_rel*>(m.get_context_rel());
return ctxt_rel->get_is_laid_out();
}
/// Test whether a data member is laid out.
///
/// @param m the data member to consider.
///
/// @return true if @p m is laid out, false otherwise.
bool
get_data_member_is_laid_out(const var_decl_sptr m)
{return get_data_member_is_laid_out(*m);}
/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl& f)
{return is_member_decl(f);}
/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl* f)
{return is_member_decl(*f);}
/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl_sptr& f)
{return is_member_decl(*f);}
/// Test whether a member function is a constructor.
///
/// @param f the member function to test.
///
/// @return true if @p f is a constructor, false otherwise.
bool
get_member_function_is_ctor(const function_decl& f)
{
ABG_ASSERT(is_member_function(f));
const method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
const mem_fn_context_rel* ctxt =
dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());
return ctxt->is_constructor();
}
/// Test whether a member function is a constructor.
///
/// @param f the member function to test.
///
/// @return true if @p f is a constructor, false otherwise.
bool
get_member_function_is_ctor(const function_decl_sptr& f)
{return get_member_function_is_ctor(*f);}
/// Setter for the is_ctor property of the member function.
///
/// @param f the member function to set.
///
/// @param f the new boolean value of the is_ctor property. Is true
/// if @p f is a constructor, false otherwise.
void
set_member_function_is_ctor(function_decl& f, bool c)
{
ABG_ASSERT(is_member_function(f));
method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
mem_fn_context_rel* ctxt =
dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());
ctxt->is_constructor(c);
}
/// Setter for the is_ctor property of the member function.
///
/// @param f the member function to set.
///
/// @param f the new boolean value of the is_ctor property. Is true
/// if @p f is a constructor, false otherwise.
void
set_member_function_is_ctor(const function_decl_sptr& f, bool c)
{set_member_function_is_ctor(*f, c);}
/// Test whether a member function is a destructor.
///
/// @param f the function to test.
///
/// @return true if @p f is a destructor, false otherwise.
bool
get_member_function_is_dtor(const function_decl& f)
{
ABG_ASSERT(is_member_function(f));
const method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
const mem_fn_context_rel* ctxt =
dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());
return ctxt->is_destructor();
}
/// Test whether a member function is a destructor.
///
/// @param f the function to test.
///
/// @return true if @p f is a destructor, false otherwise.
bool
get_member_function_is_dtor(const function_decl_sptr& f)
{return get_member_function_is_dtor(*f);}
/// Set the destructor-ness property of a member function.
///
/// @param f the function to set.
///
/// @param d true if @p f is a destructor, false otherwise.
void
set_member_function_is_dtor(function_decl& f, bool d)
{
ABG_ASSERT(is_member_function(f));
method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
mem_fn_context_rel* ctxt =
dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());
ctxt->is_destructor(d);
}
/// Set the destructor-ness property of a member function.
///
/// @param f the function to set.
///
/// @param d true if @p f is a destructor, false otherwise.
void
set_member_function_is_dtor(const function_decl_sptr& f, bool d)
{set_member_function_is_dtor(*f, d);}
/// Test whether a member function is const.
///
/// @param f the function to test.
///
/// @return true if @p f is const, false otherwise.
bool
get_member_function_is_const(const function_decl& f)
{
ABG_ASSERT(is_member_function(f));
const method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
const mem_fn_context_rel* ctxt =
dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());
return ctxt->is_const();
}
/// Test whether a member function is const.
///
/// @param f the function to test.
///
/// @return true if @p f is const, false otherwise.
bool
get_member_function_is_const(const function_decl_sptr& f)
{return get_member_function_is_const(*f);}
/// set the const-ness property of a member function.
///
/// @param f the function to set.
///
/// @param is_const the new value of the const-ness property of @p f
void
set_member_function_is_const(function_decl& f, bool is_const)
{
ABG_ASSERT(is_member_function(f));
method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
mem_fn_context_rel* ctxt =
dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());
ctxt->is_const(is_const);
}
/// set the const-ness property of a member function.
///
/// @param f the function to set.
///
/// @param is_const the new value of the const-ness property of @p f
void
set_member_function_is_const(const function_decl_sptr& f, bool is_const)
{set_member_function_is_const(*f, is_const);}
/// Test if a virtual member function has a vtable offset set.
///
/// @param f the virtual member function to consider.
///
/// @return true iff the virtual member function has its vtable offset
/// set, i.e, if the vtable offset of @p is different from -1.
bool
member_function_has_vtable_offset(const function_decl& f)
{return get_member_function_vtable_offset(f) != -1;}
/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @return the vtable offset of @p f. Note that a vtable offset of
/// value -1 means that the member function does *NOT* yet have a
/// vtable offset associated to it.
ssize_t
get_member_function_vtable_offset(const function_decl& f)
{
ABG_ASSERT(is_member_function(f));
const method_decl* m =
dynamic_cast<const method_decl*>(&f);
ABG_ASSERT(m);
const mem_fn_context_rel* ctxt =
dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());
return ctxt->vtable_offset();
}
/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @return the vtable offset of @p f. Note that a vtable offset of
/// value -1 means that the member function does *NOT* yet have a
/// vtable offset associated to it.
ssize_t
get_member_function_vtable_offset(const function_decl_sptr& f)
{return get_member_function_vtable_offset(*f);}
/// Set the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @param s the new vtable offset. Please note that a vtable offset
/// of value -1 means that the virtual member function does not (yet)
/// have any vtable offset associated to it.
void
set_member_function_vtable_offset(function_decl& f, ssize_t s)
{
ABG_ASSERT(is_member_function(f));
method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
mem_fn_context_rel* ctxt =
dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());
ctxt->vtable_offset(s);
}
/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @param s the new vtable offset. Please note that a vtable offset
/// of value -1 means that the virtual member function does not (yet)
/// have any vtable offset associated to it.
void
set_member_function_vtable_offset(const function_decl_sptr& f, ssize_t s)
{return set_member_function_vtable_offset(*f, s);}
/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl& f)
{
ABG_ASSERT(is_member_function(f));
const method_decl* m =
dynamic_cast<const method_decl*>(&f);
ABG_ASSERT(m);
const mem_fn_context_rel* ctxt =
dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());
return ctxt->is_virtual();
}
/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl_sptr& mem_fn)
{return mem_fn ? get_member_function_is_virtual(*mem_fn) : false;}
/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl* mem_fn)
{return mem_fn ? get_member_function_is_virtual(*mem_fn) : false;}
/// Set the virtual-ness of a member function.
///
/// @param f the member function to consider.
///
/// @param is_virtual set to true if the function is virtual.
void
set_member_function_is_virtual(function_decl& f, bool is_virtual)
{
ABG_ASSERT(is_member_function(f));
method_decl* m = is_method_decl(&f);
ABG_ASSERT(m);
mem_fn_context_rel* ctxt =
dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());
ctxt->is_virtual(is_virtual);
}
/// Set the virtual-ness of a member function.
///
/// @param f the member function to consider.
///
/// @param is_virtual set to true if the function is virtual.
void
set_member_function_is_virtual(const function_decl_sptr& fn, bool is_virtual)
{
if (fn)
{
set_member_function_is_virtual(*fn, is_virtual);
fixup_virtual_member_function
(dynamic_pointer_cast<method_decl>(fn));
}
}
/// Recursively returns the the underlying type of a typedef. The
/// return type should not be a typedef of anything anymore.
///
///
/// Also recursively strip typedefs from the sub-types of the type
/// given in arguments.
///
/// Note that this function builds types in which typedefs are
/// stripped off. Usually, types are held by their scope, so their
/// life time is bound to the life time of their scope. But as this
/// function cannot really insert the built type into it's scope, it
/// must ensure that the newly built type stays live long enough.
///
/// So, if the newly built type has a canonical type, this function
/// returns the canonical type. Otherwise, this function ensure that
/// the newly built type has a life time that is the same as the life
/// time of the entire libabigail library.
///
/// @param type the type to strip the typedefs from.
///
/// @return the resulting type stripped from its typedefs, or just
/// return @p type if it has no typedef in any of its sub-types.
type_base_sptr
strip_typedef(const type_base_sptr type)
{
if (!type)
return type;
// If type is a class type then do not try to strip typedefs from it.
// And if it has no canonical type (which can mean that it's a
// declaration-only class), then, make sure its live for ever and
// return it.
if (class_decl_sptr cl = is_class_type(type))
{
if (!cl->get_canonical_type())
keep_type_alive(type);
return type;
}
environment* env = type->get_environment();
ABG_ASSERT(env);
type_base_sptr t = type;
if (const typedef_decl_sptr ty = is_typedef(t))
t = strip_typedef(type_or_void(ty->get_underlying_type(), env));
else if (const reference_type_def_sptr ty = is_reference_type(t))
{
type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type(),
env));
ABG_ASSERT(p);
t.reset(new reference_type_def(p,
ty->is_lvalue(),
ty->get_size_in_bits(),
ty->get_alignment_in_bits(),
ty->get_location()));
}
else if (const pointer_type_def_sptr ty = is_pointer_type(t))
{
type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type(),
env));
ABG_ASSERT(p);
t.reset(new pointer_type_def(p,
ty->get_size_in_bits(),
ty->get_alignment_in_bits(),
ty->get_location()));
}
else if (const qualified_type_def_sptr ty = is_qualified_type(t))
{
type_base_sptr p = strip_typedef(type_or_void(ty->get_underlying_type(),
env));
ABG_ASSERT(p);
t.reset(new qualified_type_def(p,
ty->get_cv_quals(),
ty->get_location()));
}
else if (const array_type_def_sptr ty = is_array_type(t))
{
type_base_sptr p = strip_typedef(ty->get_element_type());
ABG_ASSERT(p);
t.reset(new array_type_def(p, ty->get_subranges(), ty->get_location()));
}
else if (const method_type_sptr ty = is_method_type(t))
{
function_decl::parameters parm;
for (function_decl::parameters::const_iterator i =
ty->get_parameters().begin();
i != ty->get_parameters().end();
++i)
{
function_decl::parameter_sptr p = *i;
type_base_sptr typ = strip_typedef(p->get_type());
ABG_ASSERT(typ);
function_decl::parameter_sptr stripped
(new function_decl::parameter(typ,
p->get_index(),
p->get_name(),
p->get_location(),
p->get_variadic_marker(),
p->get_is_artificial()));
parm.push_back(stripped);
}
type_base_sptr p = strip_typedef(ty->get_return_type());
ABG_ASSERT(!!p == !!ty->get_return_type());
t.reset(new method_type(p, ty->get_class_type(),
parm, ty->get_is_const(),
ty->get_size_in_bits(),
ty->get_alignment_in_bits()));
}
else if (const function_type_sptr ty = is_function_type(t))
{
function_decl::parameters parm;
for (function_decl::parameters::const_iterator i =
ty->get_parameters().begin();
i != ty->get_parameters().end();
++i)
{
function_decl::parameter_sptr p = *i;
type_base_sptr typ = strip_typedef(p->get_type());
ABG_ASSERT(typ);
function_decl::parameter_sptr stripped
(new function_decl::parameter(typ,
p->get_index(),
p->get_name(),
p->get_location(),
p->get_variadic_marker(),
p->get_is_artificial()));
parm.push_back(stripped);
}
type_base_sptr p = strip_typedef(ty->get_return_type());
ABG_ASSERT(!!p == !!ty->get_return_type());
t.reset(new function_type(p, parm,
ty->get_size_in_bits(),
ty->get_alignment_in_bits()));
}
if (!t->get_environment())
set_environment_for_artifact(t, env);
if (!t->get_translation_unit())
t->set_translation_unit(type->get_translation_unit());
if (!(type->get_canonical_type() && canonicalize(t)))
keep_type_alive(t);
return t->get_canonical_type() ? t->get_canonical_type() : t;
}
/// Strip qualification from a qualified type, when it makes sense.
///
/// DWARF constructs "const reference". This is redundant because a
/// reference is always const. It also constructs the useless "const
/// void" type. The issue is these redundant types then leak into the
/// IR and make for bad diagnostics.
///
/// This function thus strips the const qualifier from the type in
/// that case. It might contain code to strip other cases like this
/// in the future.
///
/// @param t the type to strip const qualification from.
///
/// @return the stripped type or just return @p t.
decl_base_sptr
strip_useless_const_qualification(const qualified_type_def_sptr t)
{
if (!t)
return t;
decl_base_sptr result = t;
type_base_sptr u = t->get_underlying_type();
environment* env = t->get_environment();
if ((t->get_cv_quals() & qualified_type_def::CV_CONST
&& (is_reference_type(u)))
|| (t->get_cv_quals() & qualified_type_def::CV_CONST
&& env->is_void_type(u))
|| t->get_cv_quals() == qualified_type_def::CV_NONE)
// Let's strip the const qualifier because a reference is always
// 'const' and a const void doesn't make sense. They will just
// lead to spurious changes later down the pipeline, that we'll
// have to deal with by doing painful and error-prone editing of
// the diff IR. Dropping that useless and inconsistent artefact
// right here seems to be a good way to go.
result = is_decl(u);
return result;
}
/// Return the leaf underlying type node of a @ref typedef_decl node.
///
/// If the underlying type of a @ref typedef_decl node is itself a
/// @ref typedef_decl node, then recursively look at the underlying
/// type nodes to get the first one that is not a a @ref typedef_decl
/// node. This is what a leaf underlying type node means.
///
/// Otherwise, if the underlying type node of @ref typedef_decl is
/// *NOT* a @ref typedef_decl node, then just return the underlying
/// type node.
///
/// And if the type node considered is not a @ref typedef_decl node,
/// then just return it.
///
/// @return the leaf underlying type node of a @p type.
type_base_sptr
peel_typedef_type(const type_base_sptr& type)
{
typedef_decl_sptr t = is_typedef(type);
if (!t)
return type;
if (is_typedef(t->get_underlying_type()))
return peel_typedef_type(t->get_underlying_type());
return t->get_underlying_type();
}
/// Return the leaf underlying type node of a @ref typedef_decl node.
///
/// If the underlying type of a @ref typedef_decl node is itself a
/// @ref typedef_decl node, then recursively look at the underlying
/// type nodes to get the first one that is not a a @ref typedef_decl
/// node. This is what a leaf underlying type node means.
///
/// Otherwise, if the underlying type node of @ref typedef_decl is
/// *NOT* a @ref typedef_decl node, then just return the underlying
/// type node.
///
/// And if the type node considered is not a @ref typedef_decl node,
/// then just return it.
///
/// @return the leaf underlying type node of a @p type.
const type_base*
peel_typedef_type(const type_base* type)
{
const typedef_decl* t = is_typedef(type);
if (!t)
return type;
return peel_typedef_type(t->get_underlying_type()).get();
}
/// Return the leaf pointed-to type node of a @ref pointer_type_def
/// node.
///
/// If the pointed-to type of a @ref pointer_type_def node is itself a
/// @ref pointer_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// pointer_type_def node. This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref pointer_type_def is
/// *NOT* a @ref pointer_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref pointer_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
type_base_sptr
peel_pointer_type(const type_base_sptr& type)
{
pointer_type_def_sptr t = is_pointer_type(type);
if (!t)
return type;
if (is_pointer_type(t->get_pointed_to_type()))
return peel_pointer_type(t->get_pointed_to_type());
return t->get_pointed_to_type();
}
/// Return the leaf pointed-to type node of a @ref pointer_type_def
/// node.
///
/// If the pointed-to type of a @ref pointer_type_def node is itself a
/// @ref pointer_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// pointer_type_def node. This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref pointer_type_def is
/// *NOT* a @ref pointer_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref pointer_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
const type_base*
peel_pointer_type(const type_base* type)
{
const pointer_type_def* t = is_pointer_type(type);
if (!t)
return type;
return peel_pointer_type(t->get_pointed_to_type()).get();
}
/// Return the leaf pointed-to type node of a @ref reference_type_def
/// node.
///
/// If the pointed-to type of a @ref reference_type_def node is itself
/// a @ref reference_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// reference_type_def node. This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref reference_type_def
/// is *NOT* a @ref reference_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref reference_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
type_base_sptr
peel_reference_type(const type_base_sptr& type)
{
reference_type_def_sptr t = is_reference_type(type);
if (!t)
return type;
if (is_reference_type(t->get_pointed_to_type()))
return peel_reference_type(t->get_pointed_to_type());
return t->get_pointed_to_type();
}
/// Return the leaf pointed-to type node of a @ref reference_type_def
/// node.
///
/// If the pointed-to type of a @ref reference_type_def node is itself
/// a @ref reference_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// reference_type_def node. This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref reference_type_def
/// is *NOT* a @ref reference_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref reference_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
const type_base*
peel_reference_type(const type_base* type)
{
const reference_type_def* t = is_reference_type(type);
if (!t)
return type;
return peel_reference_type(t->get_pointed_to_type()).get();
}
/// Return the leaf element type of an array.
///
/// If the element type is itself an array, then recursively return
/// the element type of that array itself.
///
/// @param type the array type to consider. If this is not an array
/// type, this type is returned by the function.
///
/// @return the leaf element type of the array @p type, or, if it's
/// not an array type, then just return @p.
const type_base_sptr
peel_array_type(const type_base_sptr& type)
{
const array_type_def_sptr t = is_array_type(type);
if (!t)
return type;
return peel_array_type(t->get_element_type());
}
/// Return the leaf element type of an array.
///
/// If the element type is itself an array, then recursively return
/// the element type of that array itself.
///
/// @param type the array type to consider. If this is not an array
/// type, this type is returned by the function.
///
/// @return the leaf element type of the array @p type, or, if it's
/// not an array type, then just return @p.
const type_base*
peel_array_type(const type_base* type)
{
const array_type_def* t = is_array_type(type);
if (!t)
return type;
return peel_array_type(t->get_element_type()).get();
}
/// Return the leaf underlying type of a qualified type.
///
/// If the underlying type is itself a qualified type, then
/// recursively return the first underlying type of that qualified
/// type to return the first underlying type that is not a qualified type.
///
/// If the underlying type is NOT a qualified type, then just return
/// that underlying type.
///
/// @param type the qualified type to consider.
///
/// @return the leaf underlying type.
const type_base*
peel_qualified_type(const type_base* type)
{
const qualified_type_def* t = is_qualified_type(type);
if (!t)
return type;
return peel_qualified_type(t->get_underlying_type().get());
}
/// Return the leaf underlying type of a qualified type.
///
/// If the underlying type is itself a qualified type, then
/// recursively return the first underlying type of that qualified
/// type to return the first underlying type that is not a qualified type.
///
/// If the underlying type is NOT a qualified type, then just return
/// that underlying type.
///
/// @param type the qualified type to consider.
///
/// @return the leaf underlying type.
const type_base_sptr
peel_qualified_type(const type_base_sptr& type)
{
const qualified_type_def_sptr t = is_qualified_type(type);
if (!t)
return type;
return peel_qualified_type(t->get_underlying_type());
}
/// Return the leaf underlying type of a qualified or typedef type.
///
/// If the underlying type is itself a qualified or typedef type, then
/// recursively return the first underlying type of that qualified or
/// typedef type to return the first underlying type that is not a
/// qualified or typedef type.
///
/// If the underlying type is NOT a qualified nor a typedef type, then
/// just return that underlying type.
///
/// @param type the qualified or typedef type to consider.
///
/// @return the leaf underlying type.
type_base*
peel_qualified_or_typedef_type(const type_base* type)
{
while (is_typedef(type) || is_qualified_type(type))
{
if (const typedef_decl* t = is_typedef(type))
type = peel_typedef_type(t);
if (const qualified_type_def* t = is_qualified_type(type))
type = peel_qualified_type(t);
}
return const_cast<type_base*>(type);
}
/// Return the leaf underlying type of a qualified or typedef type.
///
/// If the underlying type is itself a qualified or typedef type, then
/// recursively return the first underlying type of that qualified or
/// typedef type to return the first underlying type that is not a
/// qualified or typedef type.
///
/// If the underlying type is NOT a qualified nor a typedef type, then
/// just return that underlying type.
///
/// @param type the qualified or typedef type to consider.
///
/// @return the leaf underlying type.
type_base_sptr
peel_qualified_or_typedef_type(const type_base_sptr &t)
{
type_base_sptr type = t;
while (is_typedef(type) || is_qualified_type(type))
{
if (typedef_decl_sptr t = is_typedef(type))
type = peel_typedef_type(t);
if (qualified_type_def_sptr t = is_qualified_type(type))
type = peel_qualified_type(t);
}
return type;
}
/// Return the leaf underlying or pointed-to type node of a @ref
/// typedef_decl, @ref pointer_type_def, @ref reference_type_def,
/// or @ref array_type_def node.
///
/// @param type the type to peel.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base_sptr
peel_typedef_pointer_or_reference_type(const type_base_sptr type)
{
type_base_sptr typ = type;
while (is_typedef(typ)
|| is_pointer_type(typ)
|| is_reference_type(typ)
|| is_array_type(typ))
{
if (typedef_decl_sptr t = is_typedef(typ))
typ = peel_typedef_type(t);
if (pointer_type_def_sptr t = is_pointer_type(typ))
typ = peel_pointer_type(t);
if (reference_type_def_sptr t = is_reference_type(typ))
typ = peel_reference_type(t);
if (const array_type_def_sptr t = is_array_type(typ))
typ = peel_array_type(t);
}
return typ;
}
/// Return the leaf underlying or pointed-to type node of a @ref
/// typedef_decl, @ref pointer_type_def or @ref reference_type_def
/// node.
///
/// @param type the type to peel.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base*
peel_typedef_pointer_or_reference_type(const type_base* type)
{
while (is_typedef(type)
|| is_pointer_type(type)
|| is_reference_type(type)
|| is_array_type(type))
{
if (const typedef_decl* t = is_typedef(type))
type = peel_typedef_type(t);
if (const pointer_type_def* t = is_pointer_type(type))
type = peel_pointer_type(t);
if (const reference_type_def* t = is_reference_type(type))
type = peel_reference_type(t);
if (const array_type_def* t = is_array_type(type))
type = peel_array_type(t);
}
return const_cast<type_base*>(type);
}
/// Return the leaf underlying or pointed-to type node of a, @ref
/// pointer_type_def, @ref reference_type_def or @ref
/// qualified_type_def type node.
///
/// @param type the type to peel.
///
/// @param peel_qualified_type if true, also peel qualified types.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base*
peel_pointer_or_reference_type(const type_base *type,
bool peel_qual_type)
{
while (is_pointer_type(type)
|| is_reference_type(type)
|| is_array_type(type)
|| (peel_qual_type && is_qualified_type(type)))
{
if (const pointer_type_def* t = is_pointer_type(type))
type = peel_pointer_type(t);
if (const reference_type_def* t = is_reference_type(type))
type = peel_reference_type(t);
if (const array_type_def* t = is_array_type(type))
type = peel_array_type(t);
if (peel_qual_type)
if (const qualified_type_def* t = is_qualified_type(type))
type = peel_qualified_type(t);
}
return const_cast<type_base*>(type);
}
/// Clone an array type.
///
/// Note that the element type of the new array is shared witht the
/// old one.
///
/// @param array the array type to clone.
///
/// @return a newly built array type. Note that it needs to be added
/// to a scope (e.g, using add_decl_to_scope) for its lifetime to be
/// bound to the one of that scope. Otherwise, its lifetime is bound
/// to the lifetime of its containing shared pointer.
array_type_def_sptr
clone_array(const array_type_def_sptr& array)
{
vector<array_type_def::subrange_sptr> subranges;
for (vector<array_type_def::subrange_sptr>::const_iterator i =
array->get_subranges().begin();
i != array->get_subranges().end();
++i)
{
array_type_def::subrange_sptr subrange
(new array_type_def::subrange_type(array->get_environment(),
(*i)->get_name(),
(*i)->get_lower_bound(),
(*i)->get_upper_bound(),
(*i)->get_underlying_type(),
(*i)->get_location(),
(*i)->get_language()));
subrange->is_infinite((*i)->is_infinite());
if (scope_decl *scope = (*i)->get_scope())
add_decl_to_scope(subrange, scope);
subranges.push_back(subrange);
}
array_type_def_sptr result
(new array_type_def(array->get_element_type(),
subranges, array->get_location()));
return result;
}
/// Clone a typedef type.
///
/// Note that the underlying type of the newly constructed typedef is
/// shared with the old one.
///
/// @param t the typedef to clone.
///
/// @return the newly constructed typedef. Note that it needs to be
/// added to a scope (e.g, using add_decl_to_scope) for its lifetime
/// to be bound to the one of that scope. Otherwise, its lifetime is
/// bound to the lifetime of its containing shared pointer.
typedef_decl_sptr
clone_typedef(const typedef_decl_sptr& t)
{
if (!t)
return t;
typedef_decl_sptr result
(new typedef_decl(t->get_name(), t->get_underlying_type(),
t->get_location(), t->get_linkage_name(),
t->get_visibility()));
return result;
}
/// Clone a qualifiend type.
///
/// Note that underlying type of the newly constructed qualified type
/// is shared with the old one.
///
/// @param t the qualified type to clone.
///
/// @return the newly constructed qualified type. Note that it needs
/// to be added to a scope (e.g, using add_decl_to_scope) for its
/// lifetime to be bound to the one of that scope. Otherwise, its
/// lifetime is bound to the lifetime of its containing shared
/// pointer.
qualified_type_def_sptr
clone_qualified_type(const qualified_type_def_sptr& t)
{
if (!t)
return t;
qualified_type_def_sptr result
(new qualified_type_def(t->get_underlying_type(),
t->get_cv_quals(), t->get_location()));
return result;
}
/// Clone a typedef, an array or a qualified tree.
///
/// @param type the typedef, array or qualified tree to clone. any
/// order.
///
/// @return the cloned type, or NULL if @type was neither a typedef,
/// array nor a qualified type.
static type_base_sptr
clone_typedef_array_qualified_type(type_base_sptr type)
{
if (!type)
return type;
scope_decl* scope = is_decl(type) ? is_decl(type)->get_scope() : 0;
type_base_sptr result;
if (typedef_decl_sptr t = is_typedef(type))
result = clone_typedef(is_typedef(t));
else if (qualified_type_def_sptr t = is_qualified_type(type))
result = clone_qualified_type(t);
else if (array_type_def_sptr t = is_array_type(type))
result = clone_array(t);
else
return type_base_sptr();
if (scope)
add_decl_to_scope(is_decl(result), scope);
return result;
}
/// Clone a type tree made of an array or a typedef of array.
///
/// Note that this can be a tree which root node is a typedef an which
/// sub-tree can be any arbitrary combination of typedef, qualified
/// type and arrays.
///
/// @param t the array or typedef of qualified array to consider.
///
/// @return a clone of @p t.
type_base_sptr
clone_array_tree(const type_base_sptr t)
{
ABG_ASSERT(is_typedef_of_array(t) || is_array_type(t));
scope_decl* scope = is_decl(t)->get_scope();
type_base_sptr result = clone_typedef_array_qualified_type(t);
ABG_ASSERT(is_typedef_of_array(result) || is_array_type(result));
type_base_sptr subtree;
if (typedef_decl_sptr type = is_typedef(result))
{
type_base_sptr s =
clone_typedef_array_qualified_type(type->get_underlying_type());
if (s)
{
subtree = s;
type->set_underlying_type(subtree);
}
}
else if (array_type_def_sptr type = is_array_type(result))
{
type_base_sptr s =
clone_typedef_array_qualified_type(type->get_element_type());
if (s)
{
subtree = s;
type->set_element_type(subtree);
}
}
add_decl_to_scope(is_decl(subtree), scope);
for (;;)
{
if (typedef_decl_sptr t = is_typedef(subtree))
{
type_base_sptr s =
clone_typedef_array_qualified_type(t->get_underlying_type());
if (s)
{
scope_decl* scope =
is_decl(t->get_underlying_type())->get_scope();
ABG_ASSERT(scope);
add_decl_to_scope(is_decl(s), scope);
t->set_underlying_type (s);
subtree = s;
}
else
break;
}
else if (qualified_type_def_sptr t = is_qualified_type(subtree))
{
type_base_sptr s =
clone_typedef_array_qualified_type(t->get_underlying_type());
if (s)
{
scope_decl* scope =
is_decl(t->get_underlying_type())->get_scope();
ABG_ASSERT(scope);
add_decl_to_scope(is_decl(s), scope);
t->set_underlying_type(s);
subtree = s;
}
else
break;
}
else if (array_type_def_sptr t = is_array_type(subtree))
{
type_base_sptr e = t->get_element_type();
if (is_typedef(e) || is_qualified_type(e))
{
type_base_sptr s =
clone_typedef_array_qualified_type(e);
if (s)
{
scope_decl* scope = is_decl(e)->get_scope();
ABG_ASSERT(scope);
add_decl_to_scope(is_decl(s), scope);
t->set_element_type(s);
}
else
break;
}
break;
}
else
break;
}
return result;
}
/// Update the qualified name of a given sub-tree.
///
/// @param d the sub-tree for which to update the qualified name.
static void
update_qualified_name(decl_base * d)
{
::qualified_name_setter setter;
d->traverse(setter);
}
/// Update the qualified name of a given sub-tree.
///
/// @param d the sub-tree for which to update the qualified name.
static void
update_qualified_name(decl_base_sptr d)
{return update_qualified_name(d.get());}
// <scope_decl stuff>
/// Hash a type by returning the pointer value of its canonical type.
///
/// @param l the type to hash.
///
/// @return the the pointer value of the canonical type of @p l.
size_t
canonical_type_hash::operator()(const type_base_sptr& l) const
{return operator()(l.get());}
/// Hash a (canonical) type by returning its pointer value
///
/// @param l the canonical type to hash.
///
/// @return the pointer value of the canonical type of @p l.
size_t
canonical_type_hash::operator()(const type_base *l) const
{return reinterpret_cast<size_t>(l);}
struct scope_decl::priv
{
declarations members_;
declarations sorted_members_;
scopes member_scopes_;
canonical_type_sptr_set_type canonical_types_;
type_base_sptrs_type sorted_canonical_types_;
}; // end struct scope_decl::priv
/// Constructor of the @ref scope_decl type.
///
/// @param the environment to use for the new instance.
///
/// @param the name of the scope decl.
///
/// @param locus the source location where the scope_decl is defined.
///
/// @param vis the visibility of the declaration.
scope_decl::scope_decl(const environment* env,
const string& name,
const location& locus,
visibility vis)
: type_or_decl_base(env, ABSTRACT_SCOPE_DECL|ABSTRACT_DECL_BASE),
decl_base(env, name, locus, /*mangled_name=*/name, vis),
priv_(new priv)
{}
/// Constructor of the @ref scope_decl type.
///
/// @param the environment to use for the new instance.
///
/// @param l the source location where the scope_decl is defined.
///
/// @param vis the visibility of the declaration.
scope_decl::scope_decl(const environment* env, location& l)
: type_or_decl_base(env, ABSTRACT_SCOPE_DECL|ABSTRACT_DECL_BASE),
decl_base(env, "", l),
priv_(new priv)
{}
/// @eturn the set of canonical types of the the current scope.
canonical_type_sptr_set_type&
scope_decl::get_canonical_types()
{return priv_->canonical_types_;}
/// @eturn the set of canonical types of the the current scope.
const canonical_type_sptr_set_type&
scope_decl::get_canonical_types() const
{return const_cast<scope_decl*>(this)->get_canonical_types();}
/// Return a vector of sorted canonical types of the current scope.
///
/// The types are sorted "almost topologically". That means, they are
/// sorted using the lexicographic order of the string representing
/// the location their definition point. If a type doesn't have a
/// location, then its pretty representation is used.
///
/// @return a vector of sorted canonical types of the current scope.
const type_base_sptrs_type&
scope_decl::get_sorted_canonical_types() const
{
if (priv_->sorted_canonical_types_.empty())
{
for (canonical_type_sptr_set_type::const_iterator e =
get_canonical_types().begin();
e != get_canonical_types().end();
++e)
priv_->sorted_canonical_types_.push_back(*e);
type_topo_comp comp;
std::stable_sort(priv_->sorted_canonical_types_.begin(),
priv_->sorted_canonical_types_.end(),
comp);
}
return priv_->sorted_canonical_types_;
}
/// Getter for the member declarations carried by the current @ref
/// scope_decl.
///
/// @return the member declarations carried by the current @ref
/// scope_decl.
const scope_decl::declarations&
scope_decl::get_member_decls() const
{return priv_->members_;}
/// Getter for the member declarations carried by the current @ref
/// scope_decl.
///
/// @return the member declarations carried by the current @ref
/// scope_decl.
scope_decl::declarations&
scope_decl::get_member_decls()
{return priv_->members_;}
/// Getter for the sorted member declarations carried by the current
/// @ref scope_decl.
///
/// @return the sorted member declarations carried by the current @ref
/// scope_decl. The declarations are sorted topologically.
const scope_decl::declarations&
scope_decl::get_sorted_member_decls() const
{
decl_topo_comp comp;
if (priv_->sorted_members_.empty())
{
for (declarations::const_iterator i = get_member_decls().begin();
i != get_member_decls().end();
++i)
priv_->sorted_members_.push_back(*i);
std::stable_sort(priv_->sorted_members_.begin(),
priv_->sorted_members_.end(),
comp);
}
return priv_->sorted_members_;
}
/// Getter for the number of anonymous classes contained in this
/// scope.
///
/// @return the number of anonymous classes contained in this scope.
size_t
scope_decl::get_num_anonymous_member_classes() const
{
int result = 0;
for (declarations::const_iterator it = get_member_decls().begin();
it != get_member_decls().end();
++it)
if (class_decl_sptr t = is_class_type(*it))
if (t->get_is_anonymous())
++result;
return result;
}
/// Getter for the number of anonymous unions contained in this
/// scope.
///
/// @return the number of anonymous unions contained in this scope.
size_t
scope_decl::get_num_anonymous_member_unions() const
{
int result = 0;
for (declarations::const_iterator it = get_member_decls().begin();
it != get_member_decls().end();
++it)
if (union_decl_sptr t = is_union_type(*it))
if (t->get_is_anonymous())
++result;
return result;
}
/// Getter for the number of anonymous enums contained in this
/// scope.
///
/// @return the number of anonymous enums contained in this scope.
size_t
scope_decl::get_num_anonymous_member_enums() const
{
int result = 0;
for (declarations::const_iterator it = get_member_decls().begin();
it != get_member_decls().end();
++it)
if (enum_type_decl_sptr t = is_enum_type(*it))
if (t->get_is_anonymous())
++result;
return result;
}
/// Getter for the scopes carried by the current scope.
///
/// @return the scopes carried by the current scope.
scope_decl::scopes&
scope_decl::get_member_scopes()
{return priv_->member_scopes_;}
/// Getter for the scopes carried by the current scope.
///
/// @return the scopes carried by the current scope.
const scope_decl::scopes&
scope_decl::get_member_scopes() const
{return priv_->member_scopes_;}
/// Test if the current scope is empty.
///
/// @return true iff the current scope is empty.
bool
scope_decl::is_empty() const
{
return (get_member_decls().empty()
&& get_canonical_types().empty());
}
/// Add a member decl to this scope. Note that user code should not
/// use this, but rather use add_decl_to_scope.
///
/// Note that this function updates the qualified name of the member
/// decl that is added. It also sets the scope of the member. Thus,
/// it ABG_ASSERTs that member should not have its scope set, prior to
/// calling this function.
///
/// @param member the new member decl to add to this scope.
decl_base_sptr
scope_decl::add_member_decl(const decl_base_sptr& member)
{
ABG_ASSERT(!has_scope(member));
member->set_scope(this);
priv_->members_.push_back(member);
if (scope_decl_sptr m = dynamic_pointer_cast<scope_decl>(member))
priv_->member_scopes_.push_back(m);
update_qualified_name(member);
if (const environment* env = get_environment())
set_environment_for_artifact(member, env);
if (translation_unit* tu = get_translation_unit())
{
if (translation_unit* existing_tu = member->get_translation_unit())
ABG_ASSERT(tu == existing_tu);
else
member->set_translation_unit(tu);
}
maybe_update_types_lookup_map(member);
return member;
}
/// Insert a member decl to this scope, right before an element
/// pointed to by a given iterator. Note that user code should not
/// use this, but rather use insert_decl_into_scope.
///
/// Note that this function updates the qualified name of the inserted
/// member.
///
/// @param member the new member decl to add to this scope.
///
/// @param before an interator pointing to the element before which
/// the new member should be inserted.
decl_base_sptr
scope_decl::insert_member_decl(decl_base_sptr member,
declarations::iterator before)
{
ABG_ASSERT(!member->get_scope());
member->set_scope(this);
priv_->members_.insert(before, member);
if (scope_decl_sptr m = dynamic_pointer_cast<scope_decl>(member))
priv_-> member_scopes_.push_back(m);
update_qualified_name(member);
if (const environment* env = get_environment())
set_environment_for_artifact(member, env);
if (translation_unit* tu = get_translation_unit())
{
if (translation_unit* existing_tu = member->get_translation_unit())
ABG_ASSERT(tu == existing_tu);
else
member->set_translation_unit(tu);
}
maybe_update_types_lookup_map(member);
return member;
}
/// Remove a declaration from the current scope.
///
/// @param member the declaration to remove from the scope.
void
scope_decl::remove_member_decl(decl_base_sptr member)
{
for (declarations::iterator i = priv_->members_.begin();
i != priv_->members_.end();
++i)
{
if (**i == *member)
{
priv_->members_.erase(i);
// Do not access i after this point as it's invalided by the
// erase call.
break;
}
}
scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(member);
if (scope)
{
for (scopes::iterator i = priv_->member_scopes_.begin();
i != priv_->member_scopes_.end();
++i)
{
if (**i == *member)
{
priv_->member_scopes_.erase(i);
break;
}
}
}
}
/// Return the hash value for the current instance of scope_decl.
///
/// This method can trigger the computing of the hash value, if need be.
///
/// @return the hash value.
size_t
scope_decl::get_hash() const
{
scope_decl::hash hash_scope;
return hash_scope(this);
}
/// Compares two instances of @ref scope_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const scope_decl& l, const scope_decl& r, change_kind* k)
{
bool result = true;
if (!l.decl_base::operator==(r))
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
scope_decl::declarations::const_iterator i, j;
for (i = l.get_member_decls().begin(), j = r.get_member_decls().begin();
i != l.get_member_decls().end() && j != r.get_member_decls().end();
++i, ++j)
{
if (**i != **j)
{
result = false;
if (k)
{
*k |= SUBTYPE_CHANGE_KIND;
break;
}
else
ABG_RETURN_FALSE;
}
}
if (i != l.get_member_decls().end() || j != r.get_member_decls().end())
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
ABG_RETURN(result);
}
/// Return true iff both scopes have the same names and have the same
/// member decls.
///
/// This function doesn't check for equality of the scopes of its
/// arguments.
bool
scope_decl::operator==(const decl_base& o) const
{
const scope_decl* other = dynamic_cast<const scope_decl*>(&o);
if (!other)
return false;
return equals(*this, *other, 0);
}
/// Equality operator for @ref scope_decl_sptr.
///
/// @param l the left hand side operand of the equality operator.
///
/// @pram r the right hand side operand of the equalify operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const scope_decl_sptr& l, const scope_decl_sptr& r)
{
if (!!l != !!r)
return false;
if (l.get() == r.get())
return true;
return *l == *r;
}
/// Inequality operator for @ref scope_decl_sptr.
///
/// @param l the left hand side operand of the equality operator.
///
/// @pram r the right hand side operand of the equalify operator.
///
/// @return true iff @p l equals @p r.
bool
operator!=(const scope_decl_sptr& l, const scope_decl_sptr& r)
{return !operator==(l, r);}
/// Find a member of the current scope and return an iterator on it.
///
/// @param decl the scope member to find.
///
/// @param i the iterator to set to the member @p decl. This is set
/// iff the function returns true.
///
/// @return true if the member decl was found, false otherwise.
bool
scope_decl::find_iterator_for_member(const decl_base* decl,
declarations::iterator& i)
{
if (!decl)
return false;
if (get_member_decls().empty())
{
i = get_member_decls().end();
return false;
}
for (declarations::iterator it = get_member_decls().begin();
it != get_member_decls().end();
++it)
{
if ((*it).get() == decl)
{
i = it;
return true;
}
}
return false;
}
/// Find a member of the current scope and return an iterator on it.
///
/// @param decl the scope member to find.
///
/// @param i the iterator to set to the member @p decl. This is set
/// iff the function returns true.
///
/// @return true if the member decl was found, false otherwise.
bool
scope_decl::find_iterator_for_member(const decl_base_sptr decl,
declarations::iterator& i)
{return find_iterator_for_member(decl.get(), i);}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance of scope_decl
/// and on its member nodes.
///
/// @return true if the traversal of the tree should continue, false
/// otherwise.
bool
scope_decl::traverse(ir_node_visitor &v)
{
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
for (scope_decl::declarations::const_iterator i =
get_member_decls().begin();
i != get_member_decls ().end();
++i)
if (!(*i)->traverse(v))
break;
visiting(false);
}
return v.visit_end(this);
}
scope_decl::~scope_decl()
{}
/// Appends a declaration to a given scope, if the declaration
/// doesn't already belong to one.
///
/// @param decl the declaration to add to the scope
///
/// @param scope the scope to append the declaration to
decl_base_sptr
add_decl_to_scope(decl_base_sptr decl, scope_decl* scope)
{
ABG_ASSERT(scope);
if (scope && decl && !decl->get_scope())
decl = scope->add_member_decl(decl);
return decl;
}
/// Appends a declaration to a given scope, if the declaration doesn't
/// already belong to a scope.
///
/// @param decl the declaration to add append to the scope
///
/// @param scope the scope to append the decl to
decl_base_sptr
add_decl_to_scope(decl_base_sptr decl, const scope_decl_sptr& scope)
{return add_decl_to_scope(decl, scope.get());}
/// Remove a given decl from its scope
///
/// @param decl the decl to remove from its scope.
void
remove_decl_from_scope(decl_base_sptr decl)
{
if (!decl)
return;
scope_decl* scope = decl->get_scope();
scope->remove_member_decl(decl);
decl->set_scope(0);
}
/// Inserts a declaration into a given scope, before a given IR child
/// node of the scope.
///
/// @param decl the declaration to insert into the scope.
///
/// @param before an iterator pointing to the child IR node before
/// which to insert the declaration.
///
/// @param scope the scope into which to insert the declaration.
decl_base_sptr
insert_decl_into_scope(decl_base_sptr decl,
scope_decl::declarations::iterator before,
scope_decl* scope)
{
if (scope && decl && !decl->get_scope())
{
decl_base_sptr d = scope->insert_member_decl(decl, before);
decl = d;
}
return decl;
}
/// Inserts a declaration into a given scope, before a given IR child
/// node of the scope.
///
/// @param decl the declaration to insert into the scope.
///
/// @param before an iterator pointing to the child IR node before
/// which to insert the declaration.
///
/// @param scope the scope into which to insert the declaration.
decl_base_sptr
insert_decl_into_scope(decl_base_sptr decl,
scope_decl::declarations::iterator before,
scope_decl_sptr scope)
{return insert_decl_into_scope(decl, before, scope.get());}
/// Constructor of the @ref global_scope type.
///
/// @param tu the translation unit the scope belongs to.
global_scope::global_scope(translation_unit *tu)
: type_or_decl_base(tu->get_environment(),
GLOBAL_SCOPE_DECL
| ABSTRACT_DECL_BASE
| ABSTRACT_SCOPE_DECL),
decl_base(tu->get_environment(), "", location()),
scope_decl(tu->get_environment(), "", location()),
translation_unit_(tu)
{
runtime_type_instance(this);
}
/// return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const decl_base& decl)
{
if (const global_scope* s = dynamic_cast<const global_scope*>(&decl))
return s;
scope_decl* scope = decl.get_scope();
while (scope && !dynamic_cast<global_scope*>(scope))
scope = scope->get_scope();
return scope ? dynamic_cast<global_scope*> (scope) : 0;
}
/// return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const decl_base* decl)
{return get_global_scope(*decl);}
/// Return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const shared_ptr<decl_base> decl)
{return get_global_scope(decl.get());}
/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// Note that @p scope must come before @p decl in topological
/// order.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base* decl,
const scope_decl* scope)
{
if (!decl)
return 0;
if (scope == 0)
return get_global_scope(decl);
// Handle the case where decl is a scope itself.
const scope_decl* s = dynamic_cast<const scope_decl*>(decl);
if (!s)
s = decl->get_scope();
if (is_global_scope(s))
return scope;
// Here, decl is in the scope 'scope', or decl and 'scope' are the
// same. The caller needs to be prepared to deal with this case.
if (s == scope)
return s;
while (s && !is_global_scope(s) && s->get_scope() != scope)
s = s->get_scope();
if (!s || is_global_scope(s))
// SCOPE must come before decl in topological order, but I don't
// know how to ensure that ...
return scope;
ABG_ASSERT(s);
return s;
}
/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base_sptr decl,
const scope_decl* scope)
{return get_top_most_scope_under(decl.get(), scope);}
/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base_sptr decl,
const scope_decl_sptr scope)
{return get_top_most_scope_under(decl, scope.get());}
// </scope_decl stuff>
/// Get the string representation of a CV qualifier bitmap.
///
/// @param cv_quals the bitmap of CV qualifiers to consider.
///
/// @return the string representation.
string
get_string_representation_of_cv_quals(const qualified_type_def::CV cv_quals)
{
string repr;
if (cv_quals & qualified_type_def::CV_RESTRICT)
repr = "restrict";
if (cv_quals & qualified_type_def::CV_CONST)
{
if (!repr.empty())
repr += ' ';
repr += "const";
}
if (cv_quals & qualified_type_def::CV_VOLATILE)
{
if (!repr.empty())
repr += ' ';
repr += "volatile";
}
return repr;
}
/// Build and return a copy of the name of an ABI artifact that is
/// either a type or a decl.
///
/// @param tod the ABI artifact to get the name for.
///
/// @param qualified if yes, return the qualified name of @p tod;
/// otherwise, return the non-qualified name;
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base *tod, bool qualified)
{
string result;
type_or_decl_base* a = const_cast<type_or_decl_base*>(tod);
if (type_base* t = dynamic_cast<type_base*>(a))
result = get_type_name(t, qualified);
else if (decl_base *d = dynamic_cast<decl_base*>(a))
{
if (qualified)
result = d->get_qualified_name();
else
result = d->get_name();
}
else
// We should never reach this point.
abort();
return result;
}
/// Build and return a copy of the name of an ABI artifact that is
/// either a type of a decl.
///
/// @param tod the ABI artifact to get the name for.
///
/// @param qualified if yes, return the qualified name of @p tod;
/// otherwise, return the non-qualified name;
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base_sptr& tod, bool qualified)
{return get_name(tod.get(), qualified);}
/// Build and return a qualified name from a name and its scope.
///
/// The name is supposed to be for an entity that is part of the
/// scope.
///
/// @param the scope to consider.
///
/// @param name of the name to consider.
///
/// @return a copy of the string that represents the qualified name.
string
build_qualified_name(const scope_decl* scope, const string& name)
{
if (name.empty())
return "";
string qualified_name;
if (scope)
qualified_name = scope->get_qualified_name();
if (qualified_name.empty())
qualified_name = name;
else
qualified_name = qualified_name + "::" + name;
return qualified_name;
}
/// Build and return the qualified name of a type in its scope.
///
/// @param scope the scope of the type to consider.
///
/// @param type the type to consider.
string
build_qualified_name(const scope_decl* scope, const type_base_sptr& type)
{return build_qualified_name(scope, get_name((type)));}
// </scope_decl stuff>
/// Get the location of the declaration of a given type.
///
/// @param type the type to consider.
///
/// @return the location of the declaration of type @p type.
location
get_location(const type_base_sptr& type)
{
if (decl_base_sptr decl = get_type_declaration(type))
return get_location(decl);
return location();
}
/// Get the location of a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the location of the declaration @p decl.
location
get_location(const decl_base_sptr& decl)
{
location loc = decl->get_location();
if (!loc)
{
if (class_or_union_sptr c = is_class_or_union_type(decl))
if (c->get_is_declaration_only() && c->get_definition_of_declaration())
{
c = is_class_or_union_type(c->get_definition_of_declaration());
loc = c->get_location();
}
}
return loc;
}
/// Get the scope of a given type.
///
/// @param t the type to consider.
///
/// @return the scope of type @p t or 0 if the type has no scope yet.
scope_decl*
get_type_scope(type_base* t)
{
if (!t)
return 0;
decl_base* d = get_type_declaration(t);
if (d)
return d->get_scope();
return 0;
}
/// Get the scope of a given type.
///
/// @param t the type to consider.
///
/// @return the scope of type @p t or 0 if the type has no scope yet.
scope_decl*
get_type_scope(const type_base_sptr& t)
{return get_type_scope(t.get());}
/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base_sptr& t, bool qualified, bool internal)
{return get_type_name(t.get(), qualified, internal);}
/// Return true iff a decl is for a type type that has a generic
/// anonymous internal type name.
///
/// @param d the decl to considier.
///
/// @return true iff @p d is for a type type that has a generic
/// anonymous internal type name.
static bool
has_generic_anonymous_internal_type_name(const decl_base *d)
{
return is_class_or_union_type(d) || is_enum_type(d);
}
/// Return the generic internal name of an anonymous type.
///
/// For internal purposes, we want to define a generic name for all
/// anonymous types of a certain kind. For instance, all anonymous
/// structs will be have a generic name of "__anonymous_struct__", all
/// anonymous unions will have a generic name of
/// "__anonymous_union__", etc.
///
/// That generic name can be used as a hash to put all anonymous types
/// of a certain kind in the same hash table bucket, for instance.
static interned_string
get_generic_anonymous_internal_type_name(const decl_base *d)
{
ABG_ASSERT(has_generic_anonymous_internal_type_name(d));
const environment *env = d->get_environment();
interned_string result;
if (is_class_type(d))
result =
env->intern(tools_utils::get_anonymous_struct_internal_name_prefix());
else if (is_union_type(d))
result =
env->intern(tools_utils::get_anonymous_union_internal_name_prefix());
else if (is_enum_type(d))
result =
env->intern(tools_utils::get_anonymous_enum_internal_name_prefix());
else
ABG_ASSERT_NOT_REACHED;
return result;
}
/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base* t, bool qualified, bool internal)
{
const decl_base* d = dynamic_cast<const decl_base*>(t);
if (!d)
{
const function_type* fn_type = is_function_type(t);
ABG_ASSERT(fn_type);
return fn_type->get_cached_name(internal);
}
// All anonymous types of a given kind get to have the same internal
// name for internal purpose. This to allow them to be compared
// among themselves during type canonicalization.
if (internal && d->get_is_anonymous())
{
string r;
r += get_generic_anonymous_internal_type_name(d);
return t->get_environment()->intern(r);
}
if (qualified)
return d->get_qualified_name(internal);
return d->get_name();
}
/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base& t, bool qualified, bool internal)
{return get_type_name(&t, qualified, internal);}
/// Get the name of the pointer to a given type.
///
/// @param pointed_to_type the pointed-to-type to consider.
///
/// @param qualified this is true if the resulting name should be of a
/// pointer to a *fully-qualified* pointed-to-type.
///
/// @param internal true if the name is for libabigail-internal
/// purposes.
///
/// @return the name (string representation) of the pointer.
interned_string
get_name_of_pointer_to_type(const type_base& pointed_to_type,
bool qualified, bool internal)
{
const environment* env = pointed_to_type.get_environment();
ABG_ASSERT(env);
string tn = get_type_name(pointed_to_type, qualified, internal);
tn = tn + "*";
return env->intern(tn);
}
/// Get the name of the reference to a given type.
///
/// @param pointed_to_type the pointed-to-type to consider.
///
/// @param qualified this is true if the resulting name should be of a
/// reference to a *fully-qualified* pointed-to-type.
///
/// @param internal true if the name is for libabigail-internal
/// purposes.
///
/// @return the name (string representation) of the reference.
interned_string
get_name_of_reference_to_type(const type_base& pointed_to_type,
bool lvalue_reference,
bool qualified, bool internal)
{
const environment* env = pointed_to_type.get_environment();
ABG_ASSERT(env);
string name = get_type_name(pointed_to_type, qualified, internal);
if (lvalue_reference)
name = name + "&";
else
name = name + "&&";
return env->intern(name);
}
/// Get the name of a qualified type, given the underlying type and
/// its qualifiers.
///
/// @param underlying_type the underlying type to consider.
///
/// @param quals the CV qualifiers of the name.
///
/// @param qualified true if we should consider the fully qualified
/// name of @p underlying_type.
///
/// @param internal true if the result is to be used for
/// libabigail-internal purposes.
///
/// @return the name (string representation) of the qualified type.
interned_string
get_name_of_qualified_type(const type_base_sptr& underlying_type,
qualified_type_def::CV quals,
bool qualified, bool internal)
{
const environment* env = underlying_type->get_environment();
ABG_ASSERT(env);
string quals_repr = get_string_representation_of_cv_quals(quals);
string name = get_type_name(underlying_type, qualified, internal);
if (quals_repr.empty() && internal)
// We are asked to return the internal name, that might be used
// for type canonicalization. For that canonicalization, we need
// to make a difference between a no-op qualified type which
// underlying type is foo (the qualified type is named "none
// foo"), and the name of foo, which is just "foo".
//
// Please remember that this has to be kept in sync with what is
// done in die_qualified_name, in abg-dwarf-reader.cc. So if you
// change this code here, please change that code there too.
quals_repr = "";
if (!quals_repr.empty())
{
if (is_pointer_type(underlying_type)
|| is_reference_type(underlying_type)
|| is_array_type(underlying_type))
{
name += " ";
name += quals_repr;
}
else
name = quals_repr + " " + name;
}
return env->intern(name);
}
/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type_sptr& fn_type,
bool internal)
{return get_function_type_name(fn_type.get(), internal);}
/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type* fn_type,
bool internal)
{
ABG_ASSERT(fn_type);
if (const method_type* method = is_method_type(fn_type))
return get_method_type_name(method, internal);
return get_function_type_name(*fn_type, internal);
}
/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type& fn_type,
bool internal)
{
std::ostringstream o;
type_base_sptr return_type = fn_type.get_return_type();
const environment* env = fn_type.get_environment();
ABG_ASSERT(env);
o << get_pretty_representation(return_type, internal);
o << " (";
for (function_type::parameters::const_iterator i =
fn_type.get_parameters().begin();
i != fn_type.get_parameters().end();
++i)
{
if (i != fn_type.get_parameters().begin())
o << ", ";
o << get_pretty_representation((*i)->get_type(), internal);
}
o <<")";
return env->intern(o.str());
}
/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type_sptr fn_type,
bool internal)
{return get_method_type_name(fn_type.get(), internal);}
/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type* fn_type,
bool internal)
{
if (fn_type)
return get_method_type_name(*fn_type, internal);
return interned_string();
}
/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type& fn_type,
bool internal)
{
std::ostringstream o;
type_base_sptr return_type= fn_type.get_return_type();
const environment* env = fn_type.get_environment();
ABG_ASSERT(env);
if (return_type)
o << return_type->get_cached_pretty_representation(internal);
else
// There are still some abixml files out there in which "void"
// can be expressed as an empty type.
o << "void";
class_or_union_sptr class_type = fn_type.get_class_type();
ABG_ASSERT(class_type);
o << " (" << class_type->get_qualified_name(internal) << "::*)"
<< " (";
for (function_type::parameters::const_iterator i =
fn_type.get_parameters().begin();
i != fn_type.get_parameters().end();
++i)
{
if (i != fn_type.get_parameters().begin())
o << ", ";
if (*i)
o << (*i)->get_type()->get_cached_pretty_representation(internal);
else
// There are still some abixml files out there in which "void"
// can be expressed as an empty type.
o << "void";
}
o <<")";
return env->intern(o.str());
}
/// Build and return a copy of the pretty representation of an ABI
/// artifact that could be either a type of a decl.
///
/// param tod the ABI artifact to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of an ABI artifact
/// that could be either a type of a decl.
string
get_pretty_representation(const type_or_decl_base* tod, bool internal)
{
string result;
if (type_base* t = is_type(const_cast<type_or_decl_base*>(tod)))
result = get_pretty_representation(t, internal);
else if (decl_base* d = is_decl(const_cast<type_or_decl_base*>(tod)))
result = get_pretty_representation(d, internal);
else
// We should never reach this point
abort();
return result;
}
/// Build and return a copy of the pretty representation of an ABI
/// artifact that could be either a type of a decl.
///
/// param tod the ABI artifact to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of an ABI artifact
/// that could be either a type of a decl.
string
get_pretty_representation(const type_or_decl_base_sptr& tod, bool internal)
{return get_pretty_representation(tod.get(), internal);}
/// Get a copy of the pretty representation of a decl.
///
/// @param d the decl to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the decl.
string
get_pretty_representation(const decl_base* d, bool internal)
{
if (!d)
return "";
return d->get_pretty_representation(internal);
}
/// Get a copy of the pretty representation of a type.
///
/// @param d the type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the type.
string
get_pretty_representation(const type_base* t, bool internal)
{
if (!t)
return "void";
if (const function_type* fn_type = is_function_type(t))
return get_pretty_representation(fn_type, internal);
const decl_base* d = get_type_declaration(t);
ABG_ASSERT(d);
return get_pretty_representation(d, internal);
}
/// Get a copy of the pretty representation of a decl.
///
/// @param d the decl to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the decl.
string
get_pretty_representation(const decl_base_sptr& d, bool internal)
{return get_pretty_representation(d.get(), internal);}
/// Get a copy of the pretty representation of a type.
///
/// @param d the type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the type.
string
get_pretty_representation(const type_base_sptr& t, bool internal)
{return get_pretty_representation(t.get(), internal);}
/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type_sptr& fn_type,
bool internal)
{return get_pretty_representation(fn_type.get(), internal);}
/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type* fn_type, bool internal)
{
if (!fn_type)
return "void";
if (const method_type* method = is_method_type(fn_type))
return get_pretty_representation(method, internal);
return get_pretty_representation(*fn_type, internal);
}
/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type& fn_type, bool internal)
{
std::ostringstream o;
o << "function type " << get_function_type_name(fn_type, internal);
return o.str();
}
/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type& method, bool internal)
{
std::ostringstream o;
o << "method type " << get_method_type_name(method, internal);
return o.str();
}
/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type* method, bool internal)
{
if (!method)
return "void";
return get_pretty_representation(*method, internal);
}
/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type_sptr method, bool internal)
{return get_pretty_representation(method.get(), internal);}
/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
/// struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line. Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union& cou,
const string& indent,
bool one_line,
bool internal,
bool qualified_names)
{
string repr;
string local_indent = " ";
if (class_decl* clazz = is_class_type(&cou))
{
repr = indent;
if (!internal && clazz->is_struct())
repr += "struct";
else
repr += "class";
}
else if (is_union_type(cou))
repr = indent + "union";
else
return "";
repr += " ";
string name = cou.get_qualified_name();
if (!cou.get_is_anonymous())
repr += name;
repr += "{";
if (!one_line)
repr += "\n";
string real_indent;
const class_or_union::data_members &dmems = cou.get_data_members();
for (class_or_union::data_members::const_iterator dm = dmems.begin();
dm != dmems.end();
++dm)
{
if (dm != dmems.begin())
{
if (one_line)
real_indent = " ";
else
real_indent = "\n" + indent + local_indent;
}
if (var_decl_sptr v = is_anonymous_data_member(*dm))
repr +=
get_class_or_union_flat_representation
(anonymous_data_member_to_class_or_union(*dm),
real_indent, one_line, internal, qualified_names);
else
{
if (one_line)
{
if (dm != dmems.begin())
repr += real_indent;
repr += (*dm)->get_pretty_representation(internal,
qualified_names);
}
else
repr +=
real_indent+ (*dm)->get_pretty_representation(internal,
qualified_names);
}
repr += ";";
}
if (one_line)
repr += "}";
else
repr += indent + "}";
return repr;
}
/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
/// struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line. Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union* cou,
const string& indent,
bool one_line,
bool internal,
bool qualified_names)
{
if (cou)
return get_class_or_union_flat_representation(*cou, indent, one_line,
internal, qualified_names);
return "";
}
/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
/// struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line. Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union_sptr& cou,
const string& indent,
bool one_line,
bool internal,
bool qualified_names)
{return get_class_or_union_flat_representation(cou.get(),
indent,
one_line,
internal,
qualified_names);}
/// Get the textual representation of a type for debugging purposes.
///
/// If the type is a class/union, this shows the data members, virtual
/// member functions, size, pointer value of its canonical type, etc.
/// Otherwise, this just shows the name of the artifact as returned by
/// type_or_decl_base:get_pretty_representation().
///
/// @param artifact the artifact to show a debugging representation of.
///
/// @return a debugging string representation of @p artifact.
string
get_debug_representation(const type_or_decl_base* artifact)
{
if (!artifact)
return string("");
class_or_union * c = is_class_or_union_type(artifact);
if (c)
{
class_decl *clazz = is_class_type(c);
string name = c->get_qualified_name();
std::ostringstream o;
o << name;
if (clazz)
{
if (!clazz->get_base_specifiers().empty())
o << " :" << std::endl;
for (auto &b : clazz->get_base_specifiers())
{
o << " ";
if (b->get_is_virtual())
o << "virtual ";
o << b->get_base_class()->get_qualified_name()
<< std::endl;
}
}
o << std::endl
<< "{"
<< " // size in bits: " << c->get_size_in_bits() << "\n"
<< " // is-declaration-only: " << c->get_is_declaration_only() << "\n"
<< " // definition point: " << get_natural_or_artificial_location(c).expand() << "\n"
<< " // translation unit: " << c->get_translation_unit()->get_absolute_path() << std::endl
<< " // @: " << std::hex << is_type(c)
<< ", @canonical: " << c->get_canonical_type().get() << std::dec
<< "\n\n";
for (auto m : c->get_data_members())
{
type_base_sptr t = m->get_type();
t = peel_typedef_pointer_or_reference_type(t);
o << " "
<< m->get_pretty_representation(/*internal=*/false,
/*qualified=*/false)
<< ";";
if (t && t->get_canonical_type())
o << " // uses canonical type '@"
<< std::hex << t->get_canonical_type().get() << std::dec;
o << "'" << std::endl;
}
if (clazz && clazz->has_vtable())
{
o << " // virtual member functions\n\n";
for (auto f : clazz->get_virtual_mem_fns())
o << " " << f->get_pretty_representation(/*internal=*/false,
/*qualified=*/false)
<< ";" << std::endl;
}
o << "};" << std::endl;
return o.str();
}
else if (const enum_type_decl* e = is_enum_type(artifact))
{
string name = e->get_qualified_name();
std::ostringstream o;
o << name
<< " : "
<< e->get_underlying_type()->get_pretty_representation(/*internal=*/false,
true)
<< "\n"
<< "{\n"
<< " // size in bits: " << e->get_size_in_bits() << "\n"
<< " // is-declaration-only: " << e->get_is_declaration_only() << "\n"
<< " // definition point: " << get_natural_or_artificial_location(e).expand() << "\n"
<< " // translation unit: "
<< e->get_translation_unit()->get_absolute_path() << "\n"
<< " // @: " << std::hex << is_type(e)
<< ", @canonical: " << e->get_canonical_type().get() << std::dec
<< "\n\n";
for (const auto &enom : e->get_enumerators())
o << " " << enom.get_name() << " = " << enom.get_value() << ",\n";
o << "};\n";
return o.str();
}
return artifact->get_pretty_representation(/*internal=*/true,
/*qualified=*/true);
}
/// Get a given data member, referred to by its name, of a class type.
///
/// @param clazz the class to consider.
///
/// @param member_name name of the data member to get.
///
/// @return the resulting data member or nullptr if none was found.
var_decl_sptr
get_data_member(class_or_union *clazz, const char* member_name)
{
if (!clazz)
return var_decl_sptr();
return clazz->find_data_member(member_name);
}
/// Get a given data member, referred to by its name, of a class type.
///
/// @param clazz the class to consider.
///
/// @param member_name name of the data member to get.
///
/// @return the resulting data member or nullptr if none was found.
var_decl_sptr
get_data_member(type_base *clazz, const char* member_name)
{return get_data_member(is_class_or_union_type(clazz), member_name);}
/// Get the non-artificial (natural) location of a decl.
///
/// If the decl doesn't have a natural location then return its
/// artificial one.
///
/// @param decl the decl to consider.
///
/// @return the natural location @p decl if it has one; otherwise,
/// return its artificial one.
const location&
get_natural_or_artificial_location(const decl_base* decl)
{
ABG_ASSERT(decl);
if (decl->get_location())
return decl->get_location();
return decl->get_artificial_location();
}
/// Get the artificial location of a decl.
///
/// If the decl doesn't have an artificial location then return its
/// natural one.
///
/// @param decl the decl to consider.
///
/// @return the artificial location @p decl if it has one; otherwise,
/// return its natural one.
const location&
get_artificial_or_natural_location(const decl_base* decl)
{
ABG_ASSERT(decl);
if (decl->has_artificial_location())
return decl->get_artificial_location();
return decl->get_location();
}
/// Emit a textual representation of an artifact to std error stream
/// for debugging purposes.
///
/// This is useful to invoke from within a command line debugger like
/// GDB to help make sense of a given ABI artifact.
///
/// @param artifact the ABI artifact to emit the debugging
/// representation for.
///
/// @return the artifact @p artifact.
type_or_decl_base*
debug(const type_or_decl_base* artifact)
{
std::cerr << get_debug_representation(artifact) << std::endl;
return const_cast<type_or_decl_base*>(artifact);
}
/// Emit a textual representation of an artifact to std error stream
/// for debugging purposes.
///
/// This is useful to invoke from within a command line debugger like
/// GDB to help make sense of a given ABI artifact.
///
/// @param artifact the ABI artifact to emit the debugging
/// representation for.
///
/// @return the artifact @p artifact.
type_base*
debug(const type_base* artifact)
{
debug(static_cast<const type_or_decl_base*>(artifact));
return const_cast<type_base*>(artifact);
}
/// Emit a textual representation of an artifact to std error stream
/// for debugging purposes.
///
/// This is useful to invoke from within a command line debugger like
/// GDB to help make sense of a given ABI artifact.
///
/// @param artifact the ABI artifact to emit the debugging
/// representation for.
///
/// @return the artifact @p artifact.
decl_base*
debug(const decl_base* artifact)
{
debug(static_cast<const type_or_decl_base*>(artifact));
return const_cast<decl_base*>(artifact);
}
/// Test if two ABI artifacts are equal.
///
/// This can be useful when used from the command line of a debugger
/// like GDB.
///
/// @param l the first ABI artifact to consider in the comparison.
///
/// @param r the second ABI artifact to consider in the comparison.
///
/// @return true iff @p l equals @p r.
bool
debug_equals(const type_or_decl_base *l, const type_or_decl_base *r)
{
if (!!l != !!r)
return false;
if (!l && !r)
return true;
return (*l == *r);
}
/// By looking at the language of the TU a given ABI artifact belongs
/// to, test if the ONE Definition Rule should apply.
///
/// To date, it applies to c++, java and ada.
///
/// @param artifact the ABI artifact to consider.
///
/// @return true iff the One Definition Rule should apply.
bool
odr_is_relevant(const type_or_decl_base& artifact)
{
translation_unit::language l =
artifact.get_translation_unit()->get_language();
if (is_cplus_plus_language(l)
|| is_java_language(l)
|| is_ada_language(l))
return true;
return false;
}
/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
const decl_base*
get_type_declaration(const type_base* t)
{return dynamic_cast<const decl_base*>(t);}
/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
decl_base*
get_type_declaration(type_base* t)
{return dynamic_cast<decl_base*>(t);}
/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
decl_base_sptr
get_type_declaration(const type_base_sptr t)
{return dynamic_pointer_cast<decl_base>(t);}
/// Test if two types are equal modulo a typedef.
///
/// Type A and B are compatible if
///
/// - A and B are equal
/// - or if one type is a typedef of the other one.
///
/// @param type1 the first type to consider.
///
/// @param type2 the second type to consider.
///
/// @return true iff @p type1 and @p type2 are compatible.
bool
types_are_compatible(const type_base_sptr type1,
const type_base_sptr type2)
{
if (!type1 || !type2)
return false;
if (type1 == type2)
return true;
// Normally we should strip typedefs entirely, but this is
// potentially costly, especially on binaries with huge changesets
// like the Linux Kernel. So we just get the leaf types for now.
//
// Maybe there should be an option by which users accepts to pay the
// CPU usage toll in exchange for finer filtering?
// type_base_sptr t1 = strip_typedef(type1);
// type_base_sptr t2 = strip_typedef(type2);
type_base_sptr t1 = peel_typedef_type(type1);
type_base_sptr t2 = peel_typedef_type(type2);
return t1 == t2;
}
/// Test if two types are equal modulo a typedef.
///
/// Type A and B are compatible if
///
/// - A and B are equal
/// - or if one type is a typedef of the other one.
///
/// @param type1 the declaration of the first type to consider.
///
/// @param type2 the declaration of the second type to consider.
///
/// @return true iff @p type1 and @p type2 are compatible.
bool
types_are_compatible(const decl_base_sptr d1,
const decl_base_sptr d2)
{return types_are_compatible(is_type(d1), is_type(d2));}
/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const decl_base& decl)
{return const_cast<translation_unit*>(decl.get_translation_unit());}
/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const decl_base* decl)
{return decl ? get_translation_unit(*decl) : 0;}
/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const shared_ptr<decl_base> decl)
{return get_translation_unit(decl.get());}
/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return true iff the current scope is the global one.
bool
is_global_scope(const scope_decl& scope)
{return !!dynamic_cast<const global_scope*>(&scope);}
/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return the @ref global_scope* representing the scope @p scope or
/// 0 if @p scope is not a global scope.
const global_scope*
is_global_scope(const scope_decl* scope)
{return dynamic_cast<const global_scope*>(scope);}
/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return true iff the current scope is the global one.
bool
is_global_scope(const shared_ptr<scope_decl>scope)
{return is_global_scope(scope.get());}
/// Tests whether a given declaration is at global scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at global scope.
bool
is_at_global_scope(const decl_base& decl)
{return (is_global_scope(decl.get_scope()));}
/// Tests whether a given declaration is at global scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at global scope.
bool
is_at_global_scope(const decl_base_sptr decl)
{return (decl && is_global_scope(decl->get_scope()));}
/// Tests whether a given declaration is at global scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at global scope.
bool
is_at_global_scope(const decl_base* decl)
{return is_at_global_scope(*decl);}
/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base_sptr decl)
{return is_at_class_scope(decl.get());}
/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base* decl)
{
if (!decl)
return 0;
return is_at_class_scope(*decl);
}
/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base& decl)
{
scope_decl* scope = decl.get_scope();
if (class_or_union* cl = is_class_type(scope))
return cl;
if (class_or_union* cl = is_union_type(scope))
return cl;
return 0;
}
/// Find a data member inside an anonymous data member.
///
/// An anonymous data member has a type which is a class or union.
/// This function looks for a data member inside the type of that
/// anonymous data member.
///
/// @param anon_dm the anonymous data member to consider.
///
/// @param name the name of the data member to look for.
var_decl_sptr
find_data_member_from_anonymous_data_member(const var_decl_sptr& anon_dm,
const string& name)
{
const class_or_union* containing_class_or_union =
anonymous_data_member_to_class_or_union(anon_dm.get());
if (!containing_class_or_union)
return var_decl_sptr();
var_decl_sptr result = containing_class_or_union->find_data_member(name);
return result;
}
/// Tests whether a given decl is at template scope.
///
/// Note that only template parameters , types that are compositions,
/// and template patterns (function or class) can be at template scope.
///
/// @param decl the decl to consider.
///
/// @return true iff the decl is at template scope.
bool
is_at_template_scope(const shared_ptr<decl_base> decl)
{return (decl && dynamic_cast<template_decl*>(decl->get_scope()));}
/// Tests whether a decl is a template parameter.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is a template parameter.
bool
is_template_parameter(const shared_ptr<decl_base> decl)
{
return (decl && (dynamic_pointer_cast<type_tparameter>(decl)
|| dynamic_pointer_cast<non_type_tparameter>(decl)
|| dynamic_pointer_cast<template_tparameter>(decl)));
}
/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a shared pointer to @ref function_decl if @p d is a @ref
/// function_decl. Otherwise, a nil shared pointer.
function_decl*
is_function_decl(const type_or_decl_base* d)
{return dynamic_cast<function_decl*>(const_cast<type_or_decl_base*>(d));}
/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return true if @p d is a function_decl.
bool
is_function_decl(const type_or_decl_base& d)
{return is_function_decl(&d);}
/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a shared pointer to @ref function_decl if @p d is a @ref
/// function_decl. Otherwise, a nil shared pointer.
function_decl_sptr
is_function_decl(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<function_decl>(d);}
/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a pointer to @ref function_decl if @p d is a @ref
/// function_decl. Otherwise, a nil shared pointer.
function_decl::parameter*
is_function_parameter(const type_or_decl_base* tod)
{
return dynamic_cast<function_decl::parameter*>
(const_cast<type_or_decl_base*>(tod));
}
/// Test whether an ABI artifact is a @ref function_decl.
///
/// @param tod the declaration to test for.
///
/// @return a pointer to @ref function_decl if @p d is a @ref
/// function_decl. Otherwise, a nil shared pointer.
function_decl::parameter_sptr
is_function_parameter(const type_or_decl_base_sptr tod)
{return dynamic_pointer_cast<function_decl::parameter>(tod);}
/// Test if an ABI artifact is a declaration.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
/// declaration, or NULL if it is not.
decl_base*
is_decl(const type_or_decl_base* d)
{
if (d && (d->kind() & type_or_decl_base::ABSTRACT_DECL_BASE))
{
if (!(d->kind() & type_or_decl_base::ABSTRACT_TYPE_BASE))
// The artifact is a decl-only (like a function or a
// variable). That is, it's not a type that also has a
// declaration. In this case, we are in the fast path and we
// have a pointer to the decl sub-object handy. Just return
// it ...
return reinterpret_cast<decl_base*>
(const_cast<type_or_decl_base*>(d)->type_or_decl_base_pointer());
// ... Otherwise, we are in the slow path, which is that the
// artifact is a type which has a declaration. In that case,
// let's use the slow dynamic_cast because we don't have the
// pointer to the decl sub-object handily present.
return dynamic_cast<decl_base*>(const_cast<type_or_decl_base*>(d));
}
return 0;
}
/// Test if an ABI artifact is a declaration.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
/// declaration, or NULL if it is not.
decl_base_sptr
is_decl(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<decl_base>(d);}
/// Test if an ABI artifact is a declaration.
///
/// This is done using a slow path that uses dynamic_cast.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
decl_base*
is_decl_slow(const type_or_decl_base* t)
{return dynamic_cast<decl_base*>(const_cast<type_or_decl_base*>(t));}
/// Test if an ABI artifact is a declaration.
///
/// This is done using a slow path that uses dynamic_cast.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
decl_base_sptr
is_decl_slow(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<decl_base>(t);}
/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
bool
is_type(const type_or_decl_base& tod)
{
if (dynamic_cast<const type_base*>(&tod))
return true;
return false;
}
/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
type_base*
is_type(const type_or_decl_base* t)
{
if (t && (t->kind() & type_or_decl_base::ABSTRACT_TYPE_BASE))
return reinterpret_cast<type_base*>
(const_cast<type_or_decl_base*>(t)->type_or_decl_base_pointer());
return 0;
}
/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
type_base_sptr
is_type(const type_or_decl_base_sptr& tod)
{return dynamic_pointer_cast<type_base>(tod);}
/// Test whether a declaration is a type.
///
/// @param d the declaration to test for.
///
/// @return true if the declaration is a type, false otherwise.
/// Test if a given type is anonymous.
///
/// Note that this function considers that an anonymous class that is
/// named by a typedef is not anonymous anymore. This is the C idiom:
///
/// typedef struct {int member;} s_type;
///
/// The typedef s_type becomes the name of the originally anonymous
/// struct.
///
/// @param t the type to consider.
///
/// @return true iff @p t is anonymous.
bool
is_anonymous_type(const type_base* t)
{
const decl_base* d = get_type_declaration(t);
if (d)
if (d->get_is_anonymous())
{
if (class_or_union *cou = is_class_or_union_type(t))
{
// An anonymous class that is named by a typedef is not
// considered anonymous anymore.
if (!cou->get_naming_typedef())
return true;
}
else
return true;
}
return false;
}
/// Test if a given type is anonymous.
///
/// @param t the type to consider.
///
/// @return true iff @p t is anonymous.
bool
is_anonymous_type(const type_base_sptr& t)
{return is_anonymous_type(t.get());}
/// Test whether a type is a type_decl (a builtin type).
///
/// @return the type_decl* for @t if it's type_decl, otherwise, return
/// nil.
const type_decl*
is_type_decl(const type_or_decl_base* t)
{return dynamic_cast<const type_decl*>(t);}
/// Test whether a type is a type_decl (a builtin type).
///
/// @return the type_decl_sptr for @t if it's type_decl, otherwise,
/// return nil.
type_decl_sptr
is_type_decl(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<type_decl>(t);}
/// Test whether a type is a typedef.
///
/// @param t the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
typedef_decl_sptr
is_typedef(const type_or_decl_base_sptr t)
{return dynamic_pointer_cast<typedef_decl>(t);}
/// Test whether a type is a typedef.
///
/// @param t the declaration of the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
const typedef_decl*
is_typedef(const type_base* t)
{return dynamic_cast<const typedef_decl*>(t);}
/// Test whether a type is a typedef.
///
/// @param t the declaration of the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
typedef_decl*
is_typedef(type_base* t)
{return dynamic_cast<typedef_decl*>(t);}
/// Test if a type is an enum. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the enum_decl if @p t is an @ref enum_decl or null
/// otherwise.
enum_type_decl_sptr
is_compatible_with_enum_type(const type_base_sptr& t)
{
if (!t)
return enum_type_decl_sptr();
// Normally we should strip typedefs entirely, but this is
// potentially costly, especially on binaries with huge changesets
// like the Linux Kernel. So we just get the leaf types for now.
//
// Maybe there should be an option by which users accepts to pay the
// CPU usage toll in exchange for finer filtering?
// type_base_sptr ty = strip_typedef(t);
type_base_sptr ty = peel_typedef_type(t);;
return is_enum_type(ty);
}
/// Test if a type is an enum. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the enum_decl if @p t is an @ref enum_decl or null
/// otherwise.
enum_type_decl_sptr
is_compatible_with_enum_type(const decl_base_sptr& t)
{return is_compatible_with_enum_type(is_type(t));}
/// Test if a decl is an enum_type_decl
///
/// @param d the decl to test for.
///
/// @return the enum_type_decl* if @p d is an enum, nil otherwise.
const enum_type_decl*
is_enum_type(const type_or_decl_base* d)
{return dynamic_cast<const enum_type_decl*>(d);}
/// Test if a decl is an enum_type_decl
///
/// @param d the decl to test for.
///
/// @return the enum_type_decl_sptr if @p d is an enum, nil otherwise.
enum_type_decl_sptr
is_enum_type(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<enum_type_decl>(d);}
/// Test if a type is a class. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_compatible_with_class_type(const type_base_sptr& t)
{
if (!t)
return class_decl_sptr();
// Normally we should strip typedefs entirely, but this is
// potentially costly, especially on binaries with huge changesets
// like the Linux Kernel. So we just get the leaf types for now.
//
// Maybe there should be an option by which users accepts to pay the
// CPU usage toll in exchange for finer filtering?
// type_base_sptr ty = strip_typedef(t);
type_base_sptr ty = peel_typedef_type(t);
return is_class_type(ty);
}
/// Test if a type is a class. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_compatible_with_class_type(const decl_base_sptr& t)
{return is_compatible_with_class_type(is_type(t));}
/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return true iff @p t is a class_decl.
bool
is_class_type(const type_or_decl_base& t)
{return is_class_type(&t);}
/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl*
is_class_type(const type_or_decl_base* t)
{
if (!t)
return 0;
if (t->kind() & type_or_decl_base::CLASS_TYPE)
return reinterpret_cast<class_decl*>
(const_cast<type_or_decl_base*>(t)->runtime_type_instance());
return 0;
}
/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_class_type(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<class_decl>(d);}
/// Test wheter a type is a declaration-only class.
///
/// @param t the type to considier.
///
/// @return true iff @p t is a declaration-only class.
bool
is_declaration_only_class_or_union_type(const type_base *t)
{
if (const class_or_union *klass = is_class_or_union_type(t))
return klass->get_is_declaration_only();
return false;
}
/// Test wheter a type is a declaration-only class.
///
/// @param t the type to considier.
///
/// @return true iff @p t is a declaration-only class.
bool
is_declaration_only_class_type(const type_base_sptr& t)
{return is_declaration_only_class_or_union_type(t.get());}
/// Test if a type is a @ref class_or_union.
///
/// @param t the type to consider.
///
/// @return the @ref class_or_union is @p is a @ref class_or_union, or
/// nil otherwise.
class_or_union*
is_class_or_union_type(const type_or_decl_base* t)
{return dynamic_cast<class_or_union*>(const_cast<type_or_decl_base*>(t));}
/// Test if a type is a @ref class_or_union.
///
/// @param t the type to consider.
///
/// @return the @ref class_or_union is @p is a @ref class_or_union, or
/// nil otherwise.
shared_ptr<class_or_union>
is_class_or_union_type(const shared_ptr<type_or_decl_base>& t)
{return dynamic_pointer_cast<class_or_union>(t);}
/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a union_decl.
bool
is_union_type(const type_or_decl_base& t)
{return is_union_type(&t);}
/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return the @ref union_decl is @p is a @ref union_decl, or nil
/// otherwise.
union_decl*
is_union_type(const type_or_decl_base* t)
{return dynamic_cast<union_decl*>(const_cast<type_or_decl_base*>(t));}
/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return the @ref union_decl is @p is a @ref union_decl, or nil
/// otherwise.
union_decl_sptr
is_union_type(const shared_ptr<type_or_decl_base>& t)
{return dynamic_pointer_cast<union_decl>(t);}
/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
pointer_type_def*
is_pointer_type(type_or_decl_base* t)
{
if (!t)
return 0;
if (t->kind() & type_or_decl_base::POINTER_TYPE)
return reinterpret_cast<pointer_type_def*>
(const_cast<type_or_decl_base*>(t)->runtime_type_instance());
return 0;
}
/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
const pointer_type_def*
is_pointer_type(const type_or_decl_base* t)
{
return is_pointer_type(const_cast<type_or_decl_base*>(t));
}
/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
pointer_type_def_sptr
is_pointer_type(const type_or_decl_base_sptr &t)
{return dynamic_pointer_cast<pointer_type_def>(t);}
/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
reference_type_def*
is_reference_type(type_or_decl_base* t)
{return dynamic_cast<reference_type_def*>(t);}
/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
const reference_type_def*
is_reference_type(const type_or_decl_base* t)
{return dynamic_cast<const reference_type_def*>(t);}
/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
reference_type_def_sptr
is_reference_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<reference_type_def>(t);}
/// Test if a type is a pointer to void type.
///
/// Note that this looks trough typedefs or CV qualifiers to look for
/// the void pointer.
///
/// @param type the type to consider.
///
/// @return the actual void pointer if @p is a void pointer or NULL if
/// it's not.
const type_base*
is_void_pointer_type(const type_base* type)
{
type = peel_qualified_or_typedef_type(type);
const pointer_type_def * t = is_pointer_type(type);
if (!t)
return 0;
// Look through typedefs in the pointed-to type as well.
type_base * ty = t->get_pointed_to_type().get();
ty = peel_qualified_or_typedef_type(ty);
if (ty->get_environment()->is_void_type(ty))
return ty;
return 0;
}
/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
qualified_type_def*
is_qualified_type(const type_or_decl_base* t)
{return dynamic_cast<qualified_type_def*>(const_cast<type_or_decl_base*>(t));}
/// Test whether a type is a qualified_type_def.
///
/// @param t the type to test.
///
/// @return the @ref qualified_type_def_sptr if @p t is a
/// qualified_type_def, null otherwise.
qualified_type_def_sptr
is_qualified_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<qualified_type_def>(t);}
/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
function_type_sptr
is_function_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<function_type>(t);}
/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
function_type*
is_function_type(type_or_decl_base* t)
{return dynamic_cast<function_type*>(t);}
/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
const function_type*
is_function_type(const type_or_decl_base* t)
{return dynamic_cast<const function_type*>(t);}
/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
method_type_sptr
is_method_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<method_type>(t);}
/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
const method_type*
is_method_type(const type_or_decl_base* t)
{return dynamic_cast<const method_type*>(t);}
/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
method_type*
is_method_type(type_or_decl_base* t)
{return dynamic_cast<method_type*>(t);}
/// If a class (or union) is a decl-only class, get its definition.
/// Otherwise, just return the initial class.
///
/// @param the_class the class (or union) to consider.
///
/// @return either the definition of the class, or the class itself.
class_or_union*
look_through_decl_only_class(class_or_union* the_class)
{return is_class_or_union_type(look_through_decl_only(the_class));}
/// If a class (or union) is a decl-only class, get its definition.
/// Otherwise, just return the initial class.
///
/// @param klass the class (or union) to consider.
///
/// @return either the definition of the class, or the class itself.
class_or_union_sptr
look_through_decl_only_class(class_or_union_sptr klass)
{return is_class_or_union_type(look_through_decl_only(klass));}
/// If an enum is a decl-only enum, get its definition.
/// Otherwise, just return the initial enum.
///
/// @param enom the enum to consider.
///
/// @return either the definition of the enum, or the enum itself.
enum_type_decl_sptr
look_through_decl_only_enum(enum_type_decl_sptr enom)
{return is_enum_type(look_through_decl_only(enom));}
/// If a decl is decl-only enum, get its definition. Otherwise, just
/// return the initial decl.
///
/// @param d the decl to consider.
///
/// @return either the definition of the enum, or the decl itself.
decl_base*
look_through_decl_only(decl_base* d)
{
decl_base_sptr decl(d, sptr_utils::noop_deleter());
return look_through_decl_only(decl).get();
}
/// If a decl is decl-only get its definition. Otherwise, just return nil.
///
/// @param d the decl to consider.
///
/// @return either the definition of the decl, or nil.
decl_base_sptr
look_through_decl_only(decl_base_sptr d)
{
if (!d)
return d;
while (d->get_is_declaration_only())
{
decl_base_sptr definition = d->get_definition_of_declaration();
if (!definition)
break;
d = definition;
}
return d;
}
/// Tests if a declaration is a variable declaration.
///
/// @param decl the decl to test.
///
/// @return the var_decl_sptr iff decl is a variable declaration; nil
/// otherwise.
var_decl*
is_var_decl(const type_or_decl_base* tod)
{return dynamic_cast<var_decl*>(const_cast<type_or_decl_base*>(tod));}
/// Tests if a declaration is a variable declaration.
///
/// @param decl the decl to test.
///
/// @return the var_decl_sptr iff decl is a variable declaration; nil
/// otherwise.
var_decl_sptr
is_var_decl(const type_or_decl_base_sptr& decl)
{return dynamic_pointer_cast<var_decl>(decl);}
/// Tests if a declaration is a namespace declaration.
///
/// @param d the decalration to consider.
///
/// @return the namespace declaration if @p d is a namespace.
namespace_decl_sptr
is_namespace(const decl_base_sptr& d)
{return dynamic_pointer_cast<namespace_decl>(d);}
/// Tests if a declaration is a namespace declaration.
///
/// @param d the decalration to consider.
///
/// @return the namespace declaration if @p d is a namespace.
namespace_decl*
is_namespace(const decl_base* d)
{return dynamic_cast<namespace_decl*>(const_cast<decl_base*>(d));}
/// Tests whether a decl is a template parameter composition type.
///
/// @param decl the declaration to consider.
///
/// @return true iff decl is a template parameter composition type.
bool
is_template_parm_composition_type(const shared_ptr<decl_base> decl)
{
return (decl
&& is_at_template_scope(decl)
&& is_type(decl)
&& !is_template_parameter(decl));
}
/// Test whether a decl is the pattern of a function template.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is the pattern of a function template.
bool
is_function_template_pattern(const shared_ptr<decl_base> decl)
{
return (decl
&& dynamic_pointer_cast<function_decl>(decl)
&& dynamic_cast<template_decl*>(decl->get_scope()));
}
/// Test if a type is an array_type_def.
///
/// @param type the type to consider.
///
/// @return true iff @p type is an array_type_def.
array_type_def*
is_array_type(const type_or_decl_base* type)
{return dynamic_cast<array_type_def*>(const_cast<type_or_decl_base*>(type));}
/// Test if a type is an array_type_def.
///
/// @param type the type to consider.
///
/// @return true iff @p type is an array_type_def.
array_type_def_sptr
is_array_type(const type_or_decl_base_sptr& type)
{return dynamic_pointer_cast<array_type_def>(type);}
/// Tests if the element of a given array is a qualified type.
///
/// @param array the array type to consider.
///
/// @return the qualified element of the array iff it's a qualified
/// type. Otherwise, return a nil object.
qualified_type_def_sptr
is_array_of_qualified_element(const array_type_def_sptr& array)
{
if (!array)
return qualified_type_def_sptr();
return is_qualified_type(array->get_element_type());
}
/// Test if an array type is an array to a qualified element type.
///
/// @param type the array type to consider.
///
/// @return true the array @p type iff it's an array to a qualified
/// element type.
array_type_def_sptr
is_array_of_qualified_element(const type_base_sptr& type)
{
if (array_type_def_sptr array = is_array_type(type))
if (is_array_of_qualified_element(array))
return array;
return array_type_def_sptr();
}
/// Test if a type is a typedef of an array.
///
/// Note that the function looks through qualified and typedefs types
/// of the underlying type of the current typedef. In other words, if
/// we are looking at a typedef of a CV-qualified array, or at a
/// typedef of a CV-qualified typedef of an array, this function will
/// still return TRUE.
///
/// @param t the type to consider.
///
/// @return true if t is a typedef which underlying type is an array.
/// That array might be either cv-qualified array or a typedef'ed
/// array, or a combination of both.
array_type_def_sptr
is_typedef_of_array(const type_base_sptr& t)
{
array_type_def_sptr result;
if (typedef_decl_sptr typdef = is_typedef(t))
{
type_base_sptr u =
peel_qualified_or_typedef_type(typdef->get_underlying_type());
result = is_array_type(u);
}
return result;
}
/// Test if a type is an array_type_def::subrange_type.
///
/// @param type the type to consider.
///
/// @return the array_type_def::subrange_type which @p type is a type
/// of, or nil if it's not of that type.
array_type_def::subrange_type*
is_subrange_type(const type_or_decl_base *type)
{
return dynamic_cast<array_type_def::subrange_type*>
(const_cast<type_or_decl_base*>(type));
}
/// Test if a type is an array_type_def::subrange_type.
///
/// @param type the type to consider.
///
/// @return the array_type_def::subrange_type which @p type is a type
/// of, or nil if it's not of that type.
array_type_def::subrange_sptr
is_subrange_type(const type_or_decl_base_sptr &type)
{return dynamic_pointer_cast<array_type_def::subrange_type>(type);}
/// Tests whether a decl is a template.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is a function template, class template, or
/// template template parameter.
bool
is_template_decl(const shared_ptr<decl_base> decl)
{return decl && dynamic_pointer_cast<template_decl>(decl);}
/// This enum describe the kind of entity to lookup, while using the
/// lookup API.
enum lookup_entity_kind
{
LOOKUP_ENTITY_TYPE,
LOOKUP_ENTITY_VAR,
};
/// Find the first relevant delimiter (the "::" string) in a fully
/// qualified C++ type name, starting from a given position. The
/// delimiter returned separates a type name from the name of its
/// context.
///
/// This is supposed to work correctly on names in cases like this:
///
/// foo<ns1::name1, ns2::name2>
///
/// In that case when called with with parameter @p begin set to 0, no
/// delimiter is returned, because the type name in this case is:
/// 'foo<ns1::name1, ns2::name2>'.
///
/// But in this case:
///
/// foo<p1, bar::name>::some_type
///
/// The "::" returned is the one right before 'some_type'.
///
/// @param fqn the fully qualified name of the type to consider.
///
/// @param begin the position from which to look for the delimiter.
///
/// @param delim_pos out parameter. Is set to the position of the
/// delimiter iff the function returned true.
///
/// @return true iff the function found and returned the delimiter.
static bool
find_next_delim_in_cplus_type(const string& fqn,
size_t begin,
size_t& delim_pos)
{
int angle_count = 0;
bool found = false;
size_t i = begin;
for (; i < fqn.size(); ++i)
{
if (fqn[i] == '<')
++angle_count;
else if (fqn[i] == '>')
--angle_count;
else if (i + 1 < fqn.size()
&& !angle_count
&& fqn[i] == ':'
&& fqn[i+1] == ':')
{
delim_pos = i;
found = true;
break;
}
}
return found;
}
/// Decompose a fully qualified name into the list of its components.
///
/// @param fqn the fully qualified name to decompose.
///
/// @param comps the resulting list of component to fill.
void
fqn_to_components(const string& fqn,
list<string>& comps)
{
string::size_type fqn_size = fqn.size(), comp_begin = 0, comp_end = fqn_size;
do
{
if (!find_next_delim_in_cplus_type(fqn, comp_begin, comp_end))
comp_end = fqn_size;
string comp = fqn.substr(comp_begin, comp_end - comp_begin);
comps.push_back(comp);
comp_begin = comp_end + 2;
if (comp_begin >= fqn_size)
break;
} while (true);
}
/// Turn a set of qualified name components (that name a type) into a
/// qualified name string.
///
/// @param comps the name components
///
/// @return the resulting string, which would be the qualified name of
/// a type.
string
components_to_type_name(const list<string>& comps)
{
string result;
for (list<string>::const_iterator c = comps.begin();
c != comps.end();
++c)
if (c == comps.begin())
result = *c;
else
result += "::" + *c;
return result;
}
/// This predicate returns true if a given container iterator points
/// to the last element of the container, false otherwise.
///
/// @tparam T the type of the container of the iterator.
///
/// @param container the container the iterator points into.
///
/// @param i the iterator to consider.
///
/// @return true iff the iterator points to the last element of @p
/// container.
template<typename T>
static bool
iterator_is_last(T& container,
typename T::const_iterator i)
{
typename T::const_iterator next = i;
++next;
return (next == container.end());
}
//--------------------------------
// <type and decls lookup stuff>
// ------------------------------
/// Lookup all the type*s* that have a given fully qualified name.
///
/// @param type_name the fully qualified name of the type to
/// lookup.
///
/// @param type_map the map to look into.
///
/// @return the vector containing the types named @p type_name. If
/// the lookup didn't yield any type, then this function returns nil.
static const type_base_wptrs_type*
lookup_types_in_map(const interned_string& type_name,
const istring_type_base_wptrs_map_type& type_map)
{
istring_type_base_wptrs_map_type::const_iterator i = type_map.find(type_name);
if (i != type_map.end())
return &i->second;
return 0;
}
/// Lookup a type (with a given name) in a map that associates a type
/// name to a type. If there are several types with a given name,
/// then return the last of such types, that is, the last one that got
/// registered.
///
/// @tparam TypeKind the type of the type this function is supposed to
/// return.
///
/// @param type_name the name of the type to lookup.
///
/// @param type_map the map in which to look.
///
/// @return a shared_ptr to the type found. If no type was found or
/// if the type found was not of type @p TypeKind then the function
/// returns nil.
template <class TypeKind>
static shared_ptr<TypeKind>
lookup_type_in_map(const interned_string& type_name,
const istring_type_base_wptrs_map_type& type_map)
{
istring_type_base_wptrs_map_type::const_iterator i = type_map.find(type_name);
if (i != type_map.end())
return dynamic_pointer_cast<TypeKind>(type_base_sptr(i->second.back()));
return shared_ptr<TypeKind>();
}
/// Lookup a basic type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the basic type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the basic type found or nil if no basic type was found.
type_decl_sptr
lookup_basic_type(const interned_string& type_name, const translation_unit& tu)
{
return lookup_type_in_map<type_decl>(type_name,
tu.get_types().basic_types());
}
/// Lookup a basic type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the basic type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the basic type found or nil if no basic type was found.
type_decl_sptr
lookup_basic_type(const string& type_name, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(type_name);
return lookup_basic_type(s, tu);
}
/// Lookup a class type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param fqn the fully qualified name of the class type node to look
/// up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the class type IR node found, NULL
/// otherwise.
class_decl_sptr
lookup_class_type(const string& fqn, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(fqn);
return lookup_class_type(s, tu);
}
/// Lookup a class type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the class type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the class type found or nil if no class type was found.
class_decl_sptr
lookup_class_type(const interned_string& type_name, const translation_unit& tu)
{
return lookup_type_in_map<class_decl>(type_name,
tu.get_types().class_types());
}
/// Lookup a union type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the union type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the union type found or nil if no union type was found.
union_decl_sptr
lookup_union_type(const interned_string& type_name, const translation_unit& tu)
{
return lookup_type_in_map<union_decl>(type_name,
tu.get_types().union_types());
}
/// Lookup a union type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to look into.
///
/// @return the union type found or nil if no union type was found.
union_decl_sptr
lookup_union_type(const string& fqn, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(fqn);
return lookup_union_type(s, tu);
}
/// Lookup a union type in a given corpus, from its location.
///
/// @param loc the location of the union type to look for.
///
/// @param corp the corpus to look it from.
///
/// @return the resulting union_decl.
union_decl_sptr
lookup_union_type_per_location(const interned_string &loc, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_type_per_loc_map().union_types();
union_decl_sptr result = lookup_type_in_map<union_decl>(loc, m);
return result;
}
/// Lookup a union type in a given corpus, from its location.
///
/// @param loc the location of the union type to look for.
///
/// @param corp the corpus to look it from.
///
/// @return the resulting union_decl.
union_decl_sptr
lookup_union_type_per_location(const string& loc, const corpus& corp)
{
const environment* env = corp.get_environment();
ABG_ASSERT(env);
return lookup_union_type_per_location(env->intern(loc), corp);
}
/// Lookup an enum type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the enum type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the enum type found or nil if no enum type was found.
enum_type_decl_sptr
lookup_enum_type(const interned_string& type_name, const translation_unit& tu)
{
return lookup_type_in_map<enum_type_decl>(type_name,
tu.get_types().enum_types());
}
/// Lookup an enum type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the enum type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the enum type found or nil if no enum type was found.
enum_type_decl_sptr
lookup_enum_type(const string& type_name, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(type_name);
return lookup_enum_type(s, tu);
}
/// Lookup a typedef type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the typedef type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the typedef type found or nil if no typedef type was
/// found.
typedef_decl_sptr
lookup_typedef_type(const interned_string& type_name,
const translation_unit& tu)
{
return lookup_type_in_map<typedef_decl>(type_name,
tu.get_types().typedef_types());
}
/// Lookup a typedef type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the typedef type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the typedef type found or nil if no typedef type was
/// found.
typedef_decl_sptr
lookup_typedef_type(const string& type_name, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(type_name);
return lookup_typedef_type(s, tu);
}
/// Lookup a qualified type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the qualified type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the qualified type found or nil if no qualified type was
/// found.
qualified_type_def_sptr
lookup_qualified_type(const interned_string& type_name,
const translation_unit& tu)
{
const type_maps& m = tu.get_types();
return lookup_type_in_map<qualified_type_def>(type_name,
m.qualified_types());
}
/// Lookup a qualified type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param underlying_type the underying type of the qualified type to
/// look up.
///
/// @param quals the CV-qualifiers of the qualified type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the qualified type found or nil if no qualified type was
/// found.
qualified_type_def_sptr
lookup_qualified_type(const type_base_sptr& underlying_type,
qualified_type_def::CV quals,
const translation_unit& tu)
{
interned_string type_name = get_name_of_qualified_type(underlying_type,
quals);
return lookup_qualified_type(type_name, tu);
}
/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the pointer type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const interned_string& type_name,
const translation_unit& tu)
{
const type_maps& m = tu.get_types();
return lookup_type_in_map<pointer_type_def>(type_name,
m.pointer_types());
}
/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the pointer type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const string& type_name, const translation_unit& tu)
{
const environment* env = tu.get_environment();
ABG_ASSERT(env);
interned_string s = env->intern(type_name);
return lookup_pointer_type(s, tu);
}
/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param pointed_to_type the pointed-to-type of the pointer to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const type_base_sptr& pointed_to_type,
const translation_unit& tu)
{
interned_string type_name = get_name_of_pointer_to_type(*pointed_to_type);
return lookup_pointer_type(type_name, tu);
}
/// Lookup a reference type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the reference type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the reference type found or nil if no reference type was
/// found.
reference_type_def_sptr
lookup_reference_type(const interned_string& type_name,
const translation_unit& tu)
{
const type_maps& m = tu.get_types();
return lookup_type_in_map<reference_type_def>(type_name,
m.reference_types());
}
/// Lookup a reference type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param pointed_to_type the pointed-to-type of the reference to
/// look up.
///
/// @param tu the translation unit to look into.
///
/// @return the reference type found or nil if no reference type was
/// found.
const reference_type_def_sptr
lookup_reference_type(const type_base_sptr& pointed_to_type,
bool lvalue_reference,
const translation_unit& tu)
{
interned_string type_name =
get_name_of_reference_to_type(*pointed_to_type, lvalue_reference);
return lookup_reference_type(type_name, tu);
}
/// Lookup an array type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the array type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the array type found or nil if no array type was found.
array_type_def_sptr
lookup_array_type(const interned_string& type_name,
const translation_unit& tu)
{
const type_maps& m = tu.get_types();
return lookup_type_in_map<array_type_def>(type_name,
m.array_types());
}
/// Lookup a function type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param type_name the name of the type to lookup.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const interned_string& type_name,
const translation_unit& tu)
{
const type_maps& m = tu.get_types();
return lookup_type_in_map<function_type>(type_name,
m.function_types());
}
/// Lookup a function type from a translation unit.
///
/// This walks all the function types held by the translation unit and
/// compare their sub-type *names*. If the names match then return
/// the function type found in the translation unit.
///
/// @param t the function type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const function_type& t,
const translation_unit& tu)
{
interned_string type_name = get_type_name(t);
return lookup_function_type(type_name, tu);
}
/// Lookup a function type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit. So this is as fast as
/// possible.
///
/// @param t the function type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const function_type_sptr& t,
const translation_unit& tu)
{return lookup_function_type(*t, tu);}
/// Lookup a type in a translation unit.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to consider.
///
/// @return the declaration of the type if found, NULL otherwise.
const type_base_sptr
lookup_type(const interned_string& fqn,
const translation_unit& tu)
{
type_base_sptr result;
((result = lookup_typedef_type(fqn, tu))
|| (result = lookup_class_type(fqn, tu))
|| (result = lookup_union_type(fqn, tu))
|| (result = lookup_enum_type(fqn, tu))
|| (result = lookup_qualified_type(fqn, tu))
|| (result = lookup_pointer_type(fqn, tu))
|| (result = lookup_reference_type(fqn, tu))
|| (result = lookup_array_type(fqn, tu))
|| (result = lookup_function_type(fqn, tu))
|| (result = lookup_basic_type(fqn, tu)));
return result;
}
/// Lookup a type in a translation unit, starting from the global
/// namespace.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to consider.
///
/// @return the declaration of the type if found, NULL otherwise.
type_base_sptr
lookup_type(const string& fqn, const translation_unit& tu)
{
const environment *env = tu.get_environment();
ABG_ASSERT(env);
interned_string ifqn = env->intern(fqn);
return lookup_type(ifqn, tu);
}
/// Lookup a type from a translation unit.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
const type_base_sptr
lookup_type(const type_base_sptr type,
const translation_unit& tu)
{
interned_string type_name = get_type_name(type);
return lookup_type(type_name, tu);
}
/// Lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name. Looking up a type from those maps is thus much
/// faster.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the type if found, NULL otherwise.
const type_base_sptr
lookup_type_in_scope(const string& fqn,
const scope_decl_sptr& skope)
{
list<string> comps;
fqn_to_components(fqn, comps);
return lookup_type_in_scope(comps, skope);
}
/// Lookup a @ref var_decl in a scope.
///
/// @param fqn the fuly qualified name of the @var_decl to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the @ref var_decl if found, NULL
/// otherwise.
const decl_base_sptr
lookup_var_decl_in_scope(const string& fqn,
const scope_decl_sptr& skope)
{
list<string> comps;
fqn_to_components(fqn, comps);
return lookup_var_decl_in_scope(comps, skope);
}
/// A generic function (template) to get the name of a node, whatever
/// node it is. This has to be specialized for the kind of node we
/// want.
///
/// Note that a node is a member of a scope.
///
/// @tparam NodeKind the kind of node to consider.
///
/// @param node the node to get the name from.
///
/// @return the name of the node.
template<typename NodeKind>
static const interned_string&
get_node_name(shared_ptr<NodeKind> node);
/// Gets the name of a class_decl node.
///
/// @param node the decl_base node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(class_decl_sptr node)
{return node->get_name();}
/// Gets the name of a type_base node.
///
/// @param node the type_base node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(type_base_sptr node)
{return get_type_declaration(node)->get_name();}
/// Gets the name of a var_decl node.
///
/// @param node the var_decl node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(var_decl_sptr node)
{return node->get_name();}
/// Generic function to get the declaration of a given node, whatever
/// it is. There has to be specializations for the kind of the nodes
/// we want to support.
///
/// @tparam NodeKind the type of the node we are looking at.
///
/// @return the declaration.
template<typename NodeKind>
static decl_base_sptr
convert_node_to_decl(shared_ptr<NodeKind> node);
/// Lookup a node in a given scope.
///
/// @tparam the type of the node to lookup.
///
/// @param fqn the components of the fully qualified name of the node
/// to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the looked up node, or NULL if it
/// wasn't found.
template<typename NodeKind>
static const type_or_decl_base_sptr
lookup_node_in_scope(const list<string>& fqn,
const scope_decl_sptr& skope)
{
type_or_decl_base_sptr resulting_decl;
shared_ptr<NodeKind> node;
bool it_is_last = false;
scope_decl_sptr cur_scope = skope, new_scope, scope;
for (list<string>::const_iterator c = fqn.begin(); c != fqn.end(); ++c)
{
new_scope.reset();
it_is_last = iterator_is_last(fqn, c);
for (scope_decl::declarations::const_iterator m =
cur_scope->get_member_decls().begin();
m != cur_scope->get_member_decls().end();
++m)
{
if (!it_is_last)
{
// looking for a scope
scope = dynamic_pointer_cast<scope_decl>(*m);
if (scope && scope->get_name() == *c)
{
new_scope = scope;
break;
}
}
else
{
//looking for a final type.
node = dynamic_pointer_cast<NodeKind>(*m);
if (node && get_node_name(node) == *c)
{
if (class_decl_sptr cl =
dynamic_pointer_cast<class_decl>(node))
if (cl->get_is_declaration_only()
&& !cl->get_definition_of_declaration())
continue;
resulting_decl = node;
break;
}
}
}
if (!new_scope && !resulting_decl)
return decl_base_sptr();
cur_scope = new_scope;
}
ABG_ASSERT(resulting_decl);
return resulting_decl;
}
/// lookup a type in a scope.
///
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name. Looking up a type from those maps is thus much
/// faster.
///
/// @param comps the components of the fully qualified name of the
/// type to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the type found.
const type_base_sptr
lookup_type_in_scope(const list<string>& comps,
const scope_decl_sptr& scope)
{return is_type(lookup_node_in_scope<type_base>(comps, scope));}
/// lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name. Looking up a type from those maps is thus much
/// faster.
///
/// @param type the type to look for.
///
/// @param access_path a vector of scopes the path of scopes to follow
/// before reaching the scope into which to look for @p type. Note
/// that the deepest scope (the one immediately containing @p type) is
/// at index 0 of this vector, and the top-most scope is the last
/// element of the vector.
///
/// @param scope the top-most scope into which to look for @p type.
///
/// @return the scope found in @p scope, or NULL if it wasn't found.
static const type_base_sptr
lookup_type_in_scope(const type_base& type,
const vector<scope_decl*>& access_path,
const scope_decl* scope)
{
vector<scope_decl*> a = access_path;
type_base_sptr result;
scope_decl* first_scope = 0;
if (!a.empty())
{
first_scope = a.back();
ABG_ASSERT(first_scope->get_name() == scope->get_name());
a.pop_back();
}
if (a.empty())
{
interned_string n = get_type_name(type, false);
for (scope_decl::declarations::const_iterator i =
scope->get_member_decls().begin();
i != scope->get_member_decls().end();
++i)
if (is_type(*i) && (*i)->get_name() == n)
{
result = is_type(*i);
break;
}
}
else
{
first_scope = a.back();
interned_string scope_name, cur_scope_name = first_scope->get_name();
for (scope_decl::scopes::const_iterator i =
scope->get_member_scopes().begin();
i != scope->get_member_scopes().end();
++i)
{
scope_name = (*i)->get_name();
if (scope_name == cur_scope_name)
{
result = lookup_type_in_scope(type, a, (*i).get());
break;
}
}
}
return result;
}
/// lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name. Looking up a type from those maps is thus much
/// faster.
///
/// @param type the type to look for.
///
/// @param scope the top-most scope into which to look for @p type.
///
/// @return the scope found in @p scope, or NULL if it wasn't found.
static const type_base_sptr
lookup_type_in_scope(const type_base_sptr type,
const scope_decl* scope)
{
if (!type || is_function_type(type))
return type_base_sptr();
decl_base_sptr type_decl = get_type_declaration(type);
ABG_ASSERT(type_decl);
vector<scope_decl*> access_path;
for (scope_decl* s = type_decl->get_scope(); s != 0; s = s->get_scope())
{
access_path.push_back(s);
if (is_global_scope(s))
break;
}
return lookup_type_in_scope(*type, access_path, scope);
}
/// Lookup a type from a translation unit by walking the scopes of the
/// translation unit in sequence and looking into them.
///
/// This is really slow as it walks the member types of the scopes in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// translation unit or even ABI corpus in a more direct way, by using
/// the lookup_type() functins.
///
///
/// This is because both the translation unit and the corpus have a
/// map of types, indexed by their name. Looking up a type from those
/// maps is thus much faster. @param fqn the components of the fully
/// qualified name of the node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
const type_base_sptr
lookup_type_through_scopes(const type_base_sptr type,
const translation_unit& tu)
{
if (function_type_sptr fn_type = is_function_type(type))
return lookup_function_type(fn_type, tu);
return lookup_type_in_scope(type, tu.get_global_scope().get());
}
/// lookup a var_decl in a scope.
///
/// @param comps the components of the fully qualified name of the
/// var_decl to lookup.
///
/// @param skope the scope to look into.
const decl_base_sptr
lookup_var_decl_in_scope(const std::list<string>& comps,
const scope_decl_sptr& skope)
{return is_var_decl(lookup_node_in_scope<var_decl>(comps, skope));}
/// Lookup an IR node from a translation unit.
///
/// @tparam NodeKind the type of the IR node to lookup from the
/// translation unit.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
template<typename NodeKind>
static const type_or_decl_base_sptr
lookup_node_in_translation_unit(const list<string>& fqn,
const translation_unit& tu)
{return lookup_node_in_scope<NodeKind>(fqn, tu.get_global_scope());}
/// Lookup a type from a translation unit by walking its scopes in
/// sequence and by looking into them.
///
/// This is much slower than using the lookup_type() function.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
type_base_sptr
lookup_type_through_scopes(const list<string>& fqn,
const translation_unit& tu)
{return is_type(lookup_node_in_translation_unit<type_base>(fqn, tu));}
/// Lookup a class type from a translation unit by walking its scopes
/// in sequence and by looking into them.
///
/// This is much slower than using the lookup_class_type() function
/// because it walks all the scopes of the translation unit in
/// sequence and lookup the types to find one that has a given name.
///
/// @param fqn the components of the fully qualified name of the class
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the class type IR node found, NULL
/// otherwise.
class_decl_sptr
lookup_class_type_through_scopes(const list<string>& fqn,
const translation_unit& tu)
{return is_class_type(lookup_node_in_translation_unit<class_decl>(fqn, tu));}
/// Lookup a basic type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the basic
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the basic type IR node found, NULL
/// otherwise.
static type_decl_sptr
lookup_basic_type_through_translation_units(const interned_string& type_name,
const corpus& abi_corpus)
{
type_decl_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_basic_type(type_name, **tu)))
break;
return result;
}
/// Lookup a union type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the union
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the union type IR node found, NULL
/// otherwise.
static union_decl_sptr
lookup_union_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
union_decl_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_union_type(type_name, **tu)))
break;
return result;
}
/// Lookup an enum type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the enum
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the enum type IR node found, NULL
/// otherwise.
static enum_type_decl_sptr
lookup_enum_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
enum_type_decl_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_enum_type(type_name, **tu)))
break;
return result;
}
/// Lookup a typedef type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the typedef type to lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static typedef_decl_sptr
lookup_typedef_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
typedef_decl_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_typedef_type(type_name, **tu)))
break;
return result;
}
/// Lookup a qualified type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the qualified type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static qualified_type_def_sptr
lookup_qualified_type_through_translation_units(const interned_string& t_name,
const corpus & abi_corpus)
{
qualified_type_def_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_qualified_type(t_name, **tu)))
break;
return result;
}
/// Lookup a pointer type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the pointer type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static pointer_type_def_sptr
lookup_pointer_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
pointer_type_def_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_pointer_type(type_name, **tu)))
break;
return result;
}
/// Lookup a reference type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the reference type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static reference_type_def_sptr
lookup_reference_type_through_translation_units(const interned_string& t_name,
const corpus & abi_corpus)
{
reference_type_def_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_reference_type(t_name, **tu)))
break;
return result;
}
/// Lookup a array type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the array type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static array_type_def_sptr
lookup_array_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
array_type_def_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_array_type(type_name, **tu)))
break;
return result;
}
/// Lookup a function type definition in all the translation units of
/// a given ABI corpus.
///
/// @param @param qn the fully qualified name of the function type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static function_type_sptr
lookup_function_type_through_translation_units(const interned_string& type_name,
const corpus & abi_corpus)
{
function_type_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_function_type(type_name, **tu)))
break;
return result;
}
/// Lookup a type definition in all the translation units of a given
/// ABI corpus.
///
/// @param @param qn the fully qualified name of the type to lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
type_base_sptr
lookup_type_through_translation_units(const string& qn,
const corpus& abi_corpus)
{
type_base_sptr result;
for (translation_units::const_iterator tu =
abi_corpus.get_translation_units().begin();
tu != abi_corpus.get_translation_units().end();
++tu)
if ((result = lookup_type(qn, **tu)))
break;
return result;
}
/// Lookup a type from a given translation unit present in a give corpus.
///
/// @param type_name the name of the type to look for.
///
/// @parm tu_path the path of the translation unit to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting type, if any.
type_base_sptr
lookup_type_from_translation_unit(const string& type_name,
const string& tu_path,
const corpus& corp)
{
string_tu_map_type::const_iterator i = corp.priv_->path_tu_map.find(tu_path);
if (i == corp.priv_->path_tu_map.end())
return type_base_sptr();
translation_unit_sptr tu = i->second;
ABG_ASSERT(tu);
type_base_sptr t = lookup_type(type_name, *tu);
return t;
}
/// Look into an ABI corpus for a function type.
///
/// @param fn_type the function type to be looked for in the ABI
/// corpus.
///
/// @param corpus the ABI corpus into which to look for the function
/// type.
///
/// @return the function type found in the corpus.
function_type_sptr
lookup_or_synthesize_fn_type(const function_type_sptr& fn_t,
const corpus& corpus)
{
ABG_ASSERT(fn_t);
function_type_sptr result;
if ((result = lookup_function_type(fn_t, corpus)))
return result;
for (translation_units::const_iterator i =
corpus.get_translation_units().begin();
i != corpus.get_translation_units().end();
++i)
if ((result = synthesize_function_type_from_translation_unit(*fn_t,
**i)))
return result;
return result;
}
/// Look into a given corpus to find a type which has the same
/// qualified name as a giventype.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the type which has the same qualified name as the type we
/// are looking for.
///
/// @param corp the ABI corpus to look into for the type.
type_decl_sptr
lookup_basic_type(const type_decl& t, const corpus& corp)
{return lookup_basic_type(t.get_name(), corp);}
/// Look into a given corpus to find a basic type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the basic type to look
/// for.
///
/// @param corp the corpus to look into.
type_decl_sptr
lookup_basic_type(const interned_string &qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().basic_types();
type_decl_sptr result;
if (!m.empty())
result = lookup_type_in_map<type_decl>(qualified_name, m);
else
result = lookup_basic_type_through_translation_units(qualified_name, corp);
return result;
}
/// Lookup a @ref type_decl type from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting basic type, if any.
type_decl_sptr
lookup_basic_type_per_location(const interned_string &loc,
const corpus &corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_type_per_loc_map().basic_types();
type_decl_sptr result;
result = lookup_type_in_map<type_decl>(loc, m);
return result;
}
/// Lookup a @ref type_decl type from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting basic type, if any.
type_decl_sptr
lookup_basic_type_per_location(const string &loc, const corpus &corp)
{
const environment* env = corp.get_environment();
ABG_ASSERT(env);
return lookup_basic_type_per_location(env->intern(loc), corp);
}
/// Look into a given corpus to find a basic type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the basic type to look
/// for.
///
/// @param corp the corpus to look into.
type_decl_sptr
lookup_basic_type(const string& qualified_name, const corpus& corp)
{
return lookup_basic_type(corp.get_environment()->intern(qualified_name),
corp);
}
/// Look into a given corpus to find a class type which has the same
/// qualified name as a given type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the class decl type which has the same qualified name as
/// the type we are looking for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const class_decl& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_class_type(s, corp);
}
/// Look into a given corpus to find a class type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const string& qualified_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(qualified_name);
return lookup_class_type(s, corp);
}
/// Look into a given corpus to find a class type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().class_types();
class_decl_sptr result = lookup_type_in_map<class_decl>(qualified_name, m);
return result;
}
/// Look into a given corpus to find the class type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of class types named @p qualified_name.
const type_base_wptrs_type *
lookup_class_types(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().class_types();
return lookup_types_in_map(qualified_name, m);
}
/// Look into a given corpus to find the class type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of class types that which name is @p qualified_name.
const type_base_wptrs_type*
lookup_class_types(const string& qualified_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(qualified_name);
return lookup_class_types(s, corp);
}
/// Look up a @ref class_decl from a given corpus by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting class decl, if any.
class_decl_sptr
lookup_class_type_per_location(const interned_string& loc,
const corpus& corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_type_per_loc_map().class_types();
class_decl_sptr result = lookup_type_in_map<class_decl>(loc, m);
return result;
}
/// Look up a @ref class_decl from a given corpus by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting class decl, if any.
class_decl_sptr
lookup_class_type_per_location(const string &loc, const corpus &corp)
{
const environment* env = corp.get_environment();
ABG_ASSERT(env);
return lookup_class_type_per_location(env->intern(loc), corp);
}
/// Look into a given corpus to find a union type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
union_decl_sptr
lookup_union_type(const interned_string& type_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().union_types();
union_decl_sptr result = lookup_type_in_map<union_decl>(type_name, m);
if (!result)
result = lookup_union_type_through_translation_units(type_name, corp);
return result;
}
/// Look into a given corpus to find a union type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
union_decl_sptr
lookup_union_type(const string& type_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(type_name);
return lookup_union_type(s, corp);
}
/// Look into a given corpus to find an enum type which has the same
/// qualified name as a given enum type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the enum type which has the same qualified name as the
/// type we are looking for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const enum_type_decl& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_enum_type(s, corp);
}
/// Look into a given corpus to find an enum type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the enum type to look
/// for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const string& qualified_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(qualified_name);
return lookup_enum_type(s, corp);
}
/// Look into a given corpus to find an enum type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the enum type to look
/// for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
enum_type_decl_sptr result =
lookup_type_in_map<enum_type_decl>(qualified_name, m);
if (!result)
result = lookup_enum_type_through_translation_units(qualified_name, corp);
return result;
}
/// Look into a given corpus to find the enum type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of enum types that which name is @p qualified_name.
const type_base_wptrs_type *
lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
return lookup_types_in_map(qualified_name, m);
}
/// Look into a given corpus to find the enum type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of enum types that which name is @p qualified_name.
const type_base_wptrs_type*
lookup_enum_types(const string& qualified_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(qualified_name);
return lookup_enum_types(s, corp);
}
/// Look up an @ref enum_type_decl from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting enum type, if any.
enum_type_decl_sptr
lookup_enum_type_per_location(const interned_string &loc, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_type_per_loc_map().enum_types();
enum_type_decl_sptr result = lookup_type_in_map<enum_type_decl>(loc, m);
return result;
}
/// Look up an @ref enum_type_decl from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting enum type, if any.
enum_type_decl_sptr
lookup_enum_type_per_location(const string &loc, const corpus &corp)
{
const environment* env = corp.get_environment();
ABG_ASSERT(env);
return lookup_enum_type_per_location(env->intern(loc), corp);
}
/// Look into a given corpus to find a typedef type which has the
/// same qualified name as a given typedef type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the typedef type which has the same qualified name as the
/// typedef type we are looking for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const typedef_decl& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_typedef_type(s, corp);
}
/// Look into a given corpus to find a typedef type which has the
/// same qualified name as a given typedef type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the typedef type which has the same qualified name as the
/// typedef type we are looking for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const string& qualified_name, const corpus& corp)
{
interned_string s = corp.get_environment()->intern(qualified_name);
return lookup_typedef_type(s, corp);
}
/// Look into a given corpus to find a typedef type which has a
/// given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the typedef type to
/// look for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().typedef_types();
typedef_decl_sptr result =
lookup_type_in_map<typedef_decl>(qualified_name, m);
if (!result)
result = lookup_typedef_type_through_translation_units(qualified_name,
corp);
return result;
}
/// Lookup a @ref typedef_decl from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the typedef_decl found, if any.
typedef_decl_sptr
lookup_typedef_type_per_location(const interned_string &loc, const corpus &corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_type_per_loc_map().typedef_types();
typedef_decl_sptr result = lookup_type_in_map<typedef_decl>(loc, m);
return result;
}
/// Lookup a @ref typedef_decl from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the typedef_decl found, if any.
typedef_decl_sptr
lookup_typedef_type_per_location(const string &loc, const corpus &corp)
{
const environment* env = corp.get_environment();
ABG_ASSERT(env);
return lookup_typedef_type_per_location(env->intern(loc), corp);
}
/// Look into a corpus to find a class, union or typedef type which
/// has a given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the name of the type to find.
///
/// @param corp the corpus to look into.
///
/// @return the typedef or class type found.
type_base_sptr
lookup_class_or_typedef_type(const string& qualified_name, const corpus& corp)
{
type_base_sptr result = lookup_class_type(qualified_name, corp);
if (!result)
result = lookup_union_type(qualified_name, corp);
if (!result)
result = lookup_typedef_type(qualified_name, corp);
return result;
}
/// Look into a corpus to find a class, typedef or enum type which has
/// a given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the typedef, class or enum type found.
type_base_sptr
lookup_class_typedef_or_enum_type(const string& qualified_name,
const corpus& corp)
{
type_base_sptr result = lookup_class_or_typedef_type(qualified_name, corp);
if (!result)
result = lookup_enum_type(qualified_name, corp);
return result;
}
/// Look into a given corpus to find a qualified type which has the
/// same qualified name as a given type.
///
/// @param t the type which has the same qualified name as the
/// qualified type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the qualified type found.
qualified_type_def_sptr
lookup_qualified_type(const qualified_type_def& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_qualified_type(s, corp);
}
/// Look into a given corpus to find a qualified type which has a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the type found.
qualified_type_def_sptr
lookup_qualified_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_types().qualified_types();
qualified_type_def_sptr result =
lookup_type_in_map<qualified_type_def>(qualified_name, m);
if (!result)
result = lookup_qualified_type_through_translation_units(qualified_name,
corp);
return result;
}
/// Look into a given corpus to find a pointer type which has the same
/// qualified name as a given pointer type.
///
/// @param t the pointer type which has the same qualified name as the
/// type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the pointer type found.
pointer_type_def_sptr
lookup_pointer_type(const pointer_type_def& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_pointer_type(s, corp);
}
/// Look into a given corpus to find a pointer type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the pointer type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the pointer type found.
pointer_type_def_sptr
lookup_pointer_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().pointer_types();
pointer_type_def_sptr result =
lookup_type_in_map<pointer_type_def>(qualified_name, m);
if (!result)
result = lookup_pointer_type_through_translation_units(qualified_name,
corp);
return result;
}
/// Look into a given corpus to find a reference type which has the
/// same qualified name as a given reference type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the reference type which has the same qualified name as
/// the reference type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the reference type found.
reference_type_def_sptr
lookup_reference_type(const reference_type_def& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_reference_type(s, corp);
}
/// Look into a given corpus to find a reference type which has a
/// given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the reference type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the reference type found.
reference_type_def_sptr
lookup_reference_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m =
corp.get_types().reference_types();
reference_type_def_sptr result =
lookup_type_in_map<reference_type_def>(qualified_name, m);
if (!result)
result = lookup_reference_type_through_translation_units(qualified_name,
corp);
return result;
}
/// Look into a given corpus to find an array type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the array type to look
/// for.
///
/// @param corp the corpus to look into.
///
/// @return the array type found.
array_type_def_sptr
lookup_array_type(const array_type_def& t, const corpus& corp)
{
interned_string s = get_type_name(t);
return lookup_array_type(s, corp);
}
/// Look into a given corpus to find an array type which has the same
/// qualified name as a given array type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the type which has the same qualified name as the type we
/// are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the type found.
array_type_def_sptr
lookup_array_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().array_types();
array_type_def_sptr result =
lookup_type_in_map<array_type_def>(qualified_name, m);
if (!result)
result = lookup_array_type_through_translation_units(qualified_name, corp);
return result;
}
/// Look into a given corpus to find a function type which has the same
/// qualified name as a given function type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the function type which has the same qualified name as
/// the function type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const function_type&t, const corpus& corp)
{
interned_string type_name = get_type_name(t);
return lookup_function_type(type_name, corp);
}
/// Look into a given corpus to find a function type which has the same
/// qualified name as a given function type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the function type which has the same qualified name as
/// the function type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const function_type_sptr& fn_t,
const corpus& corpus)
{
if (fn_t)
return lookup_function_type(*fn_t, corpus);
return function_type_sptr();
}
/// Look into a given corpus to find a function type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const interned_string& qualified_name, const corpus& corp)
{
const istring_type_base_wptrs_map_type& m = corp.get_types().function_types();
function_type_sptr result =
lookup_type_in_map<function_type>(qualified_name, m);
if (!result)
result = lookup_function_type_through_translation_units(qualified_name,
corp);
return result;
}
/// Look into a given corpus to find a type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const interned_string& n, const corpus& corp)
{
type_base_sptr result;
((result = lookup_basic_type(n, corp))
|| (result = lookup_class_type(n, corp))
|| (result = lookup_union_type(n, corp))
|| (result = lookup_enum_type(n, corp))
|| (result = lookup_typedef_type(n, corp))
|| (result = lookup_qualified_type(n, corp))
|| (result = lookup_pointer_type(n, corp))
|| (result = lookup_reference_type(n, corp))
|| (result = lookup_array_type(n, corp))
|| (result= lookup_function_type(n, corp)));
return result;
}
/// Lookup a type from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting type, if any found.
type_base_sptr
lookup_type_per_location(const interned_string& loc, const corpus& corp)
{
// TODO: finish this.
//TODO: when we fully support types indexed by their location, this
//function should return a vector of types because at each location,
//there can be several types that are defined (yay, C and C++,
//*sigh*).
type_base_sptr result;
((result = lookup_basic_type_per_location(loc, corp))
|| (result = lookup_class_type_per_location(loc, corp))
|| (result = lookup_union_type_per_location(loc, corp))
|| (result = lookup_enum_type_per_location(loc, corp))
|| (result = lookup_typedef_type_per_location(loc, corp)));
return result;
}
/// Look into a given corpus to find a type
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const type_base&t, const corpus& corp)
{
interned_string n = get_type_name(t);
return lookup_type(n, corp);
}
/// Look into a given corpus to find a type
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map. Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const type_base_sptr&t, const corpus& corp)
{
if (t)
return lookup_type(*t, corp);
return type_base_sptr();
}
/// Update the map that associates a fully qualified name of a given
/// type to that type.
///
///
/// @param type the type we are considering.
///
/// @param types_map the map to update. It's a map that assciates a
/// fully qualified name of a type to the type itself.
///
/// @param use_type_name_as_key if true, use the name of the type as
/// the key to look it up later. If false, then use the location of
/// the type as a key to look it up later.
///
/// @return true iff the type was added to the map.
template<typename TypeKind>
bool
maybe_update_types_lookup_map(const shared_ptr<TypeKind>& type,
istring_type_base_wptrs_map_type& types_map,
bool use_type_name_as_key = true)
{
interned_string s;
if (use_type_name_as_key)
s = get_type_name(type);
else if (location l = type->get_location())
{
string str = l.expand();
s = type->get_environment()->intern(str);
}
istring_type_base_wptrs_map_type::iterator i = types_map.find(s);
bool result = false;
if (i == types_map.end())
{
types_map[s].push_back(type);
result = true;
}
else
i->second.push_back(type);
return result;
}
/// This is the specialization for type @ref class_decl of the
/// function template:
///
/// maybe_update_types_lookup_map<T>(scope_decl*,
/// const shared_ptr<T>&,
/// istring_type_base_wptrs_map_type&)
///
/// @param class_type the type to consider.
///
/// @param types_map the type map to update.
///
/// @return true iff the type was added to the map.
template<>
bool
maybe_update_types_lookup_map<class_decl>(const class_decl_sptr& class_type,
istring_type_base_wptrs_map_type& map,
bool use_type_name_as_key)
{
class_decl_sptr type = class_type;
bool update_qname_map = true;
if (type->get_is_declaration_only())
{
if (class_decl_sptr def =
is_class_type(class_type->get_definition_of_declaration()))
type = def;
else
update_qname_map = false;
}
if (!update_qname_map)
return false;
interned_string s;
if (use_type_name_as_key)
{
string qname = type->get_qualified_name();
s = type->get_environment()->intern(qname);
}
else if (location l = type->get_location())
{
string str = l.expand();
s = type->get_environment()->intern(str);
}
bool result = false;
istring_type_base_wptrs_map_type::iterator i = map.find(s);
if (i == map.end())
{
map[s].push_back(type);
result = true;
}
else
i->second.push_back(type);
return result;
}
/// This is the specialization for type @ref function_type of the
/// function template:
///
/// maybe_update_types_lookup_map<T>(scope_decl*,
/// const shared_ptr<T>&,
/// istring_type_base_wptrs_map_type&)
///
/// @param scope the scope of the type to consider.
///
/// @param class_type the type to consider.
///
/// @param types_map the type map to update.
///
/// @return true iff the type was added to the map.
template<>
bool
maybe_update_types_lookup_map<function_type>
(const function_type_sptr& type,
istring_type_base_wptrs_map_type& types_map,
bool /*use_type_name_as_key*/)
{
bool result = false;
interned_string s = get_type_name(type);
istring_type_base_wptrs_map_type::iterator i = types_map.find(s);
if (i == types_map.end())
{
types_map[s].push_back(type);
result = true;
}
else
i->second.push_back(type);
return result;
}
/// Update the map that associates the fully qualified name of a basic
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param basic_type the basic type to consider.
void
maybe_update_types_lookup_map(const type_decl_sptr& basic_type)
{
if (translation_unit *tu = basic_type->get_translation_unit())
maybe_update_types_lookup_map<type_decl>
(basic_type, tu->get_types().basic_types());
if (corpus *type_corpus = basic_type->get_corpus())
{
maybe_update_types_lookup_map<type_decl>
(basic_type,
type_corpus->priv_->get_types().basic_types());
maybe_update_types_lookup_map<type_decl>
(basic_type,
type_corpus->get_type_per_loc_map().basic_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<type_decl>
(basic_type,
group->priv_->get_types().basic_types());
maybe_update_types_lookup_map<type_decl>
(basic_type,
group->get_type_per_loc_map().basic_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a class
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param class_type the class type to consider.
void
maybe_update_types_lookup_map(const class_decl_sptr& class_type)
{
if (translation_unit *tu = class_type->get_translation_unit())
maybe_update_types_lookup_map<class_decl>
(class_type, tu->get_types().class_types());
if (corpus *type_corpus = class_type->get_corpus())
{
maybe_update_types_lookup_map<class_decl>
(class_type,
type_corpus->priv_->get_types().class_types());
maybe_update_types_lookup_map<class_decl>
(class_type,
type_corpus->get_type_per_loc_map().class_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<class_decl>
(class_type,
group->priv_->get_types().class_types());
maybe_update_types_lookup_map<class_decl>
(class_type,
group->get_type_per_loc_map().class_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a union
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param union_type the union type to consider.
void
maybe_update_types_lookup_map(const union_decl_sptr& union_type)
{
if (translation_unit *tu = union_type->get_translation_unit())
maybe_update_types_lookup_map<union_decl>
(union_type, tu->get_types().union_types());
if (corpus *type_corpus = union_type->get_corpus())
{
maybe_update_types_lookup_map<union_decl>
(union_type,
type_corpus->priv_->get_types().union_types());
maybe_update_types_lookup_map<union_decl>
(union_type,
type_corpus->get_type_per_loc_map().union_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<union_decl>
(union_type,
group->priv_->get_types().union_types());
maybe_update_types_lookup_map<union_decl>
(union_type,
group->get_type_per_loc_map().union_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of an enum
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param enum_type the type to consider.
void
maybe_update_types_lookup_map(const enum_type_decl_sptr& enum_type)
{
if (translation_unit *tu = enum_type->get_translation_unit())
maybe_update_types_lookup_map<enum_type_decl>
(enum_type, tu->get_types().enum_types());
if (corpus *type_corpus = enum_type->get_corpus())
{
maybe_update_types_lookup_map<enum_type_decl>
(enum_type,
type_corpus->priv_->get_types().enum_types());
maybe_update_types_lookup_map<enum_type_decl>
(enum_type,
type_corpus->get_type_per_loc_map().enum_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<enum_type_decl>
(enum_type,
group->priv_->get_types().enum_types());
maybe_update_types_lookup_map<enum_type_decl>
(enum_type,
group->get_type_per_loc_map().enum_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a
/// typedef type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param typedef_type the type to consider.
void
maybe_update_types_lookup_map(const typedef_decl_sptr& typedef_type)
{
if (translation_unit *tu = typedef_type->get_translation_unit())
maybe_update_types_lookup_map<typedef_decl>
(typedef_type, tu->get_types().typedef_types());
if (corpus *type_corpus = typedef_type->get_corpus())
{
maybe_update_types_lookup_map<typedef_decl>
(typedef_type,
type_corpus->priv_->get_types().typedef_types());
maybe_update_types_lookup_map<typedef_decl>
(typedef_type,
type_corpus->get_type_per_loc_map().typedef_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<typedef_decl>
(typedef_type,
group->priv_->get_types().typedef_types());
maybe_update_types_lookup_map<typedef_decl>
(typedef_type,
group->get_type_per_loc_map().typedef_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a
/// qualified type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param qualified_type the type to consider.
void
maybe_update_types_lookup_map(const qualified_type_def_sptr& qualified_type)
{
if (translation_unit *tu = qualified_type->get_translation_unit())
maybe_update_types_lookup_map<qualified_type_def>
(qualified_type, tu->get_types().qualified_types());
if (corpus *type_corpus = qualified_type->get_corpus())
{
maybe_update_types_lookup_map<qualified_type_def>
(qualified_type,
type_corpus->priv_->get_types().qualified_types());
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<qualified_type_def>
(qualified_type,
group->priv_->get_types().qualified_types());
}
}
}
/// Update the map that associates the fully qualified name of a
/// pointer type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param pointer_type the type to consider.
void
maybe_update_types_lookup_map(const pointer_type_def_sptr& pointer_type)
{
if (translation_unit *tu = pointer_type->get_translation_unit())
maybe_update_types_lookup_map<pointer_type_def>
(pointer_type, tu->get_types().pointer_types());
if (corpus *type_corpus = pointer_type->get_corpus())
{
maybe_update_types_lookup_map<pointer_type_def>
(pointer_type,
type_corpus->priv_->get_types().pointer_types());
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<pointer_type_def>
(pointer_type,
group->priv_->get_types().pointer_types());
}
}
}
/// Update the map that associates the fully qualified name of a
/// reference type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param reference_type the type to consider.
void
maybe_update_types_lookup_map(const reference_type_def_sptr& reference_type)
{
if (translation_unit *tu = reference_type->get_translation_unit())
maybe_update_types_lookup_map<reference_type_def>
(reference_type, tu->get_types().reference_types());
if (corpus *type_corpus = reference_type->get_corpus())
{
maybe_update_types_lookup_map<reference_type_def>
(reference_type,
type_corpus->priv_->get_types().reference_types());
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<reference_type_def>
(reference_type,
group->priv_->get_types().reference_types());
}
}
}
/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param array_type the type to consider.
void
maybe_update_types_lookup_map(const array_type_def_sptr& array_type)
{
if (translation_unit *tu = array_type->get_translation_unit())
maybe_update_types_lookup_map<array_type_def>
(array_type, tu->get_types().array_types());
if (corpus *type_corpus = array_type->get_corpus())
{
maybe_update_types_lookup_map<array_type_def>
(array_type,
type_corpus->priv_->get_types().array_types());
maybe_update_types_lookup_map<array_type_def>
(array_type,
type_corpus->get_type_per_loc_map().array_types(),
/*use_type_name_as_key*/false);
if (corpus *group = type_corpus->get_group())
{
maybe_update_types_lookup_map<array_type_def>
(array_type,
group->priv_->get_types().array_types());
maybe_update_types_lookup_map<array_type_def>
(array_type,
group->get_type_per_loc_map().array_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param subrange_type the type to consider.
void
maybe_update_types_lookup_map
(const array_type_def::subrange_sptr& subrange_type)
{
if (translation_unit *tu = subrange_type->get_translation_unit())
maybe_update_types_lookup_map<array_type_def::subrange_type>
(subrange_type, tu->get_types().subrange_types());
if (corpus *type_corpus = subrange_type->get_corpus())
{
maybe_update_types_lookup_map<array_type_def::subrange_type>
(subrange_type,
type_corpus->priv_->get_types().subrange_types());
maybe_update_types_lookup_map<array_type_def::subrange_type>
(subrange_type,
type_corpus->get_type_per_loc_map().subrange_types(),
/*use_type_name_as_key*/false);
if (corpus *group = subrange_type->get_corpus())
{
maybe_update_types_lookup_map<array_type_def::subrange_type>
(subrange_type,
group->priv_->get_types().subrange_types());
maybe_update_types_lookup_map<array_type_def::subrange_type>
(subrange_type,
group->get_type_per_loc_map().subrange_types(),
/*use_type_name_as_key*/false);
}
}
}
/// Update the map that associates the fully qualified name of a
/// function type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param scope the scope of the function type.
/// @param fn_type the type to consider.
void
maybe_update_types_lookup_map(const function_type_sptr& fn_type)
{
if (translation_unit *tu = fn_type->get_translation_unit())
maybe_update_types_lookup_map<function_type>
(fn_type, tu->get_types().function_types());
if (corpus *type_corpus = fn_type->get_corpus())
{
maybe_update_types_lookup_map<function_type>
(fn_type,
type_corpus->priv_->get_types().function_types());
if (corpus *group = fn_type->get_corpus())
{
maybe_update_types_lookup_map<function_type>
(fn_type,
group->priv_->get_types().function_types());
}
}
}
/// Update the map that associates the fully qualified name of a type
/// declaration with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param decl the declaration of the type to consider.
void
maybe_update_types_lookup_map(const decl_base_sptr& decl)
{
if (!is_type(decl))
return;
if (type_decl_sptr basic_type = is_type_decl(decl))
maybe_update_types_lookup_map(basic_type);
else if (class_decl_sptr class_type = is_class_type(decl))
maybe_update_types_lookup_map(class_type);
else if (union_decl_sptr union_type = is_union_type(decl))
maybe_update_types_lookup_map(union_type);
else if (enum_type_decl_sptr enum_type = is_enum_type(decl))
maybe_update_types_lookup_map(enum_type);
else if (typedef_decl_sptr typedef_type = is_typedef(decl))
maybe_update_types_lookup_map(typedef_type);
else if (qualified_type_def_sptr qualified_type = is_qualified_type(decl))
maybe_update_types_lookup_map(qualified_type);
else if (pointer_type_def_sptr pointer_type = is_pointer_type(decl))
maybe_update_types_lookup_map(pointer_type);
else if (reference_type_def_sptr reference_type = is_reference_type(decl))
maybe_update_types_lookup_map(reference_type);
else if (array_type_def_sptr array_type = is_array_type(decl))
maybe_update_types_lookup_map(array_type);
else if (array_type_def::subrange_sptr subrange_type = is_subrange_type(decl))
maybe_update_types_lookup_map(subrange_type);
else
ABG_ASSERT_NOT_REACHED;
}
/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param type the type to consider.
void
maybe_update_types_lookup_map(const type_base_sptr& type)
{
if (decl_base_sptr decl = get_type_declaration(type))
maybe_update_types_lookup_map(decl);
else
ABG_ASSERT_NOT_REACHED;
}
//--------------------------------
// </type and decls lookup stuff>
// ------------------------------
/// In a translation unit, lookup a given type or synthesize it if
/// it's a qualified type.
///
/// So this function first looks the type up in the translation unit.
/// If it's found, then OK, it's returned. Otherwise, if it's a
/// qualified, reference or pointer or function type (a composite
/// type), lookup the underlying type, synthesize the type we want
/// from it and return it.
///
/// If the underlying types is not not found, then give up and return
/// nil.
///
/// @return the type that was found or the synthesized type.
type_base_sptr
synthesize_type_from_translation_unit(const type_base_sptr& type,
translation_unit& tu)
{
type_base_sptr result;
result = lookup_type(type, tu);
if (!result)
{
if (qualified_type_def_sptr qual = is_qualified_type(type))
{
type_base_sptr underlying_type =
synthesize_type_from_translation_unit(qual->get_underlying_type(),
tu);
if (underlying_type)
{
result.reset(new qualified_type_def(underlying_type,
qual->get_cv_quals(),
qual->get_location()));
}
}
else if (pointer_type_def_sptr p = is_pointer_type(type))
{
type_base_sptr pointed_to_type =
synthesize_type_from_translation_unit(p->get_pointed_to_type(),
tu);
if (pointed_to_type)
{
result.reset(new pointer_type_def(pointed_to_type,
p->get_size_in_bits(),
p->get_alignment_in_bits(),
p->get_location()));
result->set_environment(pointed_to_type->get_environment());
}
}
else if (reference_type_def_sptr r = is_reference_type(type))
{
type_base_sptr pointed_to_type =
synthesize_type_from_translation_unit(r->get_pointed_to_type(), tu);
if (pointed_to_type)
{
result.reset(new reference_type_def(pointed_to_type,
r->is_lvalue(),
r->get_size_in_bits(),
r->get_alignment_in_bits(),
r->get_location()));
result->set_environment(pointed_to_type->get_environment());
}
}
else if (function_type_sptr f = is_function_type(type))
result = synthesize_function_type_from_translation_unit(*f, tu);
if (result)
{
add_decl_to_scope(is_decl(result), tu.get_global_scope());
canonicalize(result);
}
}
if (result)
tu.priv_->synthesized_types_.push_back(result);
return result;
}
/// In a translation unit, lookup the sub-types that make up a given
/// function type and if the sub-types are all found, synthesize and
/// return a function_type with them.
///
/// This function is like lookup_function_type_in_translation_unit()
/// execept that it constructs the function type from the sub-types
/// found in the translation, rather than just looking for the
/// function types held by the translation unit. This can be useful
/// if the translation unit doesnt hold the function type we are
/// looking for (i.e, lookup_function_type_in_translation_unit()
/// returned NULL) but we still want to see if the sub-types of the
/// function types are present in the translation unit.
///
/// @param fn_type the function type to consider.
///
/// @param tu the translation unit to look into.
///
/// @return the resulting synthesized function type if all its
/// sub-types have been found, NULL otherwise.
function_type_sptr
synthesize_function_type_from_translation_unit(const function_type& fn_type,
translation_unit& tu)
{
function_type_sptr nil = function_type_sptr();
environment* env = tu.get_environment();
ABG_ASSERT(env);
type_base_sptr return_type = fn_type.get_return_type();
type_base_sptr result_return_type;
if (!return_type || env->is_void_type(return_type))
result_return_type = env->get_void_type();
else
result_return_type = synthesize_type_from_translation_unit(return_type, tu);
if (!result_return_type)
return nil;
function_type::parameters parms;
type_base_sptr parm_type;
function_decl::parameter_sptr parm;
for (function_type::parameters::const_iterator i =
fn_type.get_parameters().begin();
i != fn_type.get_parameters().end();
++i)
{
type_base_sptr t = (*i)->get_type();
parm_type = synthesize_type_from_translation_unit(t, tu);
if (!parm_type)
return nil;
parm.reset(new function_decl::parameter(parm_type,
(*i)->get_index(),
(*i)->get_name(),
(*i)->get_location(),
(*i)->get_variadic_marker(),
(*i)->get_is_artificial()));
parms.push_back(parm);
}
class_or_union_sptr class_type;
const method_type* method = is_method_type(&fn_type);
if (method)
{
class_type = is_class_or_union_type
(synthesize_type_from_translation_unit(method->get_class_type(), tu));
ABG_ASSERT(class_type);
}
function_type_sptr result_fn_type;
if (class_type)
result_fn_type.reset(new method_type(result_return_type,
class_type,
parms,
method->get_is_const(),
fn_type.get_size_in_bits(),
fn_type.get_alignment_in_bits()));
else
result_fn_type.reset(new function_type(result_return_type,
parms,
fn_type.get_size_in_bits(),
fn_type.get_alignment_in_bits()));
tu.priv_->synthesized_types_.push_back(result_fn_type);
// The new synthesized type must be in the same environment as its
// translation unit.
result_fn_type->set_environment(tu.get_environment());
tu.bind_function_type_life_time(result_fn_type);
canonicalize(result_fn_type);
return result_fn_type;
}
/// Demangle a C++ mangled name and return the resulting string
///
/// @param mangled_name the C++ mangled name to demangle.
///
/// @return the resulting mangled name.
string
demangle_cplus_mangled_name(const string& mangled_name)
{
if (mangled_name.empty())
return "";
size_t l = 0;
int status = 0;
char * str = abi::__cxa_demangle(mangled_name.c_str(),
NULL, &l, &status);
string demangled_name = mangled_name;
if (str)
{
ABG_ASSERT(status == 0);
demangled_name = str;
free(str);
str = 0;
}
return demangled_name;
}
/// Return either the type given in parameter if it's non-null, or the
/// void type.
///
/// @param t the type to consider.
///
/// @param env the environment to use. If NULL, just abort the
/// process.
///
/// @return either @p t if it is non-null, or the void type.
type_base_sptr
type_or_void(const type_base_sptr t, const environment* env)
{
type_base_sptr r;
if (t)
r = t;
else
{
ABG_ASSERT(env);
r = type_base_sptr(env->get_void_type());
}
return r;
}
global_scope::~global_scope()
{
}
static bool
maybe_propagate_canonical_type(const type_base& lhs_type,
const type_base& rhs_type);
/// Test if two types are eligible to the "Linux Kernel Fast Type
/// Comparison Optimization", a.k.a LKFTCO.
///
/// Two types T1 and T2 (who are presumably of the same name and kind)
/// are eligible to the LKFTCO if they fulfill the following criteria/
///
/// 1/ T1 and T2 come from the same Linux Kernel Corpus and they are
/// either class, union or enums.
///
/// 2/ They are defined in the same translation unit.
///
/// @param t1 the first type to consider.
///
/// @param t2 the second type to consider.
///
/// @return true iff t1 and t2 are eligible to the LKFTCO.
static bool
types_defined_same_linux_kernel_corpus_public(const type_base& t1,
const type_base& t2)
{
const corpus *t1_corpus = t1.get_corpus(), *t2_corpus = t2.get_corpus();
string t1_file_path, t2_file_path;
/// If the t1 (and t2) are classes/unions/enums from the same linux
/// kernel corpus, let's move on. Otherwise bail out.
if (!(t1_corpus && t2_corpus
&& t1_corpus == t2_corpus
&& (t1_corpus->get_origin() & corpus::LINUX_KERNEL_BINARY_ORIGIN)
&& (is_class_or_union_type(&t1)
|| is_enum_type(&t1))))
return false;
class_or_union *c1 = 0, *c2 = 0;
c1 = is_class_or_union_type(&t1);
c2 = is_class_or_union_type(&t2);
// Two anonymous class types with no naming typedefs cannot be
// eligible to this optimization.
if ((c1 && c1->get_is_anonymous() && !c1->get_naming_typedef())
|| (c2 && c2->get_is_anonymous() && !c2->get_naming_typedef()))
return false;
// Two anonymous classes with naming typedefs should have the same
// typedef name.
if (c1
&& c2
&& c1->get_is_anonymous() && c1->get_naming_typedef()
&& c2->get_is_anonymous() && c2->get_naming_typedef())
if (c1->get_naming_typedef()->get_name()
!= c2->get_naming_typedef()->get_name())
return false;
// Two anonymous enum types cannot be eligible to this optimization.
if (const enum_type_decl *e1 = is_enum_type(&t1))
if (const enum_type_decl *e2 = is_enum_type(&t2))
if (e1->get_is_anonymous() || e2->get_is_anonymous())
return false;
// Look through declaration-only types. That is, get the associated
// definition type.
c1 = look_through_decl_only_class(c1);
c2 = look_through_decl_only_class(c2);
if (c1 && c2)
{
if (c1->get_is_declaration_only() != c2->get_is_declaration_only())
{
if (c1->get_environment()->decl_only_class_equals_definition())
// At least one of classes/union is declaration-only.
// Because we are in a context in which a declaration-only
// class/union is equal to all definitions of that
// class/union, we can assume that the two types are
// equal.
return true;
}
}
if (t1.get_size_in_bits() != t2.get_size_in_bits())
return false;
// Look at the file names of the locations of t1 and t2. If they
// are equal, then t1 and t2 are defined in the same file.
{
location l;
if (c1)
l = c1->get_location();
else
l = dynamic_cast<const decl_base&>(t1).get_location();
unsigned line = 0, col = 0;
if (l)
l.expand(t1_file_path, line, col);
if (c2)
l = c2->get_location();
else
l = dynamic_cast<const decl_base&>(t2).get_location();
if (l)
l.expand(t2_file_path, line, col);
}
if (t1_file_path.empty() || t2_file_path.empty())
return false;
if (t1_file_path == t2_file_path)
return true;
return false;
}
/// Compare a type T against a canonical type.
///
/// This function is called during the canonicalization process of the
/// type T. T is called the "candidate type" because it's in the
/// process of being canonicalized. Meaning, it's going to be
/// compared to a canonical type C. If T equals C, then the canonical
/// type of T is C.
///
/// The purpose of this function is to allow the debugging of the
/// canonicalization of T, if that debugging is activated by
/// configuring the libabigail package with
/// --enable-debug-type-canonicalization and by running "abidw
/// --debug-tc". In that case, T is going to be compared to C twice:
/// once with canonical equality and once with structural equality.
/// The two comparisons must be equal. Otherwise, the
/// canonicalization process is said to be faulty and this function
/// aborts.
///
/// This is a sub-routine of type_base::get_canonical_type_for.
///
/// @param canonical_type the canonical type to compare the candidate
/// type against.
///
/// @param candidate_type the candidate type to compare against the
/// canonical type.
///
/// @return true iff @p canonical_type equals @p candidate_type.
///
static bool
compare_types_during_canonicalization(const type_base_sptr& canonical_type,
const type_base_sptr& candidate_type)
{
#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
environment *env = canonical_type->get_environment();
if (env->debug_type_canonicalization_is_on())
{
bool canonical_equality = false, structural_equality = false;
env->priv_->use_canonical_type_comparison_ = true;
canonical_equality = canonical_type == candidate_type;
env->priv_->use_canonical_type_comparison_ = false;
structural_equality = canonical_type == candidate_type;
if (canonical_equality != structural_equality)
{
std::cerr << "structural & canonical equality different for type: "
<< canonical_type->get_pretty_representation(true, true)
<< std::endl;
ABG_ASSERT_NOT_REACHED;
}
return structural_equality;
}
#endif //end WITH_DEBUG_TYPE_CANONICALIZATION
return canonical_type == candidate_type;
}
/// Compute the canonical type for a given instance of @ref type_base.
///
/// Consider two types T and T'. The canonical type of T, denoted
/// C(T) is a type such as T == T' if and only if C(T) == C(T'). Said
/// otherwise, to compare two types, one just needs to compare their
/// canonical types using pointer equality. That makes type
/// comparison faster than the structural comparison performed by the
/// abigail::ir::equals() overloads.
///
/// If there is not yet any canonical type for @p t, then @p t is its
/// own canonical type. Otherwise, this function returns the
/// canonical type of @p t which is the canonical type that has the
/// same hash value as @p t and that structurally equals @p t. Note
/// that after invoking this function, the life time of the returned
/// canonical time is then equals to the life time of the current
/// process.
///
/// @param t a smart pointer to instance of @ref type_base we want to
/// compute a canonical type for.
///
/// @return the canonical type for the current instance of @ref
/// type_base.
type_base_sptr
type_base::get_canonical_type_for(type_base_sptr t)
{
if (!t)
return t;
environment* env = t->get_environment();
ABG_ASSERT(env);
if (is_non_canonicalized_type(t))
// This type should not be canonicalized!
return type_base_sptr();
bool decl_only_class_equals_definition =
(odr_is_relevant(*t) || env->decl_only_class_equals_definition());
class_or_union_sptr class_or_union = is_class_or_union_type(t);
// Look through declaration-only classes when we are dealing with
// C++ or languages where we assume the "One Definition Rule". In
// that context, we assume that a declaration-only non-anonymous
// class equals all fully defined classes of the same name.
//
// Otherwise, all classes, including declaration-only classes are
// canonicalized and only canonical comparison is going to be used
// in the system.
if (decl_only_class_equals_definition)
if (class_or_union)
{
class_or_union = look_through_decl_only_class(class_or_union);
if (class_or_union->get_is_declaration_only())
return type_base_sptr();
else
t = class_or_union;
}
class_decl_sptr is_class = is_class_type(t);
if (t->get_canonical_type())
return t->get_canonical_type();
// For classes and union, ensure that an anonymous class doesn't
// have a linkage name. If it does in the future, then me must be
// mindful that the linkage name respects the type identity
// constraints which states that "if two linkage names are different
// then the two types are different".
ABG_ASSERT(!class_or_union
|| !class_or_union->get_is_anonymous()
|| class_or_union->get_linkage_name().empty());
// We want the pretty representation of the type, but for an
// internal use, not for a user-facing purpose.
//
// If two classe types Foo are declared, one as a class and the
// other as a struct, but are otherwise equivalent, we want their
// pretty representation to be the same. Hence the 'internal'
// argument of ir::get_pretty_representation() is set to true here.
// So in this case, the pretty representation of Foo is going to be
// "class Foo", regardless of its struct-ness. This also applies to
// composite types which would have "class Foo" as a sub-type.
string repr = t->get_cached_pretty_representation(/*internal=*/true);
// If 't' already has a canonical type 'inside' its corpus
// (t_corpus), then this variable is going to contain that canonical
// type.
type_base_sptr canonical_type_present_in_corpus;
environment::canonical_types_map_type& types =
env->get_canonical_types_map();
type_base_sptr result;
environment::canonical_types_map_type::iterator i = types.find(repr);
if (i == types.end())
{
vector<type_base_sptr> v;
v.push_back(t);
types[repr] = v;
result = t;
}
else
{
vector<type_base_sptr> &v = i->second;
// Let's compare 't' structurally (i.e, compare its sub-types
// recursively) against the canonical types of the system. If it
// equals a given canonical type C, then it means C is the
// canonical type of 't'. Otherwise, if 't' is different from
// all the canonical types of the system, then it means 't' is a
// canonical type itself.
for (vector<type_base_sptr>::const_reverse_iterator it = v.rbegin();
it != v.rend();
++it)
{
// Before the "*it == it" comparison below is done, let's
// perform on-the-fly-canonicalization. For C types, let's
// consider that an unresolved struct declaration 'struct S'
// is different from a definition 'struct S'. This is
// because normally, at this point all the declarations of
// struct S that are compatible with the definition of
// struct S have already been resolved to that definition,
// during the DWARF parsing. The remaining unresolved
// declaration are thus considered different. With this
// setup we can properly handle cases of two *different*
// struct S being defined in the same binary (in different
// translation units), and a third struct S being only
// declared as an opaque type in a third translation unit of
// its own, with no definition in there. In that case, the
// declaration-only struct S should be left alone and not
// resolved to any of the two definitions of struct S.
bool saved_decl_only_class_equals_definition =
env->decl_only_class_equals_definition();
env->do_on_the_fly_canonicalization(true);
// Compare types by considering that decl-only classes don't
// equal their definition.
env->decl_only_class_equals_definition(false);
bool equal = (types_defined_same_linux_kernel_corpus_public(**it, *t)
|| compare_types_during_canonicalization(*it, t));
// Restore the state of the on-the-fly-canonicalization and
// the decl-only-class-being-equal-to-a-matching-definition
// flags.
env->do_on_the_fly_canonicalization(false);
env->decl_only_class_equals_definition
(saved_decl_only_class_equals_definition);
if (equal)
{
result = *it;
break;
}
}
#ifdef WITH_DEBUG_SELF_COMPARISON
if (env->self_comparison_debug_is_on())
{
// So we are debugging the canonicalization process,
// possibly via the use of 'abidw --debug-abidiff <binary>'.
corpus_sptr corp1, corp2;
env->get_self_comparison_debug_inputs(corp1, corp2);
if (corp1 && corp2 && t->get_corpus() == corp2.get())
{
// If 't' comes from the second corpus, then it *must*
// be equal to its matching canonical type coming from
// the first corpus because the second corpus is the
// abixml representation of the first corpus. In other
// words, all types coming from the second corpus must
// have canonical types coming from the first corpus.
if (result)
{
if (!env->priv_->
check_canonical_type_from_abixml_during_self_comp(t,
result))
// The canonical type of the type re-read from abixml
// type doesn't match the canonical type that was
// initially serialized down.
std::cerr << "error: wrong canonical type for '"
<< repr
<< "' / type: @"
<< std::hex
<< t.get()
<< "/ canon: @"
<< result.get()
<< std::endl;
}
else //!result
{
uintptr_t ptr_val = reinterpret_cast<uintptr_t>(t.get());
string type_id = env->get_type_id_from_pointer(ptr_val);
if (type_id.empty())
type_id = "type-id-<not-found>";
// We are in the case where 't' is different from all
// the canonical types of the same name that come from
// the first corpus.
//
// If 't' indeed comes from the second corpus then this
// clearly is a canonicalization failure.
//
// There was a problem either during the serialization
// of 't' into abixml, or during the de-serialization
// from abixml into abigail::ir. Further debugging is
// needed to determine what that root cause problem is.
//
// Note that the first canonicalization problem of this
// kind must be fixed before looking at the subsequent
// ones, because the later might well just be
// consequences of the former.
std::cerr << "error: wrong induced canonical type for '"
<< repr
<< "' from second corpus"
<< ", ptr: " << std::hex << t.get()
<< "type-id: " << type_id
<< std::endl;
}
}
}
#endif
if (!result)
{
v.push_back(t);
result = t;
}
}
return result;
}
/// This method is invoked automatically right after the current
/// instance of @ref class_decl has been canonicalized.
void
type_base::on_canonical_type_set()
{}
/// This is a subroutine of the canonicalize() function.
///
/// When the canonical type C of type T has just been computed, there
/// can be cases where T has member functions that C doesn't have.
///
/// This is possible because non virtual member functions are not
/// taken in account when comparing two types.
///
/// In that case, this function updates C so that it contains the
/// member functions.
///
/// There can also be cases where C has a method M which is not linked
/// to any underlying symbol, whereas in T, M is to link to an
/// underlying symbol. In that case, this function updates M in C so
/// that it's linked to the same underlying symbol as for M in T.
static void
maybe_adjust_canonical_type(const type_base_sptr& canonical,
const type_base_sptr& type)
{
if (!canonical
// If 'type' is *NOT* a newly canonicalized type ...
|| type->get_naked_canonical_type()
// ... or if 'type' is it's own canonical type, then get out.
|| type.get() == canonical.get())
return;
if (class_decl_sptr cl = is_class_type(type))
{
class_decl_sptr canonical_class = is_class_type(canonical);
if (canonical_class)
{
// Set symbols of member functions that might be missing
// theirs.
for (class_decl::member_functions::const_iterator i =
cl->get_member_functions().begin();
i != cl->get_member_functions().end();
++i)
if ((*i)->get_symbol())
{
if (method_decl *m = canonical_class->
find_member_function((*i)->get_linkage_name()))
{
elf_symbol_sptr s1 = (*i)->get_symbol();
if (s1 && !m->get_symbol())
// Method 'm' in the canonical type is not
// linked to the underlying symbol of '*i'.
// Let's link it now. have th
m->set_symbol(s1);
}
else
// There is a member function defined and publicly
// exported in the other class, and the canonical
// class doesn't have that member function. Let's
// copy that member function to the canonical class
// then.
copy_member_function (canonical_class, *i);
}
}
}
// If an artificial function type equals a non-artfificial one in
// the system, then the canonical type of both should be deemed
// non-artificial. This is important because only non-artificial
// canonical function types are emitted out into abixml, so if don't
// do this we risk missing to emit some function types.
if (is_function_type(type))
if (type->get_is_artificial() != canonical->get_is_artificial())
canonical->set_is_artificial(false);
}
/// Compute the canonical type of a given type.
///
/// It means that after invoking this function, comparing the intance
/// instance @ref type_base and another one (on which
/// type_base::enable_canonical_equality() would have been invoked as
/// well) is performed by just comparing the pointer values of the
/// canonical types of both types. That equality comparison is
/// supposedly faster than structural comparison of the types.
///
/// @param t a smart pointer to the instance of @ref type_base for
/// which to compute the canonical type. After this call,
/// t->get_canonical_type() will return the newly computed canonical
/// type.
///
/// @return the canonical type computed for @p t.
type_base_sptr
canonicalize(type_base_sptr t)
{
if (!t)
return t;
if (t->get_canonical_type())
return t->get_canonical_type();
type_base_sptr canonical = type_base::get_canonical_type_for(t);
maybe_adjust_canonical_type(canonical, t);
t->priv_->canonical_type = canonical;
t->priv_->naked_canonical_type = canonical.get();
if (class_decl_sptr cl = is_class_type(t))
if (type_base_sptr d = is_type(cl->get_earlier_declaration()))
if ((canonical = d->get_canonical_type()))
{
d->priv_->canonical_type = canonical;
d->priv_->naked_canonical_type = canonical.get();
}
if (canonical)
if (decl_base_sptr d = is_decl_slow(canonical))
{
scope_decl *scope = d->get_scope();
// Add the canonical type to the set of canonical types
// belonging to its scope.
if (scope)
{
if (is_type(scope))
// The scope in question is itself a type (e.g, a class
// or union). Let's call that type ST. We want to add
// 'canonical' to the set of canonical types belonging
// to ST.
if (type_base_sptr c = is_type(scope)->get_canonical_type())
// We want to add 'canonical' to set of canonical
// types belonging to the canonical type of ST. That
// way, just looking at the canonical type of ST is
// enough to get the types that belong to the scope of
// the class of equivalence of ST.
scope = is_scope_decl(is_decl(c)).get();
scope->get_canonical_types().insert(canonical);
}
// else, if the type doesn't have a scope, it's not meant to be
// emitted. This can be the case for the result of the
// function strip_typedef, for instance.
}
t->on_canonical_type_set();
return canonical;
}
/// Set the definition of this declaration-only @ref decl_base.
///
/// @param d the new definition to set.
void
decl_base::set_definition_of_declaration(const decl_base_sptr& d)
{
ABG_ASSERT(get_is_declaration_only());
priv_->definition_of_declaration_ = d;
if (type_base *t = is_type(this))
if (type_base_sptr canonical_type = is_type(d)->get_canonical_type())
t->priv_->canonical_type = canonical_type;
priv_->naked_definition_of_declaration_ = const_cast<decl_base*>(d.get());
}
/// The constructor of @ref type_base.
///
/// @param s the size of the type, in bits.
///
/// @param a the alignment of the type, in bits.
type_base::type_base(const environment* e, size_t s, size_t a)
: type_or_decl_base(e, ABSTRACT_TYPE_BASE|ABSTRACT_TYPE_BASE),
priv_(new priv(s, a))
{}
/// Getter of the canonical type of the current instance of @ref
/// type_base.
///
/// @return a smart pointer to the canonical type of the current
/// intance of @ref type_base, or an empty smart pointer if the
/// current instance of @ref type_base doesn't have any canonical
/// type.
type_base_sptr
type_base::get_canonical_type() const
{return priv_->canonical_type.lock();}
/// Getter of the canonical type pointer.
///
/// Note that this function doesn't return a smart pointer, but rather
/// the underlying pointer managed by the smart pointer. So it's as
/// fast as possible. This getter is to be used in code paths that
/// are proven to be performance hot spots; especially, when comparing
/// sensitive types like class, function, pointers and reference
/// types. Those are compared extremely frequently and thus, their
/// accessing the canonical type must be fast.
///
/// @return the canonical type pointer, not managed by a smart
/// pointer.
type_base*
type_base::get_naked_canonical_type() const
{return priv_->naked_canonical_type;}
/// Get the pretty representation of the current type.
///
/// The pretty representation is retrieved from a cache. If the cache
/// is empty, this function computes the pretty representation, put it
/// in the cache and returns it.
///
/// Note that if the type is *NOT* canonicalized, the pretty
/// representation is never cached.
///
/// @param internal if true, then the pretty representation is to be
/// used for purpuses that are internal to the libabigail library
/// itself. If you don't know what this means, then you probably
/// should set this parameter to "false".
const interned_string&
type_base::get_cached_pretty_representation(bool internal) const
{
if (internal)
{
if (!get_naked_canonical_type() || priv_->internal_cached_repr_.empty())
{
string r = ir::get_pretty_representation(this, internal);
priv_->internal_cached_repr_ = get_environment()->intern(r);
}
return priv_->internal_cached_repr_;
}
if (!get_naked_canonical_type() || priv_->cached_repr_.empty())
{
string r = ir::get_pretty_representation(this, internal);
priv_->cached_repr_ = get_environment()->intern(r);
}
return priv_->cached_repr_;
}
/// Compares two instances of @ref type_base.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p is non-null and if the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const type_base& l, const type_base& r, change_kind* k)
{
bool result = (l.get_size_in_bits() == r.get_size_in_bits()
&& l.get_alignment_in_bits() == r.get_alignment_in_bits());
if (!result)
if (k)
*k |= LOCAL_TYPE_CHANGE_KIND;
ABG_RETURN(result);
}
/// Return true iff both type declarations are equal.
///
/// Note that this doesn't test if the scopes of both types are equal.
bool
type_base::operator==(const type_base& other) const
{return equals(*this, other, 0);}
/// Inequality operator.
///
///@param other the instance of @ref type_base to compare the current
/// instance against.
///
/// @return true iff the current instance is different from @p other.
bool
type_base::operator!=(const type_base& other) const
{return !operator==(other);}
/// Setter for the size of the type.
///
/// @param s the new size -- in bits.
void
type_base::set_size_in_bits(size_t s)
{priv_->size_in_bits = s;}
/// Getter for the size of the type.
///
/// @return the size in bits of the type.
size_t
type_base::get_size_in_bits() const
{return priv_->size_in_bits;}
/// Setter for the alignment of the type.
///
/// @param a the new alignment -- in bits.
void
type_base::set_alignment_in_bits(size_t a)
{priv_->alignment_in_bits = a;}
/// Getter for the alignment of the type.
///
/// @return the alignment of the type in bits.
size_t
type_base::get_alignment_in_bits() const
{return priv_->alignment_in_bits;}
/// Default implementation of traversal for types. This function does
/// nothing. It must be implemented by every single new type that is
/// written.
///
/// Please look at e.g, class_decl::traverse() for an example of how
/// to implement this.
///
/// @param v the visitor used to visit the type.
bool
type_base::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
v.visit_begin(this);
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
type_base::~type_base()
{delete priv_;}
// </type_base definitions>
// <integral_type definitions>
/// Bitwise OR operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise OR.
integral_type::modifiers_type
operator|(integral_type::modifiers_type l, integral_type::modifiers_type r)
{
return static_cast<integral_type::modifiers_type>(static_cast<unsigned>(l)
|static_cast<unsigned>(r));
}
/// Bitwise AND operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise AND.
integral_type::modifiers_type
operator&(integral_type::modifiers_type l, integral_type::modifiers_type r)
{
return static_cast<integral_type::modifiers_type>(static_cast<unsigned>(l)
&static_cast<unsigned>(r));
}
/// Bitwise |= operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise |=.
integral_type::modifiers_type&
operator|=(integral_type::modifiers_type& l, integral_type::modifiers_type r)
{
l = l | r;
return l;
}
/// Parse a word containing one integral type modifier.
///
/// A word is considered to be a string of characters that doesn't
/// contain any white space.
///
/// @param word the word to parse. It is considered to be a string of
/// characters that doesn't contain any white space.
///
/// @param modifiers out parameter. It's set by this function to the
/// parsed modifier iff the function returned true.
///
/// @return true iff @word was successfully parsed.
static bool
parse_integral_type_modifier(const string& word,
integral_type::modifiers_type &modifiers)
{
if (word == "signed")
modifiers |= integral_type::SIGNED_MODIFIER;
else if (word == "unsigned")
modifiers |= integral_type::UNSIGNED_MODIFIER;
else if (word == "short")
modifiers |= integral_type::SHORT_MODIFIER;
else if (word == "long")
modifiers |= integral_type::LONG_MODIFIER;
else if (word == "long long")
modifiers |= integral_type::LONG_LONG_MODIFIER;
else
return false;
return true;
}
/// Parse a base type of an integral type from a string.
///
/// @param type_name the type name to parse.
///
/// @param base out parameter. This is set to the resulting base type
/// parsed, iff the function returned true.
///
/// @return true iff the function could successfully parse the base
/// type.
static bool
parse_base_integral_type(const string& type_name,
integral_type::base_type& base)
{
if (type_name == "int")
base = integral_type::INT_BASE_TYPE;
else if (type_name == "char")
base = integral_type::CHAR_BASE_TYPE;
else if (type_name == "bool" || type_name == "_Bool")
base = integral_type::BOOL_BASE_TYPE;
else if (type_name == "double")
base = integral_type::DOUBLE_BASE_TYPE;
else if (type_name =="float")
base = integral_type::FLOAT_BASE_TYPE;
else if (type_name == "char16_t")
base = integral_type::CHAR16_T_BASE_TYPE;
else if (type_name == "char32_t")
base = integral_type::CHAR32_T_BASE_TYPE;
else if (type_name == "wchar_t")
base = integral_type::WCHAR_T_BASE_TYPE;
else
return false;
return true;
}
/// Parse an integral type from a string.
///
/// @param type_name the string containing the integral type to parse.
///
/// @param base out parameter. Is set by this function to the base
/// type of the integral type, iff the function returned true.
///
/// @param modifiers out parameter If set by this function to the
/// modifier of the integral type, iff the function returned true.
///
/// @return true iff the function could parse an integral type from @p
/// type_name.
static bool
parse_integral_type(const string& type_name,
integral_type::base_type& base,
integral_type::modifiers_type& modifiers)
{
string input = type_name;
string::size_type len = input.length();
string::size_type cur_pos = 0, prev_pos = 0;
string cur_word, prev_word;
bool ok = false;
while (cur_pos < len)
{
prev_pos = cur_pos;
cur_pos = input.find(' ', prev_pos);
prev_word = cur_word;
cur_word = input.substr(prev_pos, cur_pos - prev_pos);
if (cur_pos < len && isspace(input[cur_pos]))
do
++cur_pos;
while (cur_pos < len && isspace(input[cur_pos]));
if (cur_pos < len
&& cur_word == "long"
&& prev_word != "long")
{
prev_pos = cur_pos;
cur_pos = input.find(' ', prev_pos);
string saved_prev_word = prev_word;
prev_word = cur_word;
cur_word = input.substr(prev_pos, cur_pos - prev_pos);
if (cur_word == "long")
cur_word = "long long";
else
{
cur_pos = prev_pos;
cur_word = prev_word;
prev_word = saved_prev_word;
}
}
if (!parse_integral_type_modifier(cur_word, modifiers))
{
if (!parse_base_integral_type(cur_word, base))
return false;
else
ok = true;
}
else
ok = true;
}
return ok;
}
/// Parse an integral type from a string.
///
/// @param str the string containing the integral type to parse.
///
///@param type the resulting @ref integral_type. Is set to the result
///of the parse, iff the function returns true.
///
/// @return true iff the function could parse an integral type from @p
/// str.
bool
parse_integral_type(const string& str, integral_type& type)
{
return false; // Disable all integral type name interpretation.
integral_type::base_type base_type = integral_type::INT_BASE_TYPE;
integral_type::modifiers_type modifiers = integral_type::NO_MODIFIER;
if (!parse_integral_type(str, base_type, modifiers))
return false;
// So this is an integral type.
integral_type int_type(base_type, modifiers);
type = int_type;
return true;
}
/// Default constructor of the @ref integral_type.
integral_type::integral_type()
: base_(INT_BASE_TYPE),
modifiers_(NO_MODIFIER)
{}
/// Constructor of the @ref integral_type.
///
/// @param b the base type of the integral type.
///
/// @param m the modifiers of the integral type.
integral_type::integral_type(base_type b, modifiers_type m)
: base_(b), modifiers_(m)
{}
/// Constructor of the @ref integral_type.
///
/// @param the name of the integral type to parse to initialize the
/// current instance of @ref integral_type.
integral_type::integral_type(const string& type_name)
: base_(INT_BASE_TYPE),
modifiers_(NO_MODIFIER)
{
bool could_parse = parse_integral_type(type_name, base_, modifiers_);
ABG_ASSERT(could_parse);
}
/// Getter of the base type of the @ref integral_type.
///
/// @return the base type of the @ref integral_type.
integral_type::base_type
integral_type::get_base_type() const
{return base_;}
/// Getter of the modifiers bitmap of the @ref integral_type.
///
/// @return the modifiers bitmap of the @ref integral_type.
integral_type::modifiers_type
integral_type::get_modifiers() const
{return modifiers_;}
/// Equality operator for the @ref integral_type.
///
/// @param other the other integral type to compare against.
///
/// @return true iff @p other equals the current instance of @ref
/// integral_type.
bool
integral_type::operator==(const integral_type&other) const
{return base_ == other.base_ && modifiers_ == other.modifiers_;}
/// Return the string representation of the current instance of @ref
/// integral_type.
///
/// @return the string representation of the current instance of @ref
/// integral_type.
string
integral_type::to_string() const
{
string result;
// Look at modifiers ...
if (modifiers_ & SIGNED_MODIFIER)
result += "signed ";
if (modifiers_ & UNSIGNED_MODIFIER)
result += "unsigned ";
if (modifiers_ & SHORT_MODIFIER)
result += "short ";
if (modifiers_ & LONG_MODIFIER)
result += "long ";
if (modifiers_ & LONG_LONG_MODIFIER)
result += "long long ";
// ... and look at base types.
if (base_ == INT_BASE_TYPE)
result += "int";
else if (base_ == CHAR_BASE_TYPE)
result += "char";
else if (base_ == BOOL_BASE_TYPE)
result += "bool";
else if (base_ == DOUBLE_BASE_TYPE)
result += "double";
else if (base_ == FLOAT_BASE_TYPE)
result += "float";
else if (base_ == CHAR16_T_BASE_TYPE)
result += "char16_t";
else if (base_ == CHAR32_T_BASE_TYPE)
result += "char32_t";
else if (base_ == WCHAR_T_BASE_TYPE)
result += "wchar_t";
return result;
}
/// Convert the current instance of @ref integral_type into its string
/// representation.
///
/// @return the string representation of the current instance of @ref
/// integral_type.
integral_type::operator string() const
{return to_string();}
// </integral_type definitions>
//<type_decl definitions>
/// Constructor.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the type declaration.
///
/// @param size_in_bits the size of the current type_decl, in bits.
///
/// @param alignment_in_bits the alignment of the current typ, in
/// bits.
///
/// @param locus the source location of the current type declaration.
///
/// @param linkage_name the linkage_name of the current type declaration.
///
/// @param vis the visibility of the type declaration.
type_decl::type_decl(const environment* env,
const string& name,
size_t size_in_bits,
size_t alignment_in_bits,
const location& locus,
const string& linkage_name,
visibility vis)
: type_or_decl_base(env,
BASIC_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
decl_base(env, name, locus, linkage_name, vis),
type_base(env, size_in_bits, alignment_in_bits)
{
runtime_type_instance(this);
integral_type::base_type base_type = integral_type::INT_BASE_TYPE;
integral_type::modifiers_type modifiers = integral_type::NO_MODIFIER;
integral_type int_type(base_type, modifiers);
if (parse_integral_type(name, int_type))
{
// Convert the integral_type into its canonical string
// representation.
string integral_type_name = int_type;
// Set the name of this type_decl to the canonical string
// representation above
set_name(integral_type_name);
set_qualified_name(get_name());
if (!get_linkage_name().empty())
set_linkage_name(integral_type_name);
}
}
/// Compares two instances of @ref type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const type_decl& l, const type_decl& r, change_kind* k)
{
bool result = equals(static_cast<const decl_base&>(l),
static_cast<const decl_base&>(r),
k);
if (!k && !result)
ABG_RETURN_FALSE;
result &= equals(static_cast<const type_base&>(l),
static_cast<const type_base&>(r),
k);
ABG_RETURN(result);
}
/// Return true if both types equals.
///
/// This operator re-uses the overload that takes a decl_base.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check agains.
bool
type_decl::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Return true if both types equals.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check against.
bool
type_decl::operator==(const decl_base& o) const
{
const type_decl* other = dynamic_cast<const type_decl*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Return true if both types equals.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check against.
///
/// @return true iff the current isntance equals @p o
bool
type_decl::operator==(const type_decl& o) const
{
const decl_base& other = o;
return *this == other;
}
/// Inequality operator.
///
/// @param o the other type to compare against.
///
/// @return true iff the current instance is different from @p o.
bool
type_decl::operator!=(const type_decl& o) const
{return !operator==(o);}
/// Equality operator for @ref type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator==(const type_decl_sptr& l, const type_decl_sptr& r)
{
if (!!l != !!r)
return false;
if (l.get() == r.get())
return true;
return *l == *r;
}
/// Inequality operator for @ref type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const type_decl_sptr& l, const type_decl_sptr& r)
{return !operator==(l, r);}
/// Get the pretty representation of the current instance of @ref
/// type_decl.
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representatin of the @ref type_decl.
string
type_decl::get_pretty_representation(bool internal,
bool qualified_name) const
{
if (qualified_name)
return get_qualified_name(internal);
return get_name();
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
type_decl::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
v.visit_begin(this);
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
type_decl::~type_decl()
{}
//</type_decl definitions>
// <scope_type_decl definitions>
/// Constructor.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the type.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param alignment_in_bits the alignment of the type, in bits.
///
/// @param locus the source location where the type is defined.
///
/// @param vis the visibility of the type.
scope_type_decl::scope_type_decl(const environment* env,
const string& name,
size_t size_in_bits,
size_t alignment_in_bits,
const location& locus,
visibility vis)
: type_or_decl_base(env,
ABSTRACT_SCOPE_TYPE_DECL
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
decl_base(env, name, locus, "", vis),
type_base(env, size_in_bits, alignment_in_bits),
scope_decl(env, name, locus)
{}
/// Compares two instances of @ref scope_type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const scope_type_decl& l, const scope_type_decl& r, change_kind* k)
{
bool result = equals(static_cast<const scope_decl&>(l),
static_cast<const scope_decl&>(r),
k);
if (!k && !result)
ABG_RETURN_FALSE;
result &= equals(static_cast<const type_base&>(l),
static_cast<const type_base&>(r),
k);
ABG_RETURN(result);
}
/// Equality operator between two scope_type_decl.
///
/// Note that this function does not consider the scope of the scope
/// types themselves.
///
/// @return true iff both scope types are equal.
bool
scope_type_decl::operator==(const decl_base& o) const
{
const scope_type_decl* other = dynamic_cast<const scope_type_decl*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Equality operator between two scope_type_decl.
///
/// This re-uses the equality operator that takes a decl_base.
///
/// @param o the other scope_type_decl to compare against.
///
/// @return true iff both scope types are equal.
bool
scope_type_decl::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Traverses an instance of @ref scope_type_decl, visiting all the
/// sub-types and decls that it might contain.
///
/// @param v the visitor that is used to visit every IR sub-node of
/// the current node.
///
/// @return true if either
/// - all the children nodes of the current IR node were traversed
/// and the calling code should keep going with the traversing.
/// - or the current IR node is already being traversed.
/// Otherwise, returning false means that the calling code should not
/// keep traversing the tree.
bool
scope_type_decl::traverse(ir_node_visitor& v)
{
if (visiting())
return true;
if (v.type_node_has_been_visited(this))
return true;
if (v.visit_begin(this))
{
visiting(true);
for (scope_decl::declarations::const_iterator i =
get_member_decls().begin();
i != get_member_decls ().end();
++i)
if (!(*i)->traverse(v))
break;
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
scope_type_decl::~scope_type_decl()
{}
// </scope_type_decl definitions>
// <namespace_decl>
/// Constructor.
///
/// @param the environment we are operatin from.
///
/// @param name the name of the namespace.
///
/// @param locus the source location where the namespace is defined.
///
/// @param vis the visibility of the namespace.
namespace_decl::namespace_decl(const environment* env,
const string& name,
const location& locus,
visibility vis)
// We need to call the constructor of decl_base directly here
// because it is virtually inherited by scope_decl. Note that we
// just implicitely call the default constructor for scope_decl
// here, as what we really want is to initialize the decl_base
// subobject. Wow, virtual inheritance is useful, but setting it
// up is ugly.
: type_or_decl_base(env,
NAMESPACE_DECL
| ABSTRACT_DECL_BASE
| ABSTRACT_SCOPE_DECL),
decl_base(env, name, locus, "", vis),
scope_decl(env, name, locus)
{
runtime_type_instance(this);
}
/// Build and return a copy of the pretty representation of the
/// namespace.
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return a copy of the pretty representation of the namespace.
string
namespace_decl::get_pretty_representation(bool internal,
bool qualified_name) const
{
string r =
"namespace " + scope_decl::get_pretty_representation(internal,
qualified_name);
return r;
}
/// Return true iff both namespaces and their members are equal.
///
/// Note that this function does not check if the scope of these
/// namespaces are equal.
bool
namespace_decl::operator==(const decl_base& o) const
{
const namespace_decl* other = dynamic_cast<const namespace_decl*>(&o);
if (!other)
return false;
return scope_decl::operator==(*other);
}
/// Test if the current namespace_decl is empty or contains empty
/// namespaces itself.
///
/// @return true iff the current namespace_decl is empty or contains
/// empty itself.
bool
namespace_decl::is_empty_or_has_empty_sub_namespaces() const
{
if (is_empty())
return true;
for (declarations::const_iterator i = get_member_decls().begin();
i != get_member_decls().end();
++i)
{
if (!is_namespace(*i))
return false;
namespace_decl_sptr ns = is_namespace(*i);
ABG_ASSERT(ns);
if (!ns->is_empty_or_has_empty_sub_namespaces())
return false;
}
return true;
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on its
/// member nodes.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
namespace_decl::traverse(ir_node_visitor& v)
{
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
scope_decl::declarations::const_iterator i;
for (i = get_member_decls().begin();
i != get_member_decls ().end();
++i)
{
ir_traversable_base_sptr t =
dynamic_pointer_cast<ir_traversable_base>(*i);
if (t)
if (!t->traverse (v))
break;
}
visiting(false);
}
return v.visit_end(this);
}
namespace_decl::~namespace_decl()
{
}
// </namespace_decl>
// <qualified_type_def>
/// Type of the private data of qualified_type_def.
class qualified_type_def::priv
{
friend class qualified_type_def;
qualified_type_def::CV cv_quals_;
// Before the type is canonicalized, this is used as a temporary
// internal name.
interned_string temporary_internal_name_;
// Once the type is canonicalized, this is used as the internal
// name.
interned_string internal_name_;
weak_ptr<type_base> underlying_type_;
priv()
: cv_quals_(CV_NONE)
{}
priv(qualified_type_def::CV quals,
type_base_sptr t)
: cv_quals_(quals),
underlying_type_(t)
{}
priv(qualified_type_def::CV quals)
: cv_quals_(quals)
{}
};// end class qualified_type_def::priv
/// Build the name of the current instance of qualified type.
///
/// @param fully_qualified if true, build a fully qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the newly-built name.
string
qualified_type_def::build_name(bool fully_qualified, bool internal) const
{
type_base_sptr t = get_underlying_type();
if (!t)
// The qualified type might temporarily have no underlying type,
// especially during the construction of the type, while the
// underlying type is not yet constructed. In that case, let's do
// like if the underlying type is the 'void' type.
t = get_environment()->get_void_type();
return get_name_of_qualified_type(t, get_cv_quals(),
fully_qualified,
internal);
}
/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
qualified_type_def::on_canonical_type_set()
{clear_qualified_name();}
/// Constructor of the qualified_type_def
///
/// @param type the underlying type
///
/// @param quals a bitfield representing the const/volatile qualifiers
///
/// @param locus the location of the qualified type definition
qualified_type_def::qualified_type_def(type_base_sptr type,
CV quals,
const location& locus)
: type_or_decl_base(type->get_environment(),
QUALIFIED_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(type->get_environment(), type->get_size_in_bits(),
type->get_alignment_in_bits()),
decl_base(type->get_environment(), "", locus, "",
dynamic_pointer_cast<decl_base>(type)->get_visibility()),
priv_(new priv(quals, type))
{
runtime_type_instance(this);
interned_string name = type->get_environment()->intern(build_name(false));
set_name(name);
}
/// Constructor of the qualified_type_def
///
/// @param env the environment of the type.
///
/// @param quals a bitfield representing the const/volatile qualifiers
///
/// @param locus the location of the qualified type definition
qualified_type_def::qualified_type_def(environment* env,
CV quals,
const location& locus)
: type_or_decl_base(env,
QUALIFIED_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(env, /*size_in_bits=*/0,
/*alignment_in_bits=*/0),
decl_base(env, "", locus, ""),
priv_(new priv(quals))
{
runtime_type_instance(this);
// We don't yet have an underlying type. So for naming purpose,
// let's temporarily pretend the underlying type is 'void'.
interned_string name = env->intern("void");
set_name(name);
}
/// Get the size of the qualified type def.
///
/// This is an overload for type_base::get_size_in_bits().
///
/// @return the size of the qualified type.
size_t
qualified_type_def::get_size_in_bits() const
{
size_t s = 0;
if (type_base_sptr ut = get_underlying_type())
{
// We do have the underlying type properly set, so let's make
// the size of the qualified type match the size of its
// underlying type.
s = ut->get_size_in_bits();
if (s != type_base::get_size_in_bits())
const_cast<qualified_type_def*>(this)->set_size_in_bits(s);
}
return type_base::get_size_in_bits();
}
/// Compares two instances of @ref qualified_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const qualified_type_def& l, const qualified_type_def& r, change_kind* k)
{
bool result = true;
if (l.get_cv_quals() != r.get_cv_quals())
{
result = false;
if (k)
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
if (l.get_underlying_type() != r.get_underlying_type())
{
result = false;
if (k)
{
if (!types_have_similar_structure(l.get_underlying_type().get(),
r.get_underlying_type().get()))
// Underlying type changes in which the structure of the
// type changed are considered local changes to the
// qualified type.
*k |= LOCAL_TYPE_CHANGE_KIND;
else
*k |= SUBTYPE_CHANGE_KIND;
}
else
// okay strictly speaking this is not necessary, but I am
// putting it here to maintenance; that is, so that adding
// subsequent clauses needed to compare two qualified types
// later still works.
ABG_RETURN_FALSE;
}
ABG_RETURN(result);
}
/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const decl_base& o) const
{
const qualified_type_def* other =
dynamic_cast<const qualified_type_def*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
/// Also, this re-uses the equality operator above that takes a
/// decl_base.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
/// Also, this re-uses the equality operator above that takes a
/// decl_base.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const qualified_type_def& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Implementation for the virtual qualified name builder for @ref
/// qualified_type_def.
///
/// @param qualified_name the output parameter to hold the resulting
/// qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
void
qualified_type_def::get_qualified_name(interned_string& qualified_name,
bool internal) const
{qualified_name = get_qualified_name(internal);}
/// Implementation of the virtual qualified name builder/getter.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
qualified_type_def::get_qualified_name(bool internal) const
{
const environment* env = get_environment();
ABG_ASSERT(env);
if (!get_canonical_type())
{
// The type hasn't been canonicalized yet. We want to return a
// temporary name that is not cached because the structure of
// this type (and so its name) can change until its
// canonicalized.
if (internal)
{
// We are asked to return a temporary *internal* name.
// Lets compute it and return a reference to where it's
// stored.
priv_->temporary_internal_name_ =
env->intern(build_name(true, /*internal=*/true));
return priv_->temporary_internal_name_;
}
else
{
// We are asked to return a temporary non-internal name.
set_temporary_qualified_name
(env->intern(build_name(true, /*internal=*/false)));
return peek_temporary_qualified_name();
}
}
else
{
// The type has already been canonicalized. We want to return
// the definitive name and cache it.
if (internal)
{
if (priv_->internal_name_.empty())
priv_->internal_name_ =
env->intern(build_name(/*qualified=*/true,
/*internal=*/true));
return priv_->internal_name_;
}
else
{
if (peek_qualified_name().empty())
set_qualified_name
(env->intern(build_name(/*qualified=*/true,
/*internal=*/false)));
return peek_qualified_name();
}
}
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
qualified_type_def::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr t = get_underlying_type())
t->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
qualified_type_def::~qualified_type_def()
{
}
/// Getter of the const/volatile qualifier bit field
qualified_type_def::CV
qualified_type_def::get_cv_quals() const
{return priv_->cv_quals_;}
/// Setter of the const/value qualifiers bit field
void
qualified_type_def::set_cv_quals(CV cv_quals)
{priv_->cv_quals_ = cv_quals;}
/// Compute and return the string prefix or suffix representing the
/// qualifiers hold by the current instance of @ref
/// qualified_type_def.
///
/// @return the newly-built cv string.
string
qualified_type_def::get_cv_quals_string_prefix() const
{return get_string_representation_of_cv_quals(priv_->cv_quals_);}
/// Getter of the underlying type
type_base_sptr
qualified_type_def::get_underlying_type() const
{return priv_->underlying_type_.lock();}
/// Setter of the underlying type.
///
/// @param t the new underlying type.
void
qualified_type_def::set_underlying_type(const type_base_sptr& t)
{
ABG_ASSERT(t);
priv_->underlying_type_ = t;
// Now we need to update other properties that depend on the new underlying type.
set_size_in_bits(t->get_size_in_bits());
set_alignment_in_bits(t->get_alignment_in_bits());
interned_string name = get_environment()->intern(build_name(false));
set_name(name);
if (scope_decl* s = get_scope())
{
// Now that the name has been updated, we need to update the
// lookup maps accordingly.
scope_decl::declarations::iterator i;
if (s->find_iterator_for_member(this, i))
maybe_update_types_lookup_map(*i);
else
ABG_ASSERT_NOT_REACHED;
}
}
/// Non-member equality operator for @ref qualified_type_def
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r equals.
bool
operator==(const qualified_type_def_sptr& l, const qualified_type_def_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// Non-member inequality operator for @ref qualified_type_def
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r equals.
bool
operator!=(const qualified_type_def_sptr& l, const qualified_type_def_sptr& r)
{return ! operator==(l, r);}
/// Overloaded bitwise OR operator for cv qualifiers.
qualified_type_def::CV
operator|(qualified_type_def::CV lhs, qualified_type_def::CV rhs)
{
return static_cast<qualified_type_def::CV>
(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
}
/// Overloaded bitwise |= operator for cv qualifiers.
qualified_type_def::CV&
operator|=(qualified_type_def::CV& l, qualified_type_def::CV r)
{
l = l | r;
return l;
}
/// Overloaded bitwise AND operator for CV qualifiers.
qualified_type_def::CV
operator&(qualified_type_def::CV lhs, qualified_type_def::CV rhs)
{
return static_cast<qualified_type_def::CV>
(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
}
/// Overloaded bitwise inverting operator for CV qualifiers.
qualified_type_def::CV
operator~(qualified_type_def::CV q)
{return static_cast<qualified_type_def::CV>(~static_cast<unsigned>(q));}
/// Streaming operator for qualified_type_decl::CV
///
/// @param o the output stream to serialize the cv qualifier to.
///
/// @param cv the cv qualifier to serialize.
///
/// @return the output stream used.
std::ostream&
operator<<(std::ostream& o, qualified_type_def::CV cv)
{
string str;
switch (cv)
{
case qualified_type_def::CV_NONE:
str = "none";
break;
case qualified_type_def::CV_CONST:
str = "const";
break;
case qualified_type_def::CV_VOLATILE:
str = "volatile";
break;
case qualified_type_def::CV_RESTRICT:
str = "restrict";
break;
}
o << str;
return o;
}
// </qualified_type_def>
//<pointer_type_def definitions>
/// Private data structure of the @ref pointer_type_def.
struct pointer_type_def::priv
{
type_base_wptr pointed_to_type_;
type_base* naked_pointed_to_type_;
interned_string internal_qualified_name_;
interned_string temp_internal_qualified_name_;
priv(const type_base_sptr& t)
: pointed_to_type_(type_or_void(t, 0)),
naked_pointed_to_type_(t.get())
{}
priv()
: naked_pointed_to_type_()
{}
}; //end struct pointer_type_def
/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
pointer_type_def::on_canonical_type_set()
{clear_qualified_name();}
///Constructor of @ref pointer_type_def.
///
/// @param pointed_to the pointed-to type.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param align_in_bits the alignment of the type, in bits.
///
/// @param locus the source location where the type was defined.
pointer_type_def::pointer_type_def(const type_base_sptr& pointed_to,
size_t size_in_bits,
size_t align_in_bits,
const location& locus)
: type_or_decl_base(pointed_to->get_environment(),
POINTER_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(pointed_to->get_environment(), size_in_bits, align_in_bits),
decl_base(pointed_to->get_environment(), "", locus, ""),
priv_(new priv(pointed_to))
{
runtime_type_instance(this);
try
{
ABG_ASSERT(pointed_to);
const environment* env = pointed_to->get_environment();
decl_base_sptr pto = dynamic_pointer_cast<decl_base>(pointed_to);
string name = (pto ? pto->get_name() : string("void")) + "*";
set_name(env->intern(name));
if (pto)
set_visibility(pto->get_visibility());
}
catch (...)
{}
}
///Constructor of @ref pointer_type_def.
///
/// @param env the environment of the type.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param align_in_bits the alignment of the type, in bits.
///
/// @param locus the source location where the type was defined.
pointer_type_def::pointer_type_def(environment* env, size_t size_in_bits,
size_t alignment_in_bits,
const location& locus)
: type_or_decl_base(env,
POINTER_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(env, size_in_bits, alignment_in_bits),
decl_base(env, "", locus, ""),
priv_(new priv())
{
runtime_type_instance(this);
string name = string("void") + "*";
set_name(env->intern(name));
}
/// Set the pointed-to type of the pointer.
///
/// @param t the new pointed-to type.
void
pointer_type_def::set_pointed_to_type(const type_base_sptr& t)
{
ABG_ASSERT(t);
priv_->pointed_to_type_ = t;
priv_->naked_pointed_to_type_ = t.get();
try
{
const environment* env = t->get_environment();
ABG_ASSERT(get_environment() == env);
decl_base_sptr pto = dynamic_pointer_cast<decl_base>(t);
string name = (pto ? pto->get_name() : string("void")) + "*";
set_name(env->intern(name));
if (pto)
set_visibility(pto->get_visibility());
}
catch (...)
{}
}
/// Compares two instances of @ref pointer_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const pointer_type_def& l, const pointer_type_def& r, change_kind* k)
{
bool result = l.get_pointed_to_type() == r.get_pointed_to_type();
if (!result)
if (k)
{
if (!types_have_similar_structure(&l, &r))
// pointed-to type changes in which the structure of the
// type changed are considered local changes to the pointer
// type.
*k |= LOCAL_TYPE_CHANGE_KIND;
*k |= SUBTYPE_CHANGE_KIND;
}
ABG_RETURN(result);
}
/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the this
/// types.
bool
pointer_type_def::operator==(const decl_base& o) const
{
const pointer_type_def* other = is_pointer_type(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the
/// types.
///
/// @param other the other type to compare against.
///
/// @return true iff @p other equals the current instance.
bool
pointer_type_def::operator==(const type_base& other) const
{
const decl_base* o = is_decl(&other);
if (!o)
return false;
return *this == *o;
}
/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the
/// types.
///
/// @param other the other type to compare against.
///
/// @return true iff @p other equals the current instance.
bool
pointer_type_def::operator==(const pointer_type_def& other) const
{
const decl_base& o = other;
return *this == o;
}
/// Getter of the pointed-to type.
///
/// @return the pointed-to type.
const type_base_sptr
pointer_type_def::get_pointed_to_type() const
{return priv_->pointed_to_type_.lock();}
/// Getter of a naked pointer to the pointed-to type.
///
/// @return a naked pointed to the pointed-to type.
type_base*
pointer_type_def::get_naked_pointed_to_type() const
{return priv_->naked_pointed_to_type_;}
/// Build and return the qualified name of the current instance of
/// @ref pointer_type_def.
///
/// @param qn output parameter. The resulting qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
void
pointer_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}
/// Build, cache and return the qualified name of the current instance
/// of @ref pointer_type_def. Subsequent invocations of this function
/// return the cached value.
///
/// Note that this function should work even if the underlying type is
/// momentarily empty.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
pointer_type_def::get_qualified_name(bool internal) const
{
type_base* pointed_to_type = get_naked_pointed_to_type();
if (internal)
{
if (get_canonical_type())
{
if (priv_->internal_qualified_name_.empty())
if (pointed_to_type)
priv_->internal_qualified_name_ =
get_name_of_pointer_to_type(*pointed_to_type,
/*qualified_name=*/true,
/*internal=*/true);
return priv_->internal_qualified_name_;
}
else
{
// As the type hasn't yet been canonicalized, its structure
// (and so its name) can change. So let's invalidate the
// cache where we store its name at each invocation of this
// function.
if (pointed_to_type)
priv_->temp_internal_qualified_name_ =
get_name_of_pointer_to_type(*pointed_to_type,
/*qualified_name=*/true,
/*internal=*/true);
return priv_->temp_internal_qualified_name_;
}
}
else
{
if (get_naked_canonical_type())
{
if (decl_base::peek_qualified_name().empty())
set_qualified_name
(get_name_of_pointer_to_type(*pointed_to_type,
/*qualified_name=*/true,
/*internal=*/false));
return decl_base::peek_qualified_name();
}
else
{
// As the type hasn't yet been canonicalized, its structure
// (and so its name) can change. So let's invalidate the
// cache where we store its name at each invocation of this
// function.
if (pointed_to_type)
set_qualified_name
(get_name_of_pointer_to_type(*pointed_to_type,
/*qualified_name=*/true,
/*internal=*/false));
return decl_base::peek_qualified_name();
}
}
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
pointer_type_def::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr t = get_pointed_to_type())
t->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
pointer_type_def::~pointer_type_def()
{}
/// Turn equality of shared_ptr of @ref pointer_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref pointer_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref pointer_type_def on
/// right-hand-side of the equality.
///
/// @return true if the @ref pointer_type_def pointed to by the
/// shared_ptrs are equal, false otherwise.
bool
operator==(const pointer_type_def_sptr& l, const pointer_type_def_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// Turn inequality of shared_ptr of @ref pointer_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref pointer_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref pointer_type_def on
/// right-hand-side of the equality.
///
/// @return true iff the @ref pointer_type_def pointed to by the
/// shared_ptrs are different.
bool
operator!=(const pointer_type_def_sptr& l, const pointer_type_def_sptr& r)
{return !operator==(l, r);}
// </pointer_type_def definitions>
// <reference_type_def definitions>
/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
reference_type_def::on_canonical_type_set()
{clear_qualified_name();}
/// Constructor of the reference_type_def type.
///
/// @param pointed_to the pointed to type.
///
/// @param lvalue wether the reference is an lvalue reference. If
/// false, the reference is an rvalue one.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param align_in_bits the alignment of the type, in bits.
///
/// @param locus the source location of the type.
reference_type_def::reference_type_def(const type_base_sptr pointed_to,
bool lvalue,
size_t size_in_bits,
size_t align_in_bits,
const location& locus)
: type_or_decl_base(pointed_to->get_environment(),
REFERENCE_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(pointed_to->get_environment(), size_in_bits, align_in_bits),
decl_base(pointed_to->get_environment(), "", locus, ""),
is_lvalue_(lvalue)
{
runtime_type_instance(this);
try
{
decl_base_sptr pto = dynamic_pointer_cast<decl_base>(pointed_to);
string name;
if (pto)
{
set_visibility(pto->get_visibility());
name = string(pto->get_name()) + "&";
}
else
name = string(get_type_name(is_function_type(pointed_to),
/*qualified_name=*/true)) + "&";
if (!is_lvalue())
name += "&";
environment* env = pointed_to->get_environment();
ABG_ASSERT(env);
set_name(env->intern(name));
pointed_to_type_ = type_base_wptr(type_or_void(pointed_to, 0));
}
catch (...)
{}
}
/// Constructor of the reference_type_def type.
///
/// This one creates a type that has no pointed-to type, temporarily.
/// This is useful for cases where the underlying type is not yet
/// available. It can be set later using
/// reference_type_def::set_pointed_to_type().
///
/// @param env the environment of the type.
///
/// @param lvalue wether the reference is an lvalue reference. If
/// false, the reference is an rvalue one.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param align_in_bits the alignment of the type, in bits.
///
/// @param locus the source location of the type.
reference_type_def::reference_type_def(const environment* env, bool lvalue,
size_t size_in_bits,
size_t alignment_in_bits,
const location& locus)
: type_or_decl_base(env,
REFERENCE_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(env, size_in_bits, alignment_in_bits),
decl_base(env, "", locus, ""),
is_lvalue_(lvalue)
{
runtime_type_instance(this);
string name = "void&";
if (!is_lvalue())
name += "&";
ABG_ASSERT(env);
set_name(env->intern(name));
pointed_to_type_ = type_base_wptr(env->get_void_type());
}
/// Setter of the pointed_to type of the current reference type.
///
/// @param pointed_to the new pointed to type.
void
reference_type_def::set_pointed_to_type(type_base_sptr& pointed_to_type)
{
ABG_ASSERT(pointed_to_type);
pointed_to_type_ = pointed_to_type;
decl_base_sptr pto;
try
{pto = dynamic_pointer_cast<decl_base>(pointed_to_type);}
catch (...)
{}
if (pto)
{
set_visibility(pto->get_visibility());
string name = string(pto->get_name()) + "&";
if (!is_lvalue())
name += "&";
environment* env = pto->get_environment();
ABG_ASSERT(env && env == get_environment());
set_name(env->intern(name));
}
}
/// Compares two instances of @ref reference_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const reference_type_def& l, const reference_type_def& r, change_kind* k)
{
if (l.is_lvalue() != r.is_lvalue())
{
if (k)
*k |= LOCAL_TYPE_CHANGE_KIND;
ABG_RETURN_FALSE;
}
// Compare the pointed-to-types modulo the typedefs they might have
bool result = (l.get_pointed_to_type() == r.get_pointed_to_type());
if (!result)
if (k)
{
if (!types_have_similar_structure(&l, &r))
*k |= LOCAL_TYPE_CHANGE_KIND;
*k |= SUBTYPE_CHANGE_KIND;
}
ABG_RETURN(result);
}
/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const decl_base& o) const
{
const reference_type_def* other =
dynamic_cast<const reference_type_def*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const reference_type_def& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
type_base_sptr
reference_type_def::get_pointed_to_type() const
{return pointed_to_type_.lock();}
bool
reference_type_def::is_lvalue() const
{return is_lvalue_;}
/// Build and return the qualified name of the current instance of the
/// @ref reference_type_def.
///
/// @param qn output parameter. Is set to the newly-built qualified
/// name of the current instance of @ref reference_type_def.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
void
reference_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}
/// Build, cache and return the qualified name of the current instance
/// of the @ref reference_type_def. Subsequent invocations of this
/// function return the cached value.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the newly-built qualified name of the current instance of
/// @ref reference_type_def.
const interned_string&
reference_type_def::get_qualified_name(bool internal) const
{
if (peek_qualified_name().empty()
|| !get_canonical_type())
set_qualified_name(get_name_of_reference_to_type(*get_pointed_to_type(),
is_lvalue(),
/*qualified_name=*/true,
internal));
return peek_qualified_name();
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
reference_type_def::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr t = get_pointed_to_type())
t->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
reference_type_def::~reference_type_def()
{}
/// Turn equality of shared_ptr of @ref reference_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref reference_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref reference_type_def on
/// right-hand-side of the equality.
///
/// @return true if the @ref reference_type_def pointed to by the
/// shared_ptrs are equal, false otherwise.
bool
operator==(const reference_type_def_sptr& l, const reference_type_def_sptr& r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
/// Turn inequality of shared_ptr of @ref reference_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref reference_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref reference_type_def on
/// right-hand-side of the equality.
///
/// @return true iff the @ref reference_type_def pointed to by the
/// shared_ptrs are different.
bool
operator!=(const reference_type_def_sptr& l, const reference_type_def_sptr& r)
{return !operator==(l, r);}
// </reference_type_def definitions>
// <array_type_def definitions>
// <array_type_def::subrange_type>
array_type_def::subrange_type::~subrange_type() = default;
// <array_type_def::subrante_type::bound_value>
/// Default constructor of the @ref
/// array_type_def::subrange_type::bound_value class.
///
/// Constructs an unsigned bound_value of value zero.
array_type_def::subrange_type::bound_value::bound_value()
: s_(UNSIGNED_SIGNEDNESS)
{
v_.unsigned_ = 0;
}
/// Initialize an unsigned bound_value with a given value.
///
/// @param v the initial bound value.
array_type_def::subrange_type::bound_value::bound_value(uint64_t v)
: s_(UNSIGNED_SIGNEDNESS)
{
v_.unsigned_ = v;
}
/// Initialize a signed bound_value with a given value.
///
/// @param v the initial bound value.
array_type_def::subrange_type::bound_value::bound_value(int64_t v)
: s_(SIGNED_SIGNEDNESS)
{
v_.signed_ = v;
}
/// Getter of the signedness (unsigned VS signed) of the bound value.
///
/// @return the signedness of the bound value.
enum array_type_def::subrange_type::bound_value::signedness
array_type_def::subrange_type::bound_value::get_signedness() const
{return s_;}
/// Setter of the signedness (unsigned VS signed) of the bound value.
///
/// @param s the new signedness of the bound value.
void
array_type_def::subrange_type::bound_value::set_signedness(enum signedness s)
{ s_ = s;}
/// Getter of the bound value as a signed value.
///
/// @return the bound value as signed.
int64_t
array_type_def::subrange_type::bound_value::get_signed_value() const
{return v_.signed_;
}
/// Getter of the bound value as an unsigned value.
///
/// @return the bound value as unsigned.
uint64_t
array_type_def::subrange_type::bound_value::get_unsigned_value()
{return v_.unsigned_;}
/// Setter of the bound value as unsigned.
///
/// @param v the new unsigned value.
void
array_type_def::subrange_type::bound_value::set_unsigned(uint64_t v)
{
s_ = UNSIGNED_SIGNEDNESS;
v_.unsigned_ = v;
}
/// Setter of the bound value as signed.
///
/// @param v the new signed value.
void
array_type_def::subrange_type::bound_value::set_signed(int64_t v)
{
s_ = SIGNED_SIGNEDNESS;
v_.signed_ = v;
}
/// Equality operator of the bound value.
///
/// @param v the other bound value to compare with.
///
/// @return true iff the current bound value equals @p v.
bool
array_type_def::subrange_type::bound_value::operator==(const bound_value& v) const
{
return s_ == v.s_ && v_.unsigned_ == v.v_.unsigned_;
}
// </array_type_def::subrante_type::bound_value>
struct array_type_def::subrange_type::priv
{
bound_value lower_bound_;
bound_value upper_bound_;
type_base_wptr underlying_type_;
translation_unit::language language_;
bool infinite_;
priv(bound_value ub,
translation_unit::language l = translation_unit::LANG_C11)
: upper_bound_(ub), language_(l), infinite_(false)
{}
priv(bound_value lb, bound_value ub,
translation_unit::language l = translation_unit::LANG_C11)
: lower_bound_(lb), upper_bound_(ub),
language_(l), infinite_(false)
{}
priv(bound_value lb, bound_value ub, const type_base_sptr &u,
translation_unit::language l = translation_unit::LANG_C11)
: lower_bound_(lb), upper_bound_(ub), underlying_type_(u),
language_(l), infinite_(false)
{}
};
/// Constructor of an array_type_def::subrange_type type.
///
/// @param env the environment this type was created from.
///
/// @param name the name of the subrange type.
///
/// @param lower_bound the lower bound of the array. This is
/// generally zero (at least for C and C++).
///
/// @param upper_bound the upper bound of the array.
///
/// @param underlying_type the underlying type of the subrange type.
///
/// @param loc the source location where the type is defined.
array_type_def::subrange_type::subrange_type(const environment* env,
const string& name,
bound_value lower_bound,
bound_value upper_bound,
const type_base_sptr& utype,
const location& loc,
translation_unit::language l)
: type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
type_base(env,
upper_bound.get_unsigned_value()
- lower_bound.get_unsigned_value(),
0),
decl_base(env, name, loc, ""),
priv_(new priv(lower_bound, upper_bound, utype, l))
{
runtime_type_instance(this);
}
/// Constructor of the array_type_def::subrange_type type.
///
/// @param env the environment this type is being created in.
///
/// @param name the name of the subrange type.
///
/// @param lower_bound the lower bound of the array. This is
/// generally zero (at least for C and C++).
///
/// @param upper_bound the upper bound of the array.
///
/// @param loc the source location where the type is defined.
///
/// @param l the language that generated this subrange.
array_type_def::subrange_type::subrange_type(const environment* env,
const string& name,
bound_value lower_bound,
bound_value upper_bound,
const location& loc,
translation_unit::language l)
: type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
type_base(env,
upper_bound.get_unsigned_value()
- lower_bound.get_unsigned_value(), 0),
decl_base(env, name, loc, ""),
priv_(new priv(lower_bound, upper_bound, l))
{
runtime_type_instance(this);
}
/// Constructor of the array_type_def::subrange_type type.
///
/// @param env the environment this type is being created from.
///
/// @param name of the name of type.
///
/// @param upper_bound the upper bound of the array. The lower bound
/// is considered to be zero.
///
/// @param loc the source location of the type.
///
/// @param the language that generated this type.
array_type_def::subrange_type::subrange_type(const environment* env,
const string& name,
bound_value upper_bound,
const location& loc,
translation_unit::language l)
: type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
type_base(env, upper_bound.get_unsigned_value(), 0),
decl_base(env, name, loc, ""),
priv_(new priv(upper_bound, l))
{
runtime_type_instance(this);
}
/// Getter of the underlying type of the subrange, that is, the type
/// that defines the range.
///
/// @return the underlying type.
type_base_sptr
array_type_def::subrange_type::get_underlying_type() const
{return priv_->underlying_type_.lock();}
/// Setter of the underlying type of the subrange, that is, the type
/// that defines the range.
///
/// @param u the new underlying type.
void
array_type_def::subrange_type::set_underlying_type(const type_base_sptr &u)
{
ABG_ASSERT(priv_->underlying_type_.expired());
priv_->underlying_type_ = u;
}
/// Getter of the upper bound of the subrange type.
///
/// @return the upper bound of the subrange type.
int64_t
array_type_def::subrange_type::get_upper_bound() const
{return priv_->upper_bound_.get_signed_value();}
/// Getter of the lower bound of the subrange type.
///
/// @return the lower bound of the subrange type.
int64_t
array_type_def::subrange_type::get_lower_bound() const
{return priv_->lower_bound_.get_signed_value();}
/// Setter of the upper bound of the subrange type.
///
/// @param ub the new value of the upper bound.
void
array_type_def::subrange_type::set_upper_bound(int64_t ub)
{priv_->upper_bound_ = ub;}
/// Setter of the lower bound.
///
/// @param lb the new value of the lower bound.
void
array_type_def::subrange_type::set_lower_bound(int64_t lb)
{priv_->lower_bound_ = lb;}
/// Getter of the length of the subrange type.
///
/// Note that a length of zero means the array has an infinite (or
/// rather a non-known) size.
///
/// @return the length of the subrange type.
uint64_t
array_type_def::subrange_type::get_length() const
{
if (is_infinite())
return 0;
ABG_ASSERT(get_upper_bound() >= get_lower_bound());
return get_upper_bound() - get_lower_bound() + 1;
}
/// Test if the length of the subrange type is infinite.
///
/// @return true iff the length of the subrange type is infinite.
bool
array_type_def::subrange_type::is_infinite() const
{return priv_->infinite_;}
/// Set the infinite-ness status of the subrange type.
///
/// @param f true iff the length of the subrange type should be set to
/// being infinite.
void
array_type_def::subrange_type::is_infinite(bool f)
{priv_->infinite_ = f;}
/// Getter of the language that generated this type.
///
/// @return the language of this type.
translation_unit::language
array_type_def::subrange_type::get_language() const
{return priv_->language_;}
/// Return a string representation of the sub range.
///
/// @return the string representation of the sub range.
string
array_type_def::subrange_type::as_string() const
{
std::ostringstream o;
if (is_ada_language(get_language()))
{
type_base_sptr underlying_type = get_underlying_type();
if (underlying_type)
o << ir::get_pretty_representation(underlying_type, false) << " ";
o << "range "<< get_lower_bound() << " .. " << get_upper_bound();
}
else if (is_infinite())
o << "[]";
else
o << "[" << get_length() << "]";
return o.str();
}
/// Return a string representation of a vector of subranges
///
/// @return the string representation of a vector of sub ranges.
string
array_type_def::subrange_type::vector_as_string(const vector<subrange_sptr>& v)
{
if (v.empty())
return "[]";
string r;
for (vector<subrange_sptr>::const_iterator i = v.begin();
i != v.end();
++i)
r += (*i)->as_string();
return r;
}
/// Compares two isntances of @ref array_type_def::subrange_type.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const array_type_def::subrange_type& l,
const array_type_def::subrange_type& r,
change_kind* k)
{
bool result = true;
if (l.get_lower_bound() != r.get_lower_bound()
|| l.get_upper_bound() != r.get_upper_bound()
|| l.get_name() != r.get_name())
{
result = false;
if (k)
*k |= LOCAL_TYPE_CHANGE_KIND;
else
ABG_RETURN(result);
}
#if 0
// If we enable this, we need to update the reporting code too, to
// report changes about range underlying types too.
if (l.get_underlying_type() != r.get_underlying_type())
{
result = false;
if (k)
{
if (!types_have_similar_structure(l.get_underlying_type().get(),
r.get_underlying_type().get()))
*k |= LOCAL_TYPE_CHANGE_KIND;
else
*k |= SUBTYPE_CHANGE_KIND;
}
else
ABG_RETURN(result);
}
#endif
ABG_RETURN(result);
}
/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const decl_base& o) const
{
const subrange_type* other =
dynamic_cast<const subrange_type*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const subrange_type& o) const
{
const type_base &t = o;
return operator==(t);
}
/// Inequality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o is different from the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator!=(const subrange_type& o) const
{return !operator==(o);}
/// Build a pretty representation for an
/// array_type_def::subrange_type.
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
///
/// @return a copy of the pretty representation of the current
/// instance of typedef_decl.
string
array_type_def::subrange_type::get_pretty_representation(bool, bool) const
{
string name = get_name();
string repr;
if (name.empty())
repr += "<anonymous range>";
else
repr += "<range " + get_name() + ">";
repr += as_string();
return repr;
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
array_type_def::subrange_type::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr u = get_underlying_type())
u->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
// </array_type_def::subrange_type>
struct array_type_def::priv
{
type_base_wptr element_type_;
subranges_type subranges_;
interned_string temp_internal_qualified_name_;
interned_string internal_qualified_name_;
priv(type_base_sptr t)
: element_type_(t)
{}
priv(type_base_sptr t, subranges_type subs)
: element_type_(t), subranges_(subs)
{}
priv()
{}
};
/// Constructor for the type array_type_def
///
/// Note how the constructor expects a vector of subrange
/// objects. Parsing of the array information always entails
/// parsing the subrange info as well, thus the class subrange_type
/// is defined inside class array_type_def and also parsed
/// simultaneously.
///
/// @param e_type the type of the elements contained in the array
///
/// @param subs a vector of the array's subranges(dimensions)
///
/// @param locus the source location of the array type definition.
array_type_def::array_type_def(const type_base_sptr e_type,
const std::vector<subrange_sptr>& subs,
const location& locus)
: type_or_decl_base(e_type->get_environment(),
ARRAY_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(e_type->get_environment(), 0, e_type->get_alignment_in_bits()),
decl_base(e_type->get_environment(), locus),
priv_(new priv(e_type))
{
runtime_type_instance(this);
append_subranges(subs);
}
/// Constructor for the type array_type_def
///
/// This constructor builds a temporary array that has no element type
/// associated. Later when the element type is available, it be set
/// with the array_type_def::set_element_type() member function.
///
/// Note how the constructor expects a vector of subrange
/// objects. Parsing of the array information always entails
/// parsing the subrange info as well, thus the class subrange_type
/// is defined inside class array_type_def and also parsed
/// simultaneously.
///
/// @param env the environment of the array type.
///
/// @param subs a vector of the array's subranges(dimensions)
///
/// @param locus the source location of the array type definition.
array_type_def::array_type_def(environment* env,
const std::vector<subrange_sptr>& subs,
const location& locus)
: type_or_decl_base(env,
ARRAY_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(env, 0, 0),
decl_base(env, locus),
priv_(new priv)
{
runtime_type_instance(this);
append_subranges(subs);
}
/// Update the size of the array.
///
/// This function computes the size of the array and sets it using
/// type_base::set_size_in_bits().
void
array_type_def::update_size()
{
type_base_sptr e = priv_->element_type_.lock();
if (e)
{
size_t s = e->get_size_in_bits();
if (s)
{
for (const auto &sub : get_subranges())
s *= sub->get_length();
const environment* env = e->get_environment();
ABG_ASSERT(env);
set_size_in_bits(s);
}
set_alignment_in_bits(e->get_alignment_in_bits());
}
}
string
array_type_def::get_subrange_representation() const
{
string r = subrange_type::vector_as_string(get_subranges());
return r;
}
/// Get the string representation of an @ref array_type_def.
///
/// @param a the array type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
static string
get_type_representation(const array_type_def& a, bool internal)
{
type_base_sptr e_type = a.get_element_type();
decl_base_sptr d = get_type_declaration(e_type);
string r;
if (is_ada_language(a.get_language()))
{
std::ostringstream o;
o << "array ("
<< a.get_subrange_representation()
<< ") of "
<< e_type ? e_type->get_pretty_representation(internal):string("void");
}
else
{
if (internal)
r = (e_type
? get_type_name(e_type,
/*qualified=*/true,
/*internal=*/true)
: string("void"))
+ a.get_subrange_representation();
else
r = (e_type
? get_type_name(e_type, /*qualified=*/false, /*internal=*/false)
: string("void"))
+ a.get_subrange_representation();
}
return r;
}
/// Get the pretty representation of the current instance of @ref
/// array_type_def.
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the ABI artifact.
string
array_type_def::get_pretty_representation(bool internal,
bool /*qualified_name*/) const
{return get_type_representation(*this, internal);}
/// Compares two instances of @ref array_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const array_type_def& l, const array_type_def& r, change_kind* k)
{
std::vector<array_type_def::subrange_sptr > this_subs = l.get_subranges();
std::vector<array_type_def::subrange_sptr > other_subs = r.get_subranges();
bool result = true;
if (this_subs.size() != other_subs.size())
{
result = false;
if (k)
*k |= LOCAL_TYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
std::vector<array_type_def::subrange_sptr >::const_iterator i,j;
for (i = this_subs.begin(), j = other_subs.begin();
i != this_subs.end() && j != other_subs.end();
++i, ++j)
if (**i != **j)
{
result = false;
if (k)
{
*k |= LOCAL_TYPE_CHANGE_KIND;
break;
}
else
ABG_RETURN_FALSE;
}
// Compare the element types modulo the typedefs they might have
if (l.get_element_type() != r.get_element_type())
{
result = false;
if (k)
*k |= SUBTYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
ABG_RETURN(result);
}
/// Test if two variables are equals modulo CV qualifiers.
///
/// @param l the first array of the comparison.
///
/// @param r the second array of the comparison.
///
/// @return true iff @p l equals @p r or, if they are different, the
/// difference between the too is just a matter of CV qualifiers.
bool
equals_modulo_cv_qualifier(const array_type_def* l, const array_type_def* r)
{
if (l == r)
return true;
if (!l || !r)
ABG_RETURN_FALSE;
l = is_array_type(peel_qualified_or_typedef_type(l));
r = is_array_type(peel_qualified_or_typedef_type(r));
std::vector<array_type_def::subrange_sptr > this_subs = l->get_subranges();
std::vector<array_type_def::subrange_sptr > other_subs = r->get_subranges();
if (this_subs.size() != other_subs.size())
ABG_RETURN_FALSE;
std::vector<array_type_def::subrange_sptr >::const_iterator i,j;
for (i = this_subs.begin(), j = other_subs.begin();
i != this_subs.end() && j != other_subs.end();
++i, ++j)
if (**i != **j)
ABG_RETURN_FALSE;
type_base *first_element_type =
peel_qualified_or_typedef_type(l->get_element_type().get());
type_base *second_element_type =
peel_qualified_or_typedef_type(r->get_element_type().get());
if (*first_element_type != *second_element_type)
ABG_RETURN_FALSE;
return true;
}
/// Get the language of the array.
///
/// @return the language of the array.
translation_unit::language
array_type_def::get_language() const
{
const std::vector<subrange_sptr>& subranges =
get_subranges();
if (subranges.empty())
return translation_unit::LANG_C11;
return subranges.front()->get_language();
}
bool
array_type_def::operator==(const decl_base& o) const
{
const array_type_def* other =
dynamic_cast<const array_type_def*>(&o);
if (!other)
return false;
return try_canonical_compare(this, other);
}
bool
array_type_def::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Getter of the type of an array element.
///
/// @return the type of an array element.
const type_base_sptr
array_type_def::get_element_type() const
{return priv_->element_type_.lock();}
/// Setter of the type of array element.
///
/// Beware that after using this function, one might want to
/// re-compute the canonical type of the array, if one has already
/// been computed.
///
/// The intended use of this method is to permit in-place adjustment
/// of the element type's qualifiers. In particular, the size of the
/// element type should not be changed.
///
/// @param element_type the new element type to set.
void
array_type_def::set_element_type(const type_base_sptr& element_type)
{
priv_->element_type_ = element_type;
update_size();
set_name(get_environment()->intern(get_pretty_representation()));
}
/// Append subranges from the vector @param subs to the current
/// vector of subranges.
void
array_type_def::append_subranges(const std::vector<subrange_sptr>& subs)
{
for (const auto &sub : subs)
priv_->subranges_.push_back(sub);
update_size();
set_name(get_environment()->intern(get_pretty_representation()));
}
/// @return true if one of the sub-ranges of the array is infinite, or
/// if the array has no sub-range at all, also meaning that the size
/// of the array is infinite.
bool
array_type_def::is_infinite() const
{
if (priv_->subranges_.empty())
return true;
for (std::vector<shared_ptr<subrange_type> >::const_iterator i =
priv_->subranges_.begin();
i != priv_->subranges_.end();
++i)
if ((*i)->is_infinite())
return true;
return false;
}
int
array_type_def::get_dimension_count() const
{return priv_->subranges_.size();}
/// Build and return the qualified name of the current instance of the
/// @ref array_type_def.
///
/// @param qn output parameter. Is set to the newly-built qualified
/// name of the current instance of @ref array_type_def.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
void
array_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}
/// Compute the qualified name of the array.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise. If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
array_type_def::get_qualified_name(bool internal) const
{
const environment* env = get_environment();
ABG_ASSERT(env);
if (internal)
{
if (get_canonical_type())
{
if (priv_->internal_qualified_name_.empty())
priv_->internal_qualified_name_ =
env->intern(get_type_representation(*this, /*internal=*/true));
return priv_->internal_qualified_name_;
}
else
{
priv_->temp_internal_qualified_name_ =
env->intern(get_type_representation(*this, /*internal=*/true));
return priv_->temp_internal_qualified_name_;
}
}
else
{
if (get_canonical_type())
{
if (decl_base::peek_qualified_name().empty())
set_qualified_name(env->intern(get_type_representation
(*this, /*internal=*/false)));
return decl_base::peek_qualified_name();
}
else
{
set_temporary_qualified_name(env->intern(get_type_representation
(*this,
/*internal=*/false)));
return decl_base::peek_temporary_qualified_name();
}
}
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
array_type_def::traverse(ir_node_visitor& v)
{
if (v.type_node_has_been_visited(this))
return true;
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr t = get_element_type())
t->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
const location&
array_type_def::get_location() const
{return decl_base::get_location();}
/// Get the array's subranges
const std::vector<array_type_def::subrange_sptr>&
array_type_def::get_subranges() const
{return priv_->subranges_;}
array_type_def::~array_type_def()
{}
// </array_type_def definitions>
// <enum_type_decl definitions>
class enum_type_decl::priv
{
type_base_sptr underlying_type_;
enumerators enumerators_;
friend class enum_type_decl;
priv();
public:
priv(type_base_sptr underlying_type,
enumerators& enumerators)
: underlying_type_(underlying_type),
enumerators_(enumerators)
{}
}; // end class enum_type_decl::priv
/// Constructor.
///
/// @param name the name of the type declaration.
///
/// @param locus the source location where the type was defined.
///
/// @param underlying_type the underlying type of the enum.
///
/// @param enums the enumerators of this enum type.
///
/// @param linkage_name the linkage name of the enum.
///
/// @param vis the visibility of the enum type.
enum_type_decl::enum_type_decl(const string& name,
const location& locus,
type_base_sptr underlying_type,
enumerators& enums,
const string& linkage_name,
visibility vis)
: type_or_decl_base(underlying_type->get_environment(),
ENUM_TYPE
| ABSTRACT_TYPE_BASE
| ABSTRACT_DECL_BASE),
type_base(underlying_type->get_environment(),
underlying_type->get_size_in_bits(),
underlying_type->get_alignment_in_bits()),
decl_base(underlying_type->get_environment(),
name, locus, linkage_name, vis),
priv_(new priv(underlying_type, enums))
{
runtime_type_instance(this);
for (enumerators::iterator e = get_enumerators().begin();
e != get_enumerators().end();
++e)
e->set_enum_type(this);
}
/// Return the underlying type of the enum.
type_base_sptr
enum_type_decl::get_underlying_type() const
{return priv_->underlying_type_;}
/// @return the list of enumerators of the enum.
const enum_type_decl::enumerators&
enum_type_decl::get_enumerators() const
{return priv_->enumerators_;}
/// @return the list of enumerators of the enum.
enum_type_decl::enumerators&
enum_type_decl::get_enumerators()
{return priv_->enumerators_;}
/// Get the pretty representation of the current instance of @ref
/// enum_type_decl.
///
/// @param internal set to true if the call is intended to get a
/// representation of the decl (or type) for the purpose of canonical
/// type comparison. This is mainly used in the function
/// type_base::get_canonical_type_for().
///
/// In other words if the argument for this parameter is true then the
/// call is meant for internal use (for technical use inside the
/// library itself), false otherwise. If you don't know what this is
/// for, then set it to false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representation of the enum type.
string
enum_type_decl::get_pretty_representation(bool internal,
bool qualified_name) const
{
string r = "enum ";
if (internal && get_is_anonymous())
r += get_type_name(this, qualified_name, /*internal=*/true);
else
r += decl_base::get_pretty_representation(internal,
qualified_name);
return r;
}
/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
enum_type_decl::traverse(ir_node_visitor &v)
{
if (v.type_node_has_been_visited(this))
return true;
if (visiting())
return true;
if (v.visit_begin(this))
{
visiting(true);
if (type_base_sptr t = get_underlying_type())
t->traverse(v);
visiting(false);
}
bool result = v.visit_end(this);
v.mark_type_node_as_visited(this);
return result;
}
/// Destructor for the enum type declaration.
enum_type_decl::~enum_type_decl()
{}
/// Test if two enums differ, but not by a name change.
///
/// @param l the first enum to consider.
///
/// @param r the second enum to consider.
///
/// @return true iff @p l differs from @p r by anything but a name
/// change.
bool
enum_has_non_name_change(const enum_type_decl& l,
const enum_type_decl& r,
change_kind* k)
{
bool result = false;
if (*l.get_underlying_type() != *r.get_underlying_type())
{
result = true;
if (k)
*k |= SUBTYPE_CHANGE_KIND;
else
return true;
}
enum_type_decl::enumerators::const_iterator i, j;
for (i = l.get_enumerators().begin(), j = r.get_enumerators().begin();
i != l.get_enumerators().end() && j != r.get_enumerators().end();
++i, ++j)
if (*i != *j)
{
result = true;
if (k)
{
*k |= LOCAL_TYPE_CHANGE_KIND;
break;
}
else
return true;
}
if (i != l.get_enumerators().end() || j != r.get_enumerators().end())
{
result = true;
if (k)
*k |= LOCAL_TYPE_CHANGE_KIND;
else
return true;
}
enum_type_decl &local_r = const_cast<enum_type_decl&>(r);
interned_string qn_r = l.get_environment()->intern(r.get_qualified_name());
interned_string qn_l = l.get_environment()->intern(l.get_qualified_name());
string n_l = l.get_name();
string n_r = r.get_name();
local_r.set_qualified_name(qn_l);
local_r.set_name(n_l);
if (!(l.decl_base::operator==(r) && l.type_base::operator==(r)))
{
result = true;
if (k)
{
if (!l.decl_base::operator==(r))
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
if (!l.type_base::operator==(r))
*k |= LOCAL_TYPE_CHANGE_KIND;
}
else
{
local_r.set_name(n_r);
local_r.set_qualified_name(qn_r);
return true;
}
}
local_r.set_qualified_name(qn_r);
local_r.set_name(n_r);
return result;
}
/// Test if a given enumerator is found present in an enum.
///
/// This is a subroutine of the equals function for enums.
///
/// @param enr the enumerator to consider.
///
/// @param enom the enum to consider.
///
/// @return true iff the enumerator @p enr is present in the enum @p
/// enom.
static bool
is_enumerator_present_in_enum(const enum_type_decl::enumerator &enr,
const enum_type_decl &enom)
{
for (const auto &e : enom.get_enumerators())
if (e == enr)
return true;
return false;
}
/// Check if two enumerators values are equal.
///
/// This function doesn't check if the names of the enumerators are
/// equal or not.
///
/// @param enr the first enumerator to consider.
///
/// @param enl the second enumerator to consider.
///
/// @return true iff @p enr has the same value as @p enl.
static bool
enumerators_values_are_equal(const enum_type_decl::enumerator &enr,
const enum_type_decl::enumerator &enl)
{return enr.get_value() == enl.get_value();}
/// Detect if a given enumerator value is present in an enum.
///
/// This function looks inside the enumerators of a given enum and
/// detect if the enum contains at least one enumerator or a given
/// value. The function also detects if the enumerator value we are
/// looking at is present in the enum with a different name. An
/// enumerator with the same value but with a different name is named
/// a "redundant enumerator". The function returns the set of
/// enumerators that are redundant with the value we are looking at.
///
/// @param enr the enumerator to consider.
///
/// @param enom the enum to consider.
///
/// @param redundant_enrs if the function returns true, then this
/// vector is filled with enumerators that are redundant with the
/// value of @p enr.
///
/// @return true iff the function detects that @p enom contains
/// enumerators with the same value as @p enr.
static bool
is_enumerator_value_present_in_enum(const enum_type_decl::enumerator &enr,
const enum_type_decl &enom,
vector<enum_type_decl::enumerator>& redundant_enrs)
{
bool found = false;
for (const auto &e : enom.get_enumerators())
if (enumerators_values_are_equal(e, enr))
{
found = true;
if (e != enr)
redundant_enrs.push_back(e);
}
return found;
}
/// Check if an enumerator value is redundant in a given enum.
///
/// Given an enumerator value, this function detects if an enum
/// contains at least one enumerator with the the same value but with
/// a different name.
///
/// @param enr the enumerator to consider.
///
/// @param enom the enum to consider.
///
/// @return true iff @p enr is a redundant enumerator in enom.
static bool
is_enumerator_value_redundant(const enum_type_decl::enumerator &enr,
const enum_type_decl &enom)
{
vector<enum_type_decl::enumerator> redundant_enrs;
if (is_enumerator_value_present_in_enum(enr, enom, redundant_enrs))
{
if (!redundant_enrs.empty())
return true;
}
return false;
}
/// Compares two instances of @ref enum_type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r. This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const enum_type_decl& l, const enum_type_decl& r, change_kind* k)
{
bool result = true;
if (*l.get_underlying_type() != *r.get_underlying_type())
{
result = false;
if (k)
*k |= SUBTYPE_CHANGE_KIND;
else
ABG_RETURN_FALSE;
}
if (!(l.decl_base::operator==(r) && l.type_base::operator==(r)))
{
result = false;
if (k)
{
if (!l.decl_base::operator==(r))
*k |= LOCAL_NON_TYPE_CHANGE_KIND;
if (!l.type_base::operator==(r))
*k |= LOCAL_TYPE_CHANGE_KIND;
}
else
ABG_RETURN_FALSE;
}
// Now compare the enumerators. Note that the order of declaration
// of enumerators should not matter in the comparison.
//
// Also if an enumerator value is redundant, that shouldn't impact
// the comparison.
//
// In that case, note that the two enums below are considered equal:
//
// enum foo
// {
// e0 = 0;
// e1 = 1;
// e2 = 2;
// };
//
// enum foo
// {
// e0 = 0;
// e1 = 1;
// e2 = 2;
// e_added = 1; // <-- this value is redundant with the value
// // of the enumerator e1.
// };
//
// These two enums are considered equal.
for(const auto &e : l.get_enumerators())
if (!is_enumerator_present_in_enum(e, r)
&& !is_enumerator_value_redundant(e, r))
{
result = false;
if (k)
{
*k |= LOCAL_TYPE_CHANGE_KIND;
break;
}
else
ABG_RETURN_FALSE;
}
for(const auto &e : r.get_enumerators())
if (!is_enumerator_present_in_enum(e, l)
&& !is_enumerator_value_redundant(e, r))
{
result = false;
if (k)
{
*k |= LOCAL_TYPE_CHANGE_KIND;
break;
}
else
ABG_RETURN_FALSE;
}
ABG_RETURN(result);
}
/// Equality operator.
///
/// @param o the other enum to test against.
///
/// @return true iff @p o equals the current instance of enum type
/// decl.
bool
enum_type_decl::operator==(const decl_base& o) const
{
const enum_type_decl* op = dynamic_cast<const enum_type_decl*>(&o);
if (!op)
return false;
return try_canonical_compare(this, op);
}
/// Equality operator.
///
/// @param o the other enum to test against.
///
/// @return true iff @p o is equals the current instance of enum type
/// decl.
bool
enum_type_decl::operator==(const type_base& o) const
{
const decl_base* other = dynamic_cast<const decl_base*>(&o);
if (!other)
return false;
return *this == *other;
}
/// Equality operator for @ref enum_type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator==(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r)
{
if (!!l != !!r)
return false;
if (l.get() == r.get())
return true;
decl_base_sptr o = r;
return *l == *o;
}
/// Inequality operator for @ref enum_type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator!=(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r)
{return !operator==(l, r);}
/// The type of the private data of an @ref
/// enum_type_decl::enumerator.
class enum_type_decl::enumerator::priv
{
const environment* env_;
interned_string name_;
int64_t value_;
interned_string qualified_name_;
enum_type_decl* enum_type_;
friend class enum_type_decl::enumerator;