// 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 <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-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 ty
|