| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- Mode: C++ -*- |
| // |
| // Copyright (C) 2016-2020 Red Hat, Inc. |
| // |
| // Author: Dodji Seketeli |
| |
| /// @file |
| /// |
| /// This contains the private implementation of the suppression engine |
| /// of libabigail. |
| |
| #ifndef __ABG_IR_PRIV_H__ |
| #define __ABG_IR_PRIV_H__ |
| |
| #include <string> |
| |
| #include "abg-ir.h" |
| #include "abg-corpus.h" |
| |
| namespace abigail |
| { |
| |
| namespace ir |
| { |
| |
| using std::string; |
| |
| /// The internal representation of an integral type. |
| /// |
| /// This is a "utility type" used internally to canonicalize the name |
| /// of fundamental integral types, so that "unsignd long" and "long |
| /// unsined int" end-up having the same name. |
| class integral_type |
| { |
| public: |
| /// The possible base types of integral types. We might have |
| /// forgotten many of these, so do not hesitate to add new ones. |
| /// |
| /// If you do add new ones, please also consider updating functions |
| /// parse_base_integral_type and integral_type::to_string. |
| enum base_type |
| { |
| /// The "int" base type. |
| INT_BASE_TYPE, |
| /// The "char" base type. |
| CHAR_BASE_TYPE, |
| /// The "bool" base type in C++ or "_Bool" in C11. |
| BOOL_BASE_TYPE, |
| /// The "double" base type. |
| DOUBLE_BASE_TYPE, |
| /// The "float" base type. |
| FLOAT_BASE_TYPE, |
| /// The "char16_t base type. |
| CHAR16_T_BASE_TYPE, |
| /// The "char32_t" base type. |
| CHAR32_T_BASE_TYPE, |
| /// The "wchar_t" base type. |
| WCHAR_T_BASE_TYPE |
| }; |
| |
| /// The modifiers of the base types above. Several modifiers can be |
| /// combined for a given base type. The presence of modifiers is |
| /// usually modelled by a bitmap of modifiers. |
| /// |
| /// If you add a new modifier, please consider updating functions |
| /// parse_integral_type_modifier and integral_type::to_string. |
| enum modifiers_type |
| { |
| NO_MODIFIER = 0, |
| /// The "signed" modifier. |
| SIGNED_MODIFIER = 1, |
| /// The "unsigned" modier. |
| UNSIGNED_MODIFIER = 1 << 1, |
| /// The "short" modifier. |
| SHORT_MODIFIER = 1 << 2, |
| /// The "long" modifier. |
| LONG_MODIFIER = 1 << 3, |
| /// The "long long" modifier. |
| LONG_LONG_MODIFIER = 1 << 4 |
| }; |
| |
| private: |
| base_type base_; |
| modifiers_type modifiers_; |
| |
| public: |
| |
| integral_type(); |
| integral_type(const string& name); |
| integral_type(base_type, modifiers_type); |
| |
| base_type |
| get_base_type() const; |
| |
| modifiers_type |
| get_modifiers() const; |
| |
| bool |
| operator==(const integral_type&) const; |
| |
| string |
| to_string() const; |
| |
| operator string() const; |
| }; // end class integral_type |
| |
| integral_type::modifiers_type |
| operator|(integral_type::modifiers_type l, integral_type::modifiers_type r); |
| |
| integral_type::modifiers_type |
| operator&(integral_type::modifiers_type l, integral_type::modifiers_type r); |
| |
| integral_type::modifiers_type& |
| operator|=(integral_type::modifiers_type& l, integral_type::modifiers_type r); |
| |
| bool |
| parse_integral_type(const string& type_name, |
| integral_type& type); |
| |
| /// Private type to hold private members of @ref translation_unit |
| struct translation_unit::priv |
| { |
| const environment* env_; |
| corpus* corp; |
| bool is_constructed_; |
| char address_size_; |
| language language_; |
| std::string path_; |
| std::string comp_dir_path_; |
| std::string abs_path_; |
| location_manager loc_mgr_; |
| mutable global_scope_sptr global_scope_; |
| mutable vector<type_base_sptr> synthesized_types_; |
| vector<function_type_sptr> live_fn_types_; |
| type_maps types_; |
| |
| |
| priv(const environment* env) |
| : env_(env), |
| corp(), |
| is_constructed_(), |
| address_size_(), |
| language_(LANG_UNKNOWN) |
| {} |
| |
| ~priv() |
| {} |
| |
| type_maps& |
| get_types() |
| {return types_;} |
| }; // end translation_unit::priv |
| |
| // <type_base definitions> |
| |
| /// Definition of the private data of @ref type_base. |
| struct type_base::priv |
| { |
| size_t size_in_bits; |
| size_t alignment_in_bits; |
| type_base_wptr canonical_type; |
| // The data member below holds the canonical type that is managed by |
| // the smart pointer referenced by the canonical_type data member |
| // above. We are storing this underlying (naked) pointer here, so |
| // that users can access it *fast*. Otherwise, accessing |
| // canonical_type above implies creating a shared_ptr, and that has |
| // been measured to be slow for some performance hot spots. |
| type_base* naked_canonical_type; |
| // Computing the representation of a type again and again can be |
| // costly. So we cache the internal and non-internal type |
| // representation strings here. |
| interned_string internal_cached_repr_; |
| interned_string cached_repr_; |
| // The next two data members are used while comparing types during |
| // canonicalization. They are useful for the "canonical type |
| // propagation" (aka on-the-fly-canonicalization) optimization |
| // implementation. |
| |
| // The set of canonical recursive types this type depends on. |
| unordered_set<uintptr_t> depends_on_recursive_type_; |
| bool canonical_type_propagated_; |
| |
| priv() |
| : size_in_bits(), |
| alignment_in_bits(), |
| naked_canonical_type(), |
| canonical_type_propagated_(false) |
| {} |
| |
| priv(size_t s, |
| size_t a, |
| type_base_sptr c = type_base_sptr()) |
| : size_in_bits(s), |
| alignment_in_bits(a), |
| canonical_type(c), |
| naked_canonical_type(c.get()), |
| canonical_type_propagated_(false) |
| {} |
| |
| /// Test if the current type depends on recursive type comparison. |
| /// |
| /// A recursive type T is a type T which has a sub-type that is T |
| /// (recursively) itself. |
| /// |
| /// So this function tests if the current type has a recursive |
| /// sub-type or is a recursive type itself. |
| /// |
| /// @return true if the current type depends on a recursive type. |
| bool |
| depends_on_recursive_type() const |
| {return !depends_on_recursive_type_.empty();} |
| |
| /// Test if the current type depends on a given recursive type. |
| /// |
| /// A recursive type T is a type T which has a sub-type that is T |
| /// (recursively) itself. |
| /// |
| /// So this function tests if the current type depends on a given |
| /// recursive type. |
| /// |
| /// @param dependant the type we want to test if the current type |
| /// depends on. |
| /// |
| /// @return true if the current type depends on the recursive type |
| /// @dependant. |
| bool |
| depends_on_recursive_type(const type_base* dependant) const |
| { |
| return |
| (depends_on_recursive_type_.find(reinterpret_cast<uintptr_t>(dependant)) |
| != depends_on_recursive_type_.end()); |
| } |
| |
| /// Set the flag that tells if the current type depends on a given |
| /// recursive type. |
| /// |
| /// A recursive type T is a type T which has asub-type that is T |
| /// (recursively) itself. |
| /// |
| /// So this function tests if the current type depends on a |
| /// recursive type. |
| /// |
| /// @param t the recursive type that current type depends on. |
| void |
| set_depends_on_recursive_type(const type_base * t) |
| {depends_on_recursive_type_.insert(reinterpret_cast<uintptr_t>(t));} |
| |
| /// Unset the flag that tells if the current type depends on a given |
| /// recursive type. |
| /// |
| /// A recursive type T is a type T which has asub-type that is T |
| /// (recursively) itself. |
| /// |
| /// So this function flags the current type as not being dependant |
| /// on a given recursive type. |
| /// |
| /// |
| /// @param t the recursive type to consider. |
| void |
| set_does_not_depend_on_recursive_type(const type_base *t) |
| {depends_on_recursive_type_.erase(reinterpret_cast<uintptr_t>(t));} |
| |
| /// Flag the current type as not being dependant on any recursive type. |
| void |
| set_does_not_depend_on_recursive_type() |
| {depends_on_recursive_type_.clear();} |
| |
| /// Test if the type carries a canonical type that is the result of |
| /// maybe_propagate_canonical_type(), aka, "canonical type |
| /// propagation optimization". |
| /// |
| /// @return true iff the current type carries a canonical type that |
| /// is the result of canonical type propagation. |
| bool |
| canonical_type_propagated() |
| {return canonical_type_propagated_;} |
| |
| /// Set the flag that says if the type carries a canonical type that |
| /// is the result of maybe_propagate_canonical_type(), aka, |
| /// "canonical type propagation optimization". |
| /// |
| /// @param f true iff the current type carries a canonical type that |
| /// is the result of canonical type propagation. |
| void |
| set_canonical_type_propagated(bool f) |
| {canonical_type_propagated_ = f;} |
| |
| /// If the current canonical type was set as the result of the |
| /// "canonical type propagation optimization", then clear it. |
| void |
| clear_propagated_canonical_type() |
| { |
| if (canonical_type_propagated_) |
| { |
| canonical_type.reset(); |
| naked_canonical_type = nullptr; |
| set_canonical_type_propagated(false); |
| } |
| } |
| }; // end struct type_base::priv |
| |
| // <environment definitions> |
| /// The private data of the @ref environment type. |
| struct environment::priv |
| { |
| config config_; |
| canonical_types_map_type canonical_types_; |
| mutable vector<type_base_sptr> sorted_canonical_types_; |
| type_base_sptr void_type_; |
| type_base_sptr variadic_marker_type_; |
| unordered_set<const class_or_union*> classes_being_compared_; |
| unordered_set<const function_type*> fn_types_being_compared_; |
| vector<type_base_sptr> extra_live_types_; |
| interned_string_pool string_pool_; |
| // The two vectors below represent the stack of left and right |
| // operands of the current type comparison operation that is |
| // happening during type canonicalization. |
| // |
| // Basically, that stack of operand looks like below. |
| // |
| // First, suppose we have a type T_L that has two sub-types as this: |
| // |
| // T_L |
| // | |
| // +-- L_OP0 |
| // | |
| // +-- L_OP1 |
| // |
| // Now suppose that we have another type T_R that has two sub-types |
| // as this: |
| // |
| // T_R |
| // | |
| // +-- R_OP0 |
| // | |
| // +-- R_OP1 |
| // |
| // Now suppose that we compare T_L against T_R. We are going to |
| // have a stack of pair of types. Each pair of types represents |
| // two (sub) types being compared against each other. |
| // |
| // On the stack, we will thus first have the pair (T_L, T_R) |
| // being compared. Then, we will have the pair (L_OP0, R_OP0) |
| // being compared, and then the pair (L_OP1, R_OP1) being |
| // compared. Like this: |
| // |
| // | T_L | L_OP0 | L_OP1 | <-- this goes into left_type_comp_operands_; |
| // -------- ------------- |
| // | T_R | R_OP0 | R_OP1 | <-- this goes into right_type_comp_operands_; |
| // |
| // This "stack of operands of the current type comparison, during |
| // type canonicalization" is used in the context of the @ref |
| // OnTheFlyCanonicalization optimization. It's used to detect if a |
| // sub-type of the type being canonicalized depends on a recursive |
| // type. |
| vector<const type_base*> left_type_comp_operands_; |
| vector<const type_base*> right_type_comp_operands_; |
| // Vector of types that protentially received propagated canonical types. |
| // If the canonical type propagation is confirmed, the potential |
| // canonical types must be promoted as canonical types. Otherwise if |
| // the canonical type propagation is cancelled, the canonical types |
| // must be cleared. |
| pointer_set types_with_non_confirmed_propagated_ct_; |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| // This is used for debugging purposes. |
| // When abidw is used with the option --debug-abidiff, some |
| // libabigail internals need to get a hold on the initial binary |
| // input of abidw, as well as as the abixml file that represents the |
| // ABI of that binary. |
| // |
| // So this one is the corpus for the input binary. |
| corpus_wptr first_self_comparison_corpus_; |
| // This one is the corpus for the ABIXML file representing the |
| // serialization of the input binary. |
| corpus_wptr second_self_comparison_corpus_; |
| // This is also used for debugging purposes, when using |
| // 'abidw --debug-abidiff <binary>'. It holds the set of mapping of |
| // an abixml (canonical) type and its type-id. |
| unordered_map<string, uintptr_t> type_id_canonical_type_map_; |
| // Likewise. It holds a map that associates the pointer to a type |
| // read from abixml and the type-id string it corresponds to. |
| unordered_map<uintptr_t, string> pointer_type_id_map_; |
| #endif |
| bool canonicalization_is_done_; |
| bool do_on_the_fly_canonicalization_; |
| bool decl_only_class_equals_definition_; |
| bool use_enum_binary_only_equality_; |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| bool self_comparison_debug_on_; |
| #endif |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| // This controls whether to use canonical type comparison during |
| // type comparison or not. This is only used for debugging, when we |
| // want to ensure that comparing types using canonical or structural |
| // comparison yields the same result. |
| bool use_canonical_type_comparison_; |
| // Whether we are debugging type canonicalization or not. When |
| // debugging type canonicalization, a type is compared to its |
| // potential canonical type twice: The first time with canonical |
| // comparison activated, and the second time with structural |
| // comparison activated. The two comparison should yield the same |
| // result, otherwise, canonicalization is "broken" for that |
| // particular type. |
| bool debug_type_canonicalization_; |
| #endif |
| |
| priv() |
| : canonicalization_is_done_(), |
| do_on_the_fly_canonicalization_(true), |
| decl_only_class_equals_definition_(false), |
| use_enum_binary_only_equality_(false) |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| , |
| self_comparison_debug_on_(false) |
| #endif |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| , |
| use_canonical_type_comparison_(true), |
| debug_type_canonicalization_(false) |
| #endif |
| {} |
| |
| /// 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 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) |
| { |
| ABG_ASSERT(left && right); |
| |
| left_type_comp_operands_.push_back(left); |
| right_type_comp_operands_.push_back(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 right_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 type_base *t = left_type_comp_operands_.back(); |
| ABG_ASSERT(t == left); |
| t = right_type_comp_operands_.back(); |
| ABG_ASSERT(t == right); |
| |
| left_type_comp_operands_.pop_back(); |
| right_type_comp_operands_.pop_back(); |
| } |
| |
| /// Mark all the types that comes after a certain one as NOT being |
| /// eligible for the canonical type propagation optimization. |
| /// |
| /// @param type the type that represents the "marker type". All |
| /// types after this one will be marked as being NON-eligible to |
| /// the canonical type propagation optimization. |
| /// |
| /// @param types the set of types to consider. In that vector, all |
| /// types that come after @p type are going to be marked as being |
| /// non-eligible to the canonical type propagation optimization. |
| /// |
| /// @return true iff the operation was successful. |
| bool |
| mark_dependant_types(const type_base* type, |
| vector<const type_base*>& types) |
| { |
| bool found = false; |
| for (auto t : types) |
| { |
| if (!found |
| && (reinterpret_cast<uintptr_t>(t) |
| == reinterpret_cast<uintptr_t>(type))) |
| { |
| found = true; |
| continue; |
| } |
| else if (found) |
| t->priv_->set_depends_on_recursive_type(type); |
| } |
| return found; |
| } |
| |
| /// 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. |
| /// |
| /// @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* right) |
| { |
| bool result = false; |
| |
| result |= |
| mark_dependant_types(right, |
| right_type_comp_operands_); |
| return result; |
| } |
| |
| /// Propagate the canonical type of a type to another one. |
| /// |
| /// @param src the type to propagate the canonical type from. |
| /// |
| /// @param dest the type to propagate the canonical type of @p src |
| /// to. |
| /// |
| /// @return bool iff the canonical was propagated. |
| bool |
| propagate_ct(const type_base& src, const type_base& dest) |
| { |
| type_base_sptr canonical = src.get_canonical_type(); |
| ABG_ASSERT(canonical); |
| dest.priv_->canonical_type = canonical; |
| dest.priv_->naked_canonical_type = canonical.get(); |
| dest.priv_->set_canonical_type_propagated(true); |
| return true; |
| } |
| |
| /// Mark a set of types that have been the target of canonical type |
| /// propagation and that depend on a recursive type as being |
| /// permanently canonicalized. |
| /// |
| /// To understand the sentence above, please read the description of |
| /// type canonicalization and especially about the "canonical type |
| /// propagation optimization" at @ref OnTheFlyCanonicalization, in |
| /// the src/abg-ir.cc file. |
| void |
| confirm_ct_propagation(const type_base* dependant_type) |
| { |
| pointer_set to_remove; |
| for (auto i : types_with_non_confirmed_propagated_ct_) |
| { |
| type_base *t = reinterpret_cast<type_base*>(i); |
| ABG_ASSERT(t->priv_->depends_on_recursive_type()); |
| t->priv_->set_does_not_depend_on_recursive_type(dependant_type); |
| if (!t->priv_->depends_on_recursive_type()) |
| to_remove.insert(i); |
| } |
| |
| for (auto i : to_remove) |
| types_with_non_confirmed_propagated_ct_.erase(i); |
| } |
| |
| /// Collect the types that depends on a given "target" type. |
| /// |
| /// Walk a set of types and if they depend directly or indirectly on |
| /// a "target" type, then collect them into a set. |
| /// |
| /// @param target the target type to consider. |
| /// |
| /// @param types the types to walk to detect those who depend on @p |
| /// target. |
| /// |
| /// @return true iff one or more type from @p types is found to |
| /// depend on @p target. |
| bool |
| collect_types_that_depends_on(const type_base *target, |
| const pointer_set& types, |
| pointer_set& collected) |
| { |
| bool result = false; |
| for (const auto i : types) |
| { |
| // First avoid infinite loop if we've already collected the |
| // current type. |
| if (collected.find(i) != collected.end()) |
| continue; |
| |
| type_base *t = reinterpret_cast<type_base*>(i); |
| if (t->priv_->depends_on_recursive_type(target)) |
| { |
| collected.insert(i); |
| collect_types_that_depends_on(t, types, collected); |
| result = true; |
| } |
| } |
| return result; |
| } |
| |
| /// Reset the canonical type (set it nullptr) of a set of types that |
| /// have been the target of canonical type propagation and that |
| /// depend on a given recursive type. |
| /// |
| /// Once the canonical type of a type in that set is reset, the type |
| /// is marked as non being dependant on a recursive type anymore. |
| /// |
| /// To understand the sentences above, please read the description |
| /// of type canonicalization and especially about the "canonical |
| /// type propagation optimization" at @ref OnTheFlyCanonicalization, |
| /// in the src/abg-ir.cc file. |
| /// |
| /// @param target if a type which has been subject to the canonical |
| /// type propagation optimizationdepends on a this target type, then |
| /// cancel its canonical type. |
| void |
| cancel_ct_propagation(const type_base* target) |
| { |
| pointer_set to_remove; |
| collect_types_that_depends_on(target, |
| types_with_non_confirmed_propagated_ct_, |
| to_remove); |
| |
| for (auto i : to_remove) |
| { |
| type_base *t = reinterpret_cast<type_base*>(i); |
| ABG_ASSERT(t->priv_->depends_on_recursive_type()); |
| type_base_sptr canonical = t->priv_->canonical_type.lock(); |
| if (canonical) |
| { |
| t->priv_->clear_propagated_canonical_type(); |
| t->priv_->set_does_not_depend_on_recursive_type(); |
| } |
| } |
| |
| for (auto i : to_remove) |
| types_with_non_confirmed_propagated_ct_.erase(i); |
| } |
| |
| /// Remove a given type from the set of types that have been |
| /// non-confirmed subjects of the canonical type propagation |
| /// optimization. |
| /// |
| /// @param dependant the dependant type to consider. |
| void |
| remove_from_types_with_non_confirmed_propagated_ct(const type_base* dependant) |
| { |
| uintptr_t i = reinterpret_cast<uintptr_t>(dependant); |
| types_with_non_confirmed_propagated_ct_.erase(i); |
| } |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| /// When debugging self comparison, verify that a type T |
| /// de-serialized from abixml has the same canonical type as the |
| /// initial type built from DWARF that was serialized into T in the |
| /// first place. |
| /// |
| /// @param t deserialized type (from abixml) to consider. |
| /// |
| /// @param c the canonical type @p t should have. |
| /// |
| /// @return true iff @p c is the canonical type that @p t should |
| /// have. |
| bool |
| check_canonical_type_from_abixml_during_self_comp(const type_base* t, |
| const type_base* c) |
| { |
| if (!t || !t->get_corpus() || !c) |
| return false; |
| |
| if (!(t->get_corpus()->get_origin() == ir::corpus::NATIVE_XML_ORIGIN)) |
| return false; |
| |
| // Get the abixml type-id that this type was constructed from. |
| string type_id; |
| { |
| unordered_map<uintptr_t, string>::const_iterator it = |
| pointer_type_id_map_.find(reinterpret_cast<uintptr_t>(t)); |
| if (it == pointer_type_id_map_.end()) |
| return false; |
| type_id = it->second; |
| } |
| |
| // Get the canonical type the original in-memory type (constructed |
| // from DWARF) had when it was serialized into abixml in the first place. |
| type_base *original_canonical_type = nullptr; |
| if (!type_id.empty()) |
| { |
| unordered_map<string, uintptr_t>::const_iterator it = |
| type_id_canonical_type_map_.find(type_id); |
| if (it == type_id_canonical_type_map_.end()) |
| return false; |
| original_canonical_type = reinterpret_cast<type_base*>(it->second); |
| } |
| |
| // Now perform the real check. |
| // |
| // We want to ensure that the canonical type 'c' of 't' is the |
| // same as the canonical type of initial in-memory type (built |
| // from DWARF) that was serialized into 't' (in abixml) in the |
| // first place. |
| if (original_canonical_type == c) |
| return true; |
| |
| return false; |
| } |
| |
| /// When debugging self comparison, verify that a type T |
| /// de-serialized from abixml has the same canonical type as the |
| /// initial type built from DWARF that was serialized into T in the |
| /// first place. |
| /// |
| /// @param t deserialized type (from abixml) to consider. |
| /// |
| /// @param c the canonical type @p t should have. |
| /// |
| /// @return true iff @p c is the canonical type that @p t should |
| /// have. |
| bool |
| check_canonical_type_from_abixml_during_self_comp(const type_base_sptr& t, |
| const type_base_sptr& c) |
| { |
| return check_canonical_type_from_abixml_during_self_comp(t.get(), c.get()); |
| } |
| #endif |
| };// end struct environment::priv |
| |
| // <class_or_union::priv definitions> |
| struct class_or_union::priv |
| { |
| typedef_decl_wptr naming_typedef_; |
| member_types member_types_; |
| data_members data_members_; |
| data_members non_static_data_members_; |
| member_functions member_functions_; |
| // A map that associates a linkage name to a member function. |
| string_mem_fn_sptr_map_type mem_fns_map_; |
| // A map that associates function signature strings to member |
| // function. |
| string_mem_fn_ptr_map_type signature_2_mem_fn_map_; |
| member_function_templates member_function_templates_; |
| member_class_templates member_class_templates_; |
| |
| priv() |
| {} |
| |
| priv(class_or_union::member_types& mbr_types, |
| class_or_union::data_members& data_mbrs, |
| class_or_union::member_functions& mbr_fns) |
| : member_types_(mbr_types), |
| data_members_(data_mbrs), |
| member_functions_(mbr_fns) |
| { |
| for (data_members::const_iterator i = data_members_.begin(); |
| i != data_members_.end(); |
| ++i) |
| if (!get_member_is_static(*i)) |
| non_static_data_members_.push_back(*i); |
| } |
| |
| /// Mark a class or union or union as being currently compared using |
| /// the class_or_union== operator. |
| /// |
| /// Note that is marking business is to avoid infinite loop when |
| /// comparing a class or union or union. If via the comparison of a |
| /// data member or a member function a recursive re-comparison of |
| /// the class or union is attempted, the marking business help to |
| /// detect that infinite loop possibility and avoid it. |
| /// |
| /// @param klass the class or union or union to mark as being |
| /// currently compared. |
| void |
| mark_as_being_compared(const class_or_union& klass) const |
| { |
| const environment* env = klass.get_environment(); |
| ABG_ASSERT(env); |
| env->priv_->classes_being_compared_.insert(&klass); |
| } |
| |
| /// Mark a class or union as being currently compared using the |
| /// class_or_union== operator. |
| /// |
| /// Note that is marking business is to avoid infinite loop when |
| /// comparing a class or union. If via the comparison of a data |
| /// member or a member function a recursive re-comparison of the |
| /// class or union is attempted, the marking business help to detect |
| /// that infinite loop possibility and avoid it. |
| /// |
| /// @param klass the class or union to mark as being currently |
| /// compared. |
| void |
| mark_as_being_compared(const class_or_union* klass) const |
| {mark_as_being_compared(*klass);} |
| |
| /// Mark a class or union as being currently compared using the |
| /// class_or_union== operator. |
| /// |
| /// Note that is marking business is to avoid infinite loop when |
| /// comparing a class or union. If via the comparison of a data |
| /// member or a member function a recursive re-comparison of the |
| /// class or union is attempted, the marking business help to detect |
| /// that infinite loop possibility and avoid it. |
| /// |
| /// @param klass the class or union to mark as being currently |
| /// compared. |
| void |
| mark_as_being_compared(const class_or_union_sptr& klass) const |
| {mark_as_being_compared(*klass);} |
| |
| /// If the instance of class_or_union has been previously marked as |
| /// being compared -- via an invocation of mark_as_being_compared() |
| /// this method unmarks it. Otherwise is has no effect. |
| /// |
| /// This method is not thread safe because it uses the static data |
| /// member classes_being_compared_. If you wish to use it in a |
| /// multi-threaded environment you should probably protect the |
| /// access to that static data member with a mutex or somesuch. |
| /// |
| /// @param klass the instance of class_or_union to unmark. |
| void |
| unmark_as_being_compared(const class_or_union& klass) const |
| { |
| const environment* env = klass.get_environment(); |
| ABG_ASSERT(env); |
| env->priv_->classes_being_compared_.erase(&klass); |
| } |
| |
| /// If the instance of class_or_union has been previously marked as |
| /// being compared -- via an invocation of mark_as_being_compared() |
| /// this method unmarks it. Otherwise is has no effect. |
| /// |
| /// @param klass the instance of class_or_union to unmark. |
| void |
| unmark_as_being_compared(const class_or_union* klass) const |
| { |
| if (klass) |
| return unmark_as_being_compared(*klass); |
| } |
| |
| /// Test if a given instance of class_or_union is being currently |
| /// compared. |
| /// |
| ///@param klass the class or union to test. |
| /// |
| /// @return true if @p klass is being compared, false otherwise. |
| bool |
| comparison_started(const class_or_union& klass) const |
| { |
| const environment* env = klass.get_environment(); |
| ABG_ASSERT(env); |
| return env->priv_->classes_being_compared_.count(&klass); |
| } |
| |
| /// Test if a given instance of class_or_union is being currently |
| /// compared. |
| /// |
| ///@param klass the class or union to test. |
| /// |
| /// @return true if @p klass is being compared, false otherwise. |
| bool |
| comparison_started(const class_or_union* klass) const |
| { |
| if (klass) |
| return comparison_started(*klass); |
| return false; |
| } |
| }; // end struct class_or_union::priv |
| |
| } // end namespace ir |
| |
| } // end namespace abigail |
| |
| #endif // __ABG_IR_PRIV_H__ |