// 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 #include #include #include #include #include #include #include #include #include #include #include #include "abg-internal.h" // 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 // #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 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::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(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(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(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 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 fn_type_ptr_map; // 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 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 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 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 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 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 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(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_maps::get_types_sorted_by_name() const { if (priv_->sorted_types_.empty()) { istring_type_base_wptrs_map_type::const_iterator i; vector::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_; } // // /// 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(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(this))); // The global scope must be out of the same environment as its // translation unit. priv_->global_scope_-> set_environment(const_cast(get_environment())); priv_->global_scope_->set_translation_unit (const_cast(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& 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(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(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(this)->priv_->live_fn_types_.push_back(ftype); interned_string repr = get_type_name(ftype); const_cast(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(env)); if (const translation_unit* existing_tu = ftype->get_translation_unit()) ABG_ASSERT(existing_tu == this); else ftype->set_translation_unit(const_cast(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);} // // 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 crc_; abg_compat::optional 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& crc, const abg_compat::optional& 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& crc, const abg_compat::optional& 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& crc, const abg_compat::optional& 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& 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& crc) {priv_->crc_ = crc;} /// Getter of the 'namespace' property. /// /// @return the namespace for Linux Kernel symbols, if any const abg_compat::optional& 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& 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 aliases; compute_aliases_for_elf_symbol(*this, syms, aliases); if (!aliases.empty() && include_symbol_itself) result += ", "; for (vector::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 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::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& 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;} // 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; } // // // 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() {} // // /// Convenience typedef for a map of interned_string -> bool. typedef unordered_map 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(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(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(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(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 ". /// /// 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 '. /// /// @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* 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 *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 '. /// /// @return the set of abixml type-id and the pointer value of the /// (canonical) type it's associated to. unordered_map& 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& 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 '. /// /// @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 // // /// 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 (static_cast(l) | static_cast(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 (static_cast(l) & static_cast(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(this)) priv_->type_or_decl_ptr_ = t; else if (decl_base *d = dynamic_cast(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(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(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(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(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(l), *dr = dynamic_cast(r); if (!!dl != !!dr) return false; if (dl && dr) return *dl == *dr; const type_base* tl = dynamic_cast(l), *tr = dynamic_cast(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);} // // 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(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(static_cast(l) | static_cast(r)); } change_kind operator&(change_kind l, change_kind r) { return static_cast(static_cast(l) & static_cast(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(l.get_context_rel()); context_rel *r2 = const_cast(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); } // /// 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(&f); ABG_ASSERT(m); const mem_fn_context_rel* ctxt = dynamic_cast(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(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(&f); ABG_ASSERT(m); const mem_fn_context_rel* ctxt = dynamic_cast(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(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(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); } /// 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); } /// 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); } /// 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 subranges; for (vector::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());} // /// 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(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(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(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(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(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(&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(&decl)) return s; scope_decl* scope = decl.get_scope(); while (scope && !dynamic_cast(scope)) scope = scope->get_scope(); return scope ? dynamic_cast (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) {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(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());} // /// 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(tod); if (type_base* t = dynamic_cast(a)) result = get_type_name(t, qualified); else if (decl_base *d = dynamic_cast(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)));} // /// 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(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(tod))) result = get_pretty_representation(t, internal); else if (decl_base* d = is_decl(const_cast(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(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(artifact)); return const_cast(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(artifact)); return const_cast(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(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(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(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(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) {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(&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(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_ptrscope) {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) {return (decl && dynamic_cast(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) { return (decl && (dynamic_pointer_cast(decl) || dynamic_pointer_cast(decl) || dynamic_pointer_cast(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(const_cast(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(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 (const_cast(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(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 (const_cast(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(const_cast(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(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(const_cast(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(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(&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 (const_cast(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(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(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(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(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(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(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(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(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 (const_cast(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(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(const_cast(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 is_class_or_union_type(const shared_ptr& t) {return dynamic_pointer_cast(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(const_cast(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& t) {return dynamic_pointer_cast(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 (const_cast(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(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(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(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(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(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(const_cast(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(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(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(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(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(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(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(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(const_cast(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(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(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(const_cast(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) { 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) { return (decl && dynamic_pointer_cast(decl) && dynamic_cast(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(const_cast(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(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 (const_cast(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(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) {return decl && dynamic_pointer_cast(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 /// /// 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'. /// /// But in this case: /// /// foo::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& 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& comps) { string result; for (list::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 static bool iterator_is_last(T& container, typename T::const_iterator i) { typename T::const_iterator next = i; ++next; return (next == container.end()); } //-------------------------------- // // ------------------------------ /// 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 static shared_ptr 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(type_base_sptr(i->second.back())); return shared_ptr(); } /// 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_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(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(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(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(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(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(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(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(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(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(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 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 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 static const interned_string& get_node_name(shared_ptr 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 static decl_base_sptr convert_node_to_decl(shared_ptr 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 static const type_or_decl_base_sptr lookup_node_in_scope(const list& fqn, const scope_decl_sptr& skope) { type_or_decl_base_sptr resulting_decl; shared_ptr node; bool it_is_last = false; scope_decl_sptr cur_scope = skope, new_scope, scope; for (list::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(*m); if (scope && scope->get_name() == *c) { new_scope = scope; break; } } else { //looking for a final type. node = dynamic_pointer_cast(*m); if (node && get_node_name(node) == *c) { if (class_decl_sptr cl = dynamic_pointer_cast(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& comps, const scope_decl_sptr& scope) {return is_type(lookup_node_in_scope(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& access_path, const scope_decl* scope) { vector 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 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& comps, const scope_decl_sptr& skope) {return is_var_decl(lookup_node_in_scope(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 static const type_or_decl_base_sptr lookup_node_in_translation_unit(const list& fqn, const translation_unit& tu) {return lookup_node_in_scope(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& fqn, const translation_unit& tu) {return is_type(lookup_node_in_translation_unit(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& fqn, const translation_unit& tu) {return is_class_type(lookup_node_in_translation_unit(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(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(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(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(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(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(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(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(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(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_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(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(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(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(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 bool maybe_update_types_lookup_map(const shared_ptr& 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(scope_decl*, /// const shared_ptr&, /// 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(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(scope_decl*, /// const shared_ptr&, /// 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 (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 (basic_type, tu->get_types().basic_types()); if (corpus *type_corpus = basic_type->get_corpus()) { maybe_update_types_lookup_map (basic_type, type_corpus->priv_->get_types().basic_types()); maybe_update_types_lookup_map (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 (basic_type, group->priv_->get_types().basic_types()); maybe_update_types_lookup_map (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_type, tu->get_types().class_types()); if (corpus *type_corpus = class_type->get_corpus()) { maybe_update_types_lookup_map (class_type, type_corpus->priv_->get_types().class_types()); maybe_update_types_lookup_map (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_type, group->priv_->get_types().class_types()); maybe_update_types_lookup_map (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_type, tu->get_types().union_types()); if (corpus *type_corpus = union_type->get_corpus()) { maybe_update_types_lookup_map (union_type, type_corpus->priv_->get_types().union_types()); maybe_update_types_lookup_map (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_type, group->priv_->get_types().union_types()); maybe_update_types_lookup_map (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, tu->get_types().enum_types()); if (corpus *type_corpus = enum_type->get_corpus()) { maybe_update_types_lookup_map (enum_type, type_corpus->priv_->get_types().enum_types()); maybe_update_types_lookup_map (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, group->priv_->get_types().enum_types()); maybe_update_types_lookup_map (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_type, tu->get_types().typedef_types()); if (corpus *type_corpus = typedef_type->get_corpus()) { maybe_update_types_lookup_map (typedef_type, type_corpus->priv_->get_types().typedef_types()); maybe_update_types_lookup_map (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_type, group->priv_->get_types().typedef_types()); maybe_update_types_lookup_map (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, tu->get_types().qualified_types()); if (corpus *type_corpus = qualified_type->get_corpus()) { maybe_update_types_lookup_map (qualified_type, type_corpus->priv_->get_types().qualified_types()); if (corpus *group = type_corpus->get_group()) { maybe_update_types_lookup_map (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, tu->get_types().pointer_types()); if (corpus *type_corpus = pointer_type->get_corpus()) { maybe_update_types_lookup_map (pointer_type, type_corpus->priv_->get_types().pointer_types()); if (corpus *group = type_corpus->get_group()) { maybe_update_types_lookup_map (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, tu->get_types().reference_types()); if (corpus *type_corpus = reference_type->get_corpus()) { maybe_update_types_lookup_map (reference_type, type_corpus->priv_->get_types().reference_types()); if (corpus *group = type_corpus->get_group()) { maybe_update_types_lookup_map (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, tu->get_types().array_types()); if (corpus *type_corpus = array_type->get_corpus()) { maybe_update_types_lookup_map (array_type, type_corpus->priv_->get_types().array_types()); maybe_update_types_lookup_map (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, group->priv_->get_types().array_types()); maybe_update_types_lookup_map (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 (subrange_type, tu->get_types().subrange_types()); if (corpus *type_corpus = subrange_type->get_corpus()) { maybe_update_types_lookup_map (subrange_type, type_corpus->priv_->get_types().subrange_types()); maybe_update_types_lookup_map (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 (subrange_type, group->priv_->get_types().subrange_types()); maybe_update_types_lookup_map (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 (fn_type, tu->get_types().function_types()); if (corpus *type_corpus = fn_type->get_corpus()) { maybe_update_types_lookup_map (fn_type, type_corpus->priv_->get_types().function_types()); if (corpus *group = fn_type->get_corpus()) { maybe_update_types_lookup_map (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; } //-------------------------------- // // ------------------------------ /// 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(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(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 v; v.push_back(t); types[repr] = v; result = t; } else { vector &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::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 '. 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(t.get()); string type_id = env->get_type_id_from_pointer(ptr_val); if (type_id.empty()) type_id = "type-id-"; // 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(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_;} // // /// 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(static_cast(l) |static_cast(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(static_cast(l) &static_cast(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();} // // /// 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(l), static_cast(r), k); if (!k && !result) ABG_RETURN_FALSE; result &= equals(static_cast(l), static_cast(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(&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(&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() {} // // /// 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(l), static_cast(r), k); if (!k && !result) ABG_RETURN_FALSE; result &= equals(static_cast(l), static_cast(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(&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(&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() {} // // /// 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(&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(*i); if (t) if (!t->traverse (v)) break; } visiting(false); } return v.visit_end(this); } namespace_decl::~namespace_decl() { } // // /// 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 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(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(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(&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(&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(&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 (static_cast(lhs) | static_cast(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 (static_cast(lhs) & static_cast(rhs)); } /// Overloaded bitwise inverting operator for CV qualifiers. qualified_type_def::CV operator~(qualified_type_def::CV q) {return static_cast(~static_cast(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; } // // /// 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(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(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);} // // /// 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(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(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(&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(&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(&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);} // // // array_type_def::subrange_type::~subrange_type() = default; // /// 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_; } // 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& v) { if (v.empty()) return "[]"; string r; for (vector::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(&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(&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 += ""; else repr += ""; 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; } // 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& 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& 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 this_subs = l.get_subranges(); std::vector 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::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 this_subs = l->get_subranges(); std::vector other_subs = r->get_subranges(); if (this_subs.size() != other_subs.size()) ABG_RETURN_FALSE; std::vector::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& 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(&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(&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& 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 >::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::get_subranges() const {return priv_->subranges_;} array_type_def::~array_type_def() {} // // 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(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& 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 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(&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(&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; public: priv() : env_(), enum_type_() {} priv(const environment* env, const string& name, int64_t value, enum_type_decl* e = 0) : env_(env), name_(env ? env->intern(name) : interned_string()), value_(value), enum_type_(e) {} }; // end class enum_type_def::enumerator::priv /// Default constructor of the @ref enum_type_decl::enumerator type. enum_type_decl::enumerator::enumerator() : priv_(new priv) {} enum_type_decl::enumerator::~enumerator() = default; /// Constructor of the @ref enum_type_decl::enumerator type. /// /// @param env the environment we are operating from. /// /// @param name the name of the enumerator. /// /// @param value the value of the enumerator. enum_type_decl::enumerator::enumerator(const environment* env, const string& name, int64_t value) : priv_(new priv(env, name, value)) {} /// Copy constructor of the @ref enum_type_decl::enumerator type. /// /// @param other enumerator to copy. enum_type_decl::enumerator::enumerator(const enumerator& other) : priv_(new priv(other.get_environment(), other.get_name(), other.get_value(), other.get_enum_type())) {} /// Assignment operator of the @ref enum_type_decl::enumerator type. /// /// @param o enum_type_decl::enumerator& enum_type_decl::enumerator::operator=(const enumerator& o) { priv_->env_ = o.get_environment(); priv_->name_ = o.get_name(); priv_->value_ = o.get_value(); priv_->enum_type_ = o.get_enum_type(); return *this; } /// Equality operator /// /// @param other the enumerator to compare to the current /// instance of enum_type_decl::enumerator. /// /// @return true if @p other equals the current instance of /// enum_type_decl::enumerator. bool enum_type_decl::enumerator::operator==(const enumerator& other) const { bool names_equal = true; names_equal = (get_name() == other.get_name()); return names_equal && (get_value() == other.get_value()); } /// Inequality operator. /// /// @param other the other instance to compare against. /// /// @return true iff @p other is different from the current instance. bool enum_type_decl::enumerator::operator!=(const enumerator& other) const {return !operator==(other);} /// Getter of the environment of this enumerator. /// /// @return the environment of this enumerator. const environment* enum_type_decl::enumerator::get_environment() const {return priv_->env_;} /// Getter for the name of the current instance of /// enum_type_decl::enumerator. /// /// @return a reference to the name of the current instance of /// enum_type_decl::enumerator. const interned_string& enum_type_decl::enumerator::get_name() const {return priv_->name_;} /// Getter for the qualified name of the current instance of /// enum_type_decl::enumerator. The first invocation of the method /// builds the qualified name, caches it and return a reference to the /// cached qualified name. Subsequent invocations just 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 qualified name of the current instance of /// enum_type_decl::enumerator. const interned_string& enum_type_decl::enumerator::get_qualified_name(bool internal) const { if (priv_->qualified_name_.empty()) { const environment* env = priv_->enum_type_->get_environment(); ABG_ASSERT(env); priv_->qualified_name_ = env->intern(get_enum_type()->get_qualified_name(internal) + "::" + get_name()); } return priv_->qualified_name_; } /// Setter for the name of @ref enum_type_decl::enumerator. /// /// @param n the new name. void enum_type_decl::enumerator::set_name(const string& n) { const environment* env = get_environment(); ABG_ASSERT(env); priv_->name_ = env->intern(n); } /// Getter for the value of @ref enum_type_decl::enumerator. /// /// @return the value of the current instance of /// enum_type_decl::enumerator. int64_t enum_type_decl::enumerator::get_value() const {return priv_->value_;} /// Setter for the value of @ref enum_type_decl::enumerator. /// /// @param v the new value of the enum_type_decl::enumerator. void enum_type_decl::enumerator::set_value(int64_t v) {priv_->value_= v;} /// Getter for the enum type that this enumerator is for. /// /// @return the enum type that this enumerator is for. enum_type_decl* enum_type_decl::enumerator::get_enum_type() const {return priv_->enum_type_;} /// Setter for the enum type that this enumerator is for. /// /// @param e the new enum type. void enum_type_decl::enumerator::set_enum_type(enum_type_decl* e) {priv_->enum_type_ = e;} // // /// Private data structure of the @ref typedef_decl. struct typedef_decl::priv { type_base_wptr underlying_type_; string internal_qualified_name_; string temp_internal_qualified_name_; priv(const type_base_sptr& t) : underlying_type_(t) {} }; // end struct typedef_decl::priv /// Constructor of the typedef_decl type. /// /// @param name the name of the typedef. /// /// @param underlying_type the underlying type of the typedef. /// /// @param locus the source location of the typedef declaration. /// /// @param linkage_name the mangled name of the typedef. /// /// @param vis the visibility of the typedef type. typedef_decl::typedef_decl(const string& name, const type_base_sptr underlying_type, const location& locus, const string& linkage_name, visibility vis) : type_or_decl_base(underlying_type->get_environment(), TYPEDEF_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)) { runtime_type_instance(this); } /// Constructor of the typedef_decl type. /// /// @param name the name of the typedef. /// /// @param env the environment of the current typedef. /// /// @param locus the source location of the typedef declaration. /// /// @param mangled_name the mangled name of the typedef. /// /// @param vis the visibility of the typedef type. typedef_decl::typedef_decl(const string& name, environment* env, const location& locus, const string& mangled_name, visibility vis) : type_or_decl_base(env, TYPEDEF_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE), type_base(env, /*size_in_bits=*/0, /*alignment_in_bits=*/0), decl_base(env, name, locus, mangled_name, vis), priv_(new priv(nullptr)) { runtime_type_instance(this); } /// Return the size of the typedef. /// /// This function looks at the size of the underlying type and ensures /// that it's the same as the size of the typedef. /// /// @return the size of the typedef. size_t typedef_decl::get_size_in_bits() const { if (!get_underlying_type()) return 0; size_t s = get_underlying_type()->get_size_in_bits(); if (s != type_base::get_size_in_bits()) const_cast(this)->set_size_in_bits(s); return type_base::get_size_in_bits(); } /// Return the alignment of the typedef. /// /// This function looks at the alignment of the underlying type and /// ensures that it's the same as the alignment of the typedef. /// /// @return the size of the typedef. size_t typedef_decl::get_alignment_in_bits() const { if (!get_underlying_type()) return 0; size_t s = get_underlying_type()->get_alignment_in_bits(); if (s != type_base::get_alignment_in_bits()) const_cast(this)->set_alignment_in_bits(s); return type_base::get_alignment_in_bits(); } /// Compares two instances of @ref typedef_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 typedef_decl& l, const typedef_decl& r, change_kind* k) { bool result = true; if (!equals(static_cast(l), static_cast(r), k)) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } if (*l.get_underlying_type() != *r.get_underlying_type()) { // Changes to the underlying type of a typedef are considered // local, a bit like for pointers. result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } ABG_RETURN(result); } /// Equality operator /// /// @param o the other typedef_decl to test against. bool typedef_decl::operator==(const decl_base& o) const { const typedef_decl* other = dynamic_cast(&o); if (!other) return false; return try_canonical_compare(this, other); } /// Equality operator /// /// @param o the other typedef_decl to test against. /// /// @return true if the current instance of @ref typedef_decl equals /// @p o. bool typedef_decl::operator==(const type_base& o) const { const decl_base* other = dynamic_cast(&o); if (!other) return false; return *this == *other; } /// Build a pretty representation for a typedef_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 a copy of the pretty representation of the current /// instance of typedef_decl. string typedef_decl::get_pretty_representation(bool internal, bool qualified_name) const { string result = "typedef "; if (qualified_name) result += get_qualified_name(internal); else result += get_name(); return result; } /// Getter of the underlying type of the typedef. /// /// @return the underlying_type. type_base_sptr typedef_decl::get_underlying_type() const {return priv_->underlying_type_.lock();} /// Setter ofthe underlying type of the typedef. /// /// @param t the new underlying type of the typedef. void typedef_decl::set_underlying_type(const type_base_sptr& t) { priv_->underlying_type_ = t; set_size_in_bits(t->get_size_in_bits()); set_alignment_in_bits(t->get_alignment_in_bits()); } /// 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 typedef_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; } typedef_decl::~typedef_decl() {} // // struct var_decl::priv { type_base_wptr type_; type_base* naked_type_; decl_base::binding binding_; elf_symbol_sptr symbol_; interned_string id_; priv() : naked_type_(), binding_(decl_base::BINDING_GLOBAL) {} priv(type_base_sptr t, decl_base::binding b) : type_(t), naked_type_(t.get()), binding_(b) {} }; // end struct var_decl::priv /// Constructor of the @ref var_decl type. /// /// @param name the name of the variable declaration /// /// @param type the type of the variable declaration /// /// @param locus the source location where the variable was defined. /// /// @param linkage_name the linkage name of the variable. /// /// @param vis the visibility of of the variable. /// /// @param bind the binding kind of the variable. var_decl::var_decl(const string& name, type_base_sptr type, const location& locus, const string& linkage_name, visibility vis, binding bind) : type_or_decl_base(type->get_environment(), VAR_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, locus, linkage_name, vis), priv_(new priv(type, bind)) { runtime_type_instance(this); } /// Getter of the type of the variable. /// /// @return the type of the variable. const type_base_sptr var_decl::get_type() const {return priv_->type_.lock();} /// Getter of the type of the variable. /// /// This getter returns a bare pointer, as opposed to a smart pointer. /// It's to be used on performance sensitive code paths identified by /// careful profiling. /// /// @return the type of the variable, as a bare pointer. const type_base* var_decl::get_naked_type() const {return priv_->naked_type_;} /// Getter of the binding of the variable. /// /// @return the biding of the variable. decl_base::binding var_decl::get_binding() const {return priv_->binding_;} /// Setter of the binding of the variable. /// /// @param b the new binding value. void var_decl::set_binding(decl_base::binding b) {priv_->binding_ = b;} /// Sets the underlying ELF symbol for the current variable. /// /// And underlyin$g ELF symbol for the current variable might exist /// only if the corpus that this variable originates from was /// constructed from an ELF binary file. /// /// Note that comparing two variables that have underlying ELF symbols /// involves comparing their underlying elf symbols. The decl name /// for the variable thus becomes irrelevant in the comparison. /// /// @param sym the new ELF symbol for this variable decl. void var_decl::set_symbol(const elf_symbol_sptr& sym) { priv_->symbol_ = sym; // The variable id cache that depends on the symbol must be // invalidated because the symbol changed. priv_->id_ = get_environment()->intern(""); } /// Gets the the underlying ELF symbol for the current variable, /// that was set using var_decl::set_symbol(). Please read the /// documentation for that member function for more information about /// "underlying ELF symbols". /// /// @return sym the underlying ELF symbol for this variable decl, if /// one exists. const elf_symbol_sptr& var_decl::get_symbol() const {return priv_->symbol_;} /// Create a new var_decl that is a clone of the current one. /// /// @return the cloned var_decl. var_decl_sptr var_decl::clone() const { var_decl_sptr v(new var_decl(get_name(), get_type(), get_location(), get_linkage_name(), get_visibility(), get_binding())); v->set_symbol(get_symbol()); if (is_member_decl(*this)) { class_decl* scope = dynamic_cast(get_scope()); scope->add_data_member(v, get_member_access_specifier(*this), get_data_member_is_laid_out(*this), get_member_is_static(*this), get_data_member_offset(*this)); } else add_decl_to_scope(v, get_scope()); return v; } /// Setter of the scope of the current var_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. /// /// @param scope the new scope. void var_decl::set_scope(scope_decl* scope) { if (!get_context_rel()) set_context_rel(new dm_context_rel(scope)); else get_context_rel()->set_scope(scope); } /// Compares two instances of @ref var_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 var_decl& l, const var_decl& r, change_kind* k) { bool result = true; // First test types of variables. This should be fast because in // the general case, most types should be canonicalized. if (*l.get_naked_type() != *r.get_naked_type()) { result = false; if (k) { if (!types_have_similar_structure(l.get_naked_type(), r.get_naked_type())) *k |= (LOCAL_TYPE_CHANGE_KIND); else *k |= SUBTYPE_CHANGE_KIND; } else ABG_RETURN_FALSE; } // If there are underlying elf symbols for these variables, // compare them. And then compare the other parts. const elf_symbol_sptr &s0 = l.get_symbol(), &s1 = r.get_symbol(); if (!!s0 != !!s1) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } else if (s0 && s0 != s1) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } bool symbols_are_equal = (s0 && s1 && result); if (symbols_are_equal) { // The variables have underlying elf symbols that are equal, so // now, let's compare the decl_base part of the variables w/o // considering their decl names. const environment* env = l.get_environment(); const interned_string n1 = l.get_qualified_name(), n2 = r.get_qualified_name(); const_cast(l).set_qualified_name(env->intern("")); const_cast(r).set_qualified_name(env->intern("")); bool decl_bases_different = !l.decl_base::operator==(r); const_cast(l).set_qualified_name(n1); const_cast(r).set_qualified_name(n2); if (decl_bases_different) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } } else if (!l.decl_base::operator==(r)) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } const dm_context_rel* c0 = dynamic_cast(l.get_context_rel()); const dm_context_rel* c1 = dynamic_cast(r.get_context_rel()); ABG_ASSERT(c0 && c1); if (*c0 != *c1) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } ABG_RETURN(result); } /// Comparison operator of @ref var_decl. /// /// @param o the instance of @ref var_decl to compare against. /// /// @return true iff the current instance of @ref var_decl equals @p o. bool var_decl::operator==(const decl_base& o) const { const var_decl* other = dynamic_cast(&o); if (!other) return false; return equals(*this, *other, 0); } /// Return an ID that tries to uniquely identify the variable inside a /// program or a library. /// /// So if the variable has an underlying elf symbol, the ID is the /// concatenation of the symbol name and its version. Otherwise, the /// ID is the linkage name if its non-null. Otherwise, it's the /// pretty representation of the variable. /// /// @return the ID. interned_string var_decl::get_id() const { if (priv_->id_.empty()) { string repr = get_name(); string sym_str; if (elf_symbol_sptr s = get_symbol()) sym_str = s->get_id_string(); else if (!get_linkage_name().empty()) sym_str = get_linkage_name(); const environment* env = get_type()->get_environment(); ABG_ASSERT(env); priv_->id_ = env->intern(repr); if (!sym_str.empty()) priv_->id_ = env->intern(priv_->id_ + "{" + sym_str + "}"); } return priv_->id_; } /// Return the hash value for the current instance. /// /// @return the hash value. size_t var_decl::get_hash() const { var_decl::hash hash_var; return hash_var(this); } /// Get the qualified name of a given variable or data member. /// /// /// Note that if the current instance of @ref var_decl is an anonymous /// data member, then the qualified name is actually the flat /// representation (the definition) of the type of the anonymous data /// member. We chose the flat representation because otherwise, the /// name of an *anonymous* data member is empty, by construction, e.g: /// /// struct foo { /// int a; /// union { /// char b; /// char c; /// }; // <---- this data member is anonymous. /// int d; /// } /// /// The string returned for the anonymous member here is going to be: /// /// "union {char b; char c}" /// /// @param internal if true then this is for a purpose to the library, /// otherwise, it's for being displayed to users. /// /// @return the resulting qualified name. const interned_string& var_decl::get_qualified_name(bool internal) const { if (is_anonymous_data_member(this) && decl_base::get_qualified_name().empty()) { // Display the anonymous data member in a way that makes sense. string r = get_pretty_representation(internal); set_qualified_name(get_environment()->intern(r)); } return decl_base::get_qualified_name(internal); } /// Build and return the pretty representation of this variable. /// /// @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 this variable. string var_decl::get_pretty_representation(bool internal, bool qualified_name) const { string result; if (is_member_decl(this) && get_member_is_static(this)) result = "static "; // Detect if the current instance of var_decl is a member of // an anonymous class or union. bool member_of_anonymous_class = false; if (class_or_union* scope = is_at_class_scope(this)) if (scope->get_is_anonymous()) member_of_anonymous_class = true; if (array_type_def_sptr t = is_array_type(get_type())) { string name; if (member_of_anonymous_class || !qualified_name) name = get_name(); else name = get_qualified_name(internal); type_base_sptr et = t->get_element_type(); ABG_ASSERT(et); decl_base_sptr decl = get_type_declaration(et); ABG_ASSERT(decl); result += decl->get_qualified_name(internal) + " " + name + t->get_subrange_representation(); } else { if (/*The current var_decl is to be used as an anonymous data member. */ get_name().empty()) { // Display the anonymous data member in a way that // makes sense. result += get_class_or_union_flat_representation (is_class_or_union_type(get_type()), "", /*one_line=*/true, internal); } else if (data_member_has_anonymous_type(this)) { result += get_class_or_union_flat_representation (is_class_or_union_type(get_type()), "", /*one_line=*/true, internal); result += " "; if (member_of_anonymous_class || !qualified_name) // It doesn't make sense to name the member of an // anonymous class or union like: // "__anonymous__::data_member_name". So let's just use // its non-qualified name. result += get_name(); else result += get_qualified_name(internal); } else { result += get_type_declaration(get_type())->get_qualified_name(internal) + " "; if (member_of_anonymous_class || !qualified_name) // It doesn't make sense to name the member of an // anonymous class or union like: // "__anonymous__::data_member_name". So let's just use // its non-qualified name. result += get_name(); else result += get_qualified_name(internal); } } return result; } /// Get a name that is valid even for an anonymous data member. /// /// If the current @ref var_decl is an anonymous data member, then /// return its pretty representation. As of now, that pretty /// representation is actually its flat representation as returned by /// get_class_or_union_flat_representation(). /// /// Otherwise, just return the name of the current @ref var_decl. /// /// @param qualified if true, return the qualified name. This doesn't /// have an effet if the current @ref var_decl represents an anonymous /// data member. string var_decl::get_anon_dm_reliable_name(bool qualified) const { string name; if (is_anonymous_data_member(this)) // This function is used in the comparison engine to determine // which anonymous data member was deleted. So it's not involved // in type comparison or canonicalization. We don't want to use // the 'internal' version of the pretty presentation. name = get_pretty_representation(/*internal=*/false, qualified); else name = get_name(); return 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 var_decl::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (type_base_sptr t = get_type()) t->traverse(v); visiting(false); } return v.visit_end(this); } var_decl::~var_decl() {} // // /// The type of the private data of the @ref function_type type. struct function_type::priv { parameters parms_; type_base_wptr return_type_; interned_string cached_name_; interned_string internal_cached_name_; interned_string temp_internal_cached_name_; priv() {} priv(const parameters& parms, type_base_sptr return_type) : parms_(parms), return_type_(return_type) {} priv(type_base_sptr return_type) : return_type_(return_type) {} /// Mark a given @ref function_type as being compared. /// /// @param type the @ref function_type to mark as being compared. void mark_as_being_compared(const function_type& type) const { const environment* env = type.get_environment(); ABG_ASSERT(env); env->priv_->fn_types_being_compared_.insert(&type); } /// If a given @ref function_type was marked as being compared, this /// function unmarks it. /// /// @param type the @ref function_type to mark as *NOT* being /// compared. void unmark_as_being_compared(const function_type& type) const { const environment* env = type.get_environment(); ABG_ASSERT(env); env->priv_->fn_types_being_compared_.erase(&type); } /// Tests if a @ref function_type is currently being compared. /// /// @param type the function type to take into account. /// /// @return true if @p type is being compared. bool comparison_started(const function_type& type) const { const environment* env = type.get_environment(); ABG_ASSERT(env); return env->priv_->fn_types_being_compared_.count(&type); } };// end struc function_type::priv /// 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 function_type::on_canonical_type_set() { priv_->cached_name_.clear(); priv_->internal_cached_name_.clear(); } /// The most straightforward constructor for the function_type class. /// /// @param return_type the return type of the function type. /// /// @param parms the list of parameters of the function type. /// Stricto sensu, we just need a list of types; we are using a list /// of parameters (where each parameter also carries the name of the /// parameter and its source location) to try and provide better /// diagnostics whenever it makes sense. If it appears that this /// wasts too many resources, we can fall back to taking just a /// vector of types here. /// /// @param size_in_bits the size of this type, in bits. /// /// @param alignment_in_bits the alignment of this type, in bits. /// /// @param size_in_bits the size of this type. function_type::function_type(type_base_sptr return_type, const parameters& parms, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(return_type->get_environment(), FUNCTION_TYPE | ABSTRACT_TYPE_BASE), type_base(return_type->get_environment(), size_in_bits, alignment_in_bits), priv_(new priv(parms, return_type)) { runtime_type_instance(this); for (parameters::size_type i = 0, j = 1; i < priv_->parms_.size(); ++i, ++j) { if (i == 0 && priv_->parms_[i]->get_is_artificial()) // If the first parameter is artificial, then it certainly // means that this is a member function, and the first // parameter is the implicit this pointer. In that case, set // the index of that implicit parameter to zero. Otherwise, // the index of the first parameter starts at one. j = 0; priv_->parms_[i]->set_index(j); } } /// A constructor for a function_type that takes no parameters. /// /// @param return_type the return type of this function_type. /// /// @param size_in_bits the size of this type, in bits. /// /// @param alignment_in_bits the alignment of this type, in bits. function_type::function_type(type_base_sptr return_type, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(return_type->get_environment(), FUNCTION_TYPE | ABSTRACT_TYPE_BASE), type_base(return_type->get_environment(), size_in_bits, alignment_in_bits), priv_(new priv(return_type)) { runtime_type_instance(this); } /// A constructor for a function_type that takes no parameter and /// that has no return_type yet. These missing parts can (and must) /// be added later. /// /// @param env the environment we are operating from. /// /// @param size_in_bits the size of this type, in bits. /// /// @param alignment_in_bits the alignment of this type, in bits. function_type::function_type(const environment* env, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(env, FUNCTION_TYPE | ABSTRACT_TYPE_BASE), type_base(env, size_in_bits, alignment_in_bits), priv_(new priv) { runtime_type_instance(this); } /// Getter for the return type of the current instance of @ref /// function_type. /// /// @return the return type. type_base_sptr function_type::get_return_type() const {return priv_->return_type_.lock();} /// Setter of the return type of the current instance of @ref /// function_type. /// /// @param t the new return type to set. void function_type::set_return_type(type_base_sptr t) {priv_->return_type_ = t;} /// Getter for the set of parameters of the current intance of @ref /// function_type. /// /// @return the parameters of the current instance of @ref /// function_type. const function_decl::parameters& function_type::get_parameters() const {return priv_->parms_;} /// Get the Ith parameter of the vector of parameters of the current /// instance of @ref function_type. /// /// Note that the first parameter is at index 0. That parameter is /// the first parameter that comes after the possible implicit "this" /// parameter, when the current instance @ref function_type is for a /// member function. Otherwise, if the current instance of @ref /// function_type is for a non-member function, the parameter at index /// 0 is the first parameter of the function. /// /// /// @param i the index of the parameter to return. If i is greater /// than the index of the last parameter, then this function returns /// an empty parameter (smart) pointer. /// /// @return the @p i th parameter that is not implicit. const function_decl::parameter_sptr function_type::get_parm_at_index_from_first_non_implicit_parm(size_t i) const { parameter_sptr result; if (dynamic_cast(this)) { if (i + 1 < get_parameters().size()) result = get_parameters()[i + 1]; } else { if (i < get_parameters().size()) result = get_parameters()[i]; } return result; } /// Setter for the parameters of the current instance of @ref /// function_type. /// /// @param p the new vector of parameters to set. void function_type::set_parameters(const parameters &p) { priv_->parms_ = p; for (parameters::size_type i = 0, j = 1; i < priv_->parms_.size(); ++i, ++j) { if (i == 0 && priv_->parms_[i]->get_is_artificial()) // If the first parameter is artificial, then it certainly // means that this is a member function, and the first // parameter is the implicit this pointer. In that case, set // the index of that implicit parameter to zero. Otherwise, // the index of the first parameter starts at one. j = 0; priv_->parms_[i]->set_index(j); } } /// Append a new parameter to the vector of parameters of the current /// instance of @ref function_type. /// /// @param parm the parameter to append. void function_type::append_parameter(parameter_sptr parm) { parm->set_index(priv_->parms_.size()); priv_->parms_.push_back(parm); } /// Test if the current instance of @ref function_type is for a /// variadic function. /// /// A variadic function is a function that takes a variable number of /// arguments. /// /// @return true iff the current instance of @ref function_type is for /// a variadic function. bool function_type::is_variadic() const { return (!priv_->parms_.empty() && priv_->parms_.back()->get_variadic_marker()); } /// Compare two function types. /// /// In case these function types are actually method types, this /// function avoids comparing two parameters (of the function types) /// if the types of the parameters are actually the types of the /// classes of the method types. This prevents infinite recursion /// during the comparison of two classes that are structurally /// identical. /// /// This is a subroutine of the equality operator of function_type. /// /// @param lhs the first function type to consider /// /// @param rhs the second function type to consider /// /// @param k a pointer to a bitfield set by the function to give /// information about the kind of changes carried by @p lhs and @p /// rhs. It 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 lhs == rhs, false otherwise. bool equals(const function_type& l, const function_type& r, change_kind* k) { #define RETURN(value) return return_comparison_result(l, r, value) RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r); mark_types_as_being_compared(l, r); bool result = true; if (!l.type_base::operator==(r)) { result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else RETURN(result); } class_or_union* l_class = 0, *r_class = 0; if (const method_type* m = dynamic_cast(&l)) l_class = m->get_class_type().get(); if (const method_type* m = dynamic_cast(&r)) r_class = m->get_class_type().get(); // Compare the names of the class of the method if (!!l_class != !!r_class) { result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else RETURN(result); } else if (l_class && (l_class->get_qualified_name() != r_class->get_qualified_name())) { result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else RETURN(result); } // Then compare the return type; Beware if it's t's a class type // that is the same as the method class name; we can recurse for // ever in that case. decl_base* l_return_type_decl = get_type_declaration(l.get_return_type()).get(); decl_base* r_return_type_decl = get_type_declaration(r.get_return_type()).get(); bool compare_result_types = true; string l_rt_name = l_return_type_decl ? l_return_type_decl->get_qualified_name() : string(); string r_rt_name = r_return_type_decl ? r_return_type_decl->get_qualified_name() : string(); if ((l_class && (l_class->get_qualified_name() == l_rt_name)) || (r_class && (r_class->get_qualified_name() == r_rt_name))) compare_result_types = false; if (compare_result_types) { if (l.get_return_type() != r.get_return_type()) { result = false; if (k) { if (!types_have_similar_structure(l.get_return_type(), r.get_return_type())) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; } else RETURN(result); } } else if (l_rt_name != r_rt_name) { result = false; if (k) *k |= SUBTYPE_CHANGE_KIND; else RETURN(result); } vector >::const_iterator i,j; for (i = l.get_first_parm(), j = r.get_first_parm(); i != l.get_parameters().end() && j != r.get_parameters().end(); ++i, ++j) { if (**i != **j) { result = false; if (k) { if (!types_have_similar_structure((*i)->get_type(), (*j)->get_type())) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; } else RETURN(result); } } if ((i != l.get_parameters().end() || j != r.get_parameters().end())) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else RETURN(result); } RETURN(result); #undef RETURN } /// Get the first parameter of the function. /// /// If the function is a non-static member function, the parameter /// returned is the first one following the implicit 'this' parameter. /// /// @return the first non implicit parameter of the function. function_type::parameters::const_iterator function_type::get_first_non_implicit_parm() const { if (get_parameters().empty()) return get_parameters().end(); bool is_method = dynamic_cast(this); parameters::const_iterator i = get_parameters().begin(); if (is_method) ++i; return i; } /// Get the first parameter of the function. /// /// Note that if the function is a non-static member function, the /// parameter returned is the implicit 'this' parameter. /// /// @return the first parameter of the function. function_type::parameters::const_iterator function_type::get_first_parm() const {return get_parameters().begin();} /// Get the name of the current @ref function_type. /// /// The name is retrieved from a cache. If the cache is empty, this /// function computes the name of the type, stores it in the cache and /// returns it. Subsequent invocation of the function are going to /// just hit the cache. /// /// Note that if the type is *NOT* canonicalized then function type /// name is never cached. /// /// @param internal if true then it means the function type name is /// going to be used for purposes that are internal to libabigail /// itself. If you don't know what this is then you probably should /// set this parameter to 'false'. /// /// @return the name of the function type. const interned_string& function_type::get_cached_name(bool internal) const { if (internal) { if (get_naked_canonical_type()) { if (priv_->internal_cached_name_.empty()) priv_->internal_cached_name_ = get_function_type_name(this, /*internal=*/true); return priv_->internal_cached_name_; } else { priv_->temp_internal_cached_name_ = get_function_type_name(this, /*internal=*/true); return priv_->temp_internal_cached_name_; } } else { if (get_naked_canonical_type()) { if (priv_->cached_name_.empty()) priv_->cached_name_ = get_function_type_name(this, /*internal=*/false); return priv_->cached_name_; } else { priv_->cached_name_ = get_function_type_name(this, /*internal=*/false); return priv_->cached_name_; } } } /// Equality operator for function_type. /// /// @param o the other function_type to compare against. /// /// @return true iff the two function_type are equal. bool function_type::operator==(const type_base& other) const { const function_type* o = dynamic_cast(&other); if (!o) return false; return try_canonical_compare(this, o); } /// Return a copy of the pretty representation of the current @ref /// function_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 @ref /// function_type. string function_type::get_pretty_representation(bool internal, bool /*qualified_name*/) const {return ir::get_pretty_representation(this, internal);} /// Traverses an instance of @ref function_type, 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 function_type::traverse(ir_node_visitor& v) { // TODO: should we allow the walker to avoid visiting function type // twice? I think that if we do, then ir_node_visitor needs an // option to specifically disallow this feature for function types. if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); bool keep_going = true; if (type_base_sptr t = get_return_type()) { if (!t->traverse(v)) keep_going = false; } if (keep_going) for (parameters::const_iterator i = get_parameters().begin(); i != get_parameters().end(); ++i) if (type_base_sptr parm_type = (*i)->get_type()) if (!parm_type->traverse(v)) break; visiting(false); } return v.visit_end(this); } function_type::~function_type() {} // // struct method_type::priv { class_or_union_wptr class_type_; bool is_const; priv() : is_const() {} }; // end struct method_type::priv /// Constructor for instances of method_type. /// /// Instances of method_decl must be of type method_type. /// /// @param return_type the type of the return value of the method. /// /// @param class_type the base type of the method type. That is, the /// type of the class the method belongs to. /// /// @param p the vector of the parameters of the method. /// /// @param is_const whether this method type is for a const method. /// Note that const-ness is a property of the method *type* and of the /// relationship between a method *declaration* and its scope. /// /// @param size_in_bits the size of an instance of method_type, /// expressed in bits. /// /// @param alignment_in_bits the alignment of an instance of /// method_type, expressed in bits. method_type::method_type (type_base_sptr return_type, class_or_union_sptr class_type, const std::vector& p, bool is_const, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(class_type->get_environment(), METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE), type_base(class_type->get_environment(), size_in_bits, alignment_in_bits), function_type(return_type, p, size_in_bits, alignment_in_bits), priv_(new priv) { runtime_type_instance(this); set_class_type(class_type); set_is_const(is_const); } /// Constructor of instances of method_type. /// ///Instances of method_decl must be of type method_type. /// /// @param return_type the type of the return value of the method. /// /// @param class_type the type of the class the method belongs to. /// The actual (dynamic) type of class_type must be a pointer /// class_type. We are setting it to pointer to type_base here to /// help client code that is compiled without rtti and thus cannot /// perform dynamic casts. /// /// @param p the vector of the parameters of the method type. /// /// @param is_const whether this method type is for a const method. /// Note that const-ness is a property of the method *type* and of the /// relationship between a method *declaration* and its scope. /// /// @param size_in_bits the size of an instance of method_type, /// expressed in bits. /// /// @param alignment_in_bits the alignment of an instance of /// method_type, expressed in bits. method_type::method_type(type_base_sptr return_type, type_base_sptr class_type, const std::vector& p, bool is_const, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(class_type->get_environment(), METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE), type_base(class_type->get_environment(), size_in_bits, alignment_in_bits), function_type(return_type, p, size_in_bits, alignment_in_bits), priv_(new priv) { runtime_type_instance(this); set_class_type(is_class_type(class_type)); set_is_const(is_const); } /// Constructor of the qualified_type_def /// /// @param env the environment we are operating from. /// /// @param size_in_bits the size of the type, expressed in bits. /// /// @param alignment_in_bits the alignment of the type, expressed in bits method_type::method_type(const environment* env, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(env, METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE), type_base(env, size_in_bits, alignment_in_bits), function_type(env, size_in_bits, alignment_in_bits), priv_(new priv) { runtime_type_instance(this); } /// Constructor of instances of method_type. /// /// When constructed with this constructor, and instane of method_type /// must set a return type using method_type::set_return_type /// /// @param class_typ the base type of the method type. That is, the /// type of the class (or union) the method belongs to. /// /// @param size_in_bits the size of an instance of method_type, /// expressed in bits. /// /// @param alignment_in_bits the alignment of an instance of /// method_type, expressed in bits. method_type::method_type(class_or_union_sptr class_type, bool is_const, size_t size_in_bits, size_t alignment_in_bits) : type_or_decl_base(class_type->get_environment(), METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE), type_base(class_type->get_environment(), size_in_bits, alignment_in_bits), function_type(class_type->get_environment(), size_in_bits, alignment_in_bits), priv_(new priv) { runtime_type_instance(this); set_class_type(class_type); set_is_const(is_const); } /// Get the class type this method belongs to. /// /// @return the class type. class_or_union_sptr method_type::get_class_type() const {return class_or_union_sptr(priv_->class_type_);} /// Sets the class type of the current instance of method_type. /// /// The class type is the type of the class the method belongs to. /// /// @param t the new class type to set. void method_type::set_class_type(const class_or_union_sptr& t) { if (!t) return; priv_->class_type_ = t; } /// Return a copy of the pretty representation of the current @ref /// method_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 @ref /// method_type. string method_type::get_pretty_representation(bool internal, bool /*qualified_name*/) const {return ir::get_pretty_representation(*this, internal);} /// Setter of the "is-const" property of @ref method_type. /// /// @param the new value of the "is-const" property. void method_type::set_is_const(bool f) {priv_->is_const = f;} /// Getter of the "is-const" property of @ref method_type. /// /// @return true iff the "is-const" property was set. bool method_type::get_is_const() const {return priv_->is_const;} /// The destructor of method_type method_type::~method_type() {} // // struct function_decl::priv { bool declared_inline_; decl_base::binding binding_; function_type_wptr type_; function_type* naked_type_; elf_symbol_sptr symbol_; interned_string id_; priv() : declared_inline_(false), binding_(decl_base::BINDING_GLOBAL), naked_type_() {} priv(function_type_sptr t, bool declared_inline, decl_base::binding binding) : declared_inline_(declared_inline), binding_(binding), type_(t), naked_type_(t.get()) {} priv(function_type_sptr t, bool declared_inline, decl_base::binding binding, elf_symbol_sptr s) : declared_inline_(declared_inline), binding_(binding), type_(t), naked_type_(t.get()), symbol_(s) {} }; // end sruct function_decl::priv /// Constructor of the @ref function_decl. /// /// @param name the name of the function. /// /// @param function_type the type of the function. /// /// @param declared_inline wether the function is declared inline. /// /// @param locus the source location of the function. /// /// @param mangled_name the linkage name of the function. /// /// @param vis the visibility of the function. /// /// @param bind the binding of the function. function_decl::function_decl(const string& name, function_type_sptr function_type, bool declared_inline, const location& locus, const string& mangled_name, visibility vis, binding bind) : type_or_decl_base(function_type->get_environment(), FUNCTION_DECL | ABSTRACT_DECL_BASE), decl_base(function_type->get_environment(), name, locus, mangled_name, vis), priv_(new priv(function_type, declared_inline, bind)) { runtime_type_instance(this); } /// Constructor of the function_decl type. /// /// This flavour of constructor is for when the pointer to the /// instance of function_type that the client code has is presented as /// a pointer to type_base. In that case, this constructor saves the /// client code from doing a dynamic_cast to get the function_type /// pointer. /// /// @param name the name of the function declaration. /// /// @param fn_type the type of the function declaration. The dynamic /// type of this parameter should be 'pointer to function_type' /// /// @param declared_inline whether this function was declared inline /// /// @param locus the source location of the function declaration. /// /// @param linkage_name the mangled name of the function declaration. /// /// @param vis the visibility of the function declaration. /// /// @param bind the kind of the binding of the function /// declaration. function_decl::function_decl(const string& name, type_base_sptr fn_type, bool declared_inline, const location& locus, const string& linkage_name, visibility vis, binding bind) : type_or_decl_base(fn_type->get_environment(), FUNCTION_DECL | ABSTRACT_DECL_BASE), decl_base(fn_type->get_environment(), name, locus, linkage_name, vis), priv_(new priv(dynamic_pointer_cast(fn_type), declared_inline, bind)) { runtime_type_instance(this); } /// Get the pretty representation of the current instance of @ref function_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. /// /// @return the pretty representation for a function. string function_decl::get_pretty_representation(bool internal, bool /*qualified_name*/) const { const method_decl* mem_fn = dynamic_cast(this); string result = mem_fn ? "method ": "function "; if (mem_fn && is_member_function(mem_fn) && get_member_function_is_virtual(mem_fn)) result += "virtual "; decl_base_sptr type; if ((mem_fn && is_member_function(mem_fn) && (get_member_function_is_dtor(*mem_fn) || get_member_function_is_ctor(*mem_fn)))) /*cdtors do not have return types. */; else type = mem_fn ? get_type_declaration(mem_fn->get_type()->get_return_type()) : get_type_declaration(get_type()->get_return_type()); if (type) result += type->get_qualified_name(internal) + " "; result += get_pretty_representation_of_declarator(internal); return result; } /// Compute and return the pretty representation for the part of the /// function declaration that starts at the declarator. That is, the /// return type and the other specifiers of the beginning of the /// function's declaration ar omitted. /// /// @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 the pretty representation for the part of the function /// declaration that starts at the declarator. string function_decl::get_pretty_representation_of_declarator (bool internal) const { const method_decl* mem_fn = dynamic_cast(this); string result; if (mem_fn) { result += mem_fn->get_type()->get_class_type()->get_qualified_name() + "::" + mem_fn->get_name(); } else result += get_qualified_name(); result += "("; parameters::const_iterator i = get_parameters().begin(), end = get_parameters().end(); // Skip the first parameter if this is a method. if (mem_fn && i != end) ++i; parameter_sptr parm; parameter_sptr first_parm; if (i != end) first_parm = *i; for (; i != end; ++i) { parm = *i; if (parm.get() != first_parm.get()) result += ", "; if (parm->get_variadic_marker() || get_environment()->is_variadic_parameter_type(parm->get_type())) result += "..."; else { decl_base_sptr type_decl = get_type_declaration(parm->get_type()); result += type_decl->get_qualified_name(internal); } } result += ")"; if (mem_fn &&((is_member_function(mem_fn) && get_member_function_is_const(*mem_fn)) || is_method_type(mem_fn->get_type())->get_is_const())) result += " const"; return result; } /// Getter for the first non-implicit parameter of a function decl. /// /// If the function is a non-static member function, the parameter /// returned is the first one following the implicit 'this' parameter. /// /// @return the first non implicit parm. function_decl::parameters::const_iterator function_decl::get_first_non_implicit_parm() const { if (get_parameters().empty()) return get_parameters().end(); bool is_method = dynamic_cast(this); parameters::const_iterator i = get_parameters().begin(); if (is_method) ++i; return i; } /// Return the type of the current instance of @ref function_decl. /// /// It's either a function_type or method_type. /// @return the type of the current instance of @ref function_decl. const shared_ptr function_decl::get_type() const {return priv_->type_.lock();} /// Fast getter of the type of the current instance of @ref function_decl. /// /// Note that this function returns the underlying pointer managed by /// the smart pointer returned by function_decl::get_type(). It's /// faster than function_decl::get_type(). This getter is to be used /// in code paths that are proven to be performance hot spots; /// especially (for instance) when comparing function types. Those /// are compared extremely frequently when libabigail is used to /// handle huge binaries with a lot of functions. /// /// @return the type of the current instance of @ref function_decl. const function_type* function_decl::get_naked_type() const {return priv_->naked_type_;} void function_decl::set_type(const function_type_sptr& fn_type) { priv_->type_ = fn_type; priv_->naked_type_ = fn_type.get(); } /// This sets the underlying ELF symbol for the current function decl. /// /// And underlyin$g ELF symbol for the current function decl might /// exist only if the corpus that this function decl originates from /// was constructed from an ELF binary file. /// /// Note that comparing two function decls that have underlying ELF /// symbols involves comparing their underlying elf symbols. The decl /// name for the function thus becomes irrelevant in the comparison. /// /// @param sym the new ELF symbol for this function decl. void function_decl::set_symbol(const elf_symbol_sptr& sym) { priv_->symbol_ = sym; // The function id cache that depends on the symbol must be // invalidated because the symbol changed. priv_->id_ = get_environment()->intern(""); } /// Gets the the underlying ELF symbol for the current variable, /// that was set using function_decl::set_symbol(). Please read the /// documentation for that member function for more information about /// "underlying ELF symbols". /// /// @return sym the underlying ELF symbol for this function decl, if /// one exists. const elf_symbol_sptr& function_decl::get_symbol() const {return priv_->symbol_;} bool function_decl::is_declared_inline() const {return priv_->declared_inline_;} decl_base::binding function_decl::get_binding() const {return priv_->binding_;} /// @return the return type of the current instance of function_decl. const shared_ptr function_decl::get_return_type() const {return get_type()->get_return_type();} /// @return the parameters of the function. const std::vector >& function_decl::get_parameters() const {return get_type()->get_parameters();} /// Append a parameter to the type of this function. /// /// @param parm the parameter to append. void function_decl::append_parameter(shared_ptr parm) {get_type()->append_parameter(parm);} /// Append a vector of parameters to the type of this function. /// /// @param parms the vector of parameters to append. void function_decl::append_parameters(std::vector >& parms) { for (std::vector >::const_iterator i = parms.begin(); i != parms.end(); ++i) get_type()->append_parameter(*i); } /// Create a new instance of function_decl that is a clone of the /// current one. /// /// @return the new clone. function_decl_sptr function_decl::clone() const { function_decl_sptr f; if (is_member_function(*this)) { method_decl_sptr m(new method_decl(get_name(), get_type(), is_declared_inline(), get_location(), get_linkage_name(), get_visibility(), get_binding())); class_or_union* scope = is_class_or_union_type(get_scope()); ABG_ASSERT(scope); scope->add_member_function(m, get_member_access_specifier(*this), get_member_function_is_virtual(*this), get_member_function_vtable_offset(*this), get_member_is_static(*this), get_member_function_is_ctor(*this), get_member_function_is_dtor(*this), get_member_function_is_const(*this)); f = m; } else { f.reset(new function_decl(get_name(), get_type(), is_declared_inline(), get_location(), get_linkage_name(), get_visibility(), get_binding())); add_decl_to_scope(f, get_scope()); } f->set_symbol(get_symbol()); return f; } /// Compares two instances of @ref function_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 function_decl& l, const function_decl& r, change_kind* k) { bool result = true; // Compare function types const type_base* t0 = l.get_naked_type(), *t1 = r.get_naked_type(); if (t0 == t1 || *t0 == *t1) ; // the types are equal, let's move on to compare the other // properties of the functions. else { result = false; if (k) { if (!types_have_similar_structure(t0, t1)) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; } else ABG_RETURN_FALSE; } const elf_symbol_sptr &s0 = l.get_symbol(), &s1 = r.get_symbol(); if (!!s0 != !!s1) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } else if (s0 && s0 != s1) { if (!elf_symbols_alias(s0, s1)) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } } bool symbols_are_equal = (s0 && s1 && result); if (symbols_are_equal) { // The functions have underlying elf symbols that are equal, // so now, let's compare the decl_base part of the functions // w/o considering their decl names. interned_string n1 = l.get_name(), n2 = r.get_name(); interned_string ln1 = l.get_linkage_name(), ln2 = r.get_linkage_name(); const_cast(l).set_name(""); const_cast(l).set_linkage_name(""); const_cast(r).set_name(""); const_cast(r).set_linkage_name(""); bool decl_bases_different = !l.decl_base::operator==(r); const_cast(l).set_name(n1); const_cast(l).set_linkage_name(ln1); const_cast(r).set_name(n2); const_cast(r).set_linkage_name(ln2); if (decl_bases_different) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } } else if (!l.decl_base::operator==(r)) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } // Compare the remaining properties if (l.is_declared_inline() != r.is_declared_inline() || l.get_binding() != r.get_binding()) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } if (is_member_function(l) != is_member_function(r)) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } if (is_member_function(l) && is_member_function(r)) { if (!((get_member_function_is_ctor(l) == get_member_function_is_ctor(r)) && (get_member_function_is_dtor(l) == get_member_function_is_dtor(r)) && (get_member_is_static(l) == get_member_is_static(r)) && (get_member_function_is_const(l) == get_member_function_is_const(r)) && (get_member_function_is_virtual(l) == get_member_function_is_virtual(r)) && (get_member_function_vtable_offset(l) == get_member_function_vtable_offset(r)))) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else ABG_RETURN_FALSE; } } ABG_RETURN(result); } /// Comparison operator for @ref function_decl. /// /// @param other the other instance of @ref function_decl to compare /// against. /// /// @return true iff the current instance of @ref function_decl equals /// @p other. bool function_decl::operator==(const decl_base& other) const { const function_decl* o = dynamic_cast(&other); if (!o) return false; return equals(*this, *o, 0); } /// Return true iff the function takes a variable number of /// parameters. /// /// @return true if the function taks a variable number /// of parameters. bool function_decl::is_variadic() const { return (!get_parameters().empty() && get_parameters().back()->get_variadic_marker()); } /// The virtual implementation of 'get_hash' for a function_decl. /// /// This allows decl_base::get_hash to work for function_decls. /// /// @return the hash value for function decl. size_t function_decl::get_hash() const { function_decl::hash hash_fn; return hash_fn(*this); } /// Return an ID that tries to uniquely identify the function inside a /// program or a library. /// /// So if the function has an underlying elf symbol, the ID is the /// concatenation of the symbol name and its version. Otherwise, the /// ID is the linkage name if its non-null. Otherwise, it's the /// pretty representation of the function. /// /// @return the ID. interned_string function_decl::get_id() const { if (priv_->id_.empty()) { const environment* env = get_type()->get_environment(); if (elf_symbol_sptr s = get_symbol()) { if (s->has_aliases()) // The symbol has several aliases, so let's use a scheme // that allows all aliased functions to have different // IDs. priv_->id_ = env->intern(get_name() + "/" + s->get_id_string()); else // Let's use the full symbol name with its version as ID. priv_->id_ = env->intern(s->get_id_string()); } else if (!get_linkage_name().empty()) priv_->id_= env->intern(get_linkage_name()); else priv_->id_ = env->intern(get_pretty_representation()); } return priv_->id_; } /// Test if two function declarations are aliases. /// /// Two functions declarations are aliases if their symbols are /// aliases, in the ELF sense. /// /// @param f1 the first function to consider. /// /// @param f2 the second function to consider. /// /// @return true iff @p f1 is an alias of @p f2 bool function_decls_alias(const function_decl& f1, const function_decl& f2) { elf_symbol_sptr s1 = f1.get_symbol(), s2 = f2.get_symbol(); if (!s1 || !s2) return false; return elf_symbols_alias(s1, s2); } /// 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 function_decl::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (type_base_sptr t = get_type()) t->traverse(v); visiting(false); } return v.visit_end(this); } /// Destructor of the @ref function_decl type. function_decl::~function_decl() {delete priv_;} /// A deep comparison operator for a shared pointer to @ref function_decl /// /// This function compares to shared pointers to @ref function_decl by /// looking at the pointed-to instances of @ref function_dec /// comparing them too. If the two pointed-to objects are equal then /// this function returns true. /// /// @param l the left-hand side argument of the equality operator. /// /// @param r the right-hand side argument of the equality operator. /// /// @return true iff @p l equals @p r. bool operator==(const function_decl_sptr& l, const function_decl_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// A deep inequality operator for smart pointers to functions. /// /// @param l the left-hand side argument of the inequality operator. /// /// @pram r the right-hand side argument of the inequality operator. /// /// @return true iff @p is not equal to @p r. bool operator!=(const function_decl_sptr& l, const function_decl_sptr& r) {return !operator==(l, r);} // // struct function_decl::parameter::priv { type_base_wptr type_; unsigned index_; bool variadic_marker_; priv() : index_(), variadic_marker_() {} priv(type_base_sptr type, unsigned index, bool variadic_marker) : type_(type), index_(index), variadic_marker_(variadic_marker) {} };// end struct function_decl::parameter::priv function_decl::parameter::parameter(const type_base_sptr type, unsigned index, const string& name, const location& loc, bool is_variadic) : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), priv_(new priv(type, index, is_variadic)) { runtime_type_instance(this); } function_decl::parameter::parameter(const type_base_sptr type, unsigned index, const string& name, const location& loc, bool is_variadic, bool is_artificial) : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), priv_(new priv(type, index, is_variadic)) { runtime_type_instance(this); set_is_artificial(is_artificial); } function_decl::parameter::parameter(const type_base_sptr type, const string& name, const location& loc, bool is_variadic, bool is_artificial) : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), priv_(new priv(type, 0, is_variadic)) { runtime_type_instance(this); set_is_artificial(is_artificial); } function_decl::parameter::parameter(const type_base_sptr type, unsigned index, bool variad) : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), "", location()), priv_(new priv(type, index, variad)) { runtime_type_instance(this); } function_decl::parameter::~parameter() = default; const type_base_sptr function_decl::parameter::get_type()const {return priv_->type_.lock();} /// @return a copy of the type name of the parameter. interned_string function_decl::parameter::get_type_name() const { const environment* env = get_environment(); ABG_ASSERT(env); type_base_sptr t = get_type(); string str; if (get_variadic_marker() || env->is_variadic_parameter_type(t)) str = "..."; else { ABG_ASSERT(t); str = abigail::ir::get_type_name(t); } return env->intern(str); } /// @return a copy of the pretty representation of the type of the /// parameter. const string function_decl::parameter::get_type_pretty_representation() const { type_base_sptr t = get_type(); string str; if (get_variadic_marker() || get_environment()->is_variadic_parameter_type(t)) str = "..."; else { ABG_ASSERT(t); str += get_type_declaration(t)->get_pretty_representation(); } return str; } /// Get a name uniquely identifying the parameter in the function. /// ///@return the unique parm name id. interned_string function_decl::parameter::get_name_id() const { const environment* env = get_environment(); ABG_ASSERT(env); std::ostringstream o; o << "parameter-" << get_index(); return env->intern(o.str()); } unsigned function_decl::parameter::get_index() const {return priv_->index_;} void function_decl::parameter::set_index(unsigned i) {priv_->index_ = i;} bool function_decl::parameter::get_variadic_marker() const {return priv_->variadic_marker_;} /// Compares two instances of @ref function_decl::parameter. /// /// 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 function_decl::parameter& l, const function_decl::parameter& r, change_kind* k) { bool result = true; if ((l.get_variadic_marker() != r.get_variadic_marker()) || (l.get_index() != r.get_index()) || (!!l.get_type() != !!r.get_type())) { result = false; if (k) { if (l.get_index() != r.get_index()) *k |= LOCAL_NON_TYPE_CHANGE_KIND; if (l.get_variadic_marker() != r.get_variadic_marker() || !!l.get_type() != !!r.get_type()) *k |= LOCAL_TYPE_CHANGE_KIND; } else ABG_RETURN_FALSE; } type_base_sptr l_type = l.get_type(); type_base_sptr r_type = r.get_type(); if (l_type != r_type) { result = false; if (k) { if (!types_have_similar_structure(l_type, r_type)) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; } else ABG_RETURN_FALSE; } ABG_RETURN(result); } bool function_decl::parameter::operator==(const parameter& o) const {return equals(*this, o, 0);} bool function_decl::parameter::operator==(const decl_base& o) const { const function_decl::parameter* p = dynamic_cast(&o); if (!p) return false; return function_decl::parameter::operator==(*p); } /// Non-member equality operator for @ref function_decl::parameter. /// /// @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 function_decl::parameter_sptr& l, const function_decl::parameter_sptr& r) { if (!!l != !!r) return false; if (!l) return true; return *l == *r; } /// Non-member inequality operator for @ref function_decl::parameter. /// /// @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 different. bool operator!=(const function_decl::parameter_sptr& l, const function_decl::parameter_sptr& r) {return !operator==(l, r);} /// Traverse the diff sub-tree under the current instance /// function_decl. /// /// @param v the visitor to invoke on each diff node of the sub-tree. /// /// @return true if the traversing has to keep going on, false /// otherwise. bool function_decl::parameter::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (type_base_sptr t = get_type()) t->traverse(v); visiting(false); } return v.visit_end(this); } /// 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 function_decl::parameter::get_hash() const { function_decl::parameter::hash hash_fn_parm; return hash_fn_parm(this); } /// Compute the qualified name of the parameter. /// /// @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. /// /// @param qn the resulting qualified name. void function_decl::parameter::get_qualified_name(interned_string& qualified_name, bool /*internal*/) const {qualified_name = get_name();} /// Compute and return a copy of the pretty representation of the /// current function parameter. /// /// @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 textual representation of the current /// function parameter. string function_decl::parameter::get_pretty_representation(bool internal, bool /*qualified_name*/) const { const environment* env = get_environment(); ABG_ASSERT(env); string type_repr; type_base_sptr t = get_type(); if (!t) type_repr = "void"; else if (env->is_variadic_parameter_type(t)) type_repr = "..."; else type_repr = ir::get_pretty_representation(t, internal); string result = type_repr; string parm_name = get_name_id(); if (!parm_name.empty()) result += " " + parm_name; return result; } // // /// A Constructor for instances of @ref class_or_union /// /// @param env the environment we are operating from. /// /// @param name the identifier of the class. /// /// @param size_in_bits the size of an instance of @ref /// class_or_union, expressed in bits /// /// @param align_in_bits the alignment of an instance of @ref class_or_union, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of @ref class_or_union. /// /// @param mem_types the vector of member types of this instance of /// @ref class_or_union. /// /// @param data_members the vector of data members of this instance of /// @ref class_or_union. /// /// @param member_fns the vector of member functions of this instance /// of @ref class_or_union. class_or_union::class_or_union(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, const location& locus, visibility vis, member_types& mem_types, data_members& data_members, member_functions& member_fns) : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, align_in_bits), scope_type_decl(env, name, size_in_bits, align_in_bits, locus, vis), priv_(new priv(mem_types, data_members, member_fns)) { for (member_types::iterator i = mem_types.begin(); i != mem_types.end(); ++i) if (!has_scope(get_type_declaration(*i))) add_decl_to_scope(get_type_declaration(*i), this); for (data_members::iterator i = data_members.begin(); i != data_members.end(); ++i) if (!has_scope(*i)) add_decl_to_scope(*i, this); for (member_functions::iterator i = member_fns.begin(); i != member_fns.end(); ++i) if (!has_scope(static_pointer_cast(*i))) add_decl_to_scope(*i, this); } /// A constructor for instances of @ref class_or_union. /// /// @param env the environment we are operating from. /// /// @param name the name of the class. /// /// @param size_in_bits the size of an instance of @ref /// class_or_union, expressed in bits /// /// @param align_in_bits the alignment of an instance of @ref class_or_union, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of @ref class_or_union. class_or_union::class_or_union(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, const location& locus, visibility vis) : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, align_in_bits), scope_type_decl(env, name, size_in_bits, align_in_bits, locus, vis), priv_(new priv) {} /// Constructor of the @ref class_or_union type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the @ref class_or_union. /// /// @param is_declaration_only a boolean saying whether the instance /// represents a declaration only, or not. class_or_union::class_or_union(const environment* env, const string& name, bool is_declaration_only) : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, location(), name), type_base(env, 0, 0), scope_type_decl(env, name, 0, 0, location()), priv_(new priv) { set_is_declaration_only(is_declaration_only); } /// 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 class_or_union::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); bool stop = false; if (!stop) for (data_members::const_iterator i = get_data_members().begin(); i != get_data_members().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_functions::const_iterator i= get_member_functions().begin(); i != get_member_functions().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_types::const_iterator i = get_member_types().begin(); i != get_member_types().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_function_templates::const_iterator i = get_member_function_templates().begin(); i != get_member_function_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_class_templates::const_iterator i = get_member_class_templates().begin(); i != get_member_class_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } visiting(false); } bool result = v.visit_end(this); v.mark_type_node_as_visited(this); return result; } /// Destrcutor of the @ref class_or_union type. class_or_union::~class_or_union() {delete priv_;} /// Add a member declaration to the current instance of class_or_union. /// The member declaration can be either a member type, data member, /// member function, or member template. /// /// @param d the member declaration to add. decl_base_sptr class_or_union::add_member_decl(const decl_base_sptr& d) {return insert_member_decl(d, get_member_decls().end());} /// Remove a given decl from the current @ref class_or_union scope. /// /// Note that only type declarations are supported by this method for /// now. Support for the other kinds of declaration is left as an /// exercise for the interested reader of the code. /// /// @param decl the declaration to remove from this @ref /// class_or_union scope. void class_or_union::remove_member_decl(decl_base_sptr decl) { type_base_sptr t = is_type(decl); // For now we want to support just removing types from classes. For // other kinds of IR node, we need more work. ABG_ASSERT(t); remove_member_type(t); } /// Fixup the members of the type of an anonymous data member. /// /// Walk all data members of (the type of) a given anonymous data /// member and set a particular property of the relationship between /// each data member and its containing type. /// /// That property records the fact that the data member belongs to the /// anonymous data member we consider. /// /// In the future, if there are other properties of this relationship /// to set in this manner, they ought to be added here. /// /// @param anon_dm the anonymous data member to consider. void class_or_union::maybe_fixup_members_of_anon_data_member(var_decl_sptr& anon_dm) { class_or_union * anon_dm_type = anonymous_data_member_to_class_or_union(anon_dm.get()); if (!anon_dm_type) return; for (class_or_union::data_members::const_iterator it = anon_dm_type->get_non_static_data_members().begin(); it != anon_dm_type->get_non_static_data_members().end(); ++it) { dm_context_rel *rel = dynamic_cast((*it)->get_context_rel()); ABG_ASSERT(rel); rel->set_anonymous_data_member(anon_dm.get()); } } /// Insert a member type. /// /// @param t the type to insert in the @ref class_or_union type. /// /// @param an iterator right before which @p t has to be inserted. void class_or_union::insert_member_type(type_base_sptr t, declarations::iterator before) { decl_base_sptr d = get_type_declaration(t); ABG_ASSERT(d); ABG_ASSERT(!has_scope(d)); priv_->member_types_.push_back(t); scope_decl::insert_member_decl(d, before); } /// Add a member type to the current instance of class_or_union. /// /// @param t the member type to add. It must not have been added to a /// scope, otherwise this will violate an ABG_ASSERTion. void class_or_union::add_member_type(type_base_sptr t) {insert_member_type(t, get_member_decls().end());} /// Add a member type to the current instance of class_or_union. /// /// @param t the type to be added as a member type to the current /// instance of class_or_union. An instance of class_or_union::member_type /// will be created out of @p t and and added to the the class. /// /// @param a the access specifier for the member type to be created. type_base_sptr class_or_union::add_member_type(type_base_sptr t, access_specifier a) { decl_base_sptr d = get_type_declaration(t); ABG_ASSERT(d); ABG_ASSERT(!is_member_decl(d)); add_member_type(t); set_member_access_specifier(d, a); return t; } /// Remove a member type from the current @ref class_or_union scope. /// /// @param t the type to remove. void class_or_union::remove_member_type(type_base_sptr t) { for (member_types::iterator i = priv_->member_types_.begin(); i != priv_->member_types_.end(); ++i) { if (*((*i)) == *t) { priv_->member_types_.erase(i); return; } } } /// Getter of the alignment of the @ref class_or_union type. /// /// If this @ref class_or_union is a declaration of a definition that /// is elsewhere, then the size of the definition is returned. /// /// @return the alignment of the @ref class_or_union type. size_t class_or_union::get_alignment_in_bits() const { if (get_is_declaration_only() && get_definition_of_declaration()) return is_class_or_union_type (get_definition_of_declaration())->get_alignment_in_bits(); return type_base::get_alignment_in_bits(); } /// Setter of the alignment of the class type. /// /// If this class is a declaration of a definition that is elsewhere, /// then the new alignment is set to the definition. /// /// @param s the new alignment. void class_or_union::set_alignment_in_bits(size_t a) { if (get_is_declaration_only() && get_definition_of_declaration()) is_class_or_union_type (get_definition_of_declaration()) ->set_alignment_in_bits(a); else type_base::set_alignment_in_bits(a); } /// Setter of the size of the @ref class_or_union type. /// /// If this @ref class_or_union is a declaration of a definition that /// is elsewhere, then the new size is set to the definition. /// /// @param s the new size. void class_or_union::set_size_in_bits(size_t s) { if (get_is_declaration_only() && get_definition_of_declaration()) is_class_or_union_type (get_definition_of_declaration())->set_size_in_bits(s); else type_base::set_size_in_bits(s); } /// Getter of the size of the @ref class_or_union type. /// /// If this @ref class_or_union is a declaration of a definition that /// is elsewhere, then the size of the definition is returned. /// /// @return the size of the @ref class_or_union type. size_t class_or_union::get_size_in_bits() const { if (get_is_declaration_only() && get_definition_of_declaration()) return is_class_or_union_type (get_definition_of_declaration())->get_size_in_bits(); return type_base::get_size_in_bits(); } /// Get the member types of this @ref class_or_union. /// /// @return a vector of the member types of this ref class_or_union. const class_or_union::member_types& class_or_union::get_member_types() const {return priv_->member_types_;} /// Get the number of anonymous member classes contained in this /// class. /// /// @return the number of anonymous member classes contained in this /// class. size_t class_or_union::get_num_anonymous_member_classes() const { int result = 0; for (member_types::const_iterator it = get_member_types().begin(); it != get_member_types().end(); ++it) if (class_decl_sptr t = is_class_type(*it)) if (t->get_is_anonymous()) ++result; return result; } /// Get the number of anonymous member unions contained in this class. /// /// @return the number of anonymous member unions contained in this /// class. size_t class_or_union::get_num_anonymous_member_unions() const { int result = 0; for (member_types::const_iterator it = get_member_types().begin(); it != get_member_types().end(); ++it) if (union_decl_sptr t = is_union_type(*it)) if (t->get_is_anonymous()) ++result; return result; } /// Get the number of anonymous member enums contained in this class. /// /// @return the number of anonymous member enums contained in this /// class. size_t class_or_union::get_num_anonymous_member_enums() const { int result = 0; for (member_types::const_iterator it = get_member_types().begin(); it != get_member_types().end(); ++it) if (enum_type_decl_sptr t = is_enum_type(*it)) if (t->get_is_anonymous()) ++result; return result; } /// Find a member type of a given name, inside the current @ref /// class_or_union. /// /// @param name the name of the member type to look for. /// /// @return a pointer to the @ref type_base that represents the member /// type of name @p name, for the current class. type_base_sptr class_or_union::find_member_type(const string& name) const { for (member_types::const_iterator i = get_member_types().begin(); i != get_member_types().end(); ++i) if (get_type_name(*i, /*qualified*/false) == name) return *i; return type_base_sptr(); } /// Add a data member to the current instance of class_or_union. /// /// @param v a var_decl to add as a data member. A proper /// class_or_union::data_member is created from @p v and added to the /// class_or_union. This var_decl should not have been already added /// to a scope. /// /// @param access the access specifier for the data member. /// /// @param is_laid_out whether the data member was laid out. That is, /// if its offset has been computed. In the pattern of a class /// template for instance, this would be set to false. /// /// @param is_static whether the data memer is static. /// /// @param offset_in_bits if @p is_laid_out is true, this is the /// offset of the data member, expressed (oh, surprise) in bits. void class_or_union::add_data_member(var_decl_sptr v, access_specifier access, bool is_laid_out, bool is_static, size_t offset_in_bits) { ABG_ASSERT(!has_scope(v)); priv_->data_members_.push_back(v); scope_decl::add_member_decl(v); set_data_member_is_laid_out(v, is_laid_out); set_data_member_offset(v, offset_in_bits); set_member_access_specifier(v, access); set_member_is_static(v, is_static); if (!is_static) { // If this is a non-static variable, add it to the set of // non-static variables, if it's not only in there. bool is_already_in = false; for (data_members::const_iterator i = priv_->non_static_data_members_.begin(); i != priv_->non_static_data_members_.end(); ++i) if (*i == v) { is_already_in = true; break; } if (!is_already_in) priv_->non_static_data_members_.push_back(v); } // If v is an anonymous data member, then fixup its data members. // For now, the only thing the fixup does is to make the data // members of the anonymous data member be aware of their containing // anonymous data member. That is helpful to compute the absolute // bit offset of each of the members of the anonymous data member. maybe_fixup_members_of_anon_data_member(v); } /// Get the data members of this @ref class_or_union. /// /// @return a vector of the data members of this @ref class_or_union. const class_or_union::data_members& class_or_union::get_data_members() const {return priv_->data_members_;} /// Find a data member of a given name in the current @ref class_or_union. /// /// @param name the name of the data member to find in the current /// @ref class_or_union. /// /// @return a pointer to the @ref var_decl that represents the data /// member to find inside the current @ref class_or_union. const var_decl_sptr class_or_union::find_data_member(const string& name) const { for (data_members::const_iterator i = get_data_members().begin(); i != get_data_members().end(); ++i) if ((*i)->get_name() == name) return *i; // We haven't found a data member with the name 'name'. Let's look // closer again, this time in our anonymous data members. for (data_members::const_iterator i = get_data_members().begin(); i != get_data_members().end(); ++i) if (is_anonymous_data_member(*i)) { class_or_union_sptr type = is_class_or_union_type((*i)->get_type()); ABG_ASSERT(type); if (var_decl_sptr data_member = type->find_data_member(name)) return data_member; } return var_decl_sptr(); } /// Find an anonymous data member in the class. /// /// @param v the anonymous data member to find. /// /// @return the anonymous data member found, or nil if none was found. const var_decl_sptr class_or_union::find_anonymous_data_member(const var_decl_sptr& v) const { if (!v->get_name().empty()) return var_decl_sptr(); for (data_members::const_iterator it = get_non_static_data_members().begin(); it != get_non_static_data_members().end(); ++it) { if (is_anonymous_data_member(*it)) if ((*it)->get_pretty_representation(/*internal=*/false, true) == v->get_pretty_representation(/*internal=*/false, true)) return *it; } return var_decl_sptr(); } /// Find a given data member. /// /// This function takes a @ref var_decl as an argument. If it has a /// non-empty name, then it tries to find a data member which has the /// same name as the argument. /// /// If it has an empty name, then the @ref var_decl is considered as /// an anonymous data member. In that case, this function tries to /// find an anonymous data member which type equals that of the @ref /// var_decl argument. /// /// @param v this carries either the name of the data member we need /// to look for, or the type of the anonymous data member we are /// looking for. const var_decl_sptr class_or_union::find_data_member(const var_decl_sptr& v) const { if (!v) return var_decl_sptr(); if (v->get_name().empty()) return find_anonymous_data_member(v); return find_data_member(v->get_name()); } /// Get the non-static data memebers of this @ref class_or_union. /// /// @return a vector of the non-static data members of this @ref /// class_or_union. const class_or_union::data_members& class_or_union::get_non_static_data_members() const {return priv_->non_static_data_members_;} /// Add a member function. /// /// @param f the new member function to add. /// /// @param a the access specifier to use for the new member function. /// /// @param is_static whether the new member function is static. /// /// @param is_ctor whether the new member function is a constructor. /// /// @param is_dtor whether the new member function is a destructor. /// /// @param is_const whether the new member function is const. void class_or_union::add_member_function(method_decl_sptr f, access_specifier a, bool is_static, bool is_ctor, bool is_dtor, bool is_const) { ABG_ASSERT(!has_scope(f)); scope_decl::add_member_decl(f); set_member_function_is_ctor(f, is_ctor); set_member_function_is_dtor(f, is_dtor); set_member_access_specifier(f, a); set_member_is_static(f, is_static); set_member_function_is_const(f, is_const); priv_->member_functions_.push_back(f); // Update the map of linkage name -> member functions. It's useful, // so that class_or_union::find_member_function() can function. if (!f->get_linkage_name().empty()) priv_->mem_fns_map_[f->get_linkage_name()] = f; } /// Get the member functions of this @ref class_or_union. /// /// @return a vector of the member functions of this @ref /// class_or_union. const class_or_union::member_functions& class_or_union::get_member_functions() const {return priv_->member_functions_;} /// Find a method, using its linkage name as a key. /// /// @param linkage_name the linkage name of the method to find. /// /// @return the method found, or nil if none was found. const method_decl* class_or_union::find_member_function(const string& linkage_name) const { return const_cast(this)->find_member_function(linkage_name); } /// Find a method, using its linkage name as a key. /// /// @param linkage_name the linkage name of the method to find. /// /// @return the method found, or nil if none was found. method_decl* class_or_union::find_member_function(const string& linkage_name) { string_mem_fn_sptr_map_type::const_iterator i = priv_->mem_fns_map_.find(linkage_name); if (i == priv_->mem_fns_map_.end()) return 0; return i->second.get(); } /// Find a method, using its linkage name as a key. /// /// @param linkage_name the linkage name of the method to find. /// /// @return the method found, or nil if none was found. method_decl_sptr class_or_union::find_member_function_sptr(const string& linkage_name) { string_mem_fn_sptr_map_type::const_iterator i = priv_->mem_fns_map_.find(linkage_name); if (i == priv_->mem_fns_map_.end()) return 0; return i->second; } /// Find a method (member function) using its signature (pretty /// representation) as a key. /// /// @param s the signature of the method. /// /// @return the method found, or nil if none was found. const method_decl* class_or_union::find_member_function_from_signature(const string& s) const { return const_cast(this)->find_member_function_from_signature(s); } /// Find a method (member function) using its signature (pretty /// representation) as a key. /// /// @param s the signature of the method. /// /// @return the method found, or nil if none was found. method_decl* class_or_union::find_member_function_from_signature(const string& s) { string_mem_fn_ptr_map_type::const_iterator i = priv_->signature_2_mem_fn_map_.find(s); if (i == priv_->signature_2_mem_fn_map_.end()) return 0; return i->second; } /// Get the member function templates of this class. /// /// @return a vector of the member function templates of this class. const member_function_templates& class_or_union::get_member_function_templates() const {return priv_->member_function_templates_;} /// Get the member class templates of this class. /// /// @return a vector of the member class templates of this class. const member_class_templates& class_or_union::get_member_class_templates() const {return priv_->member_class_templates_;} /// Append a member function template to the @ref class_or_union. /// /// @param m the member function template to append. void class_or_union::add_member_function_template(member_function_template_sptr m) { decl_base* c = m->as_function_tdecl()->get_scope(); /// TODO: use our own ABG_ASSERTion facility that adds a meaningful /// error message or something like a structured error. priv_->member_function_templates_.push_back(m); if (!c) scope_decl::add_member_decl(m->as_function_tdecl()); } /// Append a member class template to the @ref class_or_union. /// /// @param m the member function template to append. void class_or_union::add_member_class_template(member_class_template_sptr m) { decl_base* c = m->as_class_tdecl()->get_scope(); /// TODO: use our own ABG_ASSERTion facility that adds a meaningful /// error message or something like a structured error. m->set_scope(this); priv_->member_class_templates_.push_back(m); if (!c) scope_decl::add_member_decl(m->as_class_tdecl()); } ///@return true iff the current instance has no member. bool class_or_union::has_no_member() const { return (priv_->member_types_.empty() && priv_->data_members_.empty() && priv_->member_functions_.empty() && priv_->member_function_templates_.empty() && priv_->member_class_templates_.empty()); } /// Insert a data member to this @ref class_or_union type. /// /// @param d the data member to insert. /// /// @param before an iterator to the point before which to insert the /// the data member, in the coontainer that contains all the data /// members. decl_base_sptr class_or_union::insert_member_decl(decl_base_sptr d, declarations::iterator before) { if (type_base_sptr t = dynamic_pointer_cast(d)) insert_member_type(t, before); else if (var_decl_sptr v = dynamic_pointer_cast(d)) { add_data_member(v, public_access, /*is_laid_out=*/false, /*is_static=*/true, /*offset_in_bits=*/0); d = v; } else if (method_decl_sptr f = dynamic_pointer_cast(d)) add_member_function(f, public_access, /*is_static=*/false, /*is_ctor=*/false, /*is_dtor=*/false, /*is_const=*/false); else if (member_function_template_sptr f = dynamic_pointer_cast(d)) add_member_function_template(f); else if (member_class_template_sptr c = dynamic_pointer_cast(d)) add_member_class_template(c); else scope_decl::add_member_decl(d); return d; } /// Equality operator. /// /// @param other the other @ref class_or_union to compare against. /// /// @return true iff @p other equals the current @ref class_or_union. bool class_or_union::operator==(const decl_base& other) const { const class_or_union* op = dynamic_cast(&other); if (!op) return false; // If this is a decl-only type (and thus with no canonical type), // use the canonical type of the definition, if any. const class_or_union *l = 0; if (get_is_declaration_only()) l = dynamic_cast(get_naked_definition_of_declaration()); if (l == 0) l = this; // Likewise for the other class. const class_or_union *r = 0; if (op->get_is_declaration_only()) r = dynamic_cast(op->get_naked_definition_of_declaration()); if (r == 0) r = op; return try_canonical_compare(l, r); } /// Equality operator. /// /// @param other the other @ref class_or_union to compare against. /// /// @return true iff @p other equals the current @ref class_or_union. bool class_or_union::operator==(const type_base& other) const { const decl_base* o = dynamic_cast(&other); if (!o) return false; return *this == *o; } /// Equality operator. /// /// @param other the other @ref class_or_union to compare against. /// /// @return true iff @p other equals the current @ref class_or_union. bool class_or_union::operator==(const class_or_union& other) const { const decl_base& o = other; return class_or_union::operator==(o); } /// Compares two instances of @ref class_or_union. /// /// 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 class_or_union& l, const class_or_union& r, change_kind* k) { #define RETURN(value) return return_comparison_result(l, r, value); // if one of the classes is declaration-only, look through it to // get its definition. bool l_is_decl_only = l.get_is_declaration_only(); bool r_is_decl_only = r.get_is_declaration_only(); if (l_is_decl_only || r_is_decl_only) { const class_or_union* def1 = l_is_decl_only ? is_class_or_union_type(l.get_naked_definition_of_declaration()) : &l; const class_or_union* def2 = r_is_decl_only ? is_class_or_union_type(r.get_naked_definition_of_declaration()) : &r; if (!def1 || !def2) { if (!l.get_is_anonymous() && !r.get_is_anonymous() && l_is_decl_only && r_is_decl_only && comparison::filtering::is_decl_only_class_with_size_change(l, r)) // The two decl-only classes differ from their size. A // true decl-only class should not have a size property to // begin with. This comes from a DWARF oddity and can // results in a false positive, so let's not consider that // change. return true; if ((l.get_environment()->decl_only_class_equals_definition() || ((odr_is_relevant(l) && !def1) || (odr_is_relevant(r) && !def2))) && !is_anonymous_or_typedef_named(l) && !is_anonymous_or_typedef_named(r)) { const interned_string& q1 = l.get_scoped_name(); const interned_string& q2 = r.get_scoped_name(); if (q1 == q2) // Not using RETURN(true) here, because that causes // performance issues. We don't need to do // l.priv_->unmark_as_being_compared({l,r}) here because // we haven't marked l or r as being compared yet, and // doing so has a peformance cost that shows up on // performance profiles for *big* libraries. return true; else { if (k) *k |= LOCAL_TYPE_CHANGE_KIND; // Not using RETURN(true) here, because that causes // performance issues. We don't need to do // l.priv_->unmark_as_being_compared({l,r}) here because // we haven't marked l or r as being compared yet, and // doing so has a peformance cost that shows up on // performance profiles for *big* libraries. ABG_RETURN_FALSE; } } else // A decl-only class is considered different from a // class definition of the same name. { if (!!def1 != !!def2) { if (k) *k |= LOCAL_TYPE_CHANGE_KIND; ABG_RETURN_FALSE; } // both definitions are empty if (!(l.decl_base::operator==(r) && l.type_base::operator==(r))) { if (k) *k |= LOCAL_TYPE_CHANGE_KIND; ABG_RETURN_FALSE; } return true; } } RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r); mark_types_as_being_compared(l, r); bool val = *def1 == *def2; if (!val) if (k) *k |= LOCAL_TYPE_CHANGE_KIND; RETURN(val); } // No need to go further if the classes have different names or // different size / alignment. if (!(l.decl_base::operator==(r) && l.type_base::operator==(r))) { if (k) *k |= LOCAL_TYPE_CHANGE_KIND; ABG_RETURN_FALSE; } if (types_defined_same_linux_kernel_corpus_public(l, r)) return true; RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r); mark_types_as_being_compared(l, r); bool result = true; //compare data_members { if (l.get_non_static_data_members().size() != r.get_non_static_data_members().size()) { result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else RETURN(result); } for (class_or_union::data_members::const_iterator d0 = l.get_non_static_data_members().begin(), d1 = r.get_non_static_data_members().begin(); (d0 != l.get_non_static_data_members().end() && d1 != r.get_non_static_data_members().end()); ++d0, ++d1) if (**d0 != **d1) { result = false; if (k) { // Report any representation change as being local. if (!types_have_similar_structure((*d0)->get_type(), (*d1)->get_type()) || (*d0)->get_type() == (*d1)->get_type()) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; } else RETURN(result); } } // Do not compare member functions. DWARF does not necessarily // all the member functions, be they virtual or not, in all // translation units. So we cannot have a clear view of them, per // class // compare member function templates { if (l.get_member_function_templates().size() != r.get_member_function_templates().size()) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else RETURN(result); } for (member_function_templates::const_iterator fn_tmpl_it0 = l.get_member_function_templates().begin(), fn_tmpl_it1 = r.get_member_function_templates().begin(); fn_tmpl_it0 != l.get_member_function_templates().end() && fn_tmpl_it1 != r.get_member_function_templates().end(); ++fn_tmpl_it0, ++fn_tmpl_it1) if (**fn_tmpl_it0 != **fn_tmpl_it1) { result = false; if (k) { *k |= LOCAL_NON_TYPE_CHANGE_KIND; break; } else RETURN(result); } } // compare member class templates { if (l.get_member_class_templates().size() != r.get_member_class_templates().size()) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else RETURN(result); } for (member_class_templates::const_iterator cl_tmpl_it0 = l.get_member_class_templates().begin(), cl_tmpl_it1 = r.get_member_class_templates().begin(); cl_tmpl_it0 != l.get_member_class_templates().end() && cl_tmpl_it1 != r.get_member_class_templates().end(); ++cl_tmpl_it0, ++cl_tmpl_it1) if (**cl_tmpl_it0 != **cl_tmpl_it1) { result = false; if (k) { *k |= LOCAL_NON_TYPE_CHANGE_KIND; break; } else RETURN(result); } } RETURN(result); #undef RETURN } /// Copy a method of a @ref class_or_union into a new @ref /// class_or_union. /// /// @param t the @ref class_or_union into which the method is to be copied. /// /// @param method the method to copy into @p t. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const class_or_union_sptr& t, const method_decl_sptr& method) {return copy_member_function(t, method.get());} /// Copy a method of a @ref class_or_union into a new @ref /// class_or_union. /// /// @param t the @ref class_or_union into which the method is to be copied. /// /// @param method the method to copy into @p t. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const class_or_union_sptr& t, const method_decl* method) { ABG_ASSERT(t); ABG_ASSERT(method); method_type_sptr old_type = method->get_type(); ABG_ASSERT(old_type); method_type_sptr new_type(new method_type(old_type->get_return_type(), t, old_type->get_parameters(), old_type->get_is_const(), old_type->get_size_in_bits(), old_type->get_alignment_in_bits())); new_type->set_environment(t->get_environment()); keep_type_alive(new_type); method_decl_sptr new_method(new method_decl(method->get_name(), new_type, method->is_declared_inline(), method->get_location(), method->get_linkage_name(), method->get_visibility(), method->get_binding())); new_method->set_symbol(method->get_symbol()); if (class_decl_sptr class_type = is_class_type(t)) class_type->add_member_function(new_method, get_member_access_specifier(*method), get_member_function_is_virtual(*method), get_member_function_vtable_offset(*method), get_member_is_static(*method), get_member_function_is_ctor(*method), get_member_function_is_dtor(*method), get_member_function_is_const(*method)); else t->add_member_function(new_method, get_member_access_specifier(*method), get_member_is_static(*method), get_member_function_is_ctor(*method), get_member_function_is_dtor(*method), get_member_function_is_const(*method)); return new_method; } // /// @defgroup OnTheFlyCanonicalization On-the-fly Canonicalization /// @{ /// /// This optimization is also known as "canonical type propagation". /// /// During the canonicalization of a type T (which doesn't yet have a /// canonical type), T is compared structurally (member-wise) against /// a type C which already has a canonical type. The comparison /// expression is C == T. /// /// During that structural comparison, if a subtype of C (which also /// already has a canonical type) is structurally compared to a /// subtype of T (which doesn't yet have a canonical type) and if they /// are equal, then we can deduce that the canonical type of the /// subtype of C is the canonical type of the subtype of C. /// /// Thus, we can canonicalize the sub-type of the T, during the /// canonicalization of T itself. That canonicalization of the /// sub-type of T is what we call the "on-the-fly canonicalization". /// It's on the fly because it happens during a comparison -- which /// itself happens during the canonicalization of T. /// /// For now this on-the-fly canonicalization only happens when /// comparing @ref class_decl and @ref function_type. /// /// Note however that there is a case when a type is *NOT* eligible to /// this canonical type propagation optimization. /// /// 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 /// | /// +-- ST2 /// /// 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. /// /// Now suppose we are comparing T to a type T' that has the same /// structure with sub-types ST0', ST1' and ST2'. During the /// comparison of ST1 against ST1', their sub-type T is compared /// against T'. Because T (resp. T') is a recursive type that is /// already being compared, the comparison of T against T' (as a /// subtypes of ST1 and ST1') returns true, meaning they are /// considered equal. This is done so that we don't enter an infinite /// recursion. /// /// That means ST1 is also deemed equal to ST1'. If we are in the /// course of the canonicalization of T' and thus if T (as well as as /// all of its sub-types) is already canonicalized, then the canonical /// type propagation optimization will make us propagate the canonical /// type of ST1 onto ST1'. So the canonical type of ST1' will be /// equal to the canonical type of ST1 as a result of that /// optmization. /// /// But then, later down the road, when ST2 is compared against ST2', /// let's suppose that we find out that they are different. Meaning /// that ST2 != ST2'. This means that T != T', i.e, the /// canonicalization of T' failed for now. But most importantly, it /// means that the propagation of the canonical type of ST1 to ST1' /// must now be invalidated. Meaning, ST1' must now be considered as /// not having any canonical type. /// /// In other words, during type canonicalization, if ST1' depends on a /// recursive type T', its propagated canonical type must be /// invalidated (set to nullptr) if T' appears to be different from T, /// a.k.a, the canonicalization of T' temporarily failed. /// /// This means that any sub-type that depends on recursive types and /// that has been the target of the canonical type propagation /// optimization must be tracked. If the dependant recursive type /// fails its canonicalization, then the sub-type being compared must /// have its propagated canonical type cleared. In other words, its /// propagated canonical type must be cancelled. /// /// @} /// If on-the-fly canonicalization is turned on, then this function /// sets the canonical type of its second parameter to the canonical /// type of the first parameter. /// /// @param lhs_type the type which canonical type to propagate. /// /// @param rhs_type the type which canonical type to set. static bool maybe_propagate_canonical_type(const type_base& lhs_type, const type_base& rhs_type) { if (const environment *env = lhs_type.get_environment()) if (env->do_on_the_fly_canonicalization()) if (type_base_sptr canonical_type = lhs_type.get_canonical_type()) if (!rhs_type.get_canonical_type()) if (env->priv_->propagate_ct(lhs_type, rhs_type)) return true; return false; } // static void sort_virtual_member_functions(class_decl::member_functions& mem_fns); /// The private data for the class_decl type. struct class_decl::priv { base_specs bases_; unordered_map bases_map_; member_functions virtual_mem_fns_; virtual_mem_fn_map_type virtual_mem_fns_map_; bool is_struct_; priv() : is_struct_(false) {} priv(bool is_struct, class_decl::base_specs& bases) : bases_(bases), is_struct_(is_struct) { } priv(bool is_struct) : is_struct_(is_struct) {} };// end struct class_decl::priv /// A Constructor for instances of \ref class_decl /// /// @param env the environment we are operating from. /// /// @param name the identifier of the class. /// /// @param size_in_bits the size of an instance of class_decl, expressed /// in bits /// /// @param align_in_bits the alignment of an instance of class_decl, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of class_decl. /// /// @param bases the vector of base classes for this instance of class_decl. /// /// @param mbrs the vector of member types of this instance of /// class_decl. /// /// @param data_mbrs the vector of data members of this instance of /// class_decl. /// /// @param mbr_fns the vector of member functions of this instance of /// class_decl. class_decl::class_decl(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, bool is_struct, const location& locus, visibility vis, base_specs& bases, member_types& mbr_types, data_members& data_mbrs, member_functions& mbr_fns) : type_or_decl_base(env, CLASS_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, align_in_bits), class_or_union(env, name, size_in_bits, align_in_bits, locus, vis, mbr_types, data_mbrs, mbr_fns), priv_(new priv(is_struct, bases)) { runtime_type_instance(this); } /// A Constructor for instances of @ref class_decl /// /// @param env the environment we are operating from. /// /// @param name the identifier of the class. /// /// @param size_in_bits the size of an instance of class_decl, expressed /// in bits /// /// @param align_in_bits the alignment of an instance of class_decl, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of class_decl. /// /// @param bases the vector of base classes for this instance of class_decl. /// /// @param mbrs the vector of member types of this instance of /// class_decl. /// /// @param data_mbrs the vector of data members of this instance of /// class_decl. /// /// @param mbr_fns the vector of member functions of this instance of /// class_decl. /// /// @param is_anonymous whether the newly created instance is /// anonymous. class_decl::class_decl(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, bool is_struct, const location& locus, visibility vis, base_specs& bases, member_types& mbr_types, data_members& data_mbrs, member_functions& mbr_fns, bool is_anonymous) : type_or_decl_base(env, CLASS_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, // If the class is anonymous then by default it won't // have a linkage name. Also, the anonymous class does // have an internal-only unique name that is generally // not taken into account when comparing classes; such a // unique internal-only name, when used as a linkage // name might introduce spurious comparison false // negatives. /*linkage_name=*/is_anonymous ? string() : name, vis), type_base(env, size_in_bits, align_in_bits), class_or_union(env, name, size_in_bits, align_in_bits, locus, vis, mbr_types, data_mbrs, mbr_fns), priv_(new priv(is_struct, bases)) { runtime_type_instance(this); set_is_anonymous(is_anonymous); } /// A constructor for instances of class_decl. /// /// @param env the environment we are operating from. /// /// @param name the name of the class. /// /// @param size_in_bits the size of an instance of class_decl, expressed /// in bits /// /// @param align_in_bits the alignment of an instance of class_decl, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of class_decl. class_decl::class_decl(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, bool is_struct, const location& locus, visibility vis) : type_or_decl_base(env, CLASS_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, align_in_bits), class_or_union(env, name, size_in_bits, align_in_bits, locus, vis), priv_(new priv(is_struct)) { runtime_type_instance(this); } /// A constructor for instances of @ref class_decl. /// /// @param env the environment we are operating from. /// /// @param name the name of the class. /// /// @param size_in_bits the size of an instance of class_decl, expressed /// in bits /// /// @param align_in_bits the alignment of an instance of class_decl, /// expressed in bits. /// /// @param locus the source location of declaration point this class. /// /// @param vis the visibility of instances of class_decl. /// /// @param is_anonymous whether the newly created instance is /// anonymous. class_decl:: class_decl(const environment* env, const string& name, size_t size_in_bits, size_t align_in_bits, bool is_struct, const location& locus, visibility vis, bool is_anonymous) : type_or_decl_base(env, CLASS_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, // If the class is anonymous then by default it won't // have a linkage name. Also, the anonymous class does // have an internal-only unique name that is generally // not taken into account when comparing classes; such a // unique internal-only name, when used as a linkage // name might introduce spurious comparison false // negatives. /*linkage_name=*/ is_anonymous ? string() : name, vis), type_base(env, size_in_bits, align_in_bits), class_or_union(env, name, size_in_bits, align_in_bits, locus, vis), priv_(new priv(is_struct)) { runtime_type_instance(this); set_is_anonymous(is_anonymous); } /// A constuctor for instances of class_decl that represent a /// declaration without definition. /// /// @param env the environment we are operating from. /// /// @param name the name of the class. /// /// @param is_declaration_only a boolean saying whether the instance /// represents a declaration only, or not. class_decl::class_decl(const environment* env, const string& name, bool is_struct, bool is_declaration_only) : type_or_decl_base(env, CLASS_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, location(), name), type_base(env, 0, 0), class_or_union(env, name, is_declaration_only), priv_(new priv(is_struct)) { runtime_type_instance(this); } /// This method is invoked automatically right after the current /// instance of @ref class_decl has been canonicalized. /// /// Currently, the only thing it does is to sort the virtual member /// functions vector. void class_decl::on_canonical_type_set() { sort_virtual_mem_fns(); for (class_decl::virtual_mem_fn_map_type::iterator i = priv_->virtual_mem_fns_map_.begin(); i != priv_->virtual_mem_fns_map_.end(); ++i) sort_virtual_member_functions(i->second); } /// Set the "is-struct" flag of the class. /// /// @param f the new value of the flag. void class_decl::is_struct(bool f) {priv_->is_struct_ = f;} /// Test if the class is a struct. /// /// @return true iff the class is a struct. bool class_decl::is_struct() const {return priv_->is_struct_;} /// Add a base specifier to this class. /// /// @param b the new base specifier. void class_decl::add_base_specifier(base_spec_sptr b) { ABG_ASSERT(get_environment()); ABG_ASSERT(b->get_environment() == get_environment()); priv_->bases_.push_back(b); priv_->bases_map_[b->get_base_class()->get_qualified_name()] = b; if (const environment* env = get_environment()) b->set_environment(env); } /// Get the base specifiers for this class. /// /// @return a vector of the base specifiers. const class_decl::base_specs& class_decl::get_base_specifiers() const {return priv_->bases_;} /// Find a base class of a given qualified name for the current class. /// /// @param qualified_name the qualified name of the base class to look for. /// /// @return a pointer to the @ref class_decl that represents the base /// class of name @p qualified_name, if found. class_decl_sptr class_decl::find_base_class(const string& qualified_name) const { unordered_map::iterator i = priv_->bases_map_.find(qualified_name); if (i != priv_->bases_map_.end()) return i->second->get_base_class(); return class_decl_sptr(); } /// Get the virtual member functions of this class. /// /// @param return a vector of the virtual member functions of this /// class. const class_decl::member_functions& class_decl::get_virtual_mem_fns() const {return priv_->virtual_mem_fns_;} /// Get the map that associates a virtual table offset to the virtual /// member functions with that virtual table offset. /// /// Usually, there should be a 1:1 mapping between a given vtable /// offset and virtual member functions of that vtable offset. But /// because of some implementation details, there can be several C++ /// destructor functions that are *generated* by compilers, for a /// given destructor that is defined in the source code. If the /// destructor is virtual then those generated functions have some /// DWARF attributes in common with the constructor that the user /// actually defined in its source code. Among those attributes are /// the vtable offset of the destructor. /// /// @return the map that associates a virtual table offset to the /// virtual member functions with that virtual table offset. const class_decl::virtual_mem_fn_map_type& class_decl::get_virtual_mem_fns_map() const {return priv_->virtual_mem_fns_map_;} /// Sort the virtual member functions by their virtual index. void class_decl::sort_virtual_mem_fns() {sort_virtual_member_functions(priv_->virtual_mem_fns_);} /// Getter of the pretty representation of the current instance of /// @ref class_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 representaion for a class_decl. string class_decl::get_pretty_representation(bool internal, bool qualified_name) const { string cl = "class "; if (!internal && is_struct()) cl = "struct "; // When computing the pretty representation for internal purposes, // if an anonymous class is named by a typedef, then consider that // it has a name, which is the typedef name. if (get_is_anonymous()) { if (internal) return cl + get_type_name(this, qualified_name, /*internal=*/true); return get_class_or_union_flat_representation(this, "", /*one_line=*/true, internal); } string result = cl; if (qualified_name) result += get_qualified_name(internal); else result += get_name(); return result; } decl_base_sptr class_decl::insert_member_decl(decl_base_sptr d, declarations::iterator before) { if (method_decl_sptr f = dynamic_pointer_cast(d)) add_member_function(f, public_access, /*is_virtual=*/false, /*vtable_offset=*/0, /*is_static=*/false, /*is_ctor=*/false, /*is_dtor=*/false, /*is_const=*/false); else d = class_or_union::insert_member_decl(d, before); return d; } /// The private data structure of class_decl::base_spec. struct class_decl::base_spec::priv { class_decl_wptr base_class_; long offset_in_bits_; bool is_virtual_; priv(const class_decl_sptr& cl, long offset_in_bits, bool is_virtual) : base_class_(cl), offset_in_bits_(offset_in_bits), is_virtual_(is_virtual) {} }; /// Constructor for base_spec instances. /// /// @param base the base class to consider /// /// @param a the access specifier of the base class. /// /// @param offset_in_bits if positive or null, represents the offset /// of the base in the layout of its containing type.. If negative, /// means that the current base is not laid out in its containing type. /// /// @param is_virtual if true, means that the current base class is /// virtual in it's containing type. class_decl::base_spec::base_spec(const class_decl_sptr& base, access_specifier a, long offset_in_bits, bool is_virtual) : type_or_decl_base(base->get_environment(), ABSTRACT_DECL_BASE), decl_base(base->get_environment(), base->get_name(), base->get_location(), base->get_linkage_name(), base->get_visibility()), member_base(a), priv_(new priv(base, offset_in_bits, is_virtual)) { runtime_type_instance(this); } /// Get the base class referred to by the current base class /// specifier. /// /// @return the base class. class_decl_sptr class_decl::base_spec::get_base_class() const {return priv_->base_class_.lock();} /// Getter of the "is-virtual" proprerty of the base class specifier. /// /// @return true iff this specifies a virtual base class. bool class_decl::base_spec::get_is_virtual() const {return priv_->is_virtual_;} /// Getter of the offset of the base. /// /// @return the offset of the base. long class_decl::base_spec::get_offset_in_bits() const {return priv_->offset_in_bits_;} /// Calculate the hash value for a class_decl::base_spec. /// /// @return the hash value. size_t class_decl::base_spec::get_hash() const { base_spec::hash h; return h(*this); } /// Traverses an instance of @ref class_decl::base_spec, 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 class_decl::base_spec::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); get_base_class()->traverse(v); visiting(false); } return v.visit_end(this); } /// Constructor for base_spec instances. /// /// Note that this constructor is for clients that don't support RTTI /// and that have a base class of type_base, but of dynamic type /// class_decl. /// /// @param base the base class to consider. Must be a pointer to an /// instance of class_decl /// /// @param a the access specifier of the base class. /// /// @param offset_in_bits if positive or null, represents the offset /// of the base in the layout of its containing type.. If negative, /// means that the current base is not laid out in its containing type. /// /// @param is_virtual if true, means that the current base class is /// virtual in it's containing type. class_decl::base_spec::base_spec(const type_base_sptr& base, access_specifier a, long offset_in_bits, bool is_virtual) : type_or_decl_base(base->get_environment(), ABSTRACT_DECL_BASE), decl_base(base->get_environment(), get_type_declaration(base)->get_name(), get_type_declaration(base)->get_location(), get_type_declaration(base)->get_linkage_name(), get_type_declaration(base)->get_visibility()), member_base(a), priv_(new priv(dynamic_pointer_cast(base), offset_in_bits, is_virtual)) { runtime_type_instance(this); } class_decl::base_spec::~base_spec() = default; /// Compares two instances of @ref class_decl::base_spec. /// /// 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 class_decl::base_spec& l, const class_decl::base_spec& r, change_kind* k) { if (!l.member_base::operator==(r)) { if (k) *k |= LOCAL_TYPE_CHANGE_KIND; ABG_RETURN_FALSE; } return (*l.get_base_class() == *r.get_base_class()); } /// Comparison operator for @ref class_decl::base_spec. /// /// @param other the instance of @ref class_decl::base_spec to compare /// against. /// /// @return true if the current instance of @ref class_decl::base_spec /// equals @p other. bool class_decl::base_spec::operator==(const decl_base& other) const { const class_decl::base_spec* o = dynamic_cast(&other); if (!o) return false; return equals(*this, *o, 0); } /// Comparison operator for @ref class_decl::base_spec. /// /// @param other the instance of @ref class_decl::base_spec to compare /// against. /// /// @return true if the current instance of @ref class_decl::base_spec /// equals @p other. bool class_decl::base_spec::operator==(const member_base& other) const { const class_decl::base_spec* o = dynamic_cast(&other); if (!o) return false; return operator==(static_cast(*o)); } mem_fn_context_rel::~mem_fn_context_rel() { } /// A constructor for instances of method_decl. /// /// @param name the name of the method. /// /// @param type the type of the method. /// /// @param declared_inline whether the method was /// declared inline or not. /// /// @param locus the source location of the method. /// /// @param linkage_name the mangled name of the method. /// /// @param vis the visibility of the method. /// /// @param bind the binding of the method. method_decl::method_decl(const string& name, method_type_sptr type, bool declared_inline, const location& locus, const string& linkage_name, visibility vis, binding bind) : type_or_decl_base(type->get_environment(), METHOD_DECL | ABSTRACT_DECL_BASE |FUNCTION_DECL), decl_base(type->get_environment(), name, locus, linkage_name, vis), function_decl(name, static_pointer_cast(type), declared_inline, locus, linkage_name, vis, bind) { runtime_type_instance(this); set_context_rel(new mem_fn_context_rel(0)); set_member_function_is_const(*this, type->get_is_const()); } /// A constructor for instances of method_decl. /// /// @param name the name of the method. /// /// @param type the type of the method. Must be an instance of /// method_type. /// /// @param declared_inline whether the method was /// declared inline or not. /// /// @param locus the source location of the method. /// /// @param linkage_name the mangled name of the method. /// /// @param vis the visibility of the method. /// /// @param bind the binding of the method. method_decl::method_decl(const string& name, function_type_sptr type, bool declared_inline, const location& locus, const string& linkage_name, visibility vis, binding bind) : type_or_decl_base(type->get_environment(), METHOD_DECL | ABSTRACT_DECL_BASE | FUNCTION_DECL), decl_base(type->get_environment(), name, locus, linkage_name, vis), function_decl(name, static_pointer_cast (dynamic_pointer_cast(type)), declared_inline, locus, linkage_name, vis, bind) { runtime_type_instance(this); set_context_rel(new mem_fn_context_rel(0)); } /// A constructor for instances of method_decl. /// /// @param name the name of the method. /// /// @param type the type of the method. Must be an instance of /// method_type. /// /// @param declared_inline whether the method was /// declared inline or not. /// /// @param locus the source location of the method. /// /// @param linkage_name the mangled name of the method. /// /// @param vis the visibility of the method. /// /// @param bind the binding of the method. method_decl::method_decl(const string& name, type_base_sptr type, bool declared_inline, const location& locus, const string& linkage_name, visibility vis, binding bind) : type_or_decl_base(type->get_environment(), METHOD_DECL | ABSTRACT_DECL_BASE | FUNCTION_DECL), decl_base(type->get_environment(), name, locus, linkage_name, vis), function_decl(name, static_pointer_cast (dynamic_pointer_cast(type)), declared_inline, locus, linkage_name, vis, bind) { runtime_type_instance(this); set_context_rel(new mem_fn_context_rel(0)); } /// Set the linkage name of the method. /// /// @param l the new linkage name of the method. void method_decl::set_linkage_name(const string& l) { decl_base::set_linkage_name(l); // Update the linkage_name -> member function map of the containing // class declaration. if (!l.empty()) { method_type_sptr t = get_type(); class_or_union_sptr cl = t->get_class_type(); method_decl_sptr m(this, sptr_utils::noop_deleter()); cl->priv_->mem_fns_map_[l] = m; } } method_decl::~method_decl() {} const method_type_sptr method_decl::get_type() const { method_type_sptr result; if (function_decl::get_type()) result = dynamic_pointer_cast(function_decl::get_type()); return result; } /// Set the containing class of a method_decl. /// /// @param scope the new containing class_decl. void method_decl::set_scope(scope_decl* scope) { if (!get_context_rel()) set_context_rel(new mem_fn_context_rel(scope)); else get_context_rel()->set_scope(scope); } /// Equality operator for @ref method_decl_sptr. /// /// This is a deep equality operator, as it compares the @ref /// method_decl that is pointed-to by the smart pointer. /// /// @param l the left-hand side argument of the equality operator. /// /// @param r the righ-hand side argument of the equality operator. /// /// @return true iff @p l equals @p r. bool operator==(const method_decl_sptr& l, const method_decl_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Inequality operator for @ref method_decl_sptr. /// /// This is a deep equality operator, as it compares the @ref /// method_decl that is pointed-to by the smart pointer. /// /// @param l the left-hand side argument of the equality operator. /// /// @param r the righ-hand side argument of the equality operator. /// /// @return true iff @p l differs from @p r. bool operator!=(const method_decl_sptr& l, const method_decl_sptr& r) {return !operator==(l, r);} /// Test if a function_decl is actually a method_decl. /// ///@param d the @ref function_decl to consider. /// /// @return the method_decl sub-object of @p d if inherits /// a method_decl type. method_decl* is_method_decl(const type_or_decl_base *d) { return dynamic_cast (const_cast(d)); } /// Test if a function_decl is actually a method_decl. /// ///@param d the @ref function_decl to consider. /// /// @return the method_decl sub-object of @p d if inherits /// a method_decl type. method_decl* is_method_decl(const type_or_decl_base&d) {return is_method_decl(&d);} /// Test if a function_decl is actually a method_decl. /// ///@param d the @ref function_decl to consider. /// /// @return the method_decl sub-object of @p d if inherits /// a method_decl type. method_decl_sptr is_method_decl(const type_or_decl_base_sptr& d) {return dynamic_pointer_cast(d);} /// A "less than" functor to sort a vector of instances of /// method_decl that are virtual. struct virtual_member_function_less_than { /// The less than operator. First, it sorts the methods by their /// vtable index. If they have the same vtable index, it sorts them /// by the name of their ELF symbol. If they don't have elf /// symbols, it sorts them by considering their pretty /// representation. /// /// Note that this method expects virtual methods. /// /// @param f the first method to consider. /// /// @param s the second method to consider. /// /// @return true if method @p is less than method @s. bool operator()(const method_decl& f, const method_decl& s) { ABG_ASSERT(get_member_function_is_virtual(f)); ABG_ASSERT(get_member_function_is_virtual(s)); ssize_t f_offset = get_member_function_vtable_offset(f); ssize_t s_offset = get_member_function_vtable_offset(s); if (f_offset != s_offset) return f_offset < s_offset; string fn, sn; // If the functions have symbols, then compare their symbol-id // string. elf_symbol_sptr f_sym = f.get_symbol(); elf_symbol_sptr s_sym = s.get_symbol(); if ((!f_sym) != (!s_sym)) return !f_sym; if (f_sym && s_sym) { fn = f_sym->get_id_string(); sn = s_sym->get_id_string(); if (fn != sn) return fn < sn; } // Try the linkage names (important for destructors). fn = f.get_linkage_name(); sn = s.get_linkage_name(); if (fn != sn) return fn < sn; // None of the functions have symbols or linkage names that // distinguish them, so compare their pretty representation. fn = f.get_pretty_representation(); sn = s.get_pretty_representation(); if (fn != sn) return fn < sn; /// If it's just the file paths that are different then sort them /// too. string fn_filepath, sn_filepath; unsigned line = 0, column = 0; location fn_loc = f.get_location(), sn_loc = s.get_location(); if (fn_loc) fn_loc.expand(fn_filepath, line, column); if (sn_loc) sn_loc.expand(sn_filepath, line, column); return fn_filepath < sn_filepath; } /// The less than operator. First, it sorts the methods by their /// vtable index. If they have the same vtable index, it sorts them /// by the name of their ELF symbol. If they don't have elf /// symbols, it sorts them by considering their pretty /// representation. /// /// Note that this method expects to take virtual methods. /// /// @param f the first method to consider. /// /// @param s the second method to consider. bool operator()(const method_decl_sptr f, const method_decl_sptr s) {return operator()(*f, *s);} }; // end struct virtual_member_function_less_than /// Sort a vector of instances of virtual member functions. /// /// @param mem_fns the vector of member functions to sort. static void sort_virtual_member_functions(class_decl::member_functions& mem_fns) { virtual_member_function_less_than lt; std::stable_sort(mem_fns.begin(), mem_fns.end(), lt); } /// Add a member function to the current instance of @ref class_or_union. /// /// @param f a method_decl to add to the current class. This function /// should not have been already added to a scope. /// /// @param access the access specifier for the member function to add. /// /// @param is_virtual if this is true then it means the function @p f /// is a virtual function. That also means that the current instance /// of @ref class_or_union is actually an instance of @ref class_decl. /// /// @param vtable_offset the offset of the member function in the /// virtual table. This parameter is taken into account only if @p /// is_virtual is true. /// /// @param is_static whether the member function is static. /// /// @param is_ctor whether the member function is a constructor. /// /// @param is_dtor whether the member function is a destructor. /// /// @param is_const whether the member function is const. void class_or_union::add_member_function(method_decl_sptr f, access_specifier a, bool is_virtual, size_t vtable_offset, bool is_static, bool is_ctor, bool is_dtor, bool is_const) { add_member_function(f, a, is_static, is_ctor, is_dtor, is_const); if (class_decl* klass = is_class_type(this)) { set_member_function_is_virtual(f, is_virtual); if (is_virtual) { set_member_function_vtable_offset(f, vtable_offset); sort_virtual_member_functions(klass->priv_->virtual_mem_fns_); } } } /// When a virtual member function has seen its virtualness set by /// set_member_function_is_virtual(), this function ensures that the /// member function is added to the specific vectors and maps of /// virtual member function of its class. /// /// @param method the method to fixup. void fixup_virtual_member_function(method_decl_sptr method) { if (!method || !get_member_function_is_virtual(method)) return; class_decl_sptr klass = is_class_type(method->get_type()->get_class_type()); class_decl::member_functions::const_iterator m; for (m = klass->priv_->virtual_mem_fns_.begin(); m != klass->priv_->virtual_mem_fns_.end(); ++m) if (m->get() == method.get()) break; if (m == klass->priv_->virtual_mem_fns_.end()) klass->priv_->virtual_mem_fns_.push_back(method); // Build or udpate the map that associates a vtable offset to the // number of virtual member functions that "point" to it. ssize_t voffset = get_member_function_vtable_offset(method); if (voffset == -1) return; class_decl::virtual_mem_fn_map_type::iterator i = klass->priv_->virtual_mem_fns_map_.find(voffset); if (i == klass->priv_->virtual_mem_fns_map_.end()) { class_decl::member_functions virtual_mem_fns_at_voffset; virtual_mem_fns_at_voffset.push_back(method); klass->priv_->virtual_mem_fns_map_[voffset] = virtual_mem_fns_at_voffset; } else { for (m = i->second.begin() ; m != i->second.end(); ++m) if (m->get() == method.get()) break; if (m == i->second.end()) i->second.push_back(method); } } /// Return true iff the class has no entity in its scope. bool class_decl::has_no_base_nor_member() const {return priv_->bases_.empty() && has_no_member();} /// Test if the current instance of @ref class_decl has virtual member /// functions. /// /// @return true iff the current instance of @ref class_decl has /// virtual member functions. bool class_decl::has_virtual_member_functions() const {return !get_virtual_mem_fns().empty();} /// Test if the current instance of @ref class_decl has at least one /// virtual base. /// /// @return true iff the current instance of @ref class_decl has a /// virtual member function. bool class_decl::has_virtual_bases() const { for (base_specs::const_iterator b = get_base_specifiers().begin(); b != get_base_specifiers().end(); ++b) if ((*b)->get_is_virtual() || (*b)->get_base_class()->has_virtual_bases()) return true; return false; } /// Test if the current instance has a vtable. /// /// This is only valid for a C++ program. /// /// Basically this function checks if the class has either virtual /// functions, or virtual bases. bool class_decl::has_vtable() const { if (has_virtual_member_functions() || has_virtual_bases()) return true; return false; } /// Get the highest vtable offset of all the virtual methods of the /// class. /// /// @return the highest vtable offset of all the virtual methods of /// the class. ssize_t class_decl::get_biggest_vtable_offset() const { ssize_t offset = -1; for (class_decl::virtual_mem_fn_map_type::const_iterator e = get_virtual_mem_fns_map().begin(); e != get_virtual_mem_fns_map().end(); ++e) if (e->first > offset) offset = e->first; return offset; } /// Return the hash value for the current instance. /// /// @return the hash value. size_t class_decl::get_hash() const { class_decl::hash hash_class; return hash_class(this); } /// Test if two methods are equal without taking their symbol or /// linkage name into account. /// /// @param f the first method. /// /// @param s the second method. /// /// @return true iff @p f equals @p s without taking their linkage /// name or symbol into account. static bool methods_equal_modulo_elf_symbol(const method_decl_sptr& f, const method_decl_sptr& s) { method_decl_sptr first = f, second = s; elf_symbol_sptr saved_first_elf_symbol = first->get_symbol(); elf_symbol_sptr saved_second_elf_symbol = second->get_symbol(); interned_string saved_first_linkage_name = first->get_linkage_name(); interned_string saved_second_linkage_name = second->get_linkage_name(); first->set_symbol(elf_symbol_sptr()); first->set_linkage_name(""); second->set_symbol(elf_symbol_sptr()); second->set_linkage_name(""); bool equal = *first == *second; first->set_symbol(saved_first_elf_symbol); first->set_linkage_name(saved_first_linkage_name); second->set_symbol(saved_second_elf_symbol); second->set_linkage_name(saved_second_linkage_name); return equal; } /// Test if a given method is equivalent to at least of other method /// that is in a vector of methods. /// /// Note that "equivalent" here means being equal without taking the /// linkage name or the symbol of the methods into account. /// /// This is a sub-routine of the 'equals' function that compares @ref /// class_decl. /// /// @param method the method to compare. /// /// @param fns the vector of functions to compare @p method against. /// /// @return true iff @p is equivalent to at least one method in @p /// fns. static bool method_matches_at_least_one_in_vector(const method_decl_sptr& method, const class_decl::member_functions& fns) { for (class_decl::member_functions::const_iterator i = fns.begin(); i != fns.end(); ++i) // Note that the comparison must be done in this order: method == // *i This is to keep the consistency of the comparison. It's // important especially when doing type canonicalization. The // already canonicalize type is the left operand, and the type // being canonicalized is the right operand. This comes from the // code in type_base::get_canonical_type_for(). if (methods_equal_modulo_elf_symbol(method, *i)) return true; return false; } /// Cancel the canonical type that was propagated. /// /// If we are in the process of comparing a type for the purpose of /// canonicalization, and if that type has been the target of the /// canonical type propagation optimization, then clear the propagated /// canonical type. See @ref OnTheFlyCanonicalization for more about /// the canonical type optimization /// /// @param t the type to consider. static bool maybe_cancel_propagated_canonical_type(const class_or_union& t) { const environment* env = t.get_environment(); if (env && env->do_on_the_fly_canonicalization()) if (is_type(&t)->priv_->canonical_type_propagated()) { is_type(&t)->priv_->clear_propagated_canonical_type(); env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&t); return true; } return false; } /// Compares two instances of @ref class_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 class_decl& l, const class_decl& r, change_kind* k) { // if one of the classes is declaration-only then we take a fast // path here. if (l.get_is_declaration_only() || r.get_is_declaration_only()) return equals(static_cast(l), static_cast(r), k); RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(static_cast(l), static_cast(r)); bool result = true; if (!equals(static_cast(l), static_cast(r), k)) { result = false; if (!k) return result; } mark_types_as_being_compared(static_cast(l), static_cast(r)); #define RETURN(value) \ return return_comparison_result(static_cast(l), \ static_cast(r), \ value); // If comparing the class_or_union 'part' of the type led to // canonical type propagation, then cancel that because it's too // early to do that at this point. We still need to compare bases // virtual members. maybe_cancel_propagated_canonical_type(r); // Compare bases. if (l.get_base_specifiers().size() != r.get_base_specifiers().size()) { result = false; if (k) *k |= LOCAL_TYPE_CHANGE_KIND; else RETURN(result); } for (class_decl::base_specs::const_iterator b0 = l.get_base_specifiers().begin(), b1 = r.get_base_specifiers().begin(); (b0 != l.get_base_specifiers().end() && b1 != r.get_base_specifiers().end()); ++b0, ++b1) if (*b0 != *b1) { result = false; if (k) { if (!types_have_similar_structure((*b0)->get_base_class().get(), (*b1)->get_base_class().get())) *k |= LOCAL_TYPE_CHANGE_KIND; else *k |= SUBTYPE_CHANGE_KIND; break; } RETURN(result); } // Compare virtual member functions // We look at the map that associates a given vtable offset to a // vector of virtual member functions that point to that offset. // // This is because there are cases where several functions can // point to the same virtual table offset. // // This is usually the case for virtual destructors. Even though // there can be only one virtual destructor declared in source // code, there are actually potentially up to three generated // functions for that destructor. Some of these generated // functions can be clones of other functions that are among those // generated ones. In any cases, they all have the same // properties, including the vtable offset property. // So, there should be the same number of different vtable // offsets, the size of two maps must be equals. if (l.get_virtual_mem_fns_map().size() != r.get_virtual_mem_fns_map().size()) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; else RETURN(result); } // Then, each virtual member function of a given vtable offset in // the first class type, must match an equivalent virtual member // function of a the same vtable offset in the second class type. // // By "match", I mean that the two virtual member function should // be equal if we don't take into account their symbol name or // their linkage name. This is because two destructor functions // clones (for instance) might have different linkage name, but // are still equivalent if their other properties are the same. for (class_decl::virtual_mem_fn_map_type::const_iterator first_v_fn_entry = l.get_virtual_mem_fns_map().begin(); first_v_fn_entry != l.get_virtual_mem_fns_map().end(); ++first_v_fn_entry) { unsigned voffset = first_v_fn_entry->first; const class_decl::member_functions& first_vfns = first_v_fn_entry->second; const class_decl::virtual_mem_fn_map_type::const_iterator second_v_fn_entry = r.get_virtual_mem_fns_map().find(voffset); if (second_v_fn_entry == r.get_virtual_mem_fns_map().end()) { result = false; if (k) *k |= LOCAL_NON_TYPE_CHANGE_KIND; RETURN(result); } const class_decl::member_functions& second_vfns = second_v_fn_entry->second; bool matches = false; for (class_decl::member_functions::const_iterator i = first_vfns.begin(); i != first_vfns.end(); ++i) if (method_matches_at_least_one_in_vector(*i, second_vfns)) { matches = true; break; } if (!matches) { result = false; if (k) *k |= SUBTYPE_CHANGE_KIND; else RETURN(result); } } RETURN(result); #undef RETURN } /// Copy a method of a class into a new class. /// /// @param klass the class into which the method is to be copied. /// /// @param method the method to copy into @p klass. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const class_decl_sptr& clazz, const method_decl_sptr& f) {return copy_member_function(static_pointer_cast(clazz), f);} /// Copy a method of a class into a new class. /// /// @param klass the class into which the method is to be copied. /// /// @param method the method to copy into @p klass. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const class_decl_sptr& clazz, const method_decl* f) {return copy_member_function(static_pointer_cast(clazz), f);} /// Comparison operator for @ref class_decl. /// /// @param other the instance of @ref class_decl to compare against. /// /// @return true iff the current instance of @ref class_decl equals @p /// other. bool class_decl::operator==(const decl_base& other) const { const class_decl* op = is_class_type(&other); if (!op) return false; // If this is a decl-only type (and thus with no canonical type), // use the canonical type of the definition, if any. const class_decl *l = 0; if (get_is_declaration_only()) l = dynamic_cast(get_naked_definition_of_declaration()); if (l == 0) l = this; ABG_ASSERT(l); // Likewise for the other type. const class_decl *r = 0; if (op->get_is_declaration_only()) r = dynamic_cast(op->get_naked_definition_of_declaration()); if (r == 0) r = op; ABG_ASSERT(r); return try_canonical_compare(l, r); } /// Equality operator for class_decl. /// /// Re-uses the equality operator that takes a decl_base. /// /// @param other the other class_decl to compare against. /// /// @return true iff the current instance equals the other one. bool class_decl::operator==(const type_base& other) const { const decl_base* o = is_decl(&other); if (!o) return false; return *this == *o; } /// Comparison operator for @ref class_decl. /// /// @param other the instance of @ref class_decl to compare against. /// /// @return true iff the current instance of @ref class_decl equals @p /// other. bool class_decl::operator==(const class_decl& other) const { const decl_base& o = other; return *this == o; } /// Turn equality of shared_ptr of class_decl into a deep equality; /// that is, make it compare the pointed to objects too. /// /// @param l the shared_ptr of class_decl on left-hand-side of the /// equality. /// /// @param r the shared_ptr of class_decl on right-hand-side of the /// equality. /// /// @return true if the class_decl pointed to by the shared_ptrs are /// equal, false otherwise. bool operator==(const class_decl_sptr& l, const class_decl_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Turn inequality of shared_ptr of class_decl into a deep equality; /// that is, make it compare the pointed to objects too. /// /// @param l the shared_ptr of class_decl on left-hand-side of the /// equality. /// /// @param r the shared_ptr of class_decl on right-hand-side of the /// equality. /// /// @return true if the class_decl pointed to by the shared_ptrs are /// different, false otherwise. bool operator!=(const class_decl_sptr& l, const class_decl_sptr& r) {return !operator==(l, r);} /// Turn equality of shared_ptr of class_or_union into a deep /// equality; that is, make it compare the pointed to objects too. /// /// @param l the left-hand-side operand of the operator /// /// @param r the right-hand-side operand of the operator. /// /// @return true iff @p l equals @p r. bool operator==(const class_or_union_sptr& l, const class_or_union_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Turn inequality of shared_ptr of class_or_union into a deep /// equality; that is, make it compare the pointed to objects too. /// /// @param l the left-hand-side operand of the operator /// /// @param r the right-hand-side operand of the operator. /// /// @return true iff @p l is different from @p r. bool operator!=(const class_or_union_sptr& l, const class_or_union_sptr& r) {return !operator==(l, r);} /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on its /// members. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool class_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); bool stop = false; for (base_specs::const_iterator i = get_base_specifiers().begin(); i != get_base_specifiers().end(); ++i) { if (!(*i)->traverse(v)) { stop = true; break; } } if (!stop) for (data_members::const_iterator i = get_data_members().begin(); i != get_data_members().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_functions::const_iterator i= get_member_functions().begin(); i != get_member_functions().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_types::const_iterator i = get_member_types().begin(); i != get_member_types().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_function_templates::const_iterator i = get_member_function_templates().begin(); i != get_member_function_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_class_templates::const_iterator i = get_member_class_templates().begin(); i != get_member_class_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } visiting(false); } bool result = v.visit_end(this); v.mark_type_node_as_visited(this); return result; } /// Destructor of the @ref class_decl type. class_decl::~class_decl() {delete priv_;} context_rel::~context_rel() {} bool member_base::operator==(const member_base& o) const { return (get_access_specifier() == o.get_access_specifier() && get_is_static() == o.get_is_static()); } /// Equality operator for smart pointers to @ref /// class_decl::base_specs. /// /// This compares the pointed-to objects. /// /// @param l the first instance to consider. /// /// @param r the second instance to consider. /// /// @return true iff @p l equals @p r. bool operator==(const class_decl::base_spec_sptr& l, const class_decl::base_spec_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == static_cast(*r); } /// Inequality operator for smart pointers to @ref /// class_decl::base_specs. /// /// This compares the pointed-to objects. /// /// @param l the first instance to consider. /// /// @param r the second instance to consider. /// /// @return true iff @p l is different from @p r. bool operator!=(const class_decl::base_spec_sptr& l, const class_decl::base_spec_sptr& r) {return !operator==(l, r);} /// Test if an ABI artifact is a class base specifier. /// /// @param tod the ABI artifact to consider. /// /// @return a pointer to the @ref class_decl::base_spec sub-object of /// @p tod iff it's a class base specifier. class_decl::base_spec* is_class_base_spec(const type_or_decl_base* tod) { return dynamic_cast (const_cast(tod)); } /// Test if an ABI artifact is a class base specifier. /// /// @param tod the ABI artifact to consider. /// /// @return a pointer to the @ref class_decl::base_spec sub-object of /// @p tod iff it's a class base specifier. class_decl::base_spec_sptr is_class_base_spec(type_or_decl_base_sptr tod) {return dynamic_pointer_cast(tod);} bool member_function_template::operator==(const member_base& other) const { try { const member_function_template& o = dynamic_cast(other); if (!(is_constructor() == o.is_constructor() && is_const() == o.is_const() && member_base::operator==(o))) return false; if (function_tdecl_sptr ftdecl = as_function_tdecl()) { function_tdecl_sptr other_ftdecl = o.as_function_tdecl(); if (other_ftdecl) return ftdecl->function_tdecl::operator==(*other_ftdecl); } } catch(...) {} return false; } /// Equality operator for smart pointers to @ref /// member_function_template. This is compares the /// pointed-to instances. /// /// @param l the first instance to consider. /// /// @param r the second instance to consider. /// /// @return true iff @p l equals @p r. bool operator==(const member_function_template_sptr& l, const member_function_template_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Inequality operator for smart pointers to @ref /// member_function_template. This is compares the pointed-to /// instances. /// /// @param l the first instance to consider. /// /// @param r the second instance to consider. /// /// @return true iff @p l equals @p r. bool operator!=(const member_function_template_sptr& l, const member_function_template_sptr& r) {return !operator==(l, r);} /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on its /// underlying function template. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool member_function_template::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (function_tdecl_sptr f = as_function_tdecl()) f->traverse(v); visiting(false); } return v.visit_end(this); } /// Equality operator of the the @ref member_class_template class. /// /// @param other the other @ref member_class_template to compare against. /// /// @return true iff the current instance equals @p other. bool member_class_template::operator==(const member_base& other) const { try { const member_class_template& o = dynamic_cast(other); if (!member_base::operator==(o)) return false; return as_class_tdecl()->class_tdecl::operator==(o); } catch(...) {return false;} } /// Comparison operator for the @ref member_class_template /// type. /// /// @param other the other instance of @ref /// member_class_template to compare against. /// /// @return true iff the two instances are equal. bool member_class_template::operator==(const member_class_template& other) const { const decl_base* o = dynamic_cast(&other); return *this == *o; } /// Comparison operator for the @ref member_class_template /// type. /// /// @param l the first argument of the operator. /// /// @param r the second argument of the operator. /// /// @return true iff the two instances are equal. bool operator==(const member_class_template_sptr& l, const member_class_template_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Inequality operator for the @ref member_class_template /// type. /// /// @param l the first argument of the operator. /// /// @param r the second argument of the operator. /// /// @return true iff the two instances are equal. bool operator!=(const member_class_template_sptr& l, const member_class_template_sptr& r) {return !operator==(l, r);} /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on the class /// pattern of the template. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool member_class_template::traverse(ir_node_visitor& v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (class_tdecl_sptr t = as_class_tdecl()) t->traverse(v); visiting(false); } return v.visit_end(this); } /// Streaming operator for class_decl::access_specifier. /// /// @param o the output stream to serialize the access specifier to. /// /// @param a the access specifier to serialize. /// /// @return the output stream. std::ostream& operator<<(std::ostream& o, access_specifier a) { string r; switch (a) { case no_access: r = "none"; break; case private_access: r = "private"; break; case protected_access: r = "protected"; break; case public_access: r= "public"; break; }; o << r; return o; } /// Sets the static-ness property of a class member. /// /// @param d the class member to set the static-ness property for. /// Note that this must be a class member otherwise the function /// aborts the current process. /// /// @param s this must be true if the member is to be static, false /// otherwise. void set_member_is_static(decl_base& d, bool s) { ABG_ASSERT(is_member_decl(d)); context_rel* c = d.get_context_rel(); ABG_ASSERT(c); c->set_is_static(s); scope_decl* scope = d.get_scope(); if (class_or_union* cl = is_class_or_union_type(scope)) { if (var_decl* v = is_var_decl(&d)) { if (s) // remove from the non-static data members for (class_decl::data_members::iterator i = cl->priv_->non_static_data_members_.begin(); i != cl->priv_->non_static_data_members_.end(); ++i) { if ((*i)->get_name() == v->get_name()) { cl->priv_->non_static_data_members_.erase(i); break; } } else { bool is_already_in_non_static_data_members = false; for (class_or_union::data_members::iterator i = cl->priv_->non_static_data_members_.begin(); i != cl->priv_->non_static_data_members_.end(); ++i) { if ((*i)->get_name() == v->get_name()) { is_already_in_non_static_data_members = true; break; } } if (!is_already_in_non_static_data_members) { var_decl_sptr var; // add to non-static data members. for (class_or_union::data_members::const_iterator i = cl->priv_->data_members_.begin(); i != cl->priv_->data_members_.end(); ++i) { if ((*i)->get_name() == v->get_name()) { var = *i; break; } } ABG_ASSERT(var); cl->priv_->non_static_data_members_.push_back(var); } } } } } /// Sets the static-ness property of a class member. /// /// @param d the class member to set the static-ness property for. /// Note that this must be a class member otherwise the function /// aborts the current process. /// /// @param s this must be true if the member is to be static, false /// otherwise. void set_member_is_static(const decl_base_sptr& d, bool s) {set_member_is_static(*d, s);} // // /// Constructor for the @ref union_decl type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the union type. /// /// @param size_in_bits the size of the union, in bits. /// /// @param locus the location of the type. /// /// @param vis the visibility of instances of @ref union_decl. /// /// @param mbr_types the member types of the union. /// /// @param data_mbrs the data members of the union. /// /// @param member_fns the member functions of the union. union_decl::union_decl(const environment* env, const string& name, size_t size_in_bits, const location& locus, visibility vis, member_types& mbr_types, data_members& data_mbrs, member_functions& member_fns) : type_or_decl_base(env, UNION_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, 0), class_or_union(env, name, size_in_bits, 0, locus, vis, mbr_types, data_mbrs, member_fns) { runtime_type_instance(this); } /// Constructor for the @ref union_decl type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the union type. /// /// @param size_in_bits the size of the union, in bits. /// /// @param locus the location of the type. /// /// @param vis the visibility of instances of @ref union_decl. /// /// @param mbr_types the member types of the union. /// /// @param data_mbrs the data members of the union. /// /// @param member_fns the member functions of the union. /// /// @param is_anonymous whether the newly created instance is /// anonymous. union_decl::union_decl(const environment* env, const string& name, size_t size_in_bits, const location& locus, visibility vis, member_types& mbr_types, data_members& data_mbrs, member_functions& member_fns, bool is_anonymous) : type_or_decl_base(env, UNION_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE), decl_base(env, name, locus, // If the class is anonymous then by default it won't // have a linkage name. Also, the anonymous class does // have an internal-only unique name that is generally // not taken into account when comparing classes; such a // unique internal-only name, when used as a linkage // name might introduce spurious comparison false // negatives. /*linkage_name=*/is_anonymous ? string() : name, vis), type_base(env, size_in_bits, 0), class_or_union(env, name, size_in_bits, 0, locus, vis, mbr_types, data_mbrs, member_fns) { runtime_type_instance(this); set_is_anonymous(is_anonymous); } /// Constructor for the @ref union_decl type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the union type. /// /// @param size_in_bits the size of the union, in bits. /// /// @param locus the location of the type. /// /// @param vis the visibility of instances of @ref union_decl. union_decl::union_decl(const environment* env, const string& name, size_t size_in_bits, const location& locus, visibility vis) : type_or_decl_base(env, UNION_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, name, vis), type_base(env, size_in_bits, 0), class_or_union(env, name, size_in_bits, 0, locus, vis) { runtime_type_instance(this); } /// Constructor for the @ref union_decl type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the union type. /// /// @param size_in_bits the size of the union, in bits. /// /// @param locus the location of the type. /// /// @param vis the visibility of instances of @ref union_decl. /// /// @param is_anonymous whether the newly created instance is /// anonymous. union_decl::union_decl(const environment* env, const string& name, size_t size_in_bits, const location& locus, visibility vis, bool is_anonymous) : type_or_decl_base(env, UNION_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, locus, // If the class is anonymous then by default it won't // have a linkage name. Also, the anonymous class does // have an internal-only unique name that is generally // not taken into account when comparing classes; such a // unique internal-only name, when used as a linkage // name might introduce spurious comparison false // negatives. /*linkage_name=*/is_anonymous ? string() : name, vis), type_base(env, size_in_bits, 0), class_or_union(env, name, size_in_bits, 0, locus, vis) { runtime_type_instance(this); set_is_anonymous(is_anonymous); } /// Constructor for the @ref union_decl type. /// /// @param env the @ref environment we are operating from. /// /// @param name the name of the union type. /// /// @param is_declaration_only a boolean saying whether the instance /// represents a declaration only, or not. union_decl::union_decl(const environment* env, const string& name, bool is_declaration_only) : type_or_decl_base(env, UNION_TYPE | ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE | ABSTRACT_SCOPE_TYPE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, name, location(), name), type_base(env, 0, 0), class_or_union(env, name, is_declaration_only) { runtime_type_instance(this); } /// Getter of the pretty representation of the current instance of /// @ref union_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 representaion for a union_decl. string union_decl::get_pretty_representation(bool internal, bool qualified_name) const { string repr; if (get_is_anonymous()) { if (internal) repr = string("union ") + get_type_name(this, qualified_name, /*internal=*/true); else repr = get_class_or_union_flat_representation(this, "", /*one_line=*/true, internal); } else { repr = "union "; if (qualified_name) repr += get_qualified_name(internal); else repr += get_name(); } return repr; } /// Comparison operator for @ref union_decl. /// /// @param other the instance of @ref union_decl to compare against. /// /// @return true iff the current instance of @ref union_decl equals @p /// other. bool union_decl::operator==(const decl_base& other) const { const union_decl* op = dynamic_cast(&other); if (!op) return false; return try_canonical_compare(this, op); } /// Equality operator for union_decl. /// /// Re-uses the equality operator that takes a decl_base. /// /// @param other the other union_decl to compare against. /// /// @return true iff the current instance equals the other one. bool union_decl::operator==(const type_base& other) const { const decl_base *o = dynamic_cast(&other); if (!o) return false; return *this == *o; } /// Comparison operator for @ref union_decl. /// /// @param other the instance of @ref union_decl to compare against. /// /// @return true iff the current instance of @ref union_decl equals @p /// other. bool union_decl::operator==(const union_decl& other) const { const decl_base& o = other; return *this == o; } /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on its /// members. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool union_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); bool stop = false; if (!stop) for (data_members::const_iterator i = get_data_members().begin(); i != get_data_members().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_functions::const_iterator i= get_member_functions().begin(); i != get_member_functions().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_types::const_iterator i = get_member_types().begin(); i != get_member_types().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_function_templates::const_iterator i = get_member_function_templates().begin(); i != get_member_function_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } if (!stop) for (member_class_templates::const_iterator i = get_member_class_templates().begin(); i != get_member_class_templates().end(); ++i) if (!(*i)->traverse(v)) { stop = true; break; } visiting(false); } bool result = v.visit_end(this); v.mark_type_node_as_visited(this); return result; } /// Destructor of the @ref union_decl type. union_decl::~union_decl() {} /// Compares two instances of @ref union_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 union_decl& l, const union_decl& r, change_kind* k) { bool result = equals(static_cast(l), static_cast(r), k); ABG_RETURN(result); } /// Copy a method of a @ref union_decl into a new @ref /// union_decl. /// /// @param t the @ref union_decl into which the method is to be copied. /// /// @param method the method to copy into @p t. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const union_decl_sptr& union_type, const method_decl_sptr& f) {return copy_member_function(union_type, f.get());} /// Copy a method of a @ref union_decl into a new @ref /// union_decl. /// /// @param t the @ref union_decl into which the method is to be copied. /// /// @param method the method to copy into @p t. /// /// @return the resulting newly copied method. method_decl_sptr copy_member_function(const union_decl_sptr& union_type, const method_decl* f) { const class_or_union_sptr t = union_type; return copy_member_function(t, f); } /// Turn equality of shared_ptr of union_decl into a deep equality; /// that is, make it compare the pointed to objects too. /// /// @param l the left-hand-side operand of the operator /// /// @param r the right-hand-side operand of the operator. /// /// @return true iff @p l equals @p r. bool operator==(const union_decl_sptr& l, const union_decl_sptr& r) { if (l.get() == r.get()) return true; if (!!l != !!r) return false; return *l == *r; } /// Turn inequality of shared_ptr of union_decl into a deep equality; /// that is, make it compare the pointed to objects too. /// /// @param l the left-hand-side operand of the operator /// /// @param r the right-hand-side operand of the operator. /// /// @return true iff @p l is different from @p r. bool operator!=(const union_decl_sptr& l, const union_decl_sptr& r) {return !operator==(l, r);} // // /// Data type of the private data of the @template_decl type. class template_decl::priv { friend class template_decl; std::list parms_; public: priv() {} }; // end class template_decl::priv /// Add a new template parameter to the current instance of @ref /// template_decl. /// /// @param p the new template parameter to add. void template_decl::add_template_parameter(const template_parameter_sptr p) {priv_->parms_.push_back(p);} /// Get the list of template parameters of the current instance of /// @ref template_decl. /// /// @return the list of template parameters. const std::list& template_decl::get_template_parameters() const {return priv_->parms_;} /// Constructor. /// /// @param env the environment we are operating from. /// /// @param name the name of the template decl. /// /// @param locus the source location where the template declaration is /// defined. /// /// @param vis the visibility of the template declaration. template_decl::template_decl(const environment* env, const string& name, const location& locus, visibility vis) : type_or_decl_base(env, TEMPLATE_DECL | ABSTRACT_DECL_BASE), decl_base(env, name, locus, /*mangled_name=*/"", vis), priv_(new priv) { runtime_type_instance(this); } /// Destructor. template_decl::~template_decl() {} /// Equality operator. /// /// @param o the other instance to compare against. /// /// @return true iff @p equals the current instance. bool template_decl::operator==(const template_decl& o) const { try { list >::const_iterator t0, t1; for (t0 = get_template_parameters().begin(), t1 = o.get_template_parameters().begin(); (t0 != get_template_parameters().end() && t1 != o.get_template_parameters().end()); ++t0, ++t1) { if (**t0 != **t1) return false; } if (t0 != get_template_parameters().end() || t1 != o.get_template_parameters().end()) return false; return true; } catch(...) {return false;} } // // /// The type of the private data of the @ref template_parameter type. class template_parameter::priv { friend class template_parameter; unsigned index_; template_decl_wptr template_decl_; mutable bool hashing_started_; mutable bool comparison_started_; priv(); public: priv(unsigned index, template_decl_sptr enclosing_template_decl) : index_(index), template_decl_(enclosing_template_decl), hashing_started_(), comparison_started_() {} }; // end class template_parameter::priv template_parameter::template_parameter(unsigned index, template_decl_sptr enclosing_template) : priv_(new priv(index, enclosing_template)) {} unsigned template_parameter::get_index() const {return priv_->index_;} const template_decl_sptr template_parameter::get_enclosing_template_decl() const {return priv_->template_decl_.lock();} bool template_parameter::get_hashing_has_started() const {return priv_->hashing_started_;} void template_parameter::set_hashing_has_started(bool f) const {priv_->hashing_started_ = f;} bool template_parameter::operator==(const template_parameter& o) const { if (get_index() != o.get_index()) return false; if (priv_->comparison_started_) return true; bool result = false; // Avoid inifite loops due to the fact that comparison the enclosing // template decl might lead to comparing this very same template // parameter with another one ... priv_->comparison_started_ = true; if (!!get_enclosing_template_decl() != !!o.get_enclosing_template_decl()) ; else if (get_enclosing_template_decl() && (*get_enclosing_template_decl() != *o.get_enclosing_template_decl())) ; else result = true; priv_->comparison_started_ = false; return result; } /// Inequality operator. /// /// @param other the other instance to compare against. /// /// @return true iff the other instance is different from the current /// one. bool template_parameter::operator!=(const template_parameter& other) const {return !operator==(other);} /// Destructor. template_parameter::~template_parameter() {} /// The type of the private data of the @ref type_tparameter type. class type_tparameter::priv { friend class type_tparameter; }; // end class type_tparameter::priv /// Constructor of the @ref type_tparameter type. /// /// @param index the index the type template parameter. /// /// @param enclosing_tdecl the enclosing template declaration. /// /// @param name the name of the template parameter. /// /// @param locus the location of the declaration of this type template /// parameter. type_tparameter::type_tparameter(unsigned index, template_decl_sptr enclosing_tdecl, const string& name, const location& locus) : type_or_decl_base(enclosing_tdecl->get_environment(), ABSTRACT_DECL_BASE | ABSTRACT_TYPE_BASE | BASIC_TYPE), decl_base(enclosing_tdecl->get_environment(), name, locus), type_base(enclosing_tdecl->get_environment(), 0, 0), type_decl(enclosing_tdecl->get_environment(), name, 0, 0, locus), template_parameter(index, enclosing_tdecl), priv_(new priv) { runtime_type_instance(this); } bool type_tparameter::operator==(const type_base& other) const { if (!type_decl::operator==(other)) return false; try { const type_tparameter& o = dynamic_cast(other); return template_parameter::operator==(o); } catch (...) {return false;} } bool type_tparameter::operator==(const template_parameter& other) const { try { const type_base& o = dynamic_cast(other); return *this == o; } catch(...) {return false;} } bool type_tparameter::operator==(const type_tparameter& other) const {return *this == static_cast(other);} type_tparameter::~type_tparameter() {} /// The type of the private data of the @ref non_type_tparameter type. class non_type_tparameter::priv { friend class non_type_tparameter; type_base_wptr type_; priv(); public: priv(type_base_sptr type) : type_(type) {} }; // end class non_type_tparameter::priv /// The constructor for the @ref non_type_tparameter type. /// /// @param index the index of the template parameter. /// /// @param enclosing_tdecl the enclosing template declaration that /// holds this parameter parameter. /// /// @param name the name of the template parameter. /// /// @param type the type of the template parameter. /// /// @param locus the location of the declaration of this template /// parameter. non_type_tparameter::non_type_tparameter(unsigned index, template_decl_sptr enclosing_tdecl, const string& name, type_base_sptr type, const location& locus) : type_or_decl_base(type->get_environment(), ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, locus, ""), template_parameter(index, enclosing_tdecl), priv_(new priv(type)) { runtime_type_instance(this); } /// Getter for the type of the template parameter. /// /// @return the type of the template parameter. const type_base_sptr non_type_tparameter::get_type() const {return priv_->type_.lock();} /// Get the hash value of the current instance. /// /// @return the hash value. size_t non_type_tparameter::get_hash() const { non_type_tparameter::hash hash_tparm; return hash_tparm(this); } bool non_type_tparameter::operator==(const decl_base& other) const { if (!decl_base::operator==(other)) return false; try { const non_type_tparameter& o = dynamic_cast(other); return (template_parameter::operator==(o) && get_type() == o.get_type()); } catch(...) {return false;} } bool non_type_tparameter::operator==(const template_parameter& other) const { try { const decl_base& o = dynamic_cast(other); return *this == o; } catch(...) {return false;} } non_type_tparameter::~non_type_tparameter() {} // /// Type of the private data of the @ref template_tparameter type. class template_tparameter::priv { }; //end class template_tparameter::priv /// Constructor for the @ref template_tparameter. /// /// @param index the index of the template parameter. /// /// @param enclosing_tdecl the enclosing template declaration. /// /// @param name the name of the template parameter. /// /// @param locus the location of the declaration of the template /// parameter. template_tparameter::template_tparameter(unsigned index, template_decl_sptr enclosing_tdecl, const string& name, const location& locus) : type_or_decl_base(enclosing_tdecl->get_environment(), ABSTRACT_DECL_BASE | ABSTRACT_TYPE_BASE | BASIC_TYPE), decl_base(enclosing_tdecl->get_environment(), name, locus), type_base(enclosing_tdecl->get_environment(), 0, 0), type_decl(enclosing_tdecl->get_environment(), name, 0, 0, locus, name, VISIBILITY_DEFAULT), type_tparameter(index, enclosing_tdecl, name, locus), template_decl(enclosing_tdecl->get_environment(), name, locus), priv_(new priv) { runtime_type_instance(this); } bool template_tparameter::operator==(const type_base& other) const { try { const template_tparameter& o = dynamic_cast(other); return (type_tparameter::operator==(o) && template_decl::operator==(o)); } catch(...) {return false;} } bool template_tparameter::operator==(const template_parameter& o) const { try { const template_tparameter& other = dynamic_cast(o); return *this == static_cast(other); } catch(...) {return false;} } bool template_tparameter::operator==(const template_decl& o) const { try { const template_tparameter& other = dynamic_cast(o); return type_base::operator==(other); } catch(...) {return false;} } template_tparameter::~template_tparameter() {} // // /// The type of the private data of the @ref type_composition type. class type_composition::priv { friend class type_composition; type_base_wptr type_; // Forbid this. priv(); public: priv(type_base_wptr type) : type_(type) {} }; //end class type_composition::priv /// Constructor for the @ref type_composition type. /// /// @param index the index of the template type composition. /// /// @param tdecl the enclosing template parameter that owns the /// composition. /// /// @param t the resulting type. type_composition::type_composition(unsigned index, template_decl_sptr tdecl, type_base_sptr t) : type_or_decl_base(tdecl->get_environment(), ABSTRACT_DECL_BASE), decl_base(tdecl->get_environment(), "", location()), template_parameter(index, tdecl), priv_(new priv(t)) { runtime_type_instance(this); } /// Getter for the resulting composed type. /// /// @return the composed type. const type_base_sptr type_composition::get_composed_type() const {return priv_->type_.lock();} /// Setter for the resulting composed type. /// /// @param t the composed type. void type_composition::set_composed_type(type_base_sptr t) {priv_->type_ = t;} /// Get the hash value for the current instance. /// /// @return the hash value. size_t type_composition::get_hash() const { type_composition::hash hash_type_composition; return hash_type_composition(this); } type_composition::~type_composition() {} // // // class function_tdecl::priv { friend class function_tdecl; function_decl_sptr pattern_; binding binding_; priv(); public: priv(function_decl_sptr pattern, binding bind) : pattern_(pattern), binding_(bind) {} priv(binding bind) : binding_(bind) {} }; // end class function_tdecl::priv /// Constructor for a function template declaration. /// /// @param env the environment we are operating from. /// /// @param locus the location of the declaration. /// /// @param vis the visibility of the declaration. This is the /// visibility the functions instantiated from this template are going /// to have. /// /// @param bind the binding of the declaration. This is the binding /// the functions instantiated from this template are going to have. function_tdecl::function_tdecl(const environment* env, const location& locus, visibility vis, binding bind) : type_or_decl_base(env, ABSTRACT_DECL_BASE | TEMPLATE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, "", locus, "", vis), template_decl(env, "", locus, vis), scope_decl(env, "", locus), priv_(new priv(bind)) { runtime_type_instance(this); } /// Constructor for a function template declaration. /// /// @param pattern the pattern of the template. /// /// @param locus the location of the declaration. /// /// @param vis the visibility of the declaration. This is the /// visibility the functions instantiated from this template are going /// to have. /// /// @param bind the binding of the declaration. This is the binding /// the functions instantiated from this template are going to have. function_tdecl::function_tdecl(function_decl_sptr pattern, const location& locus, visibility vis, binding bind) : type_or_decl_base(pattern->get_environment(), ABSTRACT_DECL_BASE | TEMPLATE_DECL | ABSTRACT_SCOPE_DECL), decl_base(pattern->get_environment(), pattern->get_name(), locus, pattern->get_name(), vis), template_decl(pattern->get_environment(), pattern->get_name(), locus, vis), scope_decl(pattern->get_environment(), pattern->get_name(), locus), priv_(new priv(pattern, bind)) { runtime_type_instance(this); } /// Set a new pattern to the function template. /// /// @param p the new pattern. void function_tdecl::set_pattern(function_decl_sptr p) { priv_->pattern_ = p; add_decl_to_scope(p, this); set_name(p->get_name()); } /// Get the pattern of the function template. /// /// @return the pattern. function_decl_sptr function_tdecl::get_pattern() const {return priv_->pattern_;} /// Get the binding of the function template. /// /// @return the binding decl_base::binding function_tdecl::get_binding() const {return priv_->binding_;} /// Comparison operator for the @ref function_tdecl type. /// /// @param other the other instance of @ref function_tdecl to compare against. /// /// @return true iff the two instance are equal. bool function_tdecl::operator==(const decl_base& other) const { const function_tdecl* o = dynamic_cast(&other); if (o) return *this == *o; return false; } /// Comparison operator for the @ref function_tdecl type. /// /// @param other the other instance of @ref function_tdecl to compare against. /// /// @return true iff the two instance are equal. bool function_tdecl::operator==(const template_decl& other) const { const function_tdecl* o = dynamic_cast(&other); if (o) return *this == *o; return false; } /// Comparison operator for the @ref function_tdecl type. /// /// @param o the other instance of @ref function_tdecl to compare against. /// /// @return true iff the two instance are equal. bool function_tdecl::operator==(const function_tdecl& o) const { if (!(get_binding() == o.get_binding() && template_decl::operator==(o) && scope_decl::operator==(o) && !!get_pattern() == !!o.get_pattern())) return false; if (get_pattern()) return (*get_pattern() == *o.get_pattern()); return true; } /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on the /// function pattern of the template. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool function_tdecl::traverse(ir_node_visitor&v) { if (visiting()) return true; if (!v.visit_begin(this)) { visiting(true); if (get_pattern()) get_pattern()->traverse(v); visiting(false); } return v.visit_end(this); } function_tdecl::~function_tdecl() {} // // /// Type of the private data of the the @ref class_tdecl type. class class_tdecl::priv { friend class class_tdecl; class_decl_sptr pattern_; public: priv() {} priv(class_decl_sptr pattern) : pattern_(pattern) {} }; // end class class_tdecl::priv /// Constructor for the @ref class_tdecl type. /// /// @param env the environment we are operating from. /// /// @param locus the location of the declaration of the class_tdecl /// type. /// /// @param vis the visibility of the instance of class instantiated /// from this template. class_tdecl::class_tdecl(const environment* env, const location& locus, visibility vis) : type_or_decl_base(env, ABSTRACT_DECL_BASE | TEMPLATE_DECL | ABSTRACT_SCOPE_DECL), decl_base(env, "", locus, "", vis), template_decl(env, "", locus, vis), scope_decl(env, "", locus), priv_(new priv) { runtime_type_instance(this); } /// Constructor for the @ref class_tdecl type. /// /// @param pattern The details of the class template. This must NOT be a /// null pointer. If you really this to be null, please use the /// constructor above instead. /// /// @param locus the source location of the declaration of the type. /// /// @param vis the visibility of the instances of class instantiated /// from this template. class_tdecl::class_tdecl(class_decl_sptr pattern, const location& locus, visibility vis) : type_or_decl_base(pattern->get_environment(), ABSTRACT_DECL_BASE | TEMPLATE_DECL | ABSTRACT_SCOPE_DECL), decl_base(pattern->get_environment(), pattern->get_name(), locus, pattern->get_name(), vis), template_decl(pattern->get_environment(), pattern->get_name(), locus, vis), scope_decl(pattern->get_environment(), pattern->get_name(), locus), priv_(new priv(pattern)) { runtime_type_instance(this); } /// Setter of the pattern of the template. /// /// @param p the new template. void class_tdecl::set_pattern(class_decl_sptr p) { priv_->pattern_ = p; add_decl_to_scope(p, this); set_name(p->get_name()); } /// Getter of the pattern of the template. /// /// @return p the new template. class_decl_sptr class_tdecl::get_pattern() const {return priv_->pattern_;} bool class_tdecl::operator==(const decl_base& other) const { try { const class_tdecl& o = dynamic_cast(other); if (!(template_decl::operator==(o) && scope_decl::operator==(o) && !!get_pattern() == !!o.get_pattern())) return false; if (!get_pattern() || !o.get_pattern()) return true; return get_pattern()->decl_base::operator==(*o.get_pattern()); } catch(...) {} return false; } bool class_tdecl::operator==(const template_decl& other) const { try { const class_tdecl& o = dynamic_cast(other); return *this == static_cast(o); } catch(...) {return false;} } bool class_tdecl::operator==(const class_tdecl& o) const {return *this == static_cast(o);} /// This implements the ir_traversable_base::traverse pure virtual /// function. /// /// @param v the visitor used on the current instance and on the class /// pattern of the template. /// /// @return true if the entire IR node tree got traversed, false /// otherwise. bool class_tdecl::traverse(ir_node_visitor&v) { if (visiting()) return true; if (v.visit_begin(this)) { visiting(true); if (class_decl_sptr pattern = get_pattern()) pattern->traverse(v); visiting(false); } return v.visit_end(this); } class_tdecl::~class_tdecl() {} /// This visitor checks if a given type as non-canonicalized sub /// types. class non_canonicalized_subtype_detector : public ir::ir_node_visitor { type_base* type_; type_base* has_non_canonical_type_; private: non_canonicalized_subtype_detector(); public: non_canonicalized_subtype_detector(type_base* type) : type_(type), has_non_canonical_type_() {} /// Return true if the visitor detected that there is a /// non-canonicalized sub-type. /// /// @return true if the visitor detected that there is a /// non-canonicalized sub-type. type_base* has_non_canonical_type() const {return has_non_canonical_type_;} /// The intent of this visitor handler is to avoid looking into /// sub-types of member functions of the type we are traversing. bool visit_begin(function_decl* f) { // Do not look at sub-types of non-virtual member functions. if (is_member_function(f) && get_member_function_is_virtual(*f)) return false; return true; } /// When visiting a sub-type, if it's *NOT* been canonicalized, set /// the 'has_non_canonical_type' flag. And in any case, when /// visiting a sub-type, do not visit its children nodes. So this /// function only goes to the level below the level of the top-most /// type. /// /// @return true if we are at the same level as the top-most type, /// otherwise return false. bool visit_begin(type_base* t) { if (t != type_) { if (!t->get_canonical_type()) // We are looking a sub-type of 'type_' which has no // canonical type. So tada! we found one! Get out right // now with the trophy. has_non_canonical_type_ = t; return false; } return true; } /// When we are done visiting a sub-type, if it's been flagged as /// been non-canonicalized, then stop the traversing. /// /// Otherwise, keep going. /// /// @return false iff the sub-type that has been visited is /// non-canonicalized. bool visit_end(type_base* ) { if (has_non_canonical_type_) return false; return true; } }; //end class non_canonicalized_subtype_detector /// Test if a type has sub-types that are non-canonicalized. /// /// @param t the type which sub-types to consider. /// /// @return true if a type has sub-types that are non-canonicalized. type_base* type_has_non_canonicalized_subtype(type_base_sptr t) { if (!t) return 0; non_canonicalized_subtype_detector v(t.get()); t->traverse(v); return v.has_non_canonical_type(); } /// Tests if the change of a given type effectively comes from just /// its sub-types. That is, if the type has changed but its type name /// hasn't changed, then the change of the type mostly likely is a /// sub-type change. /// /// @param t_v1 the first version of the type. /// /// @param t_v2 the second version of the type. /// /// @return true iff the type changed and the change is about its /// sub-types. bool type_has_sub_type_changes(const type_base_sptr t_v1, const type_base_sptr t_v2) { type_base_sptr t1 = strip_typedef(t_v1); type_base_sptr t2 = strip_typedef(t_v2); string repr1 = get_pretty_representation(t1, /*internal=*/false), repr2 = get_pretty_representation(t2, /*internal=*/false); return (t1 != t2 && repr1 == repr2); } /// Make sure that the life time of a given (smart pointer to a) type /// is the same as the life time of the libabigail library. /// /// @param t the type to consider. void keep_type_alive(type_base_sptr t) { environment* env = t->get_environment(); ABG_ASSERT(env); env->priv_->extra_live_types_.push_back(t); } /// Hash an ABI artifact that is either a type or a decl. /// /// This function intends to provides the fastest possible hashing for /// types and decls, while being completely correct. /// /// Note that if the artifact is a type and if it has a canonical /// type, the hash value is going to be the pointer value of the /// canonical type. Otherwise, this function computes a hash value /// for the type by recursively walking the type members. This last /// code path is possibly *very* slow and should only be used when /// only handful of types are going to be hashed. /// /// If the artifact is a decl, then a combination of the hash of its /// type and the hash of the other properties of the decl is computed. /// /// @param tod the type or decl to hash. /// /// @return the resulting hash value. size_t hash_type_or_decl(const type_or_decl_base *tod) { size_t result = 0; if (tod == 0) ; else if (const type_base* t = is_type(tod)) result = hash_type(t); else if (const decl_base* d = is_decl(tod)) { if (var_decl* v = is_var_decl(d)) { ABG_ASSERT(v->get_type()); size_t h = hash_type_or_decl(v->get_type()); string repr = v->get_pretty_representation(/*internal=*/true); std::hash hash_string; h = hashing::combine_hashes(h, hash_string(repr)); result = h; } else if (function_decl* f = is_function_decl(d)) { ABG_ASSERT(f->get_type()); size_t h = hash_type_or_decl(f->get_type()); string repr = f->get_pretty_representation(/*internal=*/true); std::hash hash_string; h = hashing::combine_hashes(h, hash_string(repr)); result = h; } else if (function_decl::parameter* p = is_function_parameter(d)) { type_base_sptr parm_type = p->get_type(); ABG_ASSERT(parm_type); std::hash hash_bool; std::hash hash_unsigned; size_t h = hash_type_or_decl(parm_type); h = hashing::combine_hashes(h, hash_unsigned(p->get_index())); h = hashing::combine_hashes(h, hash_bool(p->get_variadic_marker())); result = h; } else if (class_decl::base_spec *bs = is_class_base_spec(d)) { member_base::hash hash_member; std::hash hash_size; std::hash hash_bool; type_base_sptr type = bs->get_base_class(); size_t h = hash_type_or_decl(type); h = hashing::combine_hashes(h, hash_member(*bs)); h = hashing::combine_hashes(h, hash_size(bs->get_offset_in_bits())); h = hashing::combine_hashes(h, hash_bool(bs->get_is_virtual())); result = h; } else // This is a *really* *SLOW* path. If it shows up in a // performance profile, I bet it'd be a good idea to try to // avoid it altogether. result = d->get_hash(); } else // We should never get here. abort(); return result; } /// Hash an ABI artifact that is either a type. /// /// This function intends to provides the fastest possible hashing for /// types while being completely correct. /// /// Note that if the type artifact has a canonical type, the hash /// value is going to be the pointer value of the canonical type. /// Otherwise, this function computes a hash value for the type by /// recursively walking the type members. This last code path is /// possibly *very* slow and should only be used when only handful of /// types are going to be hashed. /// /// @param t the type or decl to hash. /// /// @return the resulting hash value. size_t hash_type(const type_base *t) {return hash_as_canonical_type_or_constant(t);} /// Hash an ABI artifact that is either a type of a decl. /// /// @param tod the ABI artifact to hash. /// /// @return the hash value of the ABI artifact. size_t hash_type_or_decl(const type_or_decl_base_sptr& tod) {return hash_type_or_decl(tod.get());} /// Test if a given type is allowed to be non canonicalized /// /// This is a subroutine of hash_as_canonical_type_or_constant. /// /// For now, the only types allowed to be non canonicalized in the /// system are decl-only class/union, the void type and variadic /// parameter types. /// /// @return true iff @p t is a one of the only types allowed to be /// non-canonicalized in the system. bool is_non_canonicalized_type(const type_base *t) { if (!t) return true; const environment* env = t->get_environment(); return (is_declaration_only_class_or_union_type(t) || env->is_void_type(t) || env->is_variadic_parameter_type(t)); } /// For a given type, return its exemplar type. /// /// For a given type, its exemplar type is either its canonical type /// or the canonical type of the definition type of a given /// declaration-only type. If the neither of those two types exist, /// then the exemplar type is the given type itself. /// /// @param type the input to consider. /// /// @return the exemplar type. type_base* get_exemplar_type(const type_base* type) { if (decl_base * decl = is_decl(type)) { // Make sure we get the real definition of a decl-only type. decl = look_through_decl_only(decl); type = is_type(decl); ABG_ASSERT(type); } type_base *exemplar = type->get_naked_canonical_type(); if (!exemplar) { // The type has no canonical type. Let's be sure that it's one // of those rare types that are allowed to be non canonicalized // in the system. exemplar = const_cast(type); ABG_ASSERT(is_non_canonicalized_type(exemplar)); } return exemplar; } /// Test if a given type is allowed to be non canonicalized /// /// This is a subroutine of hash_as_canonical_type_or_constant. /// /// For now, the only types allowed to be non canonicalized in the /// system are decl-only class/union and the void type. /// /// @return true iff @p t is a one of the only types allowed to be /// non-canonicalized in the system. bool is_non_canonicalized_type(const type_base_sptr& t) {return is_non_canonicalized_type(t.get());} /// Hash a type by either returning the pointer value of its canonical /// type or by returning a constant if the type doesn't have a /// canonical type. /// /// This is a subroutine of hash_type. /// /// @param t the type to consider. /// /// @return the hash value. static size_t hash_as_canonical_type_or_constant(const type_base *t) { type_base *canonical_type = 0; if (t) canonical_type = t->get_naked_canonical_type(); if (!canonical_type) { // If the type doesn't have a canonical type, maybe it's because // it's a declaration-only type? If that's the case, let's try // to get the canonical type of the definition of this // declaration. decl_base *decl = is_decl(t); if (decl && decl->get_is_declaration_only() && decl->get_naked_definition_of_declaration()) { type_base *definition = is_type(decl->get_naked_definition_of_declaration()); ABG_ASSERT(definition); canonical_type = definition->get_naked_canonical_type(); } } if (canonical_type) return reinterpret_cast(canonical_type); // If we reached this point, it means we are seeing a // non-canonicalized type. It must be a decl-only class or a void // type, otherwise it means that for some weird reason, the type // hasn't been canonicalized. It should be! ABG_ASSERT(is_non_canonicalized_type(t)); return 0xDEADBABE; } /// Test if the pretty representation of a given @ref function_decl is /// lexicographically less then the pretty representation of another /// @ref function_decl. /// /// @param f the first @ref function_decl to consider for comparison. /// /// @param s the second @ref function_decl to consider for comparison. /// /// @return true iff the pretty representation of @p f is /// lexicographically less than the pretty representation of @p s. bool function_decl_is_less_than(const function_decl &f, const function_decl &s) { string fr = f.get_pretty_representation_of_declarator(), sr = s.get_pretty_representation_of_declarator(); if (fr != sr) return fr < sr; fr = f.get_pretty_representation(/*internal=*/true), sr = s.get_pretty_representation(/*internal=*/true); if (fr != sr) return fr < sr; if (f.get_symbol()) fr = f.get_symbol()->get_id_string(); else if (!f.get_linkage_name().empty()) fr = f.get_linkage_name(); if (s.get_symbol()) sr = s.get_symbol()->get_id_string(); else if (!s.get_linkage_name().empty()) sr = s.get_linkage_name(); return fr < sr; } /// Test if two types have similar structures, even though they are /// (or can be) different. /// /// const and volatile qualifiers are completely ignored. /// /// typedef are resolved to their definitions; their names are ignored. /// /// Two indirect types (pointers or references) have similar structure /// if their underlying types are of the same kind and have the same /// name. In the indirect types case, the size of the underlying type /// does not matter. /// /// Two direct types (i.e, non indirect) have a similar structure if /// they have the same kind, name and size. Two class types have /// similar structure if they have the same name, size, and if the /// types of their data members have similar types. /// /// @param first the first type to consider. /// /// @param second the second type to consider. /// /// @param indirect_type whether to do an indirect comparison /// /// @return true iff @p first and @p second have similar structures. bool types_have_similar_structure(const type_base_sptr& first, const type_base_sptr& second, bool indirect_type) { // This wrapper function is used during comparisons. It is called by and its // wrappee calls the main equality functions, possibly resulting in a // multiplication of the cost of comparison. // This is a convenient place to add some memoisation to avoid at // least some of the duplicate calls. This is safe only if pointer // addresses are never reused. using Arguments = std::tuple; struct Hasher { size_t operator()(const Arguments& arguments) const { const auto& [first, second, indirect] = arguments; size_t seed = 0; std::hash h; combine_hash(seed, h(first)); combine_hash(seed, h(second)); combine_hash(seed, indirect); return seed; } static void combine_hash(size_t& seed, size_t hash) { seed ^= hash + 0x9e3779b97f4a7c15 + (seed << 12) + (seed >> 4); } }; static std::unordered_map, Hasher> memo; const Arguments t{first.get(), second.get(), indirect_type}; const auto& [it, inserted] = memo.insert({t, {}}); auto& result = it->second; if (inserted) result = types_have_similar_structure( std::get<0>(t), std::get<1>(t), std::get<2>(t)); else ABG_ASSERT(result.has_value()); return *result; } /// Test if two types have similar structures, even though they are /// (or can be) different. /// /// const and volatile qualifiers are completely ignored. /// /// typedef are resolved to their definitions; their names are ignored. /// /// Two indirect types (pointers, references or arrays) have similar /// structure if their underlying types are of the same kind and have /// the same name. In the indirect types case, the size of the /// underlying type does not matter. /// /// Two direct types (i.e, non indirect) have a similar structure if /// they have the same kind, name and size. Two class types have /// similar structure if they have the same name, size, and if the /// types of their data members have similar types. /// /// @param first the first type to consider. /// /// @param second the second type to consider. /// /// @param indirect_type if true, then consider @p first and @p /// second as being underlying types of indirect types. Meaning that /// their size does not matter. /// /// @return true iff @p first and @p second have similar structures. bool types_have_similar_structure(const type_base* first, const type_base* second, bool indirect_type) { if (!!first != !!second) return false; if (!first) return false; // Treat typedefs purely as type aliases and ignore CV-qualifiers. first = peel_qualified_or_typedef_type(first); second = peel_qualified_or_typedef_type(second); // Eliminate all but N of the N^2 comparison cases. This also guarantees the // various ty2 below cannot be null. if (typeid(*first) != typeid(*second)) return false; // Peel off matching pointers. if (const pointer_type_def* ty1 = is_pointer_type(first)) { const pointer_type_def* ty2 = is_pointer_type(second); return types_have_similar_structure(ty1->get_pointed_to_type(), ty2->get_pointed_to_type(), /*indirect_type=*/true); } // Peel off matching references. if (const reference_type_def* ty1 = is_reference_type(first)) { const reference_type_def* ty2 = is_reference_type(second); if (ty1->is_lvalue() != ty2->is_lvalue()) return false; return types_have_similar_structure(ty1->get_pointed_to_type(), ty2->get_pointed_to_type(), /*indirect_type=*/true); } if (const type_decl* ty1 = is_type_decl(first)) { const type_decl* ty2 = is_type_decl(second); if (!indirect_type) if (ty1->get_size_in_bits() != ty2->get_size_in_bits()) return false; return ty1->get_name() == ty2->get_name(); } if (const enum_type_decl* ty1 = is_enum_type(first)) { const enum_type_decl* ty2 = is_enum_type(second); if (!indirect_type) if (ty1->get_size_in_bits() != ty2->get_size_in_bits()) return false; return (get_name(ty1->get_underlying_type()) == get_name(ty2->get_underlying_type())); } if (const class_decl* ty1 = is_class_type(first)) { const class_decl* ty2 = is_class_type(second); if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous() && ty1->get_name() != ty2->get_name()) return false; if (!indirect_type) { if ((ty1->get_size_in_bits() != ty2->get_size_in_bits()) || (ty1->get_non_static_data_members().size() != ty2->get_non_static_data_members().size())) return false; for (class_or_union::data_members::const_iterator i = ty1->get_non_static_data_members().begin(), j = ty2->get_non_static_data_members().begin(); (i != ty1->get_non_static_data_members().end() && j != ty2->get_non_static_data_members().end()); ++i, ++j) { var_decl_sptr dm1 = *i; var_decl_sptr dm2 = *j; if (!types_have_similar_structure(dm1->get_type().get(), dm2->get_type().get(), indirect_type)) return false; } } return true; } if (const union_decl* ty1 = is_union_type(first)) { const union_decl* ty2 = is_union_type(second); if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous() && ty1->get_name() != ty2->get_name()) return false; if (!indirect_type) return ty1->get_size_in_bits() == ty2->get_size_in_bits(); return true; } if (const array_type_def* ty1 = is_array_type(first)) { const array_type_def* ty2 = is_array_type(second); // TODO: Handle int[5][2] vs int[2][5] better. if (ty1->get_size_in_bits() != ty2->get_size_in_bits() || ty1->get_dimension_count() != ty2->get_dimension_count() || !types_have_similar_structure(ty1->get_element_type(), ty2->get_element_type(), /*indirect_type=*/true)) return false; return true; } if (const array_type_def::subrange_type *ty1 = is_subrange_type(first)) { const array_type_def::subrange_type *ty2 = is_subrange_type(second); if (ty1->get_upper_bound() != ty2->get_upper_bound() || ty1->get_lower_bound() != ty2->get_lower_bound() || ty1->get_language() != ty2->get_language() || !types_have_similar_structure(ty1->get_underlying_type(), ty2->get_underlying_type(), indirect_type)) return false; return true; } if (const function_type* ty1 = is_function_type(first)) { const function_type* ty2 = is_function_type(second); if (!types_have_similar_structure(ty1->get_return_type(), ty2->get_return_type(), indirect_type)) return false; if (ty1->get_parameters().size() != ty2->get_parameters().size()) return false; for (function_type::parameters::const_iterator i = ty1->get_parameters().begin(), j = ty2->get_parameters().begin(); (i != ty1->get_parameters().end() && j != ty2->get_parameters().end()); ++i, ++j) if (!types_have_similar_structure((*i)->get_type(), (*j)->get_type(), indirect_type)) return false; return true; } // All kinds of type should have been handled at this point. ABG_ASSERT_NOT_REACHED; return false; } /// Look for a data member of a given class, struct or union type and /// return it. /// /// The data member is designated by its name. /// /// @param type the class, struct or union type to consider. /// /// @param dm_name the name of the data member to lookup. /// /// @return the data member iff it was found in @type or NULL if no /// data member with that name was found. const var_decl* lookup_data_member(const type_base* type, const char* dm_name) { class_or_union *cou = is_class_or_union_type(type); if (!cou) return 0; return cou->find_data_member(dm_name).get(); } /// Get the function parameter designated by its index. /// /// Note that the first function parameter has index 0. /// /// @param fun the function to consider. /// /// @param parm_index the index of the function parameter to get. /// /// @return the function parameter designated by its index, of NULL if /// no function parameter with that index was found. const function_decl::parameter* get_function_parameter(const decl_base* fun, unsigned parm_index) { function_decl* fn = is_function_decl(fun); if (!fn) return 0; const function_decl::parameters &parms = fn->get_type()->get_parameters(); if (parms.size() <= parm_index) return 0; return parms[parm_index].get(); } bool ir_traversable_base::traverse(ir_node_visitor&) {return true;} // /// The private data structure of the ir_node_visitor type. struct ir_node_visitor::priv { pointer_set visited_ir_nodes; bool allow_visiting_already_visited_type_node; priv() : allow_visiting_already_visited_type_node(true) {} }; // end struct ir_node_visitory::priv /// Default Constructor of the ir_node_visitor type. ir_node_visitor::ir_node_visitor() : priv_(new priv) {} ir_node_visitor::~ir_node_visitor() = default; /// Set if the walker using this visitor is allowed to re-visit a type /// node that was previously visited or not. /// /// @param f if true, then the walker using this visitor is allowed to /// re-visit a type node that was previously visited. void ir_node_visitor::allow_visiting_already_visited_type_node(bool f) {priv_->allow_visiting_already_visited_type_node = f;} /// Get if the walker using this visitor is allowed to re-visit a type /// node that was previously visited or not. /// /// @return true iff the walker using this visitor is allowed to /// re-visit a type node that was previously visited. bool ir_node_visitor::allow_visiting_already_visited_type_node() const {return priv_->allow_visiting_already_visited_type_node;} /// Mark a given type node as having been visited. /// /// Note that for this function to work, the type node must have been /// canonicalized. Otherwise the process is aborted. /// /// @param p the type to mark as having been visited. void ir_node_visitor::mark_type_node_as_visited(type_base *p) { if (allow_visiting_already_visited_type_node()) return; if (p == 0 || type_node_has_been_visited(p)) return; type_base* canonical_type = p->get_naked_canonical_type(); ABG_ASSERT(canonical_type); size_t canonical_ptr_value = reinterpret_cast(canonical_type); priv_->visited_ir_nodes.insert(canonical_ptr_value); } /// Un-mark all visited type nodes. /// /// That is, no type node is going to be considered as having been /// visited anymore. /// /// In other words, after invoking this funciton, /// ir_node_visitor::type_node_has_been_visited() is going to return /// false on all type nodes. void ir_node_visitor::forget_visited_type_nodes() {priv_->visited_ir_nodes.clear();} /// Test if a given type node has been marked as visited. /// /// @param p the type node to consider. /// /// @return true iff the type node @p p has been marked as visited by /// the function ir_node_visitor::mark_type_node_as_visited. bool ir_node_visitor::type_node_has_been_visited(type_base* p) const { if (allow_visiting_already_visited_type_node()) return false; if (p == 0) return false; type_base *canonical_type = p->get_naked_canonical_type(); ABG_ASSERT(canonical_type); size_t ptr_value = reinterpret_cast(canonical_type); pointer_set::iterator it = priv_->visited_ir_nodes.find(ptr_value); if (it == priv_->visited_ir_nodes.end()) return false; return true; } bool ir_node_visitor::visit_begin(decl_base*) {return true;} bool ir_node_visitor::visit_end(decl_base*) {return true;} bool ir_node_visitor::visit_begin(scope_decl*) {return true;} bool ir_node_visitor::visit_end(scope_decl*) {return true;} bool ir_node_visitor::visit_begin(type_base*) {return true;} bool ir_node_visitor::visit_end(type_base*) {return true;} bool ir_node_visitor::visit_begin(scope_type_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(scope_type_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(type_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(type_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(namespace_decl* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(namespace_decl* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(qualified_type_def* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(qualified_type_def* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(pointer_type_def* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(pointer_type_def* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(reference_type_def* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(reference_type_def* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(array_type_def* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(array_type_def* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(array_type_def::subrange_type* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(array_type_def::subrange_type* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(enum_type_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(enum_type_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(typedef_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(typedef_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(function_type* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(function_type* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(var_decl* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(var_decl* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(function_decl* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(function_decl* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(function_decl::parameter* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(function_decl::parameter* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(function_tdecl* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(function_tdecl* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(class_tdecl* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(class_tdecl* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(class_or_union* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(class_or_union* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(class_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(class_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(union_decl* t) {return visit_begin(static_cast(t));} bool ir_node_visitor::visit_end(union_decl* t) {return visit_end(static_cast(t));} bool ir_node_visitor::visit_begin(class_decl::base_spec* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(class_decl::base_spec* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(member_function_template* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(member_function_template* d) {return visit_end(static_cast(d));} bool ir_node_visitor::visit_begin(member_class_template* d) {return visit_begin(static_cast(d));} bool ir_node_visitor::visit_end(member_class_template* d) {return visit_end(static_cast(d));} // // /// Generate a different string at each invocation. /// /// @return the resulting string. static string get_next_string() { static __thread size_t counter; ++counter; std::ostringstream o; o << counter; return o.str(); } /// Convenience typedef for a hash map of pointer to function_decl and /// string. typedef unordered_map fns_to_str_map_type; /// Return a string associated to a given function. Two functions /// that compare equal would yield the same string, as far as this /// routine is concerned. And two functions that are different would /// yield different strings. /// /// This is used to debug core diffing issues on functions. The /// sequence of strings can be given to the 'testdiff2' program that /// is in the tests/ directory of the source tree, to reproduce core /// diffing issues on string and thus ease the debugging. /// /// @param fn the function to generate a string for. /// /// @param m the function_decl* <-> string map to be used by this /// function to generate strings associated to a function. /// /// @return the resulting string. static const string& fn_to_str(const function_decl* fn, fns_to_str_map_type& m) { fns_to_str_map_type::const_iterator i = m.find(fn); if (i != m.end()) return i->second; string s = get_next_string(); return m[fn]= s; } /// Generate a sequence of string that matches a given sequence of /// function. In the resulting sequence, each function is "uniquely /// representated" by a string. For instance, if the same function "foo" /// appears at indexes 1 and 3, then the same string 'schmurf' (okay, /// we don't care about the actual string) would appear at index 1 and 3. /// /// @param begin the beginning of the sequence of functions to consider. /// /// @param end the end of the sequence of functions. This points to /// one-passed-the-end of the actual sequence. /// /// @param m the function_decl* <-> string map to be used by this /// function to generate strings associated to a function. /// /// @param o the output stream where to emit the generated list of /// strings to. static void fns_to_str(vector::const_iterator begin, vector::const_iterator end, fns_to_str_map_type& m, std::ostream& o) { vector::const_iterator i; for (i = begin; i != end; ++i) o << "'" << fn_to_str(*i, m) << "' "; } /// For each sequence of functions given in argument, generate a /// sequence of string that matches a given sequence of function. In /// the resulting sequence, each function is "uniquely representated" /// by a string. For instance, if the same function "foo" appears at /// indexes 1 and 3, then the same string 'schmurf' (okay, we don't /// care about the actual string) would appear at index 1 and 3. /// /// @param a_begin the beginning of the sequence of functions to consider. /// /// @param a_end the end of the sequence of functions. This points to /// one-passed-the-end of the actual sequence. /// /// @param b_begin the beginning of the second sequence of functions /// to consider. /// /// @param b_end the end of the second sequence of functions. /// /// @param m the function_decl* <-> string map to be used by this /// function to generate strings associated to a function. /// /// @param o the output stream where to emit the generated list of /// strings to. static void fns_to_str(vector::const_iterator a_begin, vector::const_iterator a_end, vector::const_iterator b_begin, vector::const_iterator b_end, fns_to_str_map_type& m, std::ostream& o) { fns_to_str(a_begin, a_end, m, o); o << "->|<- "; fns_to_str(b_begin, b_end, m, o); o << "\n"; } /// For each sequence of functions given in argument, generate a /// sequence of string that matches a given sequence of function. In /// the resulting sequence, each function is "uniquely representated" /// by a string. For instance, if the same function "foo" appears at /// indexes 1 and 3, then the same string 'schmurf' (okay, we don't /// care about the actual string) would appear at index 1 and 3. /// /// @param a_begin the beginning of the sequence of functions to consider. /// /// @param a_end the end of the sequence of functions. This points to /// one-passed-the-end of the actual sequence. /// /// @param b_begin the beginning of the second sequence of functions /// to consider. /// /// @param b_end the end of the second sequence of functions. /// /// @param o the output stream where to emit the generated list of /// strings to. void fns_to_str(vector::const_iterator a_begin, vector::const_iterator a_end, vector::const_iterator b_begin, vector::const_iterator b_end, std::ostream& o) { fns_to_str_map_type m; fns_to_str(a_begin, a_end, b_begin, b_end, m, o); } // // }// end namespace ir }//end namespace abigail namespace { /// Update the qualified parent name, qualified name and scoped name /// of a tree decl node. /// /// @return true if the tree walking should continue, false otherwise. /// /// @param d the tree node to take in account. bool qualified_name_setter::do_update(abigail::ir::decl_base* d) { std::string parent_qualified_name; abigail::ir::scope_decl* parent = d->get_scope(); if (parent) d->priv_->qualified_parent_name_ = parent->get_qualified_name(); else d->priv_->qualified_parent_name_ = abigail::interned_string(); abigail::environment* env = d->get_environment(); ABG_ASSERT(env); if (!d->priv_->qualified_parent_name_.empty()) { if (d->get_name().empty()) d->priv_->qualified_name_ = abigail::interned_string(); else d->priv_->qualified_name_ = env->intern(d->priv_->qualified_parent_name_ + "::" + d->get_name()); } if (d->priv_->scoped_name_.empty()) { if (parent && !parent->get_is_anonymous() && !parent->get_name().empty()) d->priv_->scoped_name_ = env->intern(parent->get_name() + "::" + d->get_name()); else d->priv_->scoped_name_ = env->intern(d->get_name()); } if (!is_scope_decl(d)) return false; return true; } /// This is called when we start visiting a decl node, during the /// udpate of the qualified name of a given sub-tree. /// /// @param d the decl node we are visiting. /// /// @return true iff the traversal should keep going. bool qualified_name_setter::visit_begin(abigail::ir::decl_base* d) {return do_update(d);} /// This is called when we start visiting a type node, during the /// udpate of the qualified name of a given sub-tree. /// /// @param d the decl node we are visiting. /// /// @return true iff the traversal should keep going. bool qualified_name_setter::visit_begin(abigail::ir::type_base* t) { if (abigail::ir::decl_base* d = get_type_declaration(t)) return do_update(d); return false; } }// end anonymous namespace.