| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- Mode: C++ -*- |
| // |
| // Copyright (C) 2013-2020 Red Hat, Inc. |
| // |
| // Author: Dodji Seketeli |
| |
| /// @file |
| /// |
| /// This contains the implementation of the comparison engine of |
| /// libabigail. |
| |
| #include <ctype.h> |
| #include <libgen.h> |
| #include <algorithm> |
| #include <sstream> |
| |
| #include "abg-comparison-priv.h" |
| #include "abg-reporter-priv.h" |
| |
| namespace abigail |
| { |
| |
| namespace comparison |
| { |
| |
| /// |
| /// |
| ///@defgroup DiffNode Internal Representation of the comparison engine |
| /// @{ |
| /// |
| /// @brief How changes are represented in libabigail's comparison engine. |
| /// |
| ///@par diff nodes |
| /// |
| /// The internal representation of the comparison engine is basically |
| /// a graph of @ref instances of @ref diff node. We refer to these |
| /// just as <em>diff nodes</em>. A diff node represents a change |
| /// between two ABI artifacts represented by instances of types of the |
| /// abigail::ir namespace. These two artifacts that are being |
| /// compared are called the <em>subjects of the diff</em>. |
| /// |
| /// The types of that IR are in the abigail::comparison namespace. |
| /// |
| ///@par comparing diff nodes |
| /// |
| /// Comparing two instances of @ref diff nodes amounts to comparing |
| /// the subject of the diff. In other words, two @ref diff nodes are |
| /// equal if and only if their subjects are equal. Thus, two @ref |
| /// diff nodes can have different memory addresses and yet be equal. |
| /// |
| ///@par diff reporting and context |
| /// |
| /// A diff node can be serialized to an output stream to express, in |
| /// a human-readable textual form, the different changes that exist |
| /// between its two subjects. This is done by invoking the |
| /// diff::report() method. That reporting is controlled by several |
| /// parameters that are conceptually part of the context of the diff. |
| /// That context is materialized by an instance of the @ref |
| /// diff_context type. |
| /// |
| /// Please note that the role of the instance(s) of @ref diff_context |
| /// is boreader than just controlling the reporting of @ref diff |
| /// nodes. Basically, a @ref diff node itself is created following |
| /// behaviours that are controlled by a particular instance of |
| /// diff_context. A diff node is created in a particular diff |
| /// context, so to speak. |
| /// |
| /// @} |
| /// |
| |
| /// |
| ///@defgroup CanonicalDiff Canonical diff tree nodes |
| /// @{ |
| /// |
| /// @brief How equivalent diff nodes are quickly spotted. |
| /// |
| /// @par Equivalence of diff nodes. |
| /// |
| /// Each @ref diff node has a property named <em>Canonical Diff |
| /// Node</em>. If \c D is a diff node, the canonical diff node of @c |
| /// D, noted @c C(D) is a particular diff node that is equal to @c D. |
| /// Thus, a fast way to compare two @ref diff node is to perform a |
| /// pointer comparison of their canonical diff nodes. |
| /// |
| /// A set of equivalent @ref diff nodes is a set of diff nodes that |
| /// all have the same canonical node. All the nodes of that set are |
| /// equal. |
| /// |
| /// A canonical node is registereded for a given diff node by invoking |
| /// the method diff_context::initialize_canonical_diff(). |
| /// |
| /// Please note that the diff_context holds all the canonical diffs |
| /// that got registered through it. Thus, the life time of all of |
| /// canonical diff objects is the same as the life time of the @ref |
| /// diff_context they relate to. |
| /// |
| /// @} |
| /// |
| |
| // ----------------------------------------- |
| // <private functions re-usable elsewhere> |
| // ----------------------------------------- |
| /// Sort a map of enumerators by their value. |
| /// |
| /// @param enumerators_map the map to sort. |
| /// |
| /// @param sorted the resulting vector of sorted enumerators. |
| void |
| sort_enumerators(const string_enumerator_map& enumerators_map, |
| enum_type_decl::enumerators& sorted) |
| { |
| for (string_enumerator_map::const_iterator i = enumerators_map.begin(); |
| i != enumerators_map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| enumerator_value_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of changed enumerators. |
| /// |
| /// @param enumerators_map the map to sort. |
| /// |
| ///@param output parameter. The resulting sorted enumerators. |
| void |
| sort_changed_enumerators(const string_changed_enumerator_map& enumerators_map, |
| changed_enumerators_type& sorted) |
| { |
| for (string_changed_enumerator_map::const_iterator i = |
| enumerators_map.begin(); |
| i != enumerators_map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| changed_enumerator_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of data members by the offset of their initial value. |
| /// |
| /// @param data_members the map of changed data members to sort. |
| /// |
| /// @param sorted the resulting vector of sorted changed data members. |
| void |
| sort_data_members(const string_decl_base_sptr_map &data_members, |
| vector<decl_base_sptr>& sorted) |
| { |
| sorted.reserve(data_members.size()); |
| for (string_decl_base_sptr_map::const_iterator i = data_members.begin(); |
| i != data_members.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| data_member_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort (in place) a vector of changed data members. |
| /// |
| /// @param to_sort the vector to sort. |
| void |
| sort_changed_data_members(changed_var_sptrs_type& to_sort) |
| { |
| data_member_comp comp; |
| std::sort(to_sort.begin(), to_sort.end(), comp); |
| } |
| |
| /// Sort an instance of @ref string_function_ptr_map map and stuff a |
| /// resulting sorted vector of pointers to function_decl. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector. |
| void |
| sort_string_function_ptr_map(const string_function_ptr_map& map, |
| vector<function_decl*>& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_function_ptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| function_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map that's an instance of @ref |
| /// string_member_function_sptr_map and fill a vector of member |
| /// functions with the sorted result. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector. |
| void |
| sort_string_member_function_sptr_map(const string_member_function_sptr_map& map, |
| class_or_union::member_functions& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_member_function_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| function_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort the values of a @ref string_function_decl_diff_sptr_map map |
| /// and store the result in a vector of @ref function_decl_diff_sptr |
| /// objects. |
| /// |
| /// @param map the map whose values to store. |
| /// |
| /// @param sorted the vector of function_decl_diff_sptr to store the |
| /// result of the sort into. |
| void |
| sort_string_function_decl_diff_sptr_map |
| (const string_function_decl_diff_sptr_map& map, |
| function_decl_diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_function_decl_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| function_decl_diff_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort of an instance of @ref string_var_diff_sptr_map map. |
| /// |
| /// @param map the input map to sort. |
| /// |
| /// @param sorted the ouptut sorted vector of @ref var_diff_sptr. |
| /// It's populated with the sorted content. |
| void |
| sort_string_var_diff_sptr_map(const string_var_diff_sptr_map& map, |
| var_diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_var_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| var_diff_sptr_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of string -> pointer to @ref elf_symbol. |
| /// |
| /// The result is a vector of @ref elf_symbol_sptr sorted by the |
| /// name of the symbol. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted out parameter; the sorted vector of @ref |
| /// elf_symbol_sptr. |
| void |
| sort_string_elf_symbol_map(const string_elf_symbol_map& map, |
| vector<elf_symbol_sptr>& sorted) |
| { |
| for (string_elf_symbol_map::const_iterator i = map.begin(); |
| i!= map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| elf_symbol_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of string -> pointer to @ref var_decl. |
| /// |
| /// The result is a vector of var_decl* sorted by the qualified name |
| /// of the variables. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted out parameter; the sorted vector of @ref var_decl. |
| void |
| sort_string_var_ptr_map(const string_var_ptr_map& map, |
| vector<var_decl*>& sorted) |
| { |
| for (string_var_ptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| var_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort the values of a string_var_diff_sptr_map and store the result |
| /// in a vector of var_diff_sptr. |
| /// |
| /// @param map the map of changed data members to sort. |
| /// |
| /// @param sorted the resulting vector of var_diff_sptr. |
| void |
| sort_string_data_member_diff_sptr_map(const string_var_diff_sptr_map& map, |
| var_diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_var_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| data_member_diff_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort the values of a unsigned_var_diff_sptr_map map and store the |
| /// result into a vector of var_diff_sptr. |
| /// |
| /// @param map the map of changed data members to sort. |
| /// |
| /// @param sorted the resulting vector of sorted var_diff_sptr. |
| void |
| sort_unsigned_data_member_diff_sptr_map(const unsigned_var_diff_sptr_map map, |
| var_diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (unsigned_var_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| data_member_diff_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort an map of string -> virtual member function into a vector of |
| /// virtual member functions. The virtual member functions are sorted |
| /// by increasing order of their virtual index. |
| /// |
| /// @param map the input map. |
| /// |
| /// @param sorted the resulting sorted vector of virtual function |
| /// member. |
| void |
| sort_string_virtual_member_function_diff_sptr_map |
| (const string_function_decl_diff_sptr_map& map, |
| function_decl_diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_function_decl_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| virtual_member_function_diff_comp comp; |
| sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map ofg string -> @ref diff_sptr into a vector of @ref |
| /// diff_sptr. The diff_sptr are sorted lexicographically wrt |
| /// qualified names of their first subjects. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector. |
| void |
| sort_string_diff_sptr_map(const string_diff_sptr_map& map, |
| diff_sptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| diff_comp comp; |
| sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map ofg string -> @ref diff* into a vector of @ref |
| /// diff_ptr. The diff_ptr are sorted lexicographically wrt |
| /// qualified names of their first subjects. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector. |
| void |
| sort_string_diff_ptr_map(const string_diff_ptr_map& map, |
| diff_ptrs_type& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_diff_ptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| diff_comp comp; |
| sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of string -> base_diff_sptr into a sorted vector of |
| /// base_diff_sptr. The base_diff_sptr are sorted by increasing value |
| /// of their offset in their containing type. |
| /// |
| /// @param map the input map to sort. |
| /// |
| /// @param sorted the resulting sorted vector. |
| void |
| sort_string_base_diff_sptr_map(const string_base_diff_sptr_map& map, |
| base_diff_sptrs_type& sorted) |
| { |
| for (string_base_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| base_diff_comp comp; |
| sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Lexicographically sort base specifications found |
| /// in instances of string_base_sptr_map. |
| void |
| sort_string_base_sptr_map(const string_base_sptr_map& m, |
| class_decl::base_specs& sorted) |
| { |
| for (string_base_sptr_map::const_iterator i = m.begin(); |
| i != m.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| base_spec_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of @ref fn_parm_diff by the indexes of the function |
| /// parameters. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector of changed function |
| /// parms. |
| void |
| sort_string_fn_parm_diff_sptr_map(const unsigned_fn_parm_diff_sptr_map& map, |
| vector<fn_parm_diff_sptr>& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (unsigned_fn_parm_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| fn_parm_diff_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of changed function parameters by the indexes of the |
| /// function parameters. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector of instances of @ref |
| /// fn_parm_diff_sptr |
| void |
| sort_string_fn_parm_diff_sptr_map(const string_fn_parm_diff_sptr_map& map, |
| vector<fn_parm_diff_sptr>& sorted) |
| { |
| sorted.reserve(map.size()); |
| for (string_fn_parm_diff_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| fn_parm_diff_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of string -> function parameters. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting sorted vector of |
| /// @ref vector<function_decl::parameter_sptr> |
| void |
| sort_string_parm_map(const string_parm_map& map, |
| vector<function_decl::parameter_sptr>& sorted) |
| { |
| for (string_parm_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| parm_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort the set of ABI artifacts contained in a @ref |
| /// artifact_sptr_set_type. |
| /// |
| /// @param set the set of ABI artifacts to sort. |
| /// |
| /// @param output parameter the vector containing the sorted ABI |
| /// artifacts. |
| void |
| sort_artifacts_set(const artifact_sptr_set_type& set, |
| vector<type_or_decl_base_sptr>& sorted) |
| { |
| |
| for (artifact_sptr_set_type::const_iterator it = set.begin(); |
| it != set.end(); |
| ++it) |
| sorted.push_back(*it); |
| |
| type_or_decl_base_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Sort a map of string to type_base_sptr entities. |
| /// |
| /// The entries are sorted based on the lexicographic order of the |
| /// pretty representation of the type_sptr_sptr. The sorted result is |
| /// put in a vector of type_base_sptr. |
| /// |
| /// @param map the map to sort. |
| /// |
| /// @param sorted the resulting vector of type_base_sptr |
| /// lexicographically sorted using their pretty representation. |
| void |
| sort_string_type_base_sptr_map(string_type_base_sptr_map& map, |
| vector<type_base_sptr>& sorted) |
| { |
| for (string_type_base_sptr_map::const_iterator i = map.begin(); |
| i != map.end(); |
| ++i) |
| sorted.push_back(i->second); |
| |
| type_or_decl_base_comp comp; |
| std::sort(sorted.begin(), sorted.end(), comp); |
| } |
| |
| /// Return the first underlying type that is not a qualified type. |
| /// @param t the qualified type to consider. |
| /// |
| /// @return the first underlying type that is not a qualified type, or |
| /// NULL if t is NULL. |
| type_base_sptr |
| get_leaf_type(qualified_type_def_sptr t) |
| { |
| if (!t) |
| return type_base_sptr(); |
| |
| type_base_sptr ut = t->get_underlying_type(); |
| qualified_type_def_sptr qut = dynamic_pointer_cast<qualified_type_def>(ut); |
| |
| if (!qut) |
| return ut; |
| return get_leaf_type(qut); |
| } |
| |
| /// Tests if a given diff node is to represent the changes between two |
| /// gobal decls. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d represents the changes between two global |
| /// decls. |
| bool |
| is_diff_of_global_decls(const diff* d) |
| { |
| ABG_ASSERT(d != 0); |
| |
| if (d == 0) |
| return false; |
| |
| type_or_decl_base_sptr first = d->first_subject(); |
| ABG_ASSERT(first); |
| |
| type_or_decl_base_sptr second = d->first_subject(); |
| ABG_ASSERT(second); |
| |
| if (decl_base_sptr decl = is_decl(first)) |
| if (is_at_global_scope(decl)) |
| if ((decl = is_decl(second))) |
| if (is_at_global_scope(decl)) |
| return true; |
| |
| return false; |
| } |
| |
| // ----------------------------------------- |
| // </private functions re-usable elsewhere> |
| // ----------------------------------------- |
| |
| /// The overloaded or operator for @ref visiting_kind. |
| visiting_kind |
| operator|(visiting_kind l, visiting_kind r) |
| {return static_cast<visiting_kind>(static_cast<unsigned>(l) |
| | static_cast<unsigned>(r));} |
| |
| /// The overloaded and operator for @ref visiting_kind. |
| visiting_kind |
| operator&(visiting_kind l, visiting_kind r) |
| { |
| return static_cast<visiting_kind>(static_cast<unsigned>(l) |
| & static_cast<unsigned>(r)); |
| } |
| |
| /// The overloaded 'bit inversion' operator for @ref visiting_kind. |
| visiting_kind |
| operator~(visiting_kind l) |
| {return static_cast<visiting_kind>(~static_cast<unsigned>(l));} |
| |
| /// Test if a diff node is about differences between types. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return a pointer to the actual type_diff_base* that @p diff |
| /// extends, iff it is about differences between types. |
| const type_diff_base* |
| is_type_diff(const diff* diff) |
| {return dynamic_cast<const type_diff_base*>(diff);} |
| |
| /// Test if a diff node is about differences between declarations. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return a pointer to the actual decl_diff_base @p diff extends, |
| /// iff it is about differences between declarations. |
| const decl_diff_base* |
| is_decl_diff(const diff* diff) |
| {return dynamic_cast<const decl_diff_base*>(diff);} |
| |
| /// Test if a diff node is a @ref class_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref class_diff iff @p diff is a |
| /// @ref class_diff node. |
| const class_diff* |
| is_class_diff(const diff* diff) |
| {return dynamic_cast<const class_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref enum_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to ad @ref enum_diff node iff @p diff is |
| /// a @ref enum_diff node. |
| const enum_diff* |
| is_enum_diff(const diff *diff) |
| {return dynamic_cast<const enum_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref union_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref union_diff iff @p diff is a |
| /// @ref union_diff node. |
| const union_diff* |
| is_union_diff(const diff* diff) |
| {return dynamic_cast<const union_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref class_or_union_diff node. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return a non-nil pointer to the @ref class_or_union_diff denoted |
| /// by @p d iff @p d is a @ref class_or_union_diff. |
| const class_or_union_diff* |
| is_class_or_union_diff(const diff* d) |
| {return dynamic_cast<const class_or_union_diff*>(d);} |
| |
| /// Test if a diff node is a @ref class_or_union_diff between two |
| /// anonymous classes or unions. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return a non-nil pointer to the @ref class_or_union_diff iff @p |
| /// denoted by @p d iff @p is pointer to an anonymous class or union |
| /// diff. |
| const class_or_union_diff* |
| is_anonymous_class_or_union_diff(const diff* d) |
| { |
| if (const class_or_union_diff *dif = is_class_or_union_diff(d)) |
| if (dif->first_class_or_union()->get_is_anonymous()) |
| return dif; |
| return 0; |
| } |
| |
| /// Test if a diff node is a @ref typedef_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref typedef_diff iff @p diff is a |
| /// @ref typedef_diff node. |
| const typedef_diff* |
| is_typedef_diff(const diff *diff) |
| {return dynamic_cast<const typedef_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref array_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref array_diff iff @p diff is a |
| /// @ref array_diff node. |
| const array_diff* |
| is_array_diff(const diff* diff) |
| {return dynamic_cast<const array_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref function_type_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref function_type_diff iff @p diff is a |
| /// @ref function_type_diff node. |
| const function_type_diff* |
| is_function_type_diff(const diff* diff) |
| {return dynamic_cast<const function_type_diff*>(diff);} |
| |
| /// Test if a given diff node carries a function type change with |
| /// local changes. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref function_type_diff iff @p diff |
| /// is a function_type_diff node that carries a local change. |
| const function_type_diff* |
| is_function_type_diff_with_local_changes(const diff* diff) |
| { |
| if (const function_type_diff* d = is_function_type_diff(diff)) |
| if (d->has_local_changes()) |
| return d; |
| |
| return 0; |
| } |
| |
| /// Test if a diff node is about differences between variables. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return a pointer to the actual var_diff that @p diff is a type |
| /// of, iff it is about differences between variables. |
| const var_diff* |
| is_var_diff(const diff* diff) |
| { |
| const var_diff* d = dynamic_cast<const var_diff*>(diff); |
| if (d) |
| ABG_ASSERT(is_decl_diff(diff)); |
| return d; |
| } |
| |
| /// Test if a diff node is about differences between functions. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return a pointer to the actual var_diff that @p diff is a type |
| /// of, iff it is about differences between variables. |
| const function_decl_diff* |
| is_function_decl_diff(const diff* diff) |
| { |
| const function_decl_diff *d = dynamic_cast<const function_decl_diff*>(diff); |
| if (d) |
| ABG_ASSERT(is_decl_diff(diff)); |
| return d; |
| } |
| |
| /// Test if a diff node is about differences between two pointers. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return the @p diff converted into an instance of @ref |
| /// pointer_diff iff @p diff is about differences between two |
| /// pointers. |
| const pointer_diff* |
| is_pointer_diff(const diff* diff) |
| {return dynamic_cast<const pointer_diff*>(diff);} |
| |
| /// Test if a diff node is about differences between two references. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return the @p diff converted into an instance of @ref |
| /// reference_diff iff @p diff is about differences between two |
| /// references. |
| const reference_diff* |
| is_reference_diff(const diff* diff) |
| {return dynamic_cast<const reference_diff*>(diff);} |
| |
| /// Test if a diff node is about differences between two qualified |
| /// types. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return @p diff converted into an instance of @ref |
| /// qualified_type_diff iff @p diff is about differences between two |
| /// qualified types. |
| const qualified_type_diff* |
| is_qualified_type_diff(const diff* diff) |
| {return dynamic_cast<const qualified_type_diff*>(diff);} |
| |
| /// Test if a diff node is a reference or pointer diff node to a |
| /// change that is neither basic type change nor distinct type change. |
| /// |
| /// Note that this function also works on diffs of typedefs of |
| /// reference or pointer. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return true iff @p diff is a eference or pointer diff node to a |
| /// change that is neither basic type change nor distinct type change. |
| bool |
| is_reference_or_ptr_diff_to_non_basic_nor_distinct_types(const diff* diff) |
| { |
| diff = peel_typedef_diff(diff); |
| if (const reference_diff* d = is_reference_diff(diff)) |
| { |
| diff = peel_reference_diff(d); |
| if (is_diff_of_basic_type(diff) || is_distinct_diff(diff)) |
| return false; |
| return true; |
| } |
| else if (const pointer_diff *d = is_pointer_diff(diff)) |
| { |
| diff = peel_pointer_diff(d); |
| if (is_diff_of_basic_type(diff) || is_distinct_diff(diff)) |
| return false; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Test if a diff node is about differences between two function |
| /// parameters. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return the @p diff converted into an instance of @ref |
| /// reference_diff iff @p diff is about differences between two |
| /// function parameters. |
| const fn_parm_diff* |
| is_fn_parm_diff(const diff* diff) |
| {return dynamic_cast<const fn_parm_diff*>(diff);} |
| |
| /// Test if a diff node is about differences between two base class |
| /// specifiers. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return the @p diff converted into an instance of @ref base_diff |
| /// iff @p diff is about differences between two base class |
| /// specifiers. |
| const base_diff* |
| is_base_diff(const diff* diff) |
| {return dynamic_cast<const base_diff*>(diff);} |
| |
| /// Test if a diff node is about differences between two diff nodes of |
| /// different kinds. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return the @p diff converted into an instance of @ref |
| /// distintc_diff iff @p diff is about differences between two diff |
| /// nodes of different kinds. |
| const distinct_diff* |
| is_distinct_diff(const diff *diff) |
| {return dynamic_cast<const distinct_diff*>(diff);} |
| |
| /// Test if a diff node is a @ref corpus_diff node. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @return a non-nil pointer to a @ref corpus_diff iff @p diff is a |
| /// @ref corpus_diff node. |
| const corpus_diff* |
| is_corpus_diff(const diff* diff) |
| {return dynamic_cast<const corpus_diff*>(diff);} |
| |
| /// Test if a diff node is a child node of a function parameter diff node. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return true iff @p diff is a child node of a function parameter |
| /// diff node. |
| bool |
| is_child_node_of_function_parm_diff(const diff* diff) |
| {return diff && is_fn_parm_diff(diff->parent_node());} |
| |
| /// Test if a diff node is a child node of a base diff node. |
| /// |
| /// @param diff the diff node to test. |
| /// |
| /// @return true iff @p diff is a child node of a base diff node. |
| bool |
| is_child_node_of_base_diff(const diff* diff) |
| {return diff && is_base_diff(diff->parent_node());} |
| |
| /// The default traverse function. |
| /// |
| /// @return true. |
| bool |
| diff_traversable_base::traverse(diff_node_visitor&) |
| {return true;} |
| |
| diff_context::diff_context() |
| : priv_(new diff_context::priv) |
| { |
| // Setup all the diff output filters we have. |
| filtering::filter_base_sptr f; |
| |
| f.reset(new filtering::harmless_harmful_filter); |
| add_diff_filter(f); |
| |
| // f.reset(new filtering::harmless_filter); |
| // add_diff_filter(f); |
| |
| // f.reset(new filtering::harmful_filter); |
| // add_diff_filter(f); |
| } |
| |
| diff_context::~diff_context() = default; |
| |
| /// Set the corpus diff relevant to this context. |
| /// |
| /// @param d the corpus_diff we are interested in. |
| void |
| diff_context::set_corpus_diff(const corpus_diff_sptr& d) |
| {priv_->corpus_diff_ = d;} |
| |
| /// Get the corpus diff for the current context. |
| /// |
| /// @return the corpus diff of this context. |
| const corpus_diff_sptr& |
| diff_context::get_corpus_diff() const |
| {return priv_->corpus_diff_;} |
| |
| /// Getter for the first corpus of the corpus diff of the current context. |
| /// |
| /// @return the first corpus of the corpus diff of the current |
| /// context, if no corpus diff is associated to the context. |
| corpus_sptr |
| diff_context::get_first_corpus() const |
| { |
| if (priv_->corpus_diff_) |
| return priv_->corpus_diff_->first_corpus(); |
| return corpus_sptr(); |
| } |
| |
| /// Getter for the second corpus of the corpus diff of the current |
| /// context. |
| /// |
| /// @return the second corpus of the corpus diff of the current |
| /// context, if no corpus diff is associated to the context. |
| corpus_sptr |
| diff_context::get_second_corpus() const |
| { |
| if (priv_->corpus_diff_) |
| return priv_->corpus_diff_->second_corpus(); |
| return corpus_sptr(); |
| } |
| |
| /// Getter of the reporter to be used in this context. |
| /// |
| /// @return the reporter to be used in this context. |
| reporter_base_sptr |
| diff_context::get_reporter() const |
| { |
| if (!priv_->reporter_) |
| { |
| if (show_leaf_changes_only()) |
| priv_->reporter_.reset(new leaf_reporter); |
| else |
| priv_->reporter_.reset(new default_reporter); |
| } |
| ABG_ASSERT(priv_->reporter_); |
| return priv_->reporter_; |
| } |
| |
| /// Setter of the reporter to be used in this context. |
| /// |
| /// @param r the reporter to be used in this context. |
| void |
| diff_context::set_reporter(reporter_base_sptr& r) |
| {priv_->reporter_ = r;} |
| |
| /// Tests if the current diff context already has a diff for two decls. |
| /// |
| /// @param first the first decl to consider. |
| /// |
| /// @param second the second decl to consider. |
| /// |
| /// @return a pointer to the diff for @p first @p second if found, |
| /// null otherwise. |
| diff_sptr |
| diff_context::has_diff_for(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second) const |
| { |
| types_or_decls_diff_map_type::const_iterator i = |
| priv_->types_or_decls_diff_map.find(std::make_pair(first, second)); |
| if (i != priv_->types_or_decls_diff_map.end()) |
| return i->second; |
| return diff_sptr(); |
| } |
| |
| /// Tests if the current diff context already has a diff for two types. |
| /// |
| /// @param first the first type to consider. |
| /// |
| /// @param second the second type to consider. |
| /// |
| /// @return a pointer to the diff for @p first @p second if found, |
| /// null otherwise. |
| diff_sptr |
| diff_context::has_diff_for_types(const type_base_sptr first, |
| const type_base_sptr second) const |
| {return has_diff_for(first, second);} |
| |
| /// Tests if the current diff context already has a given diff. |
| /// |
| ///@param d the diff to consider. |
| /// |
| /// @return a pointer to the diff found for @p d |
| const diff* |
| diff_context::has_diff_for(const diff* d) const |
| {return has_diff_for(d->first_subject(), d->second_subject()).get();} |
| |
| /// Tests if the current diff context already has a given diff. |
| /// |
| ///@param d the diff to consider. |
| /// |
| /// @return a pointer to the diff found for @p d |
| diff_sptr |
| diff_context::has_diff_for(const diff_sptr d) const |
| {return has_diff_for(d->first_subject(), d->second_subject());} |
| |
| /// Getter for the bitmap that represents the set of categories that |
| /// the user wants to see reported. |
| /// |
| /// @return a bitmap that represents the set of categories that the |
| /// user wants to see reported. |
| diff_category |
| diff_context::get_allowed_category() const |
| {return priv_->allowed_category_;} |
| |
| /// Setter for the bitmap that represents the set of categories that |
| /// the user wants to see reported. |
| /// |
| /// @param c a bitmap that represents the set of categories that the |
| /// user wants to see represented. |
| void |
| diff_context::set_allowed_category(diff_category c) |
| {priv_->allowed_category_ = c;} |
| |
| /// Setter for the bitmap that represents the set of categories that |
| /// the user wants to see reported |
| /// |
| /// This function perform a bitwise or between the new set of |
| /// categories and the current ones, and then sets the current |
| /// categories to the result of the or. |
| /// |
| /// @param c a bitmap that represents the set of categories that the |
| /// user wants to see represented. |
| void |
| diff_context::switch_categories_on(diff_category c) |
| {priv_->allowed_category_ = priv_->allowed_category_ | c;} |
| |
| /// Setter for the bitmap that represents the set of categories that |
| /// the user wants to see reported |
| /// |
| /// This function actually unsets bits from the current categories. |
| /// |
| /// @param c a bitmap that represents the set of categories to unset |
| /// from the current categories. |
| void |
| diff_context::switch_categories_off(diff_category c) |
| {priv_->allowed_category_ = priv_->allowed_category_ & ~c;} |
| |
| /// Add a diff for two decls to the cache of the current diff_context. |
| /// |
| /// Doing this allows to later find the added diff from its two |
| /// subject decls. |
| /// |
| /// @param first the first decl to consider. |
| /// |
| /// @param second the second decl to consider. |
| /// |
| /// @param the diff to add. |
| void |
| diff_context::add_diff(type_or_decl_base_sptr first, |
| type_or_decl_base_sptr second, |
| const diff_sptr d) |
| {priv_->types_or_decls_diff_map[std::make_pair(first, second)] = d;} |
| |
| /// Add a diff tree node to the cache of the current diff_context |
| /// |
| /// @param d the diff tree node to add. |
| void |
| diff_context::add_diff(const diff* d) |
| { |
| if (d) |
| { |
| diff_sptr dif(const_cast<diff*>(d), noop_deleter()); |
| add_diff(d->first_subject(), d->second_subject(), dif); |
| } |
| } |
| |
| /// Add a diff tree node to the cache of the current diff_context |
| /// |
| /// @param d the diff tree node to add. |
| void |
| diff_context::add_diff(const diff_sptr d) |
| { |
| if (d) |
| add_diff(d->first_subject(), d->second_subject(), d); |
| } |
| |
| /// Getter for the @ref CanonicalDiff "canonical diff node" for the |
| /// @ref diff represented by their two subjects. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @return the canonical diff for the diff node represented by the |
| /// two diff subjects @p first and @p second. If no canonical diff |
| /// node was registered for these subjects, then a nil node is |
| /// returned. |
| diff_sptr |
| diff_context::get_canonical_diff_for(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second) const |
| {return has_diff_for(first, second);} |
| |
| /// Getter for the @ref CanonicalDiff "canonical diff node" for the |
| /// @ref diff represented by the two subjects of a given diff node. |
| /// |
| /// @param d the diff node to get the canonical node for. |
| /// |
| /// @return the canonical diff for the diff node represented by the |
| /// two diff subjects of @p d. If no canonical diff node was |
| /// registered for these subjects, then a nil node is returned. |
| diff_sptr |
| diff_context::get_canonical_diff_for(const diff_sptr d) const |
| {return has_diff_for(d);} |
| |
| /// Setter for the @ref CanonicalDiff "canonical diff node" for the |
| /// @ref diff represented by their two subjects. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param d the new canonical diff. |
| void |
| diff_context::set_canonical_diff_for(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| const diff_sptr d) |
| { |
| ABG_ASSERT(d); |
| if (!has_diff_for(first, second)) |
| { |
| add_diff(first, second, d); |
| priv_->canonical_diffs.push_back(d); |
| } |
| } |
| |
| /// If there is is a @ref CanonicalDiff "canonical diff node" |
| /// registered for two diff subjects, return it. Otherwise, register |
| /// a canonical diff node for these two diff subjects and return it. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param d the new canonical diff node. |
| /// |
| /// @return the canonical diff node. |
| diff_sptr |
| diff_context::set_or_get_canonical_diff_for(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| const diff_sptr canonical_diff) |
| { |
| ABG_ASSERT(canonical_diff); |
| |
| diff_sptr canonical = get_canonical_diff_for(first, second); |
| if (!canonical) |
| { |
| canonical = canonical_diff; |
| set_canonical_diff_for(first, second, canonical); |
| } |
| return canonical; |
| } |
| |
| /// Set the canonical diff node property of a given diff node |
| /// appropriately. |
| /// |
| /// For a given diff node that has no canonical diff node, retrieve |
| /// the canonical diff node (by looking at its diff subjects and at |
| /// the current context) and set the canonical diff node property of |
| /// the diff node to that canonical diff node. If no canonical diff |
| /// node has been registered to the diff context for the subjects of |
| /// the diff node then, register the canonical diff node as being the |
| /// diff node itself; and set its canonical diff node property as |
| /// such. Otherwise, if the diff node already has a canonical diff |
| /// node, do nothing. |
| /// |
| /// @param diff the diff node to initialize the canonical diff node |
| /// property for. |
| void |
| diff_context::initialize_canonical_diff(const diff_sptr diff) |
| { |
| if (diff->get_canonical_diff() == 0) |
| { |
| diff_sptr canonical = |
| set_or_get_canonical_diff_for(diff->first_subject(), |
| diff->second_subject(), |
| diff); |
| diff->set_canonical_diff(canonical.get()); |
| } |
| } |
| |
| /// Add a diff node to the set of diff nodes that are kept alive for |
| /// the life time of the current instance of diff_context. |
| /// |
| /// Note that diff added to the diff cache are kept alive as well, and |
| /// don't need to be passed to this function to be kept alive. |
| /// |
| /// @param d the diff node to be kept alive during the life time of |
| /// the current instance of @ref diff_context. |
| void |
| diff_context::keep_diff_alive(diff_sptr& d) |
| {priv_->live_diffs_.insert(d);} |
| |
| /// Test if a diff node has been traversed. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return the first diff node against which @p d is redundant. |
| diff* |
| diff_context::diff_has_been_visited(const diff* d) const |
| { |
| const diff* canonical = d->get_canonical_diff(); |
| ABG_ASSERT(canonical); |
| |
| size_t ptr_value = reinterpret_cast<size_t>(canonical); |
| pointer_map::iterator it = priv_->visited_diff_nodes_.find(ptr_value); |
| if (it != priv_->visited_diff_nodes_.end()) |
| return reinterpret_cast<diff*>(it->second); |
| else |
| return 0; |
| } |
| |
| /// Test if a diff node has been traversed. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return the first diff node against which @p d is redundant. |
| diff_sptr |
| diff_context::diff_has_been_visited(const diff_sptr d) const |
| { |
| diff_sptr diff(diff_has_been_visited(d.get())); |
| return diff; |
| } |
| |
| /// Mark a diff node as traversed by a traversing algorithm. |
| /// |
| /// Actually, it's the @ref CanonicalDiff "canonical diff" of this |
| /// node that is marked as traversed. |
| /// |
| /// Subsequent invocations of diff_has_been_visited() on the diff node |
| /// will yield true. |
| void |
| diff_context::mark_diff_as_visited(const diff* d) |
| { |
| if (diff_has_been_visited(d)) |
| return; |
| |
| const diff* canonical = d->get_canonical_diff(); |
| ABG_ASSERT(canonical); |
| |
| size_t canonical_ptr_value = reinterpret_cast<size_t>(canonical); |
| size_t diff_ptr_value = reinterpret_cast<size_t>(d); |
| priv_->visited_diff_nodes_[canonical_ptr_value] = diff_ptr_value; |
| } |
| |
| /// Unmark all the diff nodes that were marked as being traversed. |
| void |
| diff_context::forget_visited_diffs() |
| {priv_->visited_diff_nodes_.clear();} |
| |
| /// This sets a flag that, if it's true, then during the traversing of |
| /// a diff nodes tree each node is visited at most once. |
| /// |
| /// @param f if true then during the traversing of a diff nodes tree |
| /// each node is visited at most once. |
| /// |
| void |
| diff_context::forbid_visiting_a_node_twice(bool f) |
| {priv_->forbid_visiting_a_node_twice_ = f;} |
| |
| /// This function sets a flag os that if @ref |
| /// forbid_visiting_a_node_twice() returns true, then each time the |
| /// node visitor starts visiting a new interface, it resets the |
| /// memory the systems has about already visited node. |
| /// |
| /// @param f the flag to set. |
| void |
| diff_context::forbid_visiting_a_node_twice_per_interface(bool f) |
| {priv_->reset_visited_diffs_for_each_interface_ = f;} |
| |
| /// Return a flag that, if true, then during the traversing of a diff |
| /// nodes tree each node is visited at most once. |
| /// |
| /// @return the boolean flag. |
| bool |
| diff_context::visiting_a_node_twice_is_forbidden() const |
| {return priv_->forbid_visiting_a_node_twice_;} |
| |
| /// Return a flag that, if true, then during the traversing of a diff |
| /// nodes tree each node is visited at most once, while visiting the |
| /// diff tree underneath a given interface (public function or |
| /// variable). Each time a new interface is visited, the nodes |
| /// visited while visiting previous interfaces can be visited again. |
| /// |
| /// @return the boolean flag. |
| /// |
| /// @return the boolean flag. |
| bool |
| diff_context::visiting_a_node_twice_is_forbidden_per_interface() const |
| { |
| return (priv_->forbid_visiting_a_node_twice_ |
| && priv_->reset_visited_diffs_for_each_interface_); |
| } |
| |
| /// Getter for the diff tree nodes filters to apply to diff sub-trees. |
| /// |
| /// @return the vector of tree filters to apply to diff sub-trees. |
| const filtering::filters& |
| diff_context::diff_filters() const |
| {return priv_->filters_;} |
| |
| /// Setter for the diff filters to apply to a given diff sub-tree. |
| /// |
| /// @param f the new diff filter to add to the vector of diff filters |
| /// to apply to diff sub-trees. |
| void |
| diff_context::add_diff_filter(filtering::filter_base_sptr f) |
| {priv_->filters_.push_back(f);} |
| |
| /// Apply the diff filters to a given diff sub-tree. |
| /// |
| /// If the current context is instructed to filter out some categories |
| /// then this function walks the given sub-tree and categorizes its |
| /// nodes by using the filters held by the context. |
| /// |
| /// @param diff the diff sub-tree to apply the filters to. |
| void |
| diff_context::maybe_apply_filters(diff_sptr diff) |
| { |
| if (!diff) |
| return; |
| |
| if (get_allowed_category() == EVERYTHING_CATEGORY) |
| return; |
| |
| if (!diff->has_changes()) |
| return; |
| |
| for (filtering::filters::const_iterator i = diff_filters().begin(); |
| i != diff_filters().end(); |
| ++i) |
| { |
| filtering::apply_filter(*i, diff); |
| propagate_categories(diff); |
| } |
| |
| } |
| |
| /// Apply the diff filters to the diff nodes of a @ref corpus_diff |
| /// instance. |
| /// |
| /// If the current context is instructed to filter out some categories |
| /// then this function walks the diff tree and categorizes its nodes |
| /// by using the filters held by the context. |
| /// |
| /// @param diff the corpus diff to apply the filters to. |
| void |
| diff_context::maybe_apply_filters(corpus_diff_sptr diff) |
| { |
| |
| if (!diff || !diff->has_changes()) |
| return; |
| |
| for (filtering::filters::const_iterator i = diff_filters().begin(); |
| i != diff_filters().end(); |
| ++i) |
| { |
| filtering::apply_filter(**i, diff); |
| propagate_categories(diff); |
| } |
| } |
| |
| /// Getter for the vector of suppressions that specify which diff node |
| /// reports should be dropped on the floor. |
| /// |
| /// @return the set of suppressions. |
| suppressions_type& |
| diff_context::suppressions() const |
| {return priv_->suppressions_;} |
| |
| /// Add a new suppression specification that specifies which diff node |
| /// reports should be dropped on the floor. |
| /// |
| /// @param suppr the new suppression specification to add to the |
| /// existing set of suppressions specifications of the diff context. |
| void |
| diff_context::add_suppression(const suppression_sptr suppr) |
| {priv_->suppressions_.push_back(suppr);} |
| |
| /// Add new suppression specifications that specify which diff node |
| /// reports should be dropped on the floor. |
| /// |
| /// @param supprs the new suppression specifications to add to the |
| /// existing set of suppression specifications of the diff context. |
| void |
| diff_context::add_suppressions(const suppressions_type& supprs) |
| { |
| priv_->suppressions_.insert(priv_->suppressions_.end(), |
| supprs.begin(), supprs.end()); |
| } |
| |
| /// Set the flag that indicates if the diff using this context should |
| /// show only leaf changes or not. |
| /// |
| /// @param f the new value of the flag that indicates if the diff |
| /// using this context should show only leaf changes or not. |
| void |
| diff_context::show_leaf_changes_only(bool f) |
| { |
| // This function can be called only if the reporter hasn't yet been |
| // created. Once it's been created, we are supposed to live with |
| // it. |
| ABG_ASSERT(priv_->reporter_ == 0); |
| priv_->leaf_changes_only_ = f; |
| } |
| |
| /// Get the flag that indicates if the diff using this context should |
| /// show only leaf changes or not. |
| /// |
| /// @return the value of the flag that indicates if the diff using |
| /// this context should show only leaf changes or not. |
| bool |
| diff_context::show_leaf_changes_only() const |
| {return priv_->leaf_changes_only_;} |
| |
| /// Get the flag that indicates if the diff reports using this context |
| /// should show sizes and offsets in an hexadecimal base or not. If |
| /// not, then they are to be shown in a decimal base. |
| /// |
| /// @return true iff sizes and offsets are to be shown in an |
| /// hexadecimal base. |
| bool |
| diff_context::show_hex_values() const |
| {return priv_->hex_values_;} |
| |
| /// Set the flag that indicates if diff reports using this context |
| /// should show sizes and offsets in an hexadecimal base or not. If |
| /// not, then they are to be shown in a decimal base. |
| /// |
| /// @param f if true then sizes and offsets are to be shown in an |
| /// hexadecimal base. |
| void |
| diff_context::show_hex_values(bool f) |
| {priv_->hex_values_ = f;} |
| |
| /// Get the flag that indicates if diff reports using this context |
| /// should show sizes and offsets in bits, rather than bytes. |
| /// |
| /// @return true iff sizes and offsets are to be shown in bits. |
| /// Otherwise they are to be shown in bytes. |
| bool |
| diff_context::show_offsets_sizes_in_bits() const |
| {return priv_->show_offsets_sizes_in_bits_;} |
| |
| /// Set the flag that indicates if diff reports using this context |
| /// should show sizes and offsets in bits, rather than bytes. |
| /// |
| /// @param f if true then sizes and offsets are to be shown in bits. |
| /// Otherwise they are to be shown in bytes. |
| void |
| diff_context::show_offsets_sizes_in_bits(bool f) |
| {priv_->show_offsets_sizes_in_bits_ = f;} |
| |
| /// Set a flag saying if offset changes should be reported in a |
| /// relative way. That is, if the report should say how of many bits |
| /// a class/struct data member did move. |
| /// |
| /// @param f the new boolean value of the flag. |
| void |
| diff_context::show_relative_offset_changes(bool f) |
| {priv_->show_relative_offset_changes_ = f;} |
| |
| /// Get the flag saying if offset changes should be reported in a |
| /// relative way. That is, if the report should say how of many bits |
| /// a class/struct data member did move. |
| /// |
| /// @return the boolean value of the flag. |
| bool |
| diff_context::show_relative_offset_changes(void) |
| {return priv_->show_relative_offset_changes_;} |
| |
| /// Set a flag saying if the comparison module should only show the |
| /// diff stats. |
| /// |
| /// @param f the flag to set. |
| void |
| diff_context::show_stats_only(bool f) |
| {priv_->show_stats_only_ = f;} |
| |
| /// Test if the comparison module should only show the diff stats. |
| /// |
| /// @return true if the comparison module should only show the diff |
| /// stats, false otherwise. |
| bool |
| diff_context::show_stats_only() const |
| {return priv_->show_stats_only_;} |
| |
| /// Setter for the property that says if the comparison module should |
| /// show the soname changes in its report. |
| /// |
| /// @param f the new value of the property. |
| void |
| diff_context::show_soname_change(bool f) |
| {priv_->show_soname_change_ = f;} |
| |
| /// Getter for the property that says if the comparison module should |
| /// show the soname changes in its report. |
| /// |
| /// @return the value of the property. |
| bool |
| diff_context::show_soname_change() const |
| {return priv_->show_soname_change_;} |
| |
| /// Setter for the property that says if the comparison module should |
| /// show the architecture changes in its report. |
| /// |
| /// @param f the new value of the property. |
| void |
| diff_context::show_architecture_change(bool f) |
| {priv_->show_architecture_change_ = f;} |
| |
| /// Getter for the property that says if the comparison module should |
| /// show the architecture changes in its report. |
| /// |
| /// @return the value of the property. |
| bool |
| diff_context::show_architecture_change() const |
| {return priv_->show_architecture_change_;} |
| |
| /// Set a flag saying to show the deleted functions. |
| /// |
| /// @param f true to show deleted functions. |
| void |
| diff_context::show_deleted_fns(bool f) |
| {priv_->show_deleted_fns_ = f;} |
| |
| /// @return true if we want to show the deleted functions, false |
| /// otherwise. |
| bool |
| diff_context::show_deleted_fns() const |
| {return priv_->show_deleted_fns_;} |
| |
| /// Set a flag saying to show the changed functions. |
| /// |
| /// @param f true to show the changed functions. |
| void |
| diff_context::show_changed_fns(bool f) |
| {priv_->show_changed_fns_ = f;} |
| |
| /// @return true if we want to show the changed functions, false otherwise. |
| bool |
| diff_context::show_changed_fns() const |
| {return priv_->show_changed_fns_;} |
| |
| /// Set a flag saying to show the added functions. |
| /// |
| /// @param f true to show the added functions. |
| void |
| diff_context::show_added_fns(bool f) |
| {priv_->show_added_fns_ = f;} |
| |
| /// @return true if we want to show the added functions, false |
| /// otherwise. |
| bool |
| diff_context::show_added_fns() const |
| {return priv_->show_added_fns_;} |
| |
| /// Set a flag saying to show the deleted variables. |
| /// |
| /// @param f true to show the deleted variables. |
| void |
| diff_context::show_deleted_vars(bool f) |
| {priv_->show_deleted_vars_ = f;} |
| |
| /// @return true if we want to show the deleted variables, false |
| /// otherwise. |
| bool |
| diff_context::show_deleted_vars() const |
| {return priv_->show_deleted_vars_;} |
| |
| /// Set a flag saying to show the changed variables. |
| /// |
| /// @param f true to show the changed variables. |
| void |
| diff_context::show_changed_vars(bool f) |
| {priv_->show_changed_vars_ = f;} |
| |
| /// @return true if we want to show the changed variables, false otherwise. |
| bool |
| diff_context::show_changed_vars() const |
| {return priv_->show_changed_vars_;} |
| |
| /// Set a flag saying to show the added variables. |
| /// |
| /// @param f true to show the added variables. |
| void |
| diff_context::show_added_vars(bool f) |
| {priv_->show_added_vars_ = f;} |
| |
| /// @return true if we want to show the added variables, false |
| /// otherwise. |
| bool |
| diff_context::show_added_vars() const |
| {return priv_->show_added_vars_;} |
| |
| bool |
| diff_context::show_linkage_names() const |
| {return priv_->show_linkage_names_;} |
| |
| void |
| diff_context::show_linkage_names(bool f) |
| {priv_->show_linkage_names_= f;} |
| |
| /// Set a flag saying to show location information. |
| /// |
| /// @param f true to show location information. |
| void |
| diff_context::show_locs(bool f) |
| {priv_->show_locs_= f;} |
| |
| /// @return true if we want to show location information, false |
| /// otherwise. |
| bool |
| diff_context::show_locs() const |
| {return priv_->show_locs_;} |
| |
| /// A getter for the flag that says if we should report about |
| /// functions or variables diff nodes that have *exclusively* |
| /// redundant diff tree children nodes. |
| /// |
| /// @return the flag. |
| bool |
| diff_context::show_redundant_changes() const |
| {return priv_->show_redundant_changes_;} |
| |
| /// A setter for the flag that says if we should report about |
| /// functions or variables diff nodes that have *exclusively* |
| /// redundant diff tree children nodes. |
| /// |
| /// @param f the flag to set. |
| void |
| diff_context::show_redundant_changes(bool f) |
| {priv_->show_redundant_changes_ = f;} |
| |
| /// A getter for the flag that says if we should flag indirect class |
| /// and union changes in leaf-changes-only mode. |
| /// |
| /// @return the flag. |
| bool |
| diff_context::flag_indirect_changes() const |
| {return priv_->flag_indirect_changes_;} |
| |
| /// A setter for the flag that says if we should flag indirect class |
| /// and union changes in leaf-changes-only mode. |
| /// |
| /// @param f the flag to set. |
| void |
| diff_context::flag_indirect_changes(bool f) |
| {priv_->flag_indirect_changes_ = f;} |
| |
| /// Getter for the flag that indicates if symbols not referenced by |
| /// any debug info are to be compared and reported about. |
| /// |
| /// @return the boolean flag. |
| bool |
| diff_context::show_symbols_unreferenced_by_debug_info() const |
| {return priv_->show_syms_unreferenced_by_di_;} |
| |
| /// Setter for the flag that indicates if symbols not referenced by |
| /// any debug info are to be compared and reported about. |
| /// |
| /// @param f the new flag to set. |
| void |
| diff_context::show_symbols_unreferenced_by_debug_info(bool f) |
| {priv_->show_syms_unreferenced_by_di_ = f;} |
| |
| /// Getter for the flag that indicates if symbols not referenced by |
| /// any debug info and that got added are to be reported about. |
| /// |
| /// @return true iff symbols not referenced by any debug info and that |
| /// got added are to be reported about. |
| bool |
| diff_context::show_added_symbols_unreferenced_by_debug_info() const |
| {return priv_->show_added_syms_unreferenced_by_di_;} |
| |
| /// Setter for the flag that indicates if symbols not referenced by |
| /// any debug info and that got added are to be reported about. |
| /// |
| /// @param f the new flag that says if symbols not referenced by any |
| /// debug info and that got added are to be reported about. |
| void |
| diff_context::show_added_symbols_unreferenced_by_debug_info(bool f) |
| {priv_->show_added_syms_unreferenced_by_di_ = f;} |
| |
| /// Setter for the flag that indicates if changes on types unreachable |
| /// from global functions and variables are to be reported. |
| /// |
| /// @param f if true, then changes on types unreachable from global |
| /// functions and variables are to be reported. |
| void |
| diff_context::show_unreachable_types(bool f) |
| {priv_->show_unreachable_types_ = f;} |
| |
| /// Getter for the flag that indicates if changes on types unreachable |
| /// from global functions and variables are to be reported. |
| /// |
| /// @return true iff changes on types unreachable from global |
| /// functions and variables are to be reported. |
| bool |
| diff_context::show_unreachable_types() |
| {return priv_->show_unreachable_types_;} |
| |
| /// Getter of the flag that indicates if the leaf reporter should |
| /// display a summary of the interfaces impacted by a given leaf |
| /// change or not. |
| /// |
| /// @return the flag that indicates if the leaf reporter should |
| /// display a summary of the interfaces impacted by a given leaf |
| /// change or not. |
| bool |
| diff_context::show_impacted_interfaces() const |
| {return priv_->show_impacted_interfaces_;} |
| |
| /// Setter of the flag that indicates if the leaf reporter should |
| /// display a summary of the interfaces impacted by a given leaf |
| /// change or not. |
| /// |
| /// @param f the new value of the flag that indicates if the leaf |
| /// reporter should display a summary of the interfaces impacted by a |
| /// given leaf change or not. |
| void |
| diff_context::show_impacted_interfaces(bool f) |
| {priv_->show_impacted_interfaces_ = f;} |
| |
| /// Setter for the default output stream used by code of the |
| /// comparison engine. By default the default output stream is a NULL |
| /// pointer. |
| /// |
| /// @param o a pointer to the default output stream. |
| void |
| diff_context::default_output_stream(ostream* o) |
| {priv_->default_output_stream_ = o;} |
| |
| /// Getter for the default output stream used by code of the |
| /// comparison engine. By default the default output stream is a NULL |
| /// pointer. |
| /// |
| /// @return a pointer to the default output stream. |
| ostream* |
| diff_context::default_output_stream() |
| {return priv_->default_output_stream_;} |
| |
| /// Setter for the errror output stream used by code of the comparison |
| /// engine. By default the error output stream is a NULL pointer. |
| /// |
| /// @param o a pointer to the error output stream. |
| void |
| diff_context::error_output_stream(ostream* o) |
| {priv_->error_output_stream_ = o;} |
| |
| /// Getter for the errror output stream used by code of the comparison |
| /// engine. By default the error output stream is a NULL pointer. |
| /// |
| /// @return a pointer to the error output stream. |
| ostream* |
| diff_context::error_output_stream() const |
| {return priv_->error_output_stream_;} |
| |
| /// Test if the comparison engine should dump the diff tree for the |
| /// changed functions and variables it has. |
| /// |
| /// @return true if after the comparison, the engine should dump the |
| /// diff tree for the changed functions and variables it has. |
| bool |
| diff_context::dump_diff_tree() const |
| {return priv_->dump_diff_tree_;} |
| |
| /// Set if the comparison engine should dump the diff tree for the |
| /// changed functions and variables it has. |
| /// |
| /// @param f true if after the comparison, the engine should dump the |
| /// diff tree for the changed functions and variables it has. |
| void |
| diff_context::dump_diff_tree(bool f) |
| {priv_->dump_diff_tree_ = f;} |
| |
| /// Emit a textual representation of a diff tree to the error output |
| /// stream of the current context, for debugging purposes. |
| /// |
| /// @param d the diff tree to serialize to the error output associated |
| /// to the current instance of @ref diff_context. |
| void |
| diff_context::do_dump_diff_tree(const diff_sptr d) const |
| { |
| if (error_output_stream()) |
| print_diff_tree(d, *error_output_stream()); |
| } |
| |
| /// Emit a textual representation of a @ref corpus_diff tree to the error |
| /// output stream of the current context, for debugging purposes. |
| /// |
| /// @param d the @ref corpus_diff tree to serialize to the error |
| /// output associated to the current instance of @ref diff_context. |
| void |
| diff_context::do_dump_diff_tree(const corpus_diff_sptr d) const |
| { |
| if (error_output_stream()) |
| print_diff_tree(d, *error_output_stream()); |
| } |
| // </diff_context stuff> |
| |
| // <diff stuff> |
| |
| /// Constructor for the @ref diff type. |
| /// |
| /// This constructs a diff between two subjects that are actually |
| /// declarations; the first and the second one. |
| /// |
| /// @param first_subject the first decl (subject) of the diff. |
| /// |
| /// @param second_subject the second decl (subject) of the diff. |
| diff::diff(type_or_decl_base_sptr first_subject, |
| type_or_decl_base_sptr second_subject) |
| : priv_(new priv(first_subject, second_subject, |
| diff_context_sptr(), |
| NO_CHANGE_CATEGORY, |
| /*reported_once=*/false, |
| /*currently_reporting=*/false)) |
| {} |
| |
| /// Constructor for the @ref diff type. |
| /// |
| /// This constructs a diff between two subjects that are actually |
| /// declarations; the first and the second one. |
| /// |
| /// @param first_subject the first decl (subject) of the diff. |
| /// |
| /// @param second_subject the second decl (subject) of the diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive during the entire life time of the current |
| /// instance of @ref diff. Otherwise, memory corruption issues occur. |
| diff::diff(type_or_decl_base_sptr first_subject, |
| type_or_decl_base_sptr second_subject, |
| diff_context_sptr ctxt) |
| : priv_(new priv(first_subject, second_subject, |
| ctxt, NO_CHANGE_CATEGORY, |
| /*reported_once=*/false, |
| /*currently_reporting=*/false)) |
| {} |
| |
| /// Flag a given diff node as being traversed. |
| /// |
| /// For certain diff nodes like @ref class_diff, it's important to |
| /// avoid traversing the node again while it's already being |
| /// traversed; otherwise this leads to infinite loops. So the |
| /// diff::begin_traversing() and diff::end_traversing() methods flag a |
| /// given node as being traversed (or not), so that |
| /// diff::is_traversing() can tell if the node is being traversed. |
| /// |
| /// Note that traversing a node means visiting it *and* visiting its |
| /// children nodes. |
| /// |
| /// The canonical node is marked as being traversed too. |
| /// |
| /// These functions are called by the traversing code. |
| void |
| diff::begin_traversing() |
| { |
| ABG_ASSERT(!is_traversing()); |
| if (priv_->canonical_diff_) |
| priv_->canonical_diff_->priv_->traversing_ = true; |
| priv_->traversing_ = true; |
| } |
| |
| /// Tell if a given node is being traversed or not. |
| /// |
| /// Note that traversing a node means visiting it *and* visiting its |
| /// children nodes. |
| /// |
| /// It's the canonical node which is looked at, actually. |
| /// |
| /// Please read the comments for the diff::begin_traversing() for mode |
| /// context. |
| /// |
| /// @return true if the current instance of @diff is being traversed. |
| bool |
| diff::is_traversing() const |
| { |
| if (priv_->canonical_diff_) |
| return priv_->canonical_diff_->priv_->traversing_; |
| return priv_->traversing_; |
| } |
| |
| /// Flag a given diff node as not being traversed anymore. |
| /// |
| /// Note that traversing a node means visiting it *and* visiting its |
| /// children nodes. |
| /// |
| /// Please read the comments of the function diff::begin_traversing() |
| /// for mode context. |
| void |
| diff::end_traversing() |
| { |
| ABG_ASSERT(is_traversing()); |
| if (priv_->canonical_diff_) |
| priv_->canonical_diff_->priv_->traversing_ = false; |
| priv_->traversing_ = false; |
| } |
| |
| /// Finish the building of a given kind of a diff tree node. |
| /// |
| /// For instance, certain kinds of diff tree node have specific |
| /// children nodes that are populated after the constructor of the |
| /// diff tree node has been called. In that case, calling overloads |
| /// of this method ensures that these children nodes are properly |
| /// gathered and setup. |
| void |
| diff::finish_diff_type() |
| { |
| } |
| |
| /// Getter of the first subject of the diff. |
| /// |
| /// @return the first subject of the diff. |
| type_or_decl_base_sptr |
| diff::first_subject() const |
| {return dynamic_pointer_cast<type_or_decl_base>(priv_->first_subject_);} |
| |
| /// Getter of the second subject of the diff. |
| /// |
| /// @return the second subject of the diff. |
| type_or_decl_base_sptr |
| diff::second_subject() const |
| {return dynamic_pointer_cast<type_or_decl_base>(priv_->second_subject_);} |
| |
| /// Getter for the children nodes of the current @ref diff node. |
| /// |
| /// @return a vector of the children nodes. |
| const vector<diff*>& |
| diff::children_nodes() const |
| {return priv_->children_;} |
| |
| /// Getter for the parent node of the current @ref diff node. |
| /// |
| /// @return the parent node of the current @ref diff node. |
| const diff* |
| diff::parent_node() const |
| {return priv_->parent_;} |
| |
| /// Getter for the canonical diff of the current instance of @ref |
| /// diff. |
| /// |
| /// Note that the canonical diff node for the current instanc eof diff |
| /// node must have been set by invoking |
| /// class_diff::initialize_canonical_diff() on the current instance of |
| /// diff node. |
| /// |
| /// @return the canonical diff node or null if none was set. |
| diff* |
| diff::get_canonical_diff() const |
| {return priv_->canonical_diff_;} |
| |
| /// Setter for the canonical diff of the current instance of @ref |
| /// diff. |
| /// |
| /// @param d the new canonical node to set. |
| void |
| diff::set_canonical_diff(diff * d) |
| {priv_->canonical_diff_ = d;} |
| |
| /// Add a new child node to the vector of children nodes for the |
| /// current @ref diff node. |
| /// |
| /// @param d the new child node to add to the children nodes. |
| void |
| diff::append_child_node(diff_sptr d) |
| { |
| ABG_ASSERT(d); |
| |
| // Ensure 'd' is kept alive for the life time of the context of this |
| // diff. |
| context()->keep_diff_alive(d); |
| |
| // Add the underlying pointer of 'd' to the vector of children. |
| // Note that this vector holds no reference to 'd'. This is to avoid |
| // reference cycles. The reference to 'd' is held by the context of |
| // this diff, thanks to the call to context()->keep_diff_alive(d) |
| // above. |
| priv_->children_.push_back(d.get()); |
| |
| diff_less_than_functor comp; |
| std::sort(priv_->children_.begin(), |
| priv_->children_.end(), |
| comp); |
| |
| d->priv_->parent_ = this; |
| } |
| |
| /// Getter of the context of the current diff. |
| /// |
| /// @return the context of the current diff. |
| const diff_context_sptr |
| diff::context() const |
| {return priv_->get_context();} |
| |
| /// Setter of the context of the current diff. |
| /// |
| /// @param c the new context to set. |
| void |
| diff::context(diff_context_sptr c) |
| {priv_->ctxt_ = c;} |
| |
| /// Tests if we are currently in the middle of emitting a report for |
| /// this diff. |
| /// |
| /// @return true if we are currently emitting a report for the |
| /// current diff, false otherwise. |
| bool |
| diff::currently_reporting() const |
| { |
| if (priv_->canonical_diff_) |
| return priv_->canonical_diff_->priv_->currently_reporting_; |
| return priv_->currently_reporting_; |
| } |
| |
| /// Sets a flag saying if we are currently in the middle of emitting |
| /// a report for this diff. |
| /// |
| /// @param f true if we are currently emitting a report for the |
| /// current diff, false otherwise. |
| void |
| diff::currently_reporting(bool f) const |
| { |
| if (priv_->canonical_diff_) |
| priv_->canonical_diff_->priv_->currently_reporting_ = f; |
| priv_->currently_reporting_ = f; |
| } |
| |
| /// Tests if a report has already been emitted for the current diff. |
| /// |
| /// @return true if a report has already been emitted for the |
| /// current diff, false otherwise. |
| bool |
| diff::reported_once() const |
| { |
| ABG_ASSERT(priv_->canonical_diff_); |
| return priv_->canonical_diff_->priv_->reported_once_; |
| } |
| |
| /// The generic traversing code that walks a given diff sub-tree. |
| /// |
| /// Note that there is a difference between traversing a diff node and |
| /// visiting it. Basically, traversing a diff node means visiting it |
| /// and visiting its children nodes too. So one can visit a node |
| /// without traversing it. But traversing a node without visiting it |
| /// is not possible. |
| /// |
| /// Note that by default this traversing code visits a given class of |
| /// equivalence of a diff node only once. This behaviour can been |
| /// changed by calling |
| /// diff_context::visiting_a_node_twice_is_forbidden(), but this is |
| /// very risky as it might create endless loops while visiting a diff |
| /// tree graph that has changes that refer to themselves; that is, |
| /// diff tree graphs with cycles. |
| /// |
| /// When a diff node is encountered, the |
| /// diff_node_visitor::visit_begin() method is invoked on the diff |
| /// node first. |
| /// |
| /// If the diff node has already been visited, then |
| /// node_visitor::visit_end() is called on it and the node traversing |
| /// is done; the children of the diff node are not visited in this |
| /// case. |
| /// |
| /// If the diff node has *NOT* been visited yet, then the |
| /// diff_node_visitor::visit() method is invoked with it's 'pre' |
| /// argument set to true. Then if the diff_node_visitor::visit() |
| /// returns true, then the children nodes of the diff node are |
| /// visited. Otherwise, no children nodes of the diff node is |
| /// visited and the diff_node_visitor::visit_end() is called. |
| |
| /// After the children nodes are visited (and only if they are |
| /// visited) the diff_node_visitor::visit() method is invoked with |
| /// it's 'pre' argument set to false. And then the |
| /// diff_node_visitor::visit_end() is called. |
| /// |
| /// @param v the entity that visits each node of the diff sub-tree. |
| /// |
| /// @return true to tell the caller that all of the sub-tree could be |
| /// walked. This instructs the caller to keep walking the rest of the |
| /// tree. Return false otherwise. |
| bool |
| diff::traverse(diff_node_visitor& v) |
| { |
| finish_diff_type(); |
| |
| v.visit_begin(this); |
| |
| bool already_visited = false; |
| if (context()->visiting_a_node_twice_is_forbidden() |
| && context()->diff_has_been_visited(this)) |
| already_visited = true; |
| |
| bool mark_visited_nodes_as_traversed = |
| !(v.get_visiting_kind() & DO_NOT_MARK_VISITED_NODES_AS_VISITED); |
| |
| if (!already_visited && !v.visit(this, /*pre=*/true)) |
| { |
| v.visit_end(this); |
| if (mark_visited_nodes_as_traversed) |
| context()->mark_diff_as_visited(this); |
| return false; |
| } |
| |
| if (!(v.get_visiting_kind() & SKIP_CHILDREN_VISITING_KIND) |
| && !is_traversing() |
| && !already_visited) |
| { |
| begin_traversing(); |
| for (vector<diff*>::const_iterator i = children_nodes().begin(); |
| i != children_nodes().end(); |
| ++i) |
| { |
| if (!(*i)->traverse(v)) |
| { |
| v.visit_end(this); |
| if (mark_visited_nodes_as_traversed) |
| context()->mark_diff_as_visited(this); |
| end_traversing(); |
| return false; |
| } |
| } |
| end_traversing(); |
| } |
| |
| if (!v.visit(this, /*pref=*/false)) |
| { |
| v.visit_end(this); |
| if (mark_visited_nodes_as_traversed) |
| context()->mark_diff_as_visited(this); |
| return false; |
| } |
| |
| v.visit_end(this); |
| if (!already_visited && mark_visited_nodes_as_traversed) |
| context()->mark_diff_as_visited(this); |
| |
| return true; |
| } |
| |
| /// Sets a flag saying if a report has already been emitted for the |
| /// current diff. |
| /// |
| /// @param f true if a report has already been emitted for the |
| /// current diff, false otherwise. |
| void |
| diff::reported_once(bool f) const |
| { |
| ABG_ASSERT(priv_->canonical_diff_); |
| priv_->canonical_diff_->priv_->reported_once_ = f; |
| priv_->reported_once_ = f; |
| } |
| |
| /// Getter for the local category of the current diff tree node. |
| /// |
| /// The local category represents the set of categories of a diff |
| /// node, not taking in account the categories inherited from its |
| /// children nodes. |
| /// |
| /// @return the local category of the current diff tree node. |
| diff_category |
| diff::get_local_category() const |
| {return priv_->local_category_;} |
| |
| /// Getter of the category of the class of equivalence of the current |
| /// diff tree node. |
| /// |
| /// That is, if the current diff tree node has a canonical node, |
| /// return the category of that canonical node. Otherwise, return the |
| /// category of the current node. |
| /// |
| /// @return the category of the class of equivalence of the current |
| /// tree node. |
| diff_category |
| diff::get_class_of_equiv_category() const |
| { |
| diff* canonical = get_canonical_diff(); |
| return canonical ? canonical->get_category() : get_category(); |
| } |
| |
| /// Getter for the category of the current diff tree node. |
| /// |
| /// This category represents the union of the local category and the |
| /// categories inherited from the children diff nodes. |
| /// |
| /// @return the category of the current diff tree node. |
| diff_category |
| diff::get_category() const |
| {return priv_->category_;} |
| |
| /// Adds the current diff tree node to an additional set of |
| /// categories. Note that the categories include thoses inherited |
| /// from the children nodes of this diff node. |
| /// |
| /// @param c a bit-map representing the set of categories to add the |
| /// current diff tree node to. |
| /// |
| /// @return the resulting bit-map representing the categories this |
| /// current diff tree node belongs to, including those inherited from |
| /// its children nodes. |
| diff_category |
| diff::add_to_category(diff_category c) |
| { |
| priv_->category_ = priv_->category_ | c; |
| return priv_->category_; |
| } |
| |
| /// Adds the current diff tree node to the categories resulting from |
| /// the local changes of the current diff node. |
| /// |
| /// @param c a bit-map representing the set of categories to add the |
| /// current diff tree node to. |
| /// |
| /// @return the resulting bit-map representing the categories this |
| /// current diff tree node belongs to. |
| diff_category |
| diff::add_to_local_category(diff_category c) |
| { |
| priv_->local_category_ = priv_->local_category_ | c; |
| return priv_->local_category_; |
| } |
| |
| /// Adds the current diff tree node to the categories resulting from |
| /// the local and inherited changes of the current diff node. |
| /// |
| /// @param c a bit-map representing the set of categories to add the |
| /// current diff tree node to. |
| void |
| diff::add_to_local_and_inherited_categories(diff_category c) |
| { |
| add_to_local_category(c); |
| add_to_category(c); |
| } |
| |
| /// Remove the current diff tree node from an a existing sef of |
| /// categories. The categories include those inherited from the |
| /// children nodes of the current diff node. |
| /// |
| /// @param c a bit-map representing the set of categories to add the |
| /// current diff tree node to. |
| /// |
| /// @return the resulting bit-map representing the categories this |
| /// current diff tree onde belongs to, including the categories |
| /// inherited from the children nodes of the current diff node. |
| diff_category |
| diff::remove_from_category(diff_category c) |
| { |
| priv_->category_ = priv_->category_ & ~c; |
| return priv_->category_; |
| } |
| |
| /// Remove the current diff tree node from the categories resulting |
| /// from the local changes. |
| /// |
| /// @param c a bit-map representing the set of categories to add the |
| /// current diff tree node to. |
| /// |
| /// @return the resulting bit-map representing the categories this |
| /// current diff tree onde belongs to. |
| diff_category |
| diff::remove_from_local_category(diff_category c) |
| { |
| priv_->local_category_ = priv_->local_category_ & ~c; |
| return priv_->local_category_; |
| } |
| |
| /// Set the category of the current @ref diff node. This category |
| /// includes the categories inherited from the children nodes of the |
| /// current diff node. |
| /// |
| /// @param c the new category for the current diff node. |
| void |
| diff::set_category(diff_category c) |
| {priv_->category_ = c;} |
| |
| /// Set the local category of the current @ref diff node. |
| /// |
| /// @param c the new category for the current diff node. |
| void |
| diff::set_local_category(diff_category c) |
| {priv_->local_category_ = c;} |
| |
| /// Test if this diff tree node is to be filtered out for reporting |
| /// purposes. |
| /// |
| /// The function tests if the categories of the diff tree node are |
| /// "forbidden" by the context or not. |
| /// |
| /// @return true iff the current diff node should NOT be reported. |
| bool |
| diff::is_filtered_out() const |
| { |
| if (diff * canonical = get_canonical_diff()) |
| if (canonical->get_category() & SUPPRESSED_CATEGORY |
| || canonical->get_category() & PRIVATE_TYPE_CATEGORY) |
| // The canonical type was suppressed either by a user-provided |
| // suppression specification or by a "private-type" suppression |
| // specification.. This means all the class of equivalence of |
| // that canonical type was suppressed. So this node should be |
| // suppressed too. |
| return true; |
| return priv_->is_filtered_out(get_category()); |
| } |
| |
| /// Test if this diff tree node is to be filtered out for reporting |
| /// purposes, but by considering only the categories that were *NOT* |
| /// inherited from its children nodes. |
| /// |
| /// The function tests if the local categories of the diff tree node |
| /// are "forbidden" by the context or not. |
| /// |
| /// @return true iff the current diff node should NOT be reported, |
| /// with respect to its local categories. |
| bool |
| diff::is_filtered_out_wrt_non_inherited_categories() const |
| {return priv_->is_filtered_out(get_local_category());} |
| |
| /// Test if the current diff node has been suppressed by a |
| /// user-provided suppression specification. |
| /// |
| /// @return true if the current diff node has been suppressed by a |
| /// user-provided suppression list. |
| bool |
| diff::is_suppressed() const |
| { |
| bool is_private = false; |
| return is_suppressed(is_private); |
| } |
| |
| /// Test if the current diff node has been suppressed by a |
| /// user-provided suppression specification or by an auto-generated |
| /// "private type" suppression specification. |
| /// |
| /// Note that private type suppressions are auto-generated from the |
| /// path to where public headers are, as given by the user. |
| /// |
| /// @param is_private_type out parameter if the current diff node was |
| /// suppressed because it's a private type then this parameter is set |
| /// to true. |
| /// |
| /// @return true if the current diff node has been suppressed by a |
| /// user-provided suppression list. |
| bool |
| diff::is_suppressed(bool &is_private_type) const |
| { |
| const suppressions_type& suppressions = context()->suppressions(); |
| for (suppressions_type::const_iterator i = suppressions.begin(); |
| i != suppressions.end(); |
| ++i) |
| { |
| if ((*i)->suppresses_diff(this)) |
| { |
| if (is_private_type_suppr_spec(*i)) |
| is_private_type = true; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Test if this diff tree node should be reported. |
| /// |
| /// @return true iff the current node should be reported. |
| bool |
| diff::to_be_reported() const |
| { |
| if (has_changes() && !is_filtered_out()) |
| return true; |
| return false; |
| } |
| |
| /// Test if this diff tree node should be reported when considering |
| /// the categories that were *NOT* inherited from its children nodes. |
| /// |
| /// @return true iff the current node should be reported. |
| bool |
| diff::has_local_changes_to_be_reported() const |
| { |
| if (has_local_changes() |
| && !is_filtered_out_wrt_non_inherited_categories()) |
| return true; |
| return false; |
| } |
| |
| /// Get a pretty representation of the current @ref diff node. |
| /// |
| /// This is suitable for e.g. emitting debugging traces for the diff |
| /// tree nodes. |
| /// |
| /// @return the pretty representation of the diff node. |
| const string& |
| diff::get_pretty_representation() const |
| { |
| if (priv_->pretty_representation_.empty()) |
| priv_->pretty_representation_ = "empty_diff"; |
| return priv_->pretty_representation_; |
| } |
| |
| /// Default implementation of the hierachy chaining virtual function. |
| /// |
| /// There are several types of diff nodes that have logical children |
| /// nodes; for instance, a typedef_diff has the diff of the underlying |
| /// type as a child node. A var_diff has the diff of the types of the |
| /// variables as a child node, etc. |
| /// |
| /// But because the @ref diff base has a generic representation for |
| /// children nodes of the all the types of @ref diff nodes (regardless |
| /// of the specific most-derived type of diff node) that one can get |
| /// using the method diff::children_nodes(), one need to populate that |
| /// vector of children node. |
| /// |
| /// Populating that vector of children node is done by this function; |
| /// it must be overloaded by each most-derived type of diff node that |
| /// extends the @ref diff type. |
| void |
| diff::chain_into_hierarchy() |
| {} |
| |
| // </diff stuff> |
| |
| // <type_diff_base stuff> |
| |
| type_diff_base::type_diff_base(type_base_sptr first_subject, |
| type_base_sptr second_subject, |
| diff_context_sptr ctxt) |
| : diff(first_subject, second_subject, ctxt), |
| priv_(new priv) |
| {} |
| |
| type_diff_base::~type_diff_base() |
| {} |
| // </type_diff_base stuff> |
| |
| // <decl_diff_base stuff> |
| |
| /// Constructor of @ref decl_diff_base. |
| /// |
| /// @param first_subject the first subject of the diff. |
| /// |
| /// @param second_subject the second subject of the diff. |
| /// |
| /// @param ctxt the context of the diff. This object must stay alive |
| /// at least during the life time of the current instance of @ref |
| /// decl_diff_base, otherwise, memory corruption issues occur. |
| decl_diff_base::decl_diff_base(decl_base_sptr first_subject, |
| decl_base_sptr second_subject, |
| diff_context_sptr ctxt) |
| : diff(first_subject, second_subject, ctxt), |
| priv_(new priv) |
| {} |
| |
| decl_diff_base::~decl_diff_base() |
| {} |
| |
| // </decl_diff_base stuff> |
| |
| // <distinct_diff stuff> |
| |
| /// @return a pretty representation for the @ref distinct_diff node. |
| const string& |
| distinct_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "distinct_diff["; |
| if (first_subject()) |
| o << first_subject()->get_pretty_representation(); |
| else |
| o << "null"; |
| o << ", "; |
| if (second_subject()) |
| o << second_subject()->get_pretty_representation() ; |
| else |
| o << "null"; |
| o << "]" ; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @distinct_diff. |
| /// |
| /// The children nodes can then later be retrieved using |
| /// diff::children_nodes(). |
| void |
| distinct_diff::chain_into_hierarchy() |
| { |
| ABG_ASSERT(entities_are_of_distinct_kinds(first(), second())); |
| |
| if (diff_sptr d = compatible_child_diff()) |
| append_child_node(d); |
| } |
| |
| /// Constructor for @ref distinct_diff. |
| /// |
| /// Note that the two entities considered for the diff (and passed in |
| /// parameter) must be of different kinds. |
| /// |
| /// @param first the first entity to consider for the diff. |
| /// |
| /// @param second the second entity to consider for the diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref distinct_diff. Otherwise memory |
| /// corruption issues occur. |
| distinct_diff::distinct_diff(type_or_decl_base_sptr first, |
| type_or_decl_base_sptr second, |
| diff_context_sptr ctxt) |
| : diff(first, second, ctxt), |
| priv_(new priv) |
| {ABG_ASSERT(entities_are_of_distinct_kinds(first, second));} |
| |
| /// Finish building the current instance of @ref distinct_diff. |
| void |
| distinct_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first subject of the diff. |
| /// |
| /// @return the first subject of the diff. |
| const type_or_decl_base_sptr |
| distinct_diff::first() const |
| {return first_subject();} |
| |
| /// Getter for the second subject of the diff. |
| /// |
| /// @return the second subject of the diff. |
| const type_or_decl_base_sptr |
| distinct_diff::second() const |
| {return second_subject();} |
| |
| /// Getter for the child diff of this distinct_diff instance. |
| /// |
| /// When a distinct_diff has two subjects that are different but |
| /// compatible, then the distinct_diff instance has a child diff node |
| /// (named the compatible child diff) that is the diff between the two |
| /// subjects stripped from their typedefs. Otherwise, the compatible |
| /// child diff is nul. |
| /// |
| /// Note that two diff subjects (that compare different) are |
| /// considered compatible if stripping typedefs out of them makes them |
| /// comparing equal. |
| /// |
| /// @return the compatible child diff node, if any. Otherwise, null. |
| const diff_sptr |
| distinct_diff::compatible_child_diff() const |
| { |
| if (!priv_->compatible_child_diff) |
| { |
| type_base_sptr fs = strip_typedef(is_type(first())), |
| ss = strip_typedef(is_type(second())); |
| |
| if (fs && ss |
| && !entities_are_of_distinct_kinds(get_type_declaration(fs), |
| get_type_declaration(ss))) |
| priv_->compatible_child_diff = compute_diff(get_type_declaration(fs), |
| get_type_declaration(ss), |
| context()); |
| } |
| return priv_->compatible_child_diff; |
| } |
| |
| /// Test if the two arguments are of different kind, or that are both |
| /// NULL. |
| /// |
| /// @param first the first argument to test for similarity in kind. |
| /// |
| /// @param second the second argument to test for similarity in kind. |
| /// |
| /// @return true iff the two arguments are of different kind. |
| bool |
| distinct_diff::entities_are_of_distinct_kinds(type_or_decl_base_sptr first, |
| type_or_decl_base_sptr second) |
| { |
| if (!!first != !!second) |
| return true; |
| if (!first && !second) |
| // We do consider diffs of two empty decls as a diff of distinct |
| // kinds, for now. |
| return true; |
| if (first == second) |
| return false; |
| |
| const type_or_decl_base &f = *first, &s = *second; |
| return typeid(f) != typeid(s); |
| } |
| |
| /// @return true if the two subjects of the diff are different, false |
| /// otherwise. |
| bool |
| distinct_diff::has_changes() const |
| {return first() != second();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| distinct_diff::has_local_changes() const |
| { |
| // Changes on a distinct_diff are all local. |
| if (has_changes()) |
| return LOCAL_TYPE_CHANGE_KIND; |
| return NO_CHANGE_KIND; |
| } |
| |
| /// Emit a report about the current diff instance. |
| /// |
| /// @param out the output stream to send the diff report to. |
| /// |
| /// @param indent the indentation string to use in the report. |
| void |
| distinct_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Try to diff entities that are of distinct kinds. |
| /// |
| /// @param first the first entity to consider for the diff. |
| /// |
| /// @param second the second entity to consider for the diff. |
| /// |
| /// @param ctxt the context of the diff. |
| /// |
| /// @return a non-null diff if a diff object could be built, null |
| /// otherwise. |
| distinct_diff_sptr |
| compute_diff_for_distinct_kinds(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (!distinct_diff::entities_are_of_distinct_kinds(first, second)) |
| return distinct_diff_sptr(); |
| |
| distinct_diff_sptr result(new distinct_diff(first, second, ctxt)); |
| |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| |
| /// </distinct_diff stuff> |
| |
| /// Try to compute a diff on two instances of DiffType representation. |
| /// |
| /// The function template performs the diff if and only if the decl |
| /// representations are of a DiffType. |
| /// |
| /// @tparm DiffType the type of instances to diff. |
| /// |
| /// @param first the first representation of decl to consider in the |
| /// diff computation. |
| /// |
| /// @param second the second representation of decl to consider in the |
| /// diff computation. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| ///@return the diff of the two types @p first and @p second if and |
| ///only if they represent the parametrized type DiffType. Otherwise, |
| ///returns a NULL pointer value. |
| template<typename DiffType> |
| diff_sptr |
| try_to_diff(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (shared_ptr<DiffType> f = |
| dynamic_pointer_cast<DiffType>(first)) |
| { |
| shared_ptr<DiffType> s = |
| dynamic_pointer_cast<DiffType>(second); |
| if (!s) |
| return diff_sptr(); |
| return compute_diff(f, s, ctxt); |
| } |
| return diff_sptr(); |
| } |
| |
| |
| /// This is a specialization of @ref try_to_diff() template to diff |
| /// instances of @ref class_decl. |
| /// |
| /// @param first the first representation of decl to consider in the |
| /// diff computation. |
| /// |
| /// @param second the second representation of decl to consider in the |
| /// diff computation. |
| /// |
| /// @param ctxt the diff context to use. |
| template<> |
| diff_sptr |
| try_to_diff<class_decl>(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (class_decl_sptr f = |
| dynamic_pointer_cast<class_decl>(first)) |
| { |
| class_decl_sptr s = dynamic_pointer_cast<class_decl>(second); |
| if (!s) |
| return diff_sptr(); |
| |
| if (f->get_is_declaration_only()) |
| { |
| class_decl_sptr f2 = |
| is_class_type (f->get_definition_of_declaration()); |
| if (f2) |
| f = f2; |
| } |
| if (s->get_is_declaration_only()) |
| { |
| class_decl_sptr s2 = |
| is_class_type(s->get_definition_of_declaration()); |
| if (s2) |
| s = s2; |
| } |
| return compute_diff(f, s, ctxt); |
| } |
| return diff_sptr(); |
| } |
| |
| /// Try to diff entities that are of distinct kinds. |
| /// |
| /// @param first the first entity to consider for the diff. |
| /// |
| /// @param second the second entity to consider for the diff. |
| /// |
| /// @param ctxt the context of the diff. |
| /// |
| /// @return a non-null diff if a diff object could be built, null |
| /// otherwise. |
| static diff_sptr |
| try_to_diff_distinct_kinds(const type_or_decl_base_sptr first, |
| const type_or_decl_base_sptr second, |
| diff_context_sptr ctxt) |
| {return compute_diff_for_distinct_kinds(first, second, ctxt);} |
| |
| /// Compute the difference between two types. |
| /// |
| /// The function considers every possible types known to libabigail |
| /// and runs the appropriate diff function on them. |
| /// |
| /// Whenever a new kind of type decl is supported by abigail, if we |
| /// want to be able to diff two instances of it, we need to update |
| /// this function to support it. |
| /// |
| /// @param first the first type decl to consider for the diff |
| /// |
| /// @param second the second type decl to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff. It's a pointer to a descendent of |
| /// abigail::comparison::diff. |
| static diff_sptr |
| compute_diff_for_types(const type_or_decl_base_sptr& first, |
| const type_or_decl_base_sptr& second, |
| const diff_context_sptr& ctxt) |
| { |
| type_or_decl_base_sptr f = first; |
| type_or_decl_base_sptr s = second; |
| |
| // Look through no-op qualified types. |
| f = look_through_no_op_qualified_type(is_type(f)); |
| s = look_through_no_op_qualified_type(is_type(s)); |
| |
| diff_sptr d; |
| |
| ((d = try_to_diff<type_decl>(f, s, ctxt)) |
| ||(d = try_to_diff<enum_type_decl>(f, s, ctxt)) |
| ||(d = try_to_diff<union_decl>(f, s,ctxt)) |
| ||(d = try_to_diff<class_decl>(f, s,ctxt)) |
| ||(d = try_to_diff<pointer_type_def>(f, s, ctxt)) |
| ||(d = try_to_diff<reference_type_def>(f, s, ctxt)) |
| ||(d = try_to_diff<array_type_def>(f, s, ctxt)) |
| ||(d = try_to_diff<qualified_type_def>(f, s, ctxt)) |
| ||(d = try_to_diff<typedef_decl>(f, s, ctxt)) |
| ||(d = try_to_diff<function_type>(f, s, ctxt)) |
| ||(d = try_to_diff_distinct_kinds(f, s, ctxt))); |
| |
| ABG_ASSERT(d); |
| |
| return d; |
| } |
| |
| diff_category |
| operator|(diff_category c1, diff_category c2) |
| {return static_cast<diff_category>(static_cast<unsigned>(c1) |
| | static_cast<unsigned>(c2));} |
| |
| diff_category& |
| operator|=(diff_category& c1, diff_category c2) |
| { |
| c1 = c1 | c2; |
| return c1; |
| } |
| |
| diff_category& |
| operator&=(diff_category& c1, diff_category c2) |
| { |
| c1 = c1 & c2; |
| return c1; |
| } |
| |
| diff_category |
| operator^(diff_category c1, diff_category c2) |
| {return static_cast<diff_category>(static_cast<unsigned>(c1) |
| ^ static_cast<unsigned>(c2));} |
| |
| diff_category |
| operator&(diff_category c1, diff_category c2) |
| {return static_cast<diff_category>(static_cast<unsigned>(c1) |
| & static_cast<unsigned>(c2));} |
| |
| diff_category |
| operator~(diff_category c) |
| {return static_cast<diff_category>(~static_cast<unsigned>(c));} |
| |
| |
| /// Getter of a bitmap made of the set of change categories that are |
| /// considered harmless. |
| /// |
| /// @return the bitmap made of the set of change categories that are |
| /// considered harmless. |
| diff_category |
| get_default_harmless_categories_bitmap() |
| { |
| return (abigail::comparison::ACCESS_CHANGE_CATEGORY |
| | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY |
| | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY |
| | abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY |
| | abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY |
| | abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY |
| | abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY |
| | abigail::comparison::HARMLESS_UNION_CHANGE_CATEGORY |
| | abigail::comparison::HARMLESS_DATA_MEMBER_CHANGE_CATEGORY |
| | abigail::comparison::TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY |
| | abigail::comparison::FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY |
| | abigail::comparison::FN_PARM_TYPE_CV_CHANGE_CATEGORY |
| | abigail::comparison::FN_RETURN_TYPE_CV_CHANGE_CATEGORY |
| | abigail::comparison::VAR_TYPE_CV_CHANGE_CATEGORY |
| | abigail::comparison::VOID_PTR_TO_PTR_CHANGE_CATEGORY |
| | abigail::comparison::BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY); |
| } |
| |
| /// Getter of a bitmap made of the set of change categories that are |
| /// considered harmful. |
| /// |
| /// @return the bitmap made of the set of change categories that are |
| /// considered harmful. |
| diff_category |
| get_default_harmful_categories_bitmap() |
| { |
| return (abigail::comparison::SIZE_OR_OFFSET_CHANGE_CATEGORY |
| | abigail::comparison::VIRTUAL_MEMBER_CHANGE_CATEGORY |
| | abigail::comparison::FN_PARM_ADD_REMOVE_CHANGE_CATEGORY); |
| } |
| |
| /// Serialize an instance of @ref diff_category to an output stream. |
| /// |
| /// @param o the output stream to serialize @p c to. |
| /// |
| /// @param c the instance of diff_category to serialize. |
| /// |
| /// @return the output stream to serialize @p c to. |
| ostream& |
| operator<<(ostream& o, diff_category c) |
| { |
| bool emitted_a_category = false; |
| |
| if (c == NO_CHANGE_CATEGORY) |
| { |
| o << "NO_CHANGE_CATEGORY"; |
| emitted_a_category = true; |
| } |
| |
| if (c & ACCESS_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "ACCESS_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & COMPATIBLE_TYPE_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "COMPATIBLE_TYPE_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & HARMLESS_DECL_NAME_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "HARMLESS_DECL_NAME_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & NON_VIRT_MEM_FUN_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "NON_VIRT_MEM_FUN_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & STATIC_DATA_MEMBER_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "STATIC_DATA_MEMBER_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & HARMLESS_ENUM_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "HARMLESS_ENUM_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "HARMLESS_DATA_MEMBER_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & HARMLESS_UNION_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "HARMLESS_UNION_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & SUPPRESSED_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "SUPPRESSED_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & PRIVATE_TYPE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "PRIVATE_TYPE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & SIZE_OR_OFFSET_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "SIZE_OR_OFFSET_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & VIRTUAL_MEMBER_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "VIRTUAL_MEMBER_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & REDUNDANT_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "REDUNDANT_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & FN_PARM_TYPE_CV_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "FN_PARM_TYPE_CV_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & FN_RETURN_TYPE_CV_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "FN_RETURN_TYPE_CV_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & FN_PARM_ADD_REMOVE_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "FN_PARM_ADD_REMOVE_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & VAR_TYPE_CV_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "VAR_TYPE_CV_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & VOID_PTR_TO_PTR_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "VOID_PTR_TO_PTR_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| if (c & BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY) |
| { |
| if (emitted_a_category) |
| o << "|"; |
| o << "BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY"; |
| emitted_a_category |= true; |
| } |
| |
| return o; |
| } |
| |
| /// Compute the difference between two decls. |
| /// |
| /// The function consider every possible decls known to libabigail and |
| /// runs the appropriate diff function on them. |
| /// |
| /// Whenever a new kind of non-type decl is supported by abigail, if |
| /// we want to be able to diff two instances of it, we need to update |
| /// this function to support it. |
| /// |
| /// @param first the first decl to consider for the diff |
| /// |
| /// @param second the second decl to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff. |
| static diff_sptr |
| compute_diff_for_decls(const decl_base_sptr first, |
| const decl_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| |
| diff_sptr d; |
| |
| ((d = try_to_diff<function_decl>(first, second, ctxt)) |
| || (d = try_to_diff<var_decl>(first, second, ctxt)) |
| || (d = try_to_diff_distinct_kinds(first, second, ctxt))); |
| |
| ABG_ASSERT(d); |
| |
| return d; |
| } |
| |
| /// Compute the difference between two decls. The decls can represent |
| /// either type declarations, or non-type declaration. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first decl to consider. |
| /// |
| /// @param second the second decl to consider. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff, or NULL if the diff could not be |
| /// computed. |
| diff_sptr |
| compute_diff(const decl_base_sptr first, |
| const decl_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (!first || !second) |
| return diff_sptr(); |
| |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d; |
| if (is_type(first) && is_type(second)) |
| d = compute_diff_for_types(first, second, ctxt); |
| else |
| d = compute_diff_for_decls(first, second, ctxt); |
| ABG_ASSERT(d); |
| return d; |
| } |
| |
| /// Compute the difference between two types. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first type to consider. |
| /// |
| /// @param second the second type to consider. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff, or NULL if the diff couldn't be |
| /// computed. |
| diff_sptr |
| compute_diff(const type_base_sptr first, |
| const type_base_sptr second, |
| diff_context_sptr ctxt) |
| { |
| decl_base_sptr f = get_type_declaration(first), |
| s = get_type_declaration(second); |
| |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(f,s, ctxt); |
| ABG_ASSERT(d); |
| return d; |
| } |
| |
| /// Get a copy of the pretty representation of a diff node. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return the pretty representation string. |
| string |
| get_pretty_representation(diff* d) |
| { |
| if (!d) |
| return ""; |
| string prefix= "diff of "; |
| return prefix + get_pretty_representation(d->first_subject()); |
| } |
| |
| // <var_diff stuff> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref var_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| var_diff::chain_into_hierarchy() |
| {append_child_node(type_diff());} |
| |
| /// @return the pretty representation for this current instance of |
| /// @ref var_diff. |
| const string& |
| var_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "var_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| /// Constructor for @ref var_diff. |
| /// |
| /// @param first the first instance of @ref var_decl to consider in |
| /// the diff. |
| /// |
| /// @param second the second instance of @ref var_decl to consider in |
| /// the diff. |
| /// |
| /// @param type_diff the diff between types of the instances of |
| /// var_decl. |
| /// |
| /// @param ctxt the diff context to use. |
| var_diff::var_diff(var_decl_sptr first, |
| var_decl_sptr second, |
| diff_sptr type_diff, |
| diff_context_sptr ctxt) |
| : decl_diff_base(first, second, ctxt), |
| priv_(new priv) |
| {priv_->type_diff_ = type_diff;} |
| |
| /// Finish building the current instance of @ref var_diff. |
| void |
| var_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first @ref var_decl of the diff. |
| /// |
| /// @return the first @ref var_decl of the diff. |
| var_decl_sptr |
| var_diff::first_var() const |
| {return dynamic_pointer_cast<var_decl>(first_subject());} |
| |
| /// Getter for the second @ref var_decl of the diff. |
| /// |
| /// @return the second @ref var_decl of the diff. |
| var_decl_sptr |
| var_diff::second_var() const |
| {return dynamic_pointer_cast<var_decl>(second_subject());} |
| |
| /// Getter for the diff of the types of the instances of @ref |
| /// var_decl. |
| /// |
| /// @return the diff of the types of the instances of @ref var_decl. |
| diff_sptr |
| var_diff::type_diff() const |
| { |
| if (diff_sptr result = priv_->type_diff_.lock()) |
| return result; |
| else |
| { |
| result = compute_diff(first_var()->get_type(), |
| second_var()->get_type(), |
| context()); |
| context()->keep_diff_alive(result); |
| priv_->type_diff_ = result; |
| return result; |
| } |
| } |
| |
| /// Return true iff the diff node has a change. |
| /// |
| /// @return true iff the diff node has a change. |
| bool |
| var_diff::has_changes() const |
| {return *first_var() != *second_var();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| var_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_var(), *second_var(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the stream to serialize the diff to. |
| /// |
| /// @param indent the prefix to use for the indentation of this |
| /// serialization. |
| void |
| var_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two instances of @ref var_decl. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first @ref var_decl to consider for the diff. |
| /// |
| /// @param second the second @ref var_decl to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff between the two @ref var_decl. |
| var_diff_sptr |
| compute_diff(const var_decl_sptr first, |
| const var_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| var_diff_sptr d(new var_diff(first, second, diff_sptr(), ctxt)); |
| ctxt->initialize_canonical_diff(d); |
| |
| return d; |
| } |
| |
| // </var_diff stuff> |
| |
| // <pointer_type_def stuff> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref pointer_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| pointer_diff::chain_into_hierarchy() |
| {append_child_node(underlying_type_diff());} |
| |
| /// Constructor for a pointer_diff. |
| /// |
| /// @param first the first pointer to consider for the diff. |
| /// |
| /// @param second the secon pointer to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| pointer_diff::pointer_diff(pointer_type_def_sptr first, |
| pointer_type_def_sptr second, |
| diff_sptr underlying, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(underlying)) |
| {} |
| |
| /// Finish building the current instance of @ref pointer_diff. |
| void |
| pointer_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first subject of a pointer diff |
| /// |
| /// @return the first pointer considered in this pointer diff. |
| const pointer_type_def_sptr |
| pointer_diff::first_pointer() const |
| {return dynamic_pointer_cast<pointer_type_def>(first_subject());} |
| |
| /// Getter for the second subject of a pointer diff |
| /// |
| /// @return the second pointer considered in this pointer diff. |
| const pointer_type_def_sptr |
| pointer_diff::second_pointer() const |
| {return dynamic_pointer_cast<pointer_type_def>(second_subject());} |
| |
| /// @return the pretty represenation for the current instance of @ref |
| /// pointer_diff. |
| const string& |
| pointer_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "pointer_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| pointer_diff::has_changes() const |
| {return first_pointer() != second_pointer();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| pointer_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_pointer(), *second_pointer(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Getter for the diff between the pointed-to types of the pointers |
| /// of this diff. |
| /// |
| /// @return the diff between the pointed-to types. |
| diff_sptr |
| pointer_diff::underlying_type_diff() const |
| {return priv_->underlying_type_diff_;} |
| |
| /// Setter for the diff between the pointed-to types of the pointers |
| /// of this diff. |
| /// |
| /// @param d the new diff between the pointed-to types of the pointers |
| /// of this diff. |
| void |
| pointer_diff::underlying_type_diff(const diff_sptr d) |
| {priv_->underlying_type_diff_ = d;} |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the stream to serialize the diff to. |
| /// |
| /// @param indent the prefix to use for the indentation of this |
| /// serialization. |
| void |
| pointer_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between between two pointers. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the pointer to consider for the diff. |
| /// |
| /// @param second the pointer to consider for the diff. |
| /// |
| /// @return the resulting diff between the two pointers. |
| /// |
| /// @param ctxt the diff context to use. |
| pointer_diff_sptr |
| compute_diff(pointer_type_def_sptr first, |
| pointer_type_def_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(first->get_pointed_to_type(), |
| second->get_pointed_to_type(), |
| ctxt); |
| pointer_diff_sptr result(new pointer_diff(first, second, d, ctxt)); |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| |
| // </pointer_type_def> |
| |
| // <array_type_def> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref array_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| array_diff::chain_into_hierarchy() |
| {append_child_node(element_type_diff());} |
| |
| /// Constructor for array_diff |
| /// |
| /// @param first the first array_type of the diff. |
| /// |
| /// @param second the second array_type of the diff. |
| /// |
| /// @param element_type_diff the diff between the two array element |
| /// types. |
| /// |
| /// @param ctxt the diff context to use. |
| array_diff::array_diff(const array_type_def_sptr first, |
| const array_type_def_sptr second, |
| diff_sptr element_type_diff, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(element_type_diff)) |
| {} |
| |
| /// Finish building the current instance of @ref array_diff. |
| void |
| array_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first array of the diff. |
| /// |
| /// @return the first array of the diff. |
| const array_type_def_sptr |
| array_diff::first_array() const |
| {return dynamic_pointer_cast<array_type_def>(first_subject());} |
| |
| /// Getter for the second array of the diff. |
| /// |
| /// @return for the second array of the diff. |
| const array_type_def_sptr |
| array_diff::second_array() const |
| {return dynamic_pointer_cast<array_type_def>(second_subject());} |
| |
| /// Getter for the diff between the two types of array elements. |
| /// |
| /// @return the diff between the two types of array elements. |
| const diff_sptr& |
| array_diff::element_type_diff() const |
| {return priv_->element_type_diff_;} |
| |
| /// Setter for the diff between the two array element types. |
| /// |
| /// @param d the new diff betweend the two array element types. |
| void |
| array_diff::element_type_diff(diff_sptr d) |
| {priv_->element_type_diff_ = d;} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// array_diff. |
| const string& |
| array_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "array_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| array_diff::has_changes() const |
| { |
| bool l = false; |
| |
| // the array element types match check for differing dimensions |
| // etc... |
| array_type_def_sptr |
| f = dynamic_pointer_cast<array_type_def>(first_subject()), |
| s = dynamic_pointer_cast<array_type_def>(second_subject()); |
| |
| if (f->get_name() != s->get_name()) |
| l |= true; |
| if (f->get_size_in_bits() != s->get_size_in_bits()) |
| l |= true; |
| if (f->get_alignment_in_bits() != s->get_alignment_in_bits()) |
| l |= true; |
| |
| l |= element_type_diff() |
| ? element_type_diff()->has_changes() |
| : false; |
| |
| return l; |
| } |
| |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| array_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_array(), *second_array(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the output stream to serialize the dif to. |
| /// |
| /// @param indent the string to use for indenting the report. |
| void |
| array_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two arrays. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first array to consider for the diff. |
| /// |
| /// @param second the second array to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| array_diff_sptr |
| compute_diff(array_type_def_sptr first, |
| array_type_def_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(first->get_element_type(), |
| second->get_element_type(), |
| ctxt); |
| array_diff_sptr result(new array_diff(first, second, d, ctxt)); |
| ctxt->initialize_canonical_diff(result); |
| return result; |
| } |
| // </array_type_def> |
| |
| // <reference_type_def> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref reference_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| reference_diff::chain_into_hierarchy() |
| {append_child_node(underlying_type_diff());} |
| |
| /// Constructor for reference_diff |
| /// |
| /// @param first the first reference_type of the diff. |
| /// |
| /// @param second the second reference_type of the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| reference_diff::reference_diff(const reference_type_def_sptr first, |
| const reference_type_def_sptr second, |
| diff_sptr underlying, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(underlying)) |
| {} |
| |
| /// Finish building the current instance of @ref reference_diff. |
| void |
| reference_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first reference of the diff. |
| /// |
| /// @return the first reference of the diff. |
| reference_type_def_sptr |
| reference_diff::first_reference() const |
| {return dynamic_pointer_cast<reference_type_def>(first_subject());} |
| |
| /// Getter for the second reference of the diff. |
| /// |
| /// @return for the second reference of the diff. |
| reference_type_def_sptr |
| reference_diff::second_reference() const |
| {return dynamic_pointer_cast<reference_type_def>(second_subject());} |
| |
| |
| /// Getter for the diff between the two referred-to types. |
| /// |
| /// @return the diff between the two referred-to types. |
| const diff_sptr& |
| reference_diff::underlying_type_diff() const |
| {return priv_->underlying_type_diff_;} |
| |
| /// Setter for the diff between the two referred-to types. |
| /// |
| /// @param d the new diff betweend the two referred-to types. |
| diff_sptr& |
| reference_diff::underlying_type_diff(diff_sptr d) |
| { |
| priv_->underlying_type_diff_ = d; |
| return priv_->underlying_type_diff_; |
| } |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// reference_diff. |
| const string& |
| reference_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "reference_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| reference_diff::has_changes() const |
| { |
| return first_reference() != second_reference(); |
| } |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| reference_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_reference(), *second_reference(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the output stream to serialize the dif to. |
| /// |
| /// @param indent the string to use for indenting the report. |
| void |
| reference_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two references. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first reference to consider for the diff. |
| /// |
| /// @param second the second reference to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| reference_diff_sptr |
| compute_diff(reference_type_def_sptr first, |
| reference_type_def_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(first->get_pointed_to_type(), |
| second->get_pointed_to_type(), |
| ctxt); |
| reference_diff_sptr result(new reference_diff(first, second, d, ctxt)); |
| ctxt->initialize_canonical_diff(result); |
| return result; |
| } |
| // </reference_type_def> |
| |
| // <qualified_type_diff stuff> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref qualified_type_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| qualified_type_diff::chain_into_hierarchy() |
| {append_child_node(leaf_underlying_type_diff());} |
| |
| /// Constructor for qualified_type_diff. |
| /// |
| /// @param first the first qualified type of the diff. |
| /// |
| /// @param second the second qualified type of the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| qualified_type_diff::qualified_type_diff(qualified_type_def_sptr first, |
| qualified_type_def_sptr second, |
| diff_sptr under, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(under)) |
| {} |
| |
| /// Finish building the current instance of @ref qualified_type_diff. |
| void |
| qualified_type_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first qualified type of the diff. |
| /// |
| /// @return the first qualified type of the diff. |
| const qualified_type_def_sptr |
| qualified_type_diff::first_qualified_type() const |
| {return dynamic_pointer_cast<qualified_type_def>(first_subject());} |
| |
| /// Getter for the second qualified type of the diff. |
| /// |
| /// @return the second qualified type of the diff. |
| const qualified_type_def_sptr |
| qualified_type_diff::second_qualified_type() const |
| {return dynamic_pointer_cast<qualified_type_def>(second_subject());} |
| |
| /// Getter for the diff between the underlying types of the two |
| /// qualified types. |
| /// |
| /// @return the diff between the underlying types of the two qualified |
| /// types. |
| diff_sptr |
| qualified_type_diff::underlying_type_diff() const |
| {return priv_->underlying_type_diff;} |
| |
| /// Getter for the diff between the most underlying non-qualified |
| /// types of two qualified types. |
| /// |
| /// @return the diff between the most underlying non-qualified types |
| /// of two qualified types. |
| diff_sptr |
| qualified_type_diff::leaf_underlying_type_diff() const |
| { |
| if (!priv_->leaf_underlying_type_diff) |
| priv_->leaf_underlying_type_diff |
| = compute_diff_for_types(get_leaf_type(first_qualified_type()), |
| get_leaf_type(second_qualified_type()), |
| context()); |
| |
| return priv_->leaf_underlying_type_diff; |
| } |
| |
| /// Setter for the diff between the underlying types of the two |
| /// qualified types. |
| /// |
| /// @return the diff between the underlying types of the two qualified |
| /// types. |
| void |
| qualified_type_diff::underlying_type_diff(const diff_sptr d) |
| {priv_->underlying_type_diff = d;} |
| |
| /// @return the pretty representation of the current instance of @ref |
| /// qualified_type_diff. |
| const string& |
| qualified_type_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "qualified_type_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| qualified_type_diff::has_changes() const |
| {return first_qualified_type() != second_qualified_type();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| qualified_type_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_qualified_type(), *second_qualified_type(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the output stream to serialize to. |
| /// |
| /// @param indent the string to use to indent the lines of the report. |
| void |
| qualified_type_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two qualified types. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first qualified type to consider for the diff. |
| /// |
| /// @param second the second qualified type to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| qualified_type_diff_sptr |
| compute_diff(const qualified_type_def_sptr first, |
| const qualified_type_def_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(first->get_underlying_type(), |
| second->get_underlying_type(), |
| ctxt); |
| qualified_type_diff_sptr result(new qualified_type_diff(first, second, |
| d, ctxt)); |
| ctxt->initialize_canonical_diff(result); |
| return result; |
| } |
| |
| // </qualified_type_diff stuff> |
| |
| // <enum_diff stuff> |
| |
| /// Clear the lookup tables useful for reporting an enum_diff. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed from the class_diff::priv. |
| void |
| enum_diff::clear_lookup_tables() |
| { |
| priv_->deleted_enumerators_.clear(); |
| priv_->inserted_enumerators_.clear(); |
| priv_->changed_enumerators_.clear(); |
| } |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// @return true if the lookup tables are empty, false otherwise. |
| bool |
| enum_diff::lookup_tables_empty() const |
| { |
| return (priv_->deleted_enumerators_.empty() |
| && priv_->inserted_enumerators_.empty() |
| && priv_->changed_enumerators_.empty()); |
| } |
| |
| /// If the lookup tables are not yet built, walk the differences and |
| /// fill the lookup tables. |
| void |
| enum_diff::ensure_lookup_tables_populated() |
| { |
| if (!lookup_tables_empty()) |
| return; |
| |
| { |
| edit_script e = priv_->enumerators_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| const enum_type_decl::enumerator& n = |
| first_enum()->get_enumerators()[i]; |
| const string& name = n.get_name(); |
| ABG_ASSERT(priv_->deleted_enumerators_.find(n.get_name()) |
| == priv_->deleted_enumerators_.end()); |
| priv_->deleted_enumerators_[name] = n; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| const enum_type_decl::enumerator& n = |
| second_enum()->get_enumerators()[i]; |
| const string& name = n.get_name(); |
| ABG_ASSERT(priv_->inserted_enumerators_.find(n.get_name()) |
| == priv_->inserted_enumerators_.end()); |
| string_enumerator_map::const_iterator j = |
| priv_->deleted_enumerators_.find(name); |
| if (j == priv_->deleted_enumerators_.end()) |
| priv_->inserted_enumerators_[name] = n; |
| else |
| { |
| if (j->second != n) |
| priv_->changed_enumerators_[j->first] = |
| std::make_pair(j->second, n); |
| priv_->deleted_enumerators_.erase(j); |
| } |
| } |
| } |
| } |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref enum_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| enum_diff::chain_into_hierarchy() |
| {append_child_node(underlying_type_diff());} |
| |
| /// Constructor for enum_diff. |
| /// |
| /// @param first the first enum type of the diff. |
| /// |
| /// @param second the second enum type of the diff. |
| /// |
| /// @param underlying_type_diff the diff of the two underlying types |
| /// of the two enum types. |
| /// |
| /// @param ctxt the diff context to use. |
| enum_diff::enum_diff(const enum_type_decl_sptr first, |
| const enum_type_decl_sptr second, |
| const diff_sptr underlying_type_diff, |
| const diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(underlying_type_diff)) |
| {} |
| |
| /// Finish building the current instance of @ref enum_diff. |
| void |
| enum_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// @return the first enum of the diff. |
| const enum_type_decl_sptr |
| enum_diff::first_enum() const |
| {return dynamic_pointer_cast<enum_type_decl>(first_subject());} |
| |
| /// @return the second enum of the diff. |
| const enum_type_decl_sptr |
| enum_diff::second_enum() const |
| {return dynamic_pointer_cast<enum_type_decl>(second_subject());} |
| |
| /// @return the diff of the two underlying enum types. |
| diff_sptr |
| enum_diff::underlying_type_diff() const |
| {return priv_->underlying_type_diff_;} |
| |
| /// @return a map of the enumerators that were deleted. |
| const string_enumerator_map& |
| enum_diff::deleted_enumerators() const |
| {return priv_->deleted_enumerators_;} |
| |
| /// @return a map of the enumerators that were inserted |
| const string_enumerator_map& |
| enum_diff::inserted_enumerators() const |
| {return priv_->inserted_enumerators_;} |
| |
| /// @return a map of the enumerators that were changed |
| const string_changed_enumerator_map& |
| enum_diff::changed_enumerators() const |
| {return priv_->changed_enumerators_;} |
| |
| /// @return the pretty representation of the current instance of @ref |
| /// enum_diff. |
| const string& |
| enum_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "enum_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| enum_diff::has_changes() const |
| {return first_enum() != second_enum();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| enum_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_enum(), *second_enum(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the differences between the two enums. |
| /// |
| /// @param out the output stream to send the report to. |
| /// |
| /// @param indent the string to use for indentation. |
| void |
| enum_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the set of changes between two instances of @ref |
| /// enum_type_decl. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first a pointer to the first enum_type_decl to consider. |
| /// |
| /// @param second a pointer to the second enum_type_decl to consider. |
| /// |
| /// @return the resulting diff of the two enums @p first and @p |
| /// second. |
| /// |
| /// @param ctxt the diff context to use. |
| enum_diff_sptr |
| compute_diff(const enum_type_decl_sptr first, |
| const enum_type_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr ud = compute_diff_for_types(first->get_underlying_type(), |
| second->get_underlying_type(), |
| ctxt); |
| enum_diff_sptr d(new enum_diff(first, second, ud, ctxt)); |
| |
| compute_diff(first->get_enumerators().begin(), |
| first->get_enumerators().end(), |
| second->get_enumerators().begin(), |
| second->get_enumerators().end(), |
| d->priv_->enumerators_changes_); |
| |
| d->ensure_lookup_tables_populated(); |
| |
| ctxt->initialize_canonical_diff(d); |
| |
| return d; |
| } |
| // </enum_diff stuff> |
| |
| // <class_or_union_diff stuff> |
| |
| /// Test if the current diff node carries a member type change for a |
| /// member type which name is the same as the name of a given type |
| /// declaration. |
| /// |
| /// @param d the type declaration which name should be equal to the |
| /// name of the member type that might have changed. |
| /// |
| /// @return the member type that has changed, iff there were a member |
| /// type (which name is the same as the name of @p d) that changed. |
| /// Note that the member type that is returned is the new value of the |
| /// member type that changed. |
| type_or_decl_base_sptr |
| class_or_union_diff::priv::member_type_has_changed(decl_base_sptr d) const |
| { |
| string qname = d->get_qualified_name(); |
| string_diff_sptr_map::const_iterator it = |
| changed_member_types_.find(qname); |
| |
| return ((it == changed_member_types_.end()) |
| ? type_or_decl_base_sptr() |
| : it->second->second_subject()); |
| } |
| |
| /// Test if the current diff node carries a data member change for a |
| /// data member which name is the same as the name of a given type |
| /// declaration. |
| /// |
| /// @param d the type declaration which name should be equal to the |
| /// name of the data member that might have changed. |
| /// |
| /// @return the data member that has changed, iff there were a data |
| /// member type (which name is the same as the name of @p d) that |
| /// changed. Note that the data member that is returned is the new |
| /// value of the data member that changed. |
| decl_base_sptr |
| class_or_union_diff::priv::subtype_changed_dm(decl_base_sptr d) const |
| { |
| string qname = d->get_qualified_name(); |
| string_var_diff_sptr_map::const_iterator it = |
| subtype_changed_dm_.find(qname); |
| |
| if (it == subtype_changed_dm_.end()) |
| return decl_base_sptr(); |
| return it->second->second_var(); |
| } |
| |
| /// Test if the current diff node carries a member class template |
| /// change for a member class template which name is the same as the |
| /// name of a given type declaration. |
| /// |
| /// @param d the type declaration which name should be equal to the |
| /// name of the member class template that might have changed. |
| /// |
| /// @return the member class template that has changed, iff there were |
| /// a member class template (which name is the same as the name of @p |
| /// d) that changed. Note that the member class template that is |
| /// returned is the new value of the member class template that |
| /// changed. |
| decl_base_sptr |
| class_or_union_diff::priv::member_class_tmpl_has_changed(decl_base_sptr d) const |
| { |
| string qname = d->get_qualified_name(); |
| string_diff_sptr_map::const_iterator it = |
| changed_member_class_tmpls_.find(qname); |
| |
| return ((it == changed_member_class_tmpls_.end()) |
| ? decl_base_sptr() |
| : dynamic_pointer_cast<decl_base>(it->second->second_subject())); |
| } |
| |
| /// Get the number of non static data members that were deleted. |
| /// |
| /// @return the number of non static data members that were deleted. |
| size_t |
| class_or_union_diff::priv::get_deleted_non_static_data_members_number() const |
| { |
| size_t result = 0; |
| |
| for (string_decl_base_sptr_map::const_iterator i = |
| deleted_data_members_.begin(); |
| i != deleted_data_members_.end(); |
| ++i) |
| if (is_member_decl(i->second) |
| && !get_member_is_static(i->second)) |
| ++result; |
| |
| return result; |
| } |
| |
| /// Get the number of non static data members that were inserted. |
| /// |
| /// @return the number of non static data members that were inserted. |
| size_t |
| class_or_union_diff::priv::get_inserted_non_static_data_members_number() const |
| { |
| size_t result = 0; |
| |
| for (string_decl_base_sptr_map::const_iterator i = |
| inserted_data_members_.begin(); |
| i != inserted_data_members_.end(); |
| ++i) |
| if (is_member_decl(i->second) |
| && !get_member_is_static(i->second)) |
| ++result; |
| |
| return result; |
| } |
| |
| /// Get the number of data member sub-type changes carried by the |
| /// current diff node that were filtered out. |
| /// |
| /// @param local_only if true, it means that only (filtered) local |
| /// changes are considered. |
| /// |
| /// @return the number of data member sub-type changes carried by the |
| /// current diff node that were filtered out. |
| size_t |
| class_or_union_diff::priv::count_filtered_subtype_changed_dm(bool local_only) |
| { |
| size_t num_filtered= 0; |
| for (var_diff_sptrs_type::const_iterator i = |
| sorted_subtype_changed_dm_.begin(); |
| i != sorted_subtype_changed_dm_.end(); |
| ++i) |
| { |
| if (local_only) |
| { |
| if ((*i)->has_changes() |
| && !(*i)->has_local_changes_to_be_reported()) |
| ++num_filtered; |
| } |
| else |
| { |
| if ((*i)->is_filtered_out()) |
| ++num_filtered; |
| } |
| } |
| return num_filtered; |
| } |
| |
| /// Get the number of data member changes carried by the current diff |
| /// node that were filtered out. |
| /// |
| /// @param local_only if true, it means that only (filtered) local |
| /// changes are considered. |
| /// |
| /// @return the number of data member changes carried by the current |
| /// diff node that were filtered out. |
| size_t |
| class_or_union_diff::priv::count_filtered_changed_dm(bool local_only) |
| { |
| size_t num_filtered= 0; |
| |
| for (unsigned_var_diff_sptr_map::const_iterator i = changed_dm_.begin(); |
| i != changed_dm_.end(); |
| ++i) |
| { |
| diff_sptr diff = i->second; |
| if (local_only) |
| { |
| if ((diff->has_changes() && !diff->has_local_changes_to_be_reported()) |
| || diff->is_filtered_out()) |
| ++num_filtered; |
| } |
| else |
| { |
| if (diff->is_filtered_out()) |
| ++num_filtered; |
| } |
| } |
| return num_filtered; |
| } |
| |
| /// Skip the processing of the current member function if its |
| /// virtual-ness is disallowed by the user. |
| /// |
| /// This is to be used in the member functions below that are used to |
| /// count the number of filtered inserted, deleted and changed member |
| /// functions. |
| #define SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED \ |
| do { \ |
| if (get_member_function_is_virtual(f) \ |
| || get_member_function_is_virtual(s)) \ |
| { \ |
| if (!(allowed_category | VIRTUAL_MEMBER_CHANGE_CATEGORY)) \ |
| continue; \ |
| } \ |
| else \ |
| { \ |
| if (!(allowed_category | NON_VIRT_MEM_FUN_CHANGE_CATEGORY)) \ |
| continue; \ |
| } \ |
| } while (false) |
| |
| /// Get the number of member functions changes carried by the current |
| /// diff node that were filtered out. |
| /// |
| /// @return the number of member functions changes carried by the |
| /// current diff node that were filtered out. |
| size_t |
| class_or_union_diff::priv::count_filtered_changed_mem_fns |
| (const diff_context_sptr& ctxt) |
| { |
| size_t count = 0; |
| diff_category allowed_category = ctxt->get_allowed_category(); |
| |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| sorted_changed_member_functions_.begin(); |
| i != sorted_changed_member_functions_.end(); |
| ++i) |
| { |
| method_decl_sptr f = |
| dynamic_pointer_cast<method_decl> |
| ((*i)->first_function_decl()); |
| ABG_ASSERT(f); |
| |
| method_decl_sptr s = |
| dynamic_pointer_cast<method_decl> |
| ((*i)->second_function_decl()); |
| ABG_ASSERT(s); |
| |
| SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED; |
| |
| diff_sptr diff = *i; |
| ctxt->maybe_apply_filters(diff); |
| |
| if (diff->is_filtered_out()) |
| ++count; |
| } |
| |
| return count; |
| } |
| |
| /// Get the number of member functions insertions carried by the current |
| /// diff node that were filtered out. |
| /// |
| /// @return the number of member functions insertions carried by the |
| /// current diff node that were filtered out. |
| size_t |
| class_or_union_diff::priv::count_filtered_inserted_mem_fns |
| (const diff_context_sptr& ctxt) |
| { |
| size_t count = 0; |
| diff_category allowed_category = ctxt->get_allowed_category(); |
| |
| for (string_member_function_sptr_map::const_iterator i = |
| inserted_member_functions_.begin(); |
| i != inserted_member_functions_.end(); |
| ++i) |
| { |
| method_decl_sptr f = i->second, |
| s = i->second; |
| |
| SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED; |
| |
| diff_sptr diff = compute_diff_for_decls(f, s, ctxt); |
| ctxt->maybe_apply_filters(diff); |
| |
| if (diff->get_category() != NO_CHANGE_CATEGORY |
| && diff->is_filtered_out()) |
| ++count; |
| } |
| |
| return count; |
| } |
| |
| /// Get the number of member functions deletions carried by the current |
| /// diff node that were filtered out. |
| /// |
| /// @return the number of member functions deletions carried by the |
| /// current diff node that were filtered out. |
| size_t |
| class_or_union_diff::priv::count_filtered_deleted_mem_fns |
| (const diff_context_sptr& ctxt) |
| { |
| size_t count = 0; |
| diff_category allowed_category = ctxt->get_allowed_category(); |
| |
| for (string_member_function_sptr_map::const_iterator i = |
| deleted_member_functions_.begin(); |
| i != deleted_member_functions_.end(); |
| ++i) |
| { |
| method_decl_sptr f = i->second, |
| s = i->second; |
| |
| SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED; |
| |
| diff_sptr diff = compute_diff_for_decls(f, s, ctxt); |
| ctxt->maybe_apply_filters(diff); |
| |
| if (diff->get_category() != NO_CHANGE_CATEGORY |
| && diff->is_filtered_out()) |
| ++count; |
| } |
| |
| return count; |
| } |
| |
| /// Clear the lookup tables useful for reporting. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed from the class_or_union_diff::priv. |
| void |
| class_or_union_diff::clear_lookup_tables() |
| { |
| priv_->deleted_member_types_.clear(); |
| priv_->inserted_member_types_.clear(); |
| priv_->changed_member_types_.clear(); |
| priv_->deleted_data_members_.clear(); |
| priv_->inserted_data_members_.clear(); |
| priv_->subtype_changed_dm_.clear(); |
| priv_->deleted_member_functions_.clear(); |
| priv_->inserted_member_functions_.clear(); |
| priv_->changed_member_functions_.clear(); |
| priv_->deleted_member_class_tmpls_.clear(); |
| priv_->inserted_member_class_tmpls_.clear(); |
| priv_->changed_member_class_tmpls_.clear(); |
| } |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// @return true if the lookup tables are empty, false otherwise. |
| bool |
| class_or_union_diff::lookup_tables_empty(void) const |
| { |
| return (priv_->deleted_member_types_.empty() |
| && priv_->inserted_member_types_.empty() |
| && priv_->changed_member_types_.empty() |
| && priv_->deleted_data_members_.empty() |
| && priv_->inserted_data_members_.empty() |
| && priv_->subtype_changed_dm_.empty() |
| && priv_->inserted_member_functions_.empty() |
| && priv_->deleted_member_functions_.empty() |
| && priv_->changed_member_functions_.empty() |
| && priv_->deleted_member_class_tmpls_.empty() |
| && priv_->inserted_member_class_tmpls_.empty() |
| && priv_->changed_member_class_tmpls_.empty()); |
| } |
| |
| /// If the lookup tables are not yet built, walk the differences and |
| /// fill them. |
| void |
| class_or_union_diff::ensure_lookup_tables_populated(void) const |
| { |
| { |
| edit_script& e = priv_->member_types_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| decl_base_sptr d = |
| get_type_declaration(first_class_or_union()->get_member_types()[i]); |
| class_or_union_sptr record_type = is_class_or_union_type(d); |
| if (record_type && record_type->get_is_declaration_only()) |
| continue; |
| string name = d->get_name(); |
| priv_->deleted_member_types_[name] = d; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| decl_base_sptr d = |
| get_type_declaration(second_class_or_union()->get_member_types()[i]); |
| class_or_union_sptr record_type = is_class_or_union_type(d); |
| if (record_type && record_type->get_is_declaration_only()) |
| continue; |
| string name = d->get_name(); |
| string_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_member_types_.find(name); |
| if (j != priv_->deleted_member_types_.end()) |
| { |
| if (*j->second != *d) |
| priv_->changed_member_types_[name] = |
| compute_diff(j->second, d, context()); |
| |
| priv_->deleted_member_types_.erase(j); |
| } |
| else |
| priv_->inserted_member_types_[name] = d; |
| } |
| } |
| } |
| |
| { |
| edit_script& e = priv_->data_members_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| var_decl_sptr data_member = |
| is_var_decl(first_class_or_union()->get_non_static_data_members()[i]); |
| string name = data_member->get_anon_dm_reliable_name(); |
| |
| ABG_ASSERT(priv_->deleted_data_members_.find(name) |
| == priv_->deleted_data_members_.end()); |
| priv_->deleted_data_members_[name] = data_member; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| decl_base_sptr d = |
| second_class_or_union()->get_non_static_data_members()[i]; |
| var_decl_sptr added_dm = is_var_decl(d); |
| string name = added_dm->get_anon_dm_reliable_name(); |
| ABG_ASSERT(priv_->inserted_data_members_.find(name) |
| == priv_->inserted_data_members_.end()); |
| |
| bool ignore_added_anonymous_data_member = false; |
| if (is_anonymous_data_member(added_dm)) |
| { |
| // |
| // Handle insertion of anonymous data member to |
| // replace existing data members. |
| // |
| // For instance consider this: |
| // struct S |
| // { |
| // int a; |
| // int b; |
| // int c; |
| // };// end struct S |
| // |
| // Where the data members 'a' and 'b' are replaced |
| // by an anonymous data member without changing the |
| // effective bit layout of the structure: |
| // |
| // struct S |
| // { |
| // struct |
| // { |
| // union |
| // { |
| // int a; |
| // char a_1; |
| // }; |
| // union |
| // { |
| // int b; |
| // char b_1; |
| // }; |
| // }; |
| // int c; |
| // }; // end struct S |
| // |
| var_decl_sptr replaced_dm, replacing_dm; |
| bool added_anon_dm_changes_dm = false; |
| // The vector of data members replaced by anonymous |
| // data members. |
| vector<var_decl_sptr> dms_replaced_by_anon_dm; |
| |
| // |
| // Let's start collecting the set of data members |
| // which have been replaced by anonymous types in a |
| // harmless way. These are going to be collected into |
| // dms_replaced_by_anon_dm and, ultimately, into |
| // priv_->dms_replaced_by_adms_ |
| // |
| for (string_decl_base_sptr_map::const_iterator it = |
| priv_->deleted_data_members_.begin(); |
| it != priv_->deleted_data_members_.end(); |
| ++it) |
| { |
| // We don't support this pattern for anonymous |
| // data members themselves being replaced. If |
| // that occurs then we'll just report it verbatim. |
| if (is_anonymous_data_member(it->second)) |
| continue; |
| |
| string deleted_dm_name = it->second->get_name(); |
| if ((replacing_dm = |
| find_data_member_from_anonymous_data_member(added_dm, |
| deleted_dm_name))) |
| { |
| // So it looks like replacing_dm might have |
| // replaced the data member which name is |
| // 'deleted_dm_name'. Let's look deeper to be |
| // sure. |
| // |
| // Note that replacing_dm is part (member) of |
| // an anonymous data member that might replace |
| // replaced_dm. |
| |
| // So let's get that replaced data member. |
| replaced_dm = is_var_decl(it->second); |
| size_t replaced_dm_offset = |
| get_data_member_offset(replaced_dm), |
| replacing_dm_offset = |
| get_absolute_data_member_offset(replacing_dm); |
| |
| if (replaced_dm_offset != replacing_dm_offset) |
| { |
| // So the replacing data member and the |
| // replaced data member don't have the |
| // same offset. This is not the pattern we |
| // are looking for. Rather, it looks like |
| // the anonymous data member has *changed* |
| // the data member. |
| added_anon_dm_changes_dm = true; |
| break; |
| } |
| |
| if (replaced_dm->get_type()->get_size_in_bits() |
| == replaced_dm->get_type()->get_size_in_bits()) |
| dms_replaced_by_anon_dm.push_back(replaced_dm); |
| else |
| { |
| added_anon_dm_changes_dm = true; |
| break; |
| } |
| } |
| } |
| |
| // Now walk dms_replaced_by_anon_dm to fill up |
| // priv_->dms_replaced_by_adms_ with the set of data |
| // members replaced by anonymous data members. |
| if (!added_anon_dm_changes_dm |
| && !dms_replaced_by_anon_dm.empty()) |
| { |
| // See if the added data member isn't too big. |
| type_base_sptr added_dm_type = added_dm->get_type(); |
| ABG_ASSERT(added_dm_type); |
| var_decl_sptr new_next_dm = |
| get_next_data_member(second_class_or_union(), |
| added_dm); |
| var_decl_sptr old_next_dm = |
| first_class_or_union()->find_data_member(new_next_dm); |
| |
| if (!old_next_dm |
| || (old_next_dm |
| && (get_absolute_data_member_offset(old_next_dm) |
| == get_absolute_data_member_offset(new_next_dm)))) |
| { |
| // None of the data members that are replaced |
| // by the added union should be considered as |
| // having been deleted. |
| ignore_added_anonymous_data_member = true; |
| for (vector<var_decl_sptr>::const_iterator i = |
| dms_replaced_by_anon_dm.begin(); |
| i != dms_replaced_by_anon_dm.end(); |
| ++i) |
| { |
| string n = (*i)->get_name(); |
| priv_->dms_replaced_by_adms_[n] = |
| added_dm; |
| priv_->deleted_data_members_.erase(n); |
| } |
| } |
| } |
| } |
| |
| if (!ignore_added_anonymous_data_member) |
| { |
| // Detect changed data members. |
| // |
| // A changed data member (that we shall name D) is a data |
| // member that satisfies the conditions below: |
| // |
| // 1/ It must have been added. |
| // |
| // 2/ It must have been deleted as well. |
| // |
| // 3/ It there must be a non-empty difference between the |
| // deleted D and the added D. |
| string_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_data_members_.find(name); |
| if (j != priv_->deleted_data_members_.end()) |
| { |
| if (*j->second != *d) |
| { |
| var_decl_sptr old_dm = is_var_decl(j->second); |
| priv_->subtype_changed_dm_[name]= |
| compute_diff(old_dm, added_dm, context()); |
| } |
| priv_->deleted_data_members_.erase(j); |
| } |
| else |
| priv_->inserted_data_members_[name] = d; |
| } |
| } |
| } |
| |
| // Now detect when a data member is deleted from offset N and |
| // another one is added to offset N. In that case, we want to be |
| // able to say that the data member at offset N changed. |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->deleted_data_members_.begin(); |
| i != priv_->deleted_data_members_.end(); |
| ++i) |
| { |
| unsigned offset = get_data_member_offset(i->second); |
| priv_->deleted_dm_by_offset_[offset] = i->second; |
| } |
| |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->inserted_data_members_.begin(); |
| i != priv_->inserted_data_members_.end(); |
| ++i) |
| { |
| unsigned offset = get_data_member_offset(i->second); |
| priv_->inserted_dm_by_offset_[offset] = i->second; |
| } |
| |
| for (unsigned_decl_base_sptr_map::const_iterator i = |
| priv_->inserted_dm_by_offset_.begin(); |
| i != priv_->inserted_dm_by_offset_.end(); |
| ++i) |
| { |
| unsigned_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_dm_by_offset_.find(i->first); |
| if (j != priv_->deleted_dm_by_offset_.end()) |
| { |
| var_decl_sptr old_dm = is_var_decl(j->second); |
| var_decl_sptr new_dm = is_var_decl(i->second); |
| priv_->changed_dm_[i->first] = |
| compute_diff(old_dm, new_dm, context()); |
| } |
| } |
| |
| for (unsigned_var_diff_sptr_map::const_iterator i = |
| priv_->changed_dm_.begin(); |
| i != priv_->changed_dm_.end(); |
| ++i) |
| { |
| priv_->deleted_dm_by_offset_.erase(i->first); |
| priv_->inserted_dm_by_offset_.erase(i->first); |
| priv_->deleted_data_members_.erase |
| (i->second->first_var()->get_anon_dm_reliable_name()); |
| priv_->inserted_data_members_.erase |
| (i->second->second_var()->get_anon_dm_reliable_name()); |
| } |
| } |
| sort_string_data_member_diff_sptr_map(priv_->subtype_changed_dm_, |
| priv_->sorted_subtype_changed_dm_); |
| sort_unsigned_data_member_diff_sptr_map(priv_->changed_dm_, |
| priv_->sorted_changed_dm_); |
| |
| { |
| edit_script& e = priv_->member_class_tmpls_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| decl_base_sptr d = |
| first_class_or_union()->get_member_class_templates()[i]-> |
| as_class_tdecl(); |
| string name = d->get_name(); |
| ABG_ASSERT(priv_->deleted_member_class_tmpls_.find(name) |
| == priv_->deleted_member_class_tmpls_.end()); |
| priv_->deleted_member_class_tmpls_[name] = d; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| decl_base_sptr d = |
| second_class_or_union()->get_member_class_templates()[i]-> |
| as_class_tdecl(); |
| string name = d->get_name(); |
| ABG_ASSERT(priv_->inserted_member_class_tmpls_.find(name) |
| == priv_->inserted_member_class_tmpls_.end()); |
| string_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_member_class_tmpls_.find(name); |
| if (j != priv_->deleted_member_class_tmpls_.end()) |
| { |
| if (*j->second != *d) |
| priv_->changed_member_types_[name]= |
| compute_diff(j->second, d, context()); |
| priv_->deleted_member_class_tmpls_.erase(j); |
| } |
| else |
| priv_->inserted_member_class_tmpls_[name] = d; |
| } |
| } |
| } |
| sort_string_diff_sptr_map(priv_->changed_member_types_, |
| priv_->sorted_changed_member_types_); |
| } |
| |
| /// Allocate the memory for the priv_ pimpl data member of the @ref |
| /// class_or_union_diff class. |
| void |
| class_or_union_diff::allocate_priv_data() |
| { |
| if (!priv_) |
| priv_.reset(new priv); |
| } |
| |
| /// Constructor for the @ref class_or_union_diff class. |
| /// |
| /// @param first_scope the first @ref class_or_union of the diff node. |
| /// |
| /// @param second_scope the second @ref class_or_union of the diff node. |
| /// |
| /// @param ctxt the context of the diff. |
| class_or_union_diff::class_or_union_diff(class_or_union_sptr first_scope, |
| class_or_union_sptr second_scope, |
| diff_context_sptr ctxt) |
| : type_diff_base(first_scope, second_scope, ctxt) |
| //priv_(new priv) |
| {} |
| |
| /// Finish building the current instance of @ref class_or_union_diff. |
| void |
| class_or_union_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter of the private data of the @ref class_or_union_diff type. |
| /// |
| /// Note that due to an optimization, the private data of @ref |
| /// class_or_union_diff can be shared among several instances of |
| /// class_or_union_diff, so you should never try to access |
| /// class_or_union_diff::priv directly. |
| /// |
| /// When class_or_union_diff::priv is shared, this function returns |
| /// the correct shared one. |
| /// |
| /// @return the (possibly) shared private data of the current instance |
| /// of @ref class_or_union_diff. |
| const class_or_union_diff::priv_ptr& |
| class_or_union_diff::get_priv() const |
| { |
| if (priv_) |
| return priv_; |
| |
| // If the current class_or_union_diff::priv member is empty, then look for |
| // the shared one, from the canonical type. |
| class_or_union_diff *canonical = |
| dynamic_cast<class_or_union_diff*>(get_canonical_diff()); |
| ABG_ASSERT(canonical); |
| ABG_ASSERT(canonical->priv_); |
| |
| return canonical->priv_; |
| } |
| |
| /// Destructor of class_or_union_diff. |
| class_or_union_diff::~class_or_union_diff() |
| { |
| } |
| |
| /// @return the first @ref class_or_union involved in the diff. |
| class_or_union_sptr |
| class_or_union_diff::first_class_or_union() const |
| {return is_class_or_union_type(first_subject());} |
| |
| /// @return the second @ref class_or_union involved in the diff. |
| class_or_union_sptr |
| class_or_union_diff::second_class_or_union() const |
| {return is_class_or_union_type(second_subject());} |
| |
| /// @return the edit script of the member types of the two @ref |
| /// class_or_union. |
| const edit_script& |
| class_or_union_diff::member_types_changes() const |
| {return get_priv()->member_types_changes_;} |
| |
| /// @return the edit script of the member types of the two @ref |
| /// class_or_union. |
| edit_script& |
| class_or_union_diff::member_types_changes() |
| {return get_priv()->member_types_changes_;} |
| |
| /// @return the edit script of the data members of the two @ref |
| /// class_or_union. |
| const edit_script& |
| class_or_union_diff::data_members_changes() const |
| {return get_priv()->data_members_changes_;} |
| |
| /// @return the edit script of the data members of the two @ref |
| /// class_or_union. |
| edit_script& |
| class_or_union_diff::data_members_changes() |
| {return get_priv()->data_members_changes_;} |
| |
| /// Getter for the data members that got inserted. |
| /// |
| /// @return a map of data members that got inserted. |
| const string_decl_base_sptr_map& |
| class_or_union_diff::inserted_data_members() const |
| {return get_priv()->inserted_data_members_;} |
| |
| /// Getter for the data members that got deleted. |
| /// |
| /// @return a map of data members that got deleted. |
| const string_decl_base_sptr_map& |
| class_or_union_diff::deleted_data_members() const |
| {return get_priv()->deleted_data_members_;} |
| |
| /// @return the edit script of the member functions of the two @ref |
| /// class_or_union. |
| const edit_script& |
| class_or_union_diff::member_fns_changes() const |
| {return get_priv()->member_fns_changes_;} |
| |
| /// Getter for the virtual members functions that have had a change in |
| /// a sub-type, without having a change in their symbol name. |
| /// |
| /// @return a sorted vector of virtual member functions that have a |
| /// sub-type change. |
| const function_decl_diff_sptrs_type& |
| class_or_union_diff::changed_member_fns() const |
| {return get_priv()->sorted_changed_member_functions_;} |
| |
| /// @return the edit script of the member functions of the two |
| /// classes. |
| edit_script& |
| class_or_union_diff::member_fns_changes() |
| {return get_priv()->member_fns_changes_;} |
| |
| /// @return a map of member functions that got deleted. |
| const string_member_function_sptr_map& |
| class_or_union_diff::deleted_member_fns() const |
| {return get_priv()->deleted_member_functions_;} |
| |
| /// @return a map of member functions that got inserted. |
| const string_member_function_sptr_map& |
| class_or_union_diff::inserted_member_fns() const |
| {return get_priv()->inserted_member_functions_;} |
| |
| /// Getter of the sorted vector of data members that got replaced by |
| /// another data member. |
| /// |
| /// @return sorted vector of changed data member. |
| const var_diff_sptrs_type& |
| class_or_union_diff::sorted_changed_data_members() const |
| {return get_priv()->sorted_changed_dm_;} |
| |
| /// Count the number of /filtered/ data members that got replaced by |
| /// another data member. |
| /// |
| /// @return the number of changed data member that got filtered out. |
| size_t |
| class_or_union_diff::count_filtered_changed_data_members(bool local) const |
| {return get_priv()->count_filtered_changed_dm(local);} |
| |
| /// Getter of the sorted vector of data members with a (sub-)type change. |
| /// |
| /// @return sorted vector of changed data member. |
| const var_diff_sptrs_type& |
| class_or_union_diff::sorted_subtype_changed_data_members() const |
| {return get_priv()->sorted_subtype_changed_dm_;} |
| |
| /// Count the number of /filtered/ data members with a sub-type change. |
| /// |
| /// @return the number of changed data member that got filtered out. |
| size_t |
| class_or_union_diff::count_filtered_subtype_changed_data_members(bool local) const |
| {return get_priv()->count_filtered_subtype_changed_dm(local);} |
| |
| /// Get the map of data members that got replaced by anonymous data |
| /// members. |
| /// |
| /// The key of a map entry is the name of the replaced data member and |
| /// the value is the anonymous data member that replaces it. |
| /// |
| /// @return the map of data members replaced by anonymous data |
| /// members. |
| const string_decl_base_sptr_map& |
| class_or_union_diff::data_members_replaced_by_adms() const |
| {return get_priv()->dms_replaced_by_adms_;} |
| |
| /// Get an ordered vector of of data members that got replaced by |
| /// anonymous data members. |
| /// |
| /// This returns a vector of pair of two data members: the one that |
| /// was replaced, and the anonymous data member that replaced it. |
| /// |
| /// @return the sorted vector data members replaced by anonymous data members. |
| const changed_var_sptrs_type& |
| class_or_union_diff::ordered_data_members_replaced_by_adms() const |
| { |
| if (priv_->dms_replaced_by_adms_ordered_.empty()) |
| { |
| for (string_decl_base_sptr_map::const_iterator it = |
| priv_->dms_replaced_by_adms_.begin(); |
| it != priv_->dms_replaced_by_adms_.end(); |
| ++it) |
| { |
| const var_decl_sptr dm = |
| first_class_or_union()->find_data_member(it->first); |
| ABG_ASSERT(dm); |
| changed_var_sptr changed_dm(dm, is_data_member(it->second)); |
| priv_->dms_replaced_by_adms_ordered_.push_back(changed_dm); |
| } |
| sort_changed_data_members(priv_->dms_replaced_by_adms_ordered_); |
| } |
| |
| return priv_->dms_replaced_by_adms_ordered_; |
| } |
| |
| /// @return the edit script of the member function templates of the two |
| /// @ref class_or_union. |
| const edit_script& |
| class_or_union_diff::member_fn_tmpls_changes() const |
| {return get_priv()->member_fn_tmpls_changes_;} |
| |
| /// @return the edit script of the member function templates of the |
| /// two @ref class_or_union. |
| edit_script& |
| class_or_union_diff::member_fn_tmpls_changes() |
| {return get_priv()->member_fn_tmpls_changes_;} |
| |
| /// @return the edit script of the member class templates of the two |
| /// @ref class_or_union. |
| const edit_script& |
| class_or_union_diff::member_class_tmpls_changes() const |
| {return get_priv()->member_class_tmpls_changes_;} |
| |
| /// @return the edit script of the member class templates of the two |
| /// @ref class_or_union. |
| edit_script& |
| class_or_union_diff::member_class_tmpls_changes() |
| {return get_priv()->member_class_tmpls_changes_;} |
| |
| /// Test if the current diff node carries a change. |
| bool |
| class_or_union_diff::has_changes() const |
| {return first_class_or_union() != second_class_or_union();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| class_or_union_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_class_or_union(), *second_class_or_union(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| |
| /// Report the changes carried by the current @ref class_or_union_diff |
| /// node in a textual format. |
| /// |
| /// @param out the output stream to write the textual report to. |
| /// |
| /// @param indent the number of white space to use as indentation. |
| void |
| class_or_union_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref class_or_union_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| class_or_union_diff::chain_into_hierarchy() |
| { |
| // data member changes |
| for (var_diff_sptrs_type::const_iterator i = |
| get_priv()->sorted_subtype_changed_dm_.begin(); |
| i != get_priv()->sorted_subtype_changed_dm_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| |
| for (var_diff_sptrs_type::const_iterator i = |
| get_priv()->sorted_changed_dm_.begin(); |
| i != get_priv()->sorted_changed_dm_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| |
| // member types changes |
| for (diff_sptrs_type::const_iterator i = |
| get_priv()->sorted_changed_member_types_.begin(); |
| i != get_priv()->sorted_changed_member_types_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| |
| // member function changes |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| get_priv()->sorted_changed_member_functions_.begin(); |
| i != get_priv()->sorted_changed_member_functions_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| } |
| |
| // </class_or_union_diff stuff> |
| |
| //<class_diff stuff> |
| |
| /// Clear the lookup tables useful for reporting. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed from the class_diff::priv. |
| void |
| class_diff::clear_lookup_tables(void) |
| { |
| priv_->deleted_bases_.clear(); |
| priv_->inserted_bases_.clear(); |
| priv_->changed_bases_.clear(); |
| } |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// @return true if the lookup tables are empty, false otherwise. |
| bool |
| class_diff::lookup_tables_empty(void) const |
| { |
| return (priv_->deleted_bases_.empty() |
| && priv_->inserted_bases_.empty() |
| && priv_->changed_bases_.empty()); |
| } |
| |
| /// If the lookup tables are not yet built, walk the differences and |
| /// fill them. |
| void |
| class_diff::ensure_lookup_tables_populated(void) const |
| { |
| class_or_union_diff::ensure_lookup_tables_populated(); |
| |
| if (!lookup_tables_empty()) |
| return; |
| |
| { |
| edit_script& e = get_priv()->base_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| class_decl::base_spec_sptr b = |
| first_class_decl()->get_base_specifiers()[i]; |
| string name = b->get_base_class()->get_name(); |
| ABG_ASSERT(get_priv()->deleted_bases_.find(name) |
| == get_priv()->deleted_bases_.end()); |
| get_priv()->deleted_bases_[name] = b; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| class_decl::base_spec_sptr b = |
| second_class_decl()->get_base_specifiers()[i]; |
| string name = b->get_base_class()->get_name(); |
| ABG_ASSERT(get_priv()->inserted_bases_.find(name) |
| == get_priv()->inserted_bases_.end()); |
| string_base_sptr_map::const_iterator j = |
| get_priv()->deleted_bases_.find(name); |
| if (j != get_priv()->deleted_bases_.end()) |
| { |
| if (j->second != b) |
| get_priv()->changed_bases_[name] = |
| compute_diff(j->second, b, context()); |
| get_priv()->deleted_bases_.erase(j); |
| } |
| else |
| get_priv()->inserted_bases_[name] = b; |
| } |
| } |
| } |
| |
| sort_string_base_sptr_map(get_priv()->deleted_bases_, |
| get_priv()->sorted_deleted_bases_); |
| sort_string_base_sptr_map(get_priv()->inserted_bases_, |
| get_priv()->sorted_inserted_bases_); |
| sort_string_base_diff_sptr_map(get_priv()->changed_bases_, |
| get_priv()->sorted_changed_bases_); |
| |
| { |
| const class_or_union_diff::priv_ptr &p = class_or_union_diff::get_priv(); |
| |
| edit_script& e = p->member_fns_changes_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| method_decl_sptr mem_fn = |
| first_class_decl()->get_virtual_mem_fns()[i]; |
| string name = mem_fn->get_linkage_name(); |
| if (name.empty()) |
| name = mem_fn->get_pretty_representation(); |
| ABG_ASSERT(!name.empty()); |
| if (p->deleted_member_functions_.find(name) |
| != p->deleted_member_functions_.end()) |
| continue; |
| p->deleted_member_functions_[name] = mem_fn; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| |
| method_decl_sptr mem_fn = |
| second_class_decl()->get_virtual_mem_fns()[i]; |
| string name = mem_fn->get_linkage_name(); |
| if (name.empty()) |
| name = mem_fn->get_pretty_representation(); |
| ABG_ASSERT(!name.empty()); |
| if (p->inserted_member_functions_.find(name) |
| != p->inserted_member_functions_.end()) |
| continue; |
| string_member_function_sptr_map::const_iterator j = |
| p->deleted_member_functions_.find(name); |
| |
| if (j != p->deleted_member_functions_.end()) |
| { |
| if (*j->second != *mem_fn) |
| p->changed_member_functions_[name] = |
| compute_diff(static_pointer_cast<function_decl>(j->second), |
| static_pointer_cast<function_decl>(mem_fn), |
| context()); |
| p->deleted_member_functions_.erase(j); |
| } |
| else |
| p->inserted_member_functions_[name] = mem_fn; |
| } |
| } |
| |
| // Now walk the allegedly deleted member functions; check if their |
| // underlying symbols are deleted as well; otherwise, consider |
| // that the member function in question hasn't been deleted. |
| |
| vector<string> to_delete; |
| corpus_sptr f = context()->get_first_corpus(), |
| s = context()->get_second_corpus(); |
| if (s) |
| for (string_member_function_sptr_map::const_iterator i = |
| deleted_member_fns().begin(); |
| i != deleted_member_fns().end(); |
| ++i) |
| { |
| if (get_member_function_is_virtual(i->second)) |
| continue; |
| // We assume that all non-virtual member functions functions |
| // we look at here have ELF symbols. |
| if (!i->second->get_symbol() |
| || s->lookup_function_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| } |
| |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| p->deleted_member_functions_.erase(*i); |
| |
| // Do something similar for added functions. |
| to_delete.clear(); |
| if (f) |
| for (string_member_function_sptr_map::const_iterator i = |
| inserted_member_fns().begin(); |
| i != inserted_member_fns().end(); |
| ++i) |
| { |
| if (get_member_function_is_virtual(i->second)) |
| continue; |
| // We assume that all non-virtual member functions functions |
| // we look at here have ELF symbols. |
| if (!i->second->get_symbol() |
| || f->lookup_function_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| } |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| p->inserted_member_functions_.erase(*i); |
| |
| sort_string_member_function_sptr_map(p->deleted_member_functions_, |
| p->sorted_deleted_member_functions_); |
| |
| sort_string_member_function_sptr_map(p->inserted_member_functions_, |
| p->sorted_inserted_member_functions_); |
| |
| sort_string_virtual_member_function_diff_sptr_map |
| (p->changed_member_functions_, |
| p->sorted_changed_member_functions_); |
| } |
| } |
| |
| /// Allocate the memory for the priv_ pimpl data member of the @ref |
| /// class_diff class. |
| void |
| class_diff::allocate_priv_data() |
| { |
| class_or_union_diff::allocate_priv_data(); |
| if (!priv_) |
| priv_.reset(new priv); |
| } |
| |
| /// Test whether a given base class has changed. A base class has |
| /// changed if it's in both in deleted *and* inserted bases. |
| /// |
| ///@param d the declaration for the base class to consider. |
| /// |
| /// @return the new base class if the given base class has changed, or |
| /// NULL if it hasn't. |
| class_decl::base_spec_sptr |
| class_diff::priv::base_has_changed(class_decl::base_spec_sptr d) const |
| { |
| string qname = d->get_base_class()->get_qualified_name(); |
| string_base_diff_sptr_map::const_iterator it = |
| changed_bases_.find(qname); |
| |
| return (it == changed_bases_.end()) |
| ? class_decl::base_spec_sptr() |
| : it->second->second_base(); |
| |
| } |
| |
| /// Count the number of bases classes whose changes got filtered out. |
| /// |
| /// @return the number of bases classes whose changes got filtered |
| /// out. |
| size_t |
| class_diff::priv::count_filtered_bases() |
| { |
| size_t num_filtered = 0; |
| for (base_diff_sptrs_type::const_iterator i = sorted_changed_bases_.begin(); |
| i != sorted_changed_bases_.end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| if (diff && diff->is_filtered_out()) |
| ++num_filtered; |
| } |
| return num_filtered; |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref class_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| class_diff::chain_into_hierarchy() |
| { |
| class_or_union_diff::chain_into_hierarchy(); |
| |
| // base class changes. |
| for (base_diff_sptrs_type::const_iterator i = |
| get_priv()->sorted_changed_bases_.begin(); |
| i != get_priv()->sorted_changed_bases_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| } |
| |
| /// Constructor of class_diff |
| /// |
| /// @param first_scope the first class of the diff. |
| /// |
| /// @param second_scope the second class of the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| class_diff::class_diff(class_decl_sptr first_scope, |
| class_decl_sptr second_scope, |
| diff_context_sptr ctxt) |
| : class_or_union_diff(first_scope, second_scope, ctxt) |
| // We don't initialize the priv_ data member here. This is an |
| // optimization to reduce memory consumption (and also execution |
| // time) for cases where there are a lot of instances of |
| // class_diff in the same equivalence class. In compute_diff(), |
| // the priv_ is set to the priv_ of the canonical diff node. |
| // See PR libabigail/17948. |
| {} |
| |
| class_diff::~class_diff() |
| {} |
| |
| /// Getter of the private data of the @ref class_diff type. |
| /// |
| /// Note that due to an optimization, the private data of @ref |
| /// class_diff can be shared among several instances of class_diff, so |
| /// you should never try to access class_diff::priv directly. |
| /// |
| /// When class_diff::priv is shared, this function returns the correct |
| /// shared one. |
| /// |
| /// @return the (possibly) shared private data of the current instance |
| /// of class_diff. |
| const class_diff::priv_ptr& |
| class_diff::get_priv() const |
| { |
| if (priv_) |
| return priv_; |
| |
| // If the current class_diff::priv member is empty, then look for |
| // the shared one, from the canonical type. |
| class_diff *canonical = |
| dynamic_cast<class_diff*>(get_canonical_diff()); |
| ABG_ASSERT(canonical); |
| ABG_ASSERT(canonical->priv_); |
| |
| return canonical->priv_; |
| } |
| |
| /// Finish building the current instance of @ref class_diff. |
| void |
| class_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// @return the pretty representation of the current instance of @ref |
| /// class_diff. |
| const string& |
| class_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "class_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| class_diff::has_changes() const |
| {return (first_class_decl() != second_class_decl());} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| class_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_class_decl(), *second_class_decl(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// @return the first class invoveld in the diff. |
| shared_ptr<class_decl> |
| class_diff::first_class_decl() const |
| {return dynamic_pointer_cast<class_decl>(first_subject());} |
| |
| /// Getter of the second class involved in the diff. |
| /// |
| /// @return the second class invoveld in the diff |
| shared_ptr<class_decl> |
| class_diff::second_class_decl() const |
| {return dynamic_pointer_cast<class_decl>(second_subject());} |
| |
| /// @return the edit script of the bases of the two classes. |
| const edit_script& |
| class_diff::base_changes() const |
| {return get_priv()->base_changes_;} |
| |
| /// Getter for the deleted base classes of the diff. |
| /// |
| /// @return a map containing the deleted base classes, keyed with |
| /// their pretty representation. |
| const string_base_sptr_map& |
| class_diff::deleted_bases() const |
| {return get_priv()->deleted_bases_;} |
| |
| /// Getter for the inserted base classes of the diff. |
| /// |
| /// @return a map containing the inserted base classes, keyed with |
| /// their pretty representation. |
| const string_base_sptr_map& |
| class_diff::inserted_bases() const |
| {return get_priv()->inserted_bases_;} |
| |
| /// Getter for the changed base classes of the diff. |
| /// |
| /// @return a sorted vector containing the changed base classes |
| const base_diff_sptrs_type& |
| class_diff::changed_bases() |
| {return get_priv()->sorted_changed_bases_;} |
| |
| /// @return the edit script of the bases of the two classes. |
| edit_script& |
| class_diff::base_changes() |
| {return get_priv()->base_changes_;} |
| |
| /// Produce a basic report about the changes between two class_decl. |
| /// |
| /// @param out the output stream to report the changes to. |
| /// |
| /// @param indent the string to use as an indentation prefix in the |
| /// report. |
| void |
| class_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the set of changes between two instances of class_decl. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first class_decl to consider. |
| /// |
| /// @param second the second class_decl to consider. |
| /// |
| /// @return changes the resulting changes. |
| /// |
| /// @param ctxt the diff context to use. |
| class_diff_sptr |
| compute_diff(const class_decl_sptr first, |
| const class_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| class_decl_sptr f = is_class_type(look_through_decl_only_class(first)), |
| s = is_class_type(look_through_decl_only_class(second)); |
| |
| class_diff_sptr changes(new class_diff(f, s, ctxt)); |
| |
| ctxt->initialize_canonical_diff(changes); |
| ABG_ASSERT(changes->get_canonical_diff()); |
| |
| if (!ctxt->get_canonical_diff_for(first, second)) |
| { |
| // Either first or second is a decl-only class; let's set the |
| // canonical diff here in that case. |
| diff_sptr canonical_diff = ctxt->get_canonical_diff_for(changes); |
| ABG_ASSERT(canonical_diff); |
| ctxt->set_canonical_diff_for(first, second, canonical_diff); |
| } |
| |
| // Ok, so this is an optimization. Do not freak out if it looks |
| // weird, because, well, it does look weird. This speeds up |
| // greatly, for instance, the test case given at PR |
| // libabigail/17948. |
| // |
| // We are setting the private data of the new instance of class_diff |
| // (which is 'changes') to the private data of its canonical |
| // instance. That is, we are sharing the private data of 'changes' |
| // with the private data of its canonical instance to consume less |
| // memory in cases where the equivalence class of 'changes' is huge. |
| // |
| // But if changes is its own canonical instance, then we initialize |
| // its private data properly |
| if (is_class_diff(changes->get_canonical_diff()) == changes.get()) |
| // changes is its own canonical instance, so it gets a brand new |
| // private data. |
| changes->allocate_priv_data(); |
| else |
| { |
| // changes has a non-empty equivalence class so it's going to |
| // share its private data with its canonical instance. Next |
| // time class_diff::get_priv() is invoked, it's going to return |
| // the shared private data of the canonical instance. |
| return changes; |
| } |
| |
| // Compare base specs |
| compute_diff(f->get_base_specifiers().begin(), |
| f->get_base_specifiers().end(), |
| s->get_base_specifiers().begin(), |
| s->get_base_specifiers().end(), |
| changes->base_changes()); |
| |
| // Do *not* compare member types because it generates lots of noise |
| // and I doubt it's really useful. |
| #if 0 |
| compute_diff(f->get_member_types().begin(), |
| f->get_member_types().end(), |
| s->get_member_types().begin(), |
| s->get_member_types().end(), |
| changes->member_types_changes()); |
| #endif |
| |
| // Compare data member |
| compute_diff(f->get_non_static_data_members().begin(), |
| f->get_non_static_data_members().end(), |
| s->get_non_static_data_members().begin(), |
| s->get_non_static_data_members().end(), |
| changes->data_members_changes()); |
| |
| // Compare virtual member functions |
| compute_diff(f->get_virtual_mem_fns().begin(), |
| f->get_virtual_mem_fns().end(), |
| s->get_virtual_mem_fns().begin(), |
| s->get_virtual_mem_fns().end(), |
| changes->member_fns_changes()); |
| |
| // Compare member function templates |
| compute_diff(f->get_member_function_templates().begin(), |
| f->get_member_function_templates().end(), |
| s->get_member_function_templates().begin(), |
| s->get_member_function_templates().end(), |
| changes->member_fn_tmpls_changes()); |
| |
| // Likewise, do not compare member class templates |
| #if 0 |
| compute_diff(f->get_member_class_templates().begin(), |
| f->get_member_class_templates().end(), |
| s->get_member_class_templates().begin(), |
| s->get_member_class_templates().end(), |
| changes->member_class_tmpls_changes()); |
| #endif |
| |
| changes->ensure_lookup_tables_populated(); |
| |
| return changes; |
| } |
| |
| //</class_diff stuff> |
| |
| // <base_diff stuff> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref base_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| base_diff::chain_into_hierarchy() |
| {append_child_node(get_underlying_class_diff());} |
| |
| /// @param first the first base spec to consider. |
| /// |
| /// @param second the second base spec to consider. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref base_diff. Otherwise memory corruption |
| /// issues occur. |
| base_diff::base_diff(class_decl::base_spec_sptr first, |
| class_decl::base_spec_sptr second, |
| class_diff_sptr underlying, |
| diff_context_sptr ctxt) |
| : diff(first, second, ctxt), |
| priv_(new priv(underlying)) |
| {} |
| |
| /// Finish building the current instance of @ref base_diff. |
| void |
| base_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first base spec of the diff object. |
| /// |
| /// @return the first base specifier for the diff object. |
| class_decl::base_spec_sptr |
| base_diff::first_base() const |
| {return dynamic_pointer_cast<class_decl::base_spec>(first_subject());} |
| |
| /// Getter for the second base spec of the diff object. |
| /// |
| /// @return the second base specifier for the diff object. |
| class_decl::base_spec_sptr |
| base_diff::second_base() const |
| {return dynamic_pointer_cast<class_decl::base_spec>(second_subject());} |
| |
| /// Getter for the diff object for the diff of the underlying base |
| /// classes. |
| /// |
| /// @return the diff object for the diff of the underlying base |
| /// classes. |
| const class_diff_sptr |
| base_diff::get_underlying_class_diff() const |
| {return priv_->underlying_class_diff_;} |
| |
| /// Setter for the diff object for the diff of the underlyng base |
| /// classes. |
| /// |
| /// @param d the new diff object for the diff of the underlying base |
| /// classes. |
| void |
| base_diff::set_underlying_class_diff(class_diff_sptr d) |
| {priv_->underlying_class_diff_ = d;} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// base_diff. |
| const string& |
| base_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "base_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// Return true iff the current diff node carries a change. |
| bool |
| base_diff::has_changes() const |
| {return first_base() != second_base();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| base_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_base(), *second_base(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Generates a report for the current instance of base_diff. |
| /// |
| /// @param out the output stream to send the report to. |
| /// |
| /// @param indent the string to use for indentation. |
| void |
| base_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Constructs the diff object representing a diff between two base |
| /// class specifications. |
| /// |
| /// Note that the two artifacts must have been created in the same |
| /// @ref environment, otherwise, this function aborts. |
| /// |
| /// @param first the first base class specification. |
| /// |
| /// @param second the second base class specification. |
| /// |
| /// @param ctxt the content of the diff. |
| /// |
| /// @return the resulting diff object. |
| base_diff_sptr |
| compute_diff(const class_decl::base_spec_sptr first, |
| const class_decl::base_spec_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| { |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| ABG_ASSERT(first->get_base_class()->get_environment() |
| == second->get_base_class()->get_environment()); |
| ABG_ASSERT(first->get_environment() |
| == first->get_base_class()->get_environment()); |
| } |
| |
| class_diff_sptr cl = compute_diff(first->get_base_class(), |
| second->get_base_class(), |
| ctxt); |
| base_diff_sptr changes(new base_diff(first, second, cl, ctxt)); |
| |
| ctxt->initialize_canonical_diff(changes); |
| |
| return changes; |
| } |
| |
| // </base_diff stuff> |
| |
| |
| // <union_diff stuff> |
| |
| /// Clear the lookup tables useful for reporting. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed from the union_diff::priv. |
| void |
| union_diff::clear_lookup_tables(void) |
| {class_or_union_diff::clear_lookup_tables();} |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// @return true if the lookup tables are empty, false otherwise. |
| bool |
| union_diff::lookup_tables_empty(void) const |
| {return class_or_union_diff::lookup_tables_empty();} |
| |
| /// If the lookup tables are not yet built, walk the differences and |
| /// fill them. |
| void |
| union_diff::ensure_lookup_tables_populated(void) const |
| {class_or_union_diff::ensure_lookup_tables_populated();} |
| |
| /// Allocate the memory for the priv_ pimpl data member of the @ref |
| /// union_diff class. |
| void |
| union_diff::allocate_priv_data() |
| { |
| class_or_union_diff::allocate_priv_data(); |
| } |
| |
| /// Constructor for the @ref union_diff type. |
| /// |
| /// @param first_union the first object of the comparison. |
| /// |
| /// @param second_union the second object of the comparison. |
| /// |
| /// @param ctxt the context of the comparison. |
| union_diff::union_diff(union_decl_sptr first_union, |
| union_decl_sptr second_union, |
| diff_context_sptr ctxt) |
| : class_or_union_diff(first_union, second_union, ctxt) |
| {} |
| |
| /// Finish building the current instance of @ref union_diff. |
| void |
| union_diff::finish_diff_type() |
| {class_or_union_diff::finish_diff_type();} |
| |
| /// Destructor of the union_diff node. |
| union_diff::~union_diff() |
| {} |
| |
| /// @return the first object of the comparison. |
| union_decl_sptr |
| union_diff::first_union_decl() const |
| {return is_union_type(first_subject());} |
| |
| /// @return the second object of the comparison. |
| union_decl_sptr |
| union_diff::second_union_decl() const |
| {return is_union_type(second_subject());} |
| |
| /// @return the pretty representation of the current diff node. |
| const string& |
| union_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "union_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Report the changes carried by the current @ref union_diff node in |
| /// a textual format. |
| /// |
| /// @param out the output stream to write the textual report to. |
| /// |
| /// @param indent the number of white space to use as indentation. |
| void |
| union_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the difference between two @ref union_decl types. |
| /// |
| /// Note that the two types must hav been created in the same |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first @ref union_decl to consider. |
| /// |
| /// @param second the second @ref union_decl to consider. |
| /// |
| /// @param ctxt the context of the diff to use. |
| union_diff_sptr |
| compute_diff(const union_decl_sptr first, |
| const union_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| union_diff_sptr changes(new union_diff(first, second, ctxt)); |
| |
| ctxt->initialize_canonical_diff(changes); |
| ABG_ASSERT(changes->get_canonical_diff()); |
| |
| // Ok, so this is an optimization. Do not freak out if it looks |
| // weird, because, well, it does look weird. This speeds up |
| // greatly, for instance, the test case given at PR |
| // libabigail/17948. |
| // |
| // We are setting the private data of the new instance of class_diff |
| // (which is 'changes') to the private data of its canonical |
| // instance. That is, we are sharing the private data of 'changes' |
| // with the private data of its canonical instance to consume less |
| // memory in cases where the equivalence class of 'changes' is huge. |
| // |
| // But if changes is its own canonical instance, then we initialize |
| // its private data properly. |
| if (is_union_diff(changes->get_canonical_diff()) == changes.get()) |
| // changes is its own canonical instance, so it gets a brand new |
| // private data. |
| changes->allocate_priv_data(); |
| else |
| { |
| // changes has a non-empty equivalence class so it's going to |
| // share its private data with its canonical instance. Next |
| // time class_diff::get_priv() is invoked, it's going to return |
| // the shared private data of the canonical instance. |
| return changes; |
| } |
| |
| // Compare data member |
| compute_diff(first->get_non_static_data_members().begin(), |
| first->get_non_static_data_members().end(), |
| second->get_non_static_data_members().begin(), |
| second->get_non_static_data_members().end(), |
| changes->data_members_changes()); |
| |
| #if 0 |
| // Compare member functions |
| compute_diff(first->get_mem_fns().begin(), |
| first->get_mem_fns().end(), |
| second->get_mem_fns().begin(), |
| second->get_mem_fns().end(), |
| changes->member_fns_changes()); |
| |
| // Compare member function templates |
| compute_diff(first->get_member_function_templates().begin(), |
| first->get_member_function_templates().end(), |
| second->get_member_function_templates().begin(), |
| second->get_member_function_templates().end(), |
| changes->member_fn_tmpls_changes()); |
| #endif |
| |
| changes->ensure_lookup_tables_populated(); |
| |
| return changes; |
| } |
| |
| // </union_diff stuff> |
| |
| //<scope_diff stuff> |
| |
| /// Clear the lookup tables that are useful for reporting. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed. |
| void |
| scope_diff::clear_lookup_tables() |
| { |
| priv_->deleted_types_.clear(); |
| priv_->deleted_decls_.clear(); |
| priv_->inserted_types_.clear(); |
| priv_->inserted_decls_.clear(); |
| priv_->changed_types_.clear(); |
| priv_->changed_decls_.clear(); |
| priv_->removed_types_.clear(); |
| priv_->removed_decls_.clear(); |
| priv_->added_types_.clear(); |
| priv_->added_decls_.clear(); |
| } |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// This function must be updated each time a lookup table is added or |
| /// removed. |
| /// |
| /// @return true iff all the lookup tables are empty. |
| bool |
| scope_diff::lookup_tables_empty() const |
| { |
| return (priv_->deleted_types_.empty() |
| && priv_->deleted_decls_.empty() |
| && priv_->inserted_types_.empty() |
| && priv_->inserted_decls_.empty() |
| && priv_->changed_types_.empty() |
| && priv_->changed_decls_.empty() |
| && priv_->removed_types_.empty() |
| && priv_->removed_decls_.empty() |
| && priv_->added_types_.empty() |
| && priv_->added_decls_.empty()); |
| } |
| |
| /// If the lookup tables are not yet built, walk the member_changes_ |
| /// member and fill the lookup tables. |
| void |
| scope_diff::ensure_lookup_tables_populated() |
| { |
| if (!lookup_tables_empty()) |
| return; |
| |
| edit_script& e = priv_->member_changes_; |
| |
| // Populate deleted types & decls lookup tables. |
| for (vector<deletion>::const_iterator i = e.deletions().begin(); |
| i != e.deletions().end(); |
| ++i) |
| { |
| decl_base_sptr decl = deleted_member_at(i); |
| string qname = decl->get_qualified_name(); |
| if (is_type(decl)) |
| { |
| class_decl_sptr klass_decl = dynamic_pointer_cast<class_decl>(decl); |
| if (klass_decl && klass_decl->get_is_declaration_only()) |
| continue; |
| |
| ABG_ASSERT(priv_->deleted_types_.find(qname) |
| == priv_->deleted_types_.end()); |
| priv_->deleted_types_[qname] = decl; |
| } |
| else |
| { |
| ABG_ASSERT(priv_->deleted_decls_.find(qname) |
| == priv_->deleted_decls_.end()); |
| priv_->deleted_decls_[qname] = decl; |
| } |
| } |
| |
| // Populate inserted types & decls as well as chagned types & decls |
| // lookup tables. |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator i = it->inserted_indexes().begin(); |
| i != it->inserted_indexes().end(); |
| ++i) |
| { |
| decl_base_sptr decl = inserted_member_at(i); |
| string qname = decl->get_qualified_name(); |
| if (is_type(decl)) |
| { |
| class_decl_sptr klass_decl = |
| dynamic_pointer_cast<class_decl>(decl); |
| if (klass_decl && klass_decl->get_is_declaration_only()) |
| continue; |
| |
| ABG_ASSERT(priv_->inserted_types_.find(qname) |
| == priv_->inserted_types_.end()); |
| string_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_types_.find(qname); |
| if (j != priv_->deleted_types_.end()) |
| { |
| if (*j->second != *decl) |
| priv_->changed_types_[qname] = |
| compute_diff(j->second, decl, context()); |
| priv_->deleted_types_.erase(j); |
| } |
| else |
| priv_->inserted_types_[qname] = decl; |
| } |
| else |
| { |
| ABG_ASSERT(priv_->inserted_decls_.find(qname) |
| == priv_->inserted_decls_.end()); |
| string_decl_base_sptr_map::const_iterator j = |
| priv_->deleted_decls_.find(qname); |
| if (j != priv_->deleted_decls_.end()) |
| { |
| if (*j->second != *decl) |
| priv_->changed_decls_[qname] = |
| compute_diff(j->second, decl, context()); |
| priv_->deleted_decls_.erase(j); |
| } |
| else |
| priv_->inserted_decls_[qname] = decl; |
| } |
| } |
| } |
| |
| sort_string_diff_sptr_map(priv_->changed_decls_, |
| priv_->sorted_changed_decls_); |
| sort_string_diff_sptr_map(priv_->changed_types_, |
| priv_->sorted_changed_types_); |
| |
| // Populate removed types/decls lookup tables |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->deleted_types_.begin(); |
| i != priv_->deleted_types_.end(); |
| ++i) |
| { |
| string_decl_base_sptr_map::const_iterator r = |
| priv_->inserted_types_.find(i->first); |
| if (r == priv_->inserted_types_.end()) |
| priv_->removed_types_[i->first] = i->second; |
| } |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->deleted_decls_.begin(); |
| i != priv_->deleted_decls_.end(); |
| ++i) |
| { |
| string_decl_base_sptr_map::const_iterator r = |
| priv_->inserted_decls_.find(i->first); |
| if (r == priv_->inserted_decls_.end()) |
| priv_->removed_decls_[i->first] = i->second; |
| } |
| |
| // Populate added types/decls. |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->inserted_types_.begin(); |
| i != priv_->inserted_types_.end(); |
| ++i) |
| { |
| string_decl_base_sptr_map::const_iterator r = |
| priv_->deleted_types_.find(i->first); |
| if (r == priv_->deleted_types_.end()) |
| priv_->added_types_[i->first] = i->second; |
| } |
| for (string_decl_base_sptr_map::const_iterator i = |
| priv_->inserted_decls_.begin(); |
| i != priv_->inserted_decls_.end(); |
| ++i) |
| { |
| string_decl_base_sptr_map::const_iterator r = |
| priv_->deleted_decls_.find(i->first); |
| if (r == priv_->deleted_decls_.end()) |
| priv_->added_decls_[i->first] = i->second; |
| } |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref scope_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| scope_diff::chain_into_hierarchy() |
| { |
| for (diff_sptrs_type::const_iterator i = changed_types().begin(); |
| i != changed_types().end(); |
| ++i) |
| if (*i) |
| append_child_node(*i); |
| |
| for (diff_sptrs_type::const_iterator i = changed_decls().begin(); |
| i != changed_decls().end(); |
| ++i) |
| if (*i) |
| append_child_node(*i); |
| } |
| |
| /// Constructor for scope_diff |
| /// |
| /// @param first_scope the first scope to consider for the diff. |
| /// |
| /// @param second_scope the second scope to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref scope_diff. Otherwise memory corruption |
| /// issues occur. |
| scope_diff::scope_diff(scope_decl_sptr first_scope, |
| scope_decl_sptr second_scope, |
| diff_context_sptr ctxt) |
| : diff(first_scope, second_scope, ctxt), |
| priv_(new priv) |
| {} |
| |
| /// Finish building the current instance of @ref scope_diff. |
| void |
| scope_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first scope of the diff. |
| /// |
| /// @return the first scope of the diff. |
| const scope_decl_sptr |
| scope_diff::first_scope() const |
| {return dynamic_pointer_cast<scope_decl>(first_subject());} |
| |
| /// Getter for the second scope of the diff. |
| /// |
| /// @return the second scope of the diff. |
| const scope_decl_sptr |
| scope_diff::second_scope() const |
| {return dynamic_pointer_cast<scope_decl>(second_subject());} |
| |
| /// Accessor of the edit script of the members of a scope. |
| /// |
| /// This edit script is computed using the equality operator that |
| /// applies to shared_ptr<decl_base>. |
| /// |
| /// That has interesting consequences. For instance, consider two |
| /// scopes S0 and S1. S0 contains a class C0 and S1 contains a class |
| /// S0'. C0 and C0' have the same qualified name, but have different |
| /// members. The edit script will consider that C0 has been deleted |
| /// from S0 and that S0' has been inserted. This is a low level |
| /// canonical representation of the changes; a higher level |
| /// representation would give us a simpler way to say "the class C0 |
| /// has been modified into C0'". But worry not. We do have such |
| /// higher representation as well; that is what changed_types() and |
| /// changed_decls() is for. |
| /// |
| /// @return the edit script of the changes encapsulatd in this |
| /// instance of scope_diff. |
| const edit_script& |
| scope_diff::member_changes() const |
| {return priv_->member_changes_;} |
| |
| /// Accessor of the edit script of the members of a scope. |
| /// |
| /// This edit script is computed using the equality operator that |
| /// applies to shared_ptr<decl_base>. |
| /// |
| /// That has interesting consequences. For instance, consider two |
| /// scopes S0 and S1. S0 contains a class C0 and S1 contains a class |
| /// S0'. C0 and C0' have the same qualified name, but have different |
| /// members. The edit script will consider that C0 has been deleted |
| /// from S0 and that S0' has been inserted. This is a low level |
| /// canonical representation of the changes; a higher level |
| /// representation would give us a simpler way to say "the class C0 |
| /// has been modified into C0'". But worry not. We do have such |
| /// higher representation as well; that is what changed_types() and |
| /// changed_decls() is for. |
| /// |
| /// @return the edit script of the changes encapsulatd in this |
| /// instance of scope_diff. |
| edit_script& |
| scope_diff::member_changes() |
| {return priv_->member_changes_;} |
| |
| /// Accessor that eases the manipulation of the edit script associated |
| /// to this instance. It returns the scope member that is reported |
| /// (in the edit script) as deleted at a given index. |
| /// |
| /// @param i the index (in the edit script) of an element of the first |
| /// scope that has been reported as being delete. |
| /// |
| /// @return the scope member that has been reported by the edit script |
| /// as being deleted at index i. |
| const decl_base_sptr |
| scope_diff::deleted_member_at(unsigned i) const |
| { |
| scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(first_subject()); |
| return scope->get_member_decls()[i]; |
| } |
| |
| /// Accessor that eases the manipulation of the edit script associated |
| /// to this instance. It returns the scope member (of the first scope |
| /// of this diff instance) that is reported (in the edit script) as |
| /// deleted at a given iterator. |
| /// |
| /// @param i the iterator of an element of the first scope that has |
| /// been reported as being delete. |
| /// |
| /// @return the scope member of the first scope of this diff that has |
| /// been reported by the edit script as being deleted at iterator i. |
| const decl_base_sptr |
| scope_diff::deleted_member_at(vector<deletion>::const_iterator i) const |
| {return deleted_member_at(i->index());} |
| |
| /// Accessor that eases the manipulation of the edit script associated |
| /// to this instance. It returns the scope member (of the second |
| /// scope of this diff instance) that is reported as being inserted |
| /// from a given index. |
| /// |
| /// @param i the index of an element of the second scope this diff |
| /// that has been reported by the edit script as being inserted. |
| /// |
| /// @return the scope member of the second scope of this diff that has |
| /// been reported as being inserted from index i. |
| const decl_base_sptr |
| scope_diff::inserted_member_at(unsigned i) |
| { |
| scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(second_subject()); |
| return scope->get_member_decls()[i]; |
| } |
| |
| /// Accessor that eases the manipulation of the edit script associated |
| /// to this instance. It returns the scope member (of the second |
| /// scope of this diff instance) that is reported as being inserted |
| /// from a given iterator. |
| /// |
| /// @param i the iterator of an element of the second scope this diff |
| /// that has been reported by the edit script as being inserted. |
| /// |
| /// @return the scope member of the second scope of this diff that has |
| /// been reported as being inserted from iterator i. |
| const decl_base_sptr |
| scope_diff::inserted_member_at(vector<unsigned>::const_iterator i) |
| {return inserted_member_at(*i);} |
| |
| /// @return a sorted vector of the types which content has changed |
| /// from the first scope to the other. |
| const diff_sptrs_type& |
| scope_diff::changed_types() const |
| {return priv_->sorted_changed_types_;} |
| |
| /// @return a sorted vector of the decls which content has changed |
| /// from the first scope to the other. |
| const diff_sptrs_type& |
| scope_diff::changed_decls() const |
| {return priv_->sorted_changed_decls_;} |
| |
| const string_decl_base_sptr_map& |
| scope_diff::removed_types() const |
| {return priv_->removed_types_;} |
| |
| const string_decl_base_sptr_map& |
| scope_diff::removed_decls() const |
| {return priv_->removed_decls_;} |
| |
| const string_decl_base_sptr_map& |
| scope_diff::added_types() const |
| {return priv_->added_types_;} |
| |
| const string_decl_base_sptr_map& |
| scope_diff::added_decls() const |
| {return priv_->added_decls_;} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// scope_diff. |
| const string& |
| scope_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "scope_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// Return true iff the current diff node carries a change. |
| bool |
| scope_diff::has_changes() const |
| { |
| // TODO: add the number of really removed/added stuff. |
| return changed_types().size() + changed_decls().size(); |
| } |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| scope_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_scope(), *second_scope(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Report the changes of one scope against another. |
| /// |
| /// @param out the out stream to report the changes to. |
| /// |
| /// @param indent the string to use for indentation. |
| void |
| scope_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two scopes. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first scope to consider in computing the diff. |
| /// |
| /// @param second the second scope to consider in the diff |
| /// computation. The second scope is diffed against the first scope. |
| /// |
| /// @param d a pointer to the diff object to populate with the |
| /// computed diff. |
| /// |
| /// @return return the populated \a d parameter passed to this |
| /// function. |
| /// |
| /// @param ctxt the diff context to use. |
| scope_diff_sptr |
| compute_diff(const scope_decl_sptr first, |
| const scope_decl_sptr second, |
| scope_diff_sptr d, |
| diff_context_sptr ctxt) |
| { |
| ABG_ASSERT(d->first_scope() == first && d->second_scope() == second); |
| |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| compute_diff(first->get_member_decls().begin(), |
| first->get_member_decls().end(), |
| second->get_member_decls().begin(), |
| second->get_member_decls().end(), |
| d->member_changes()); |
| |
| d->ensure_lookup_tables_populated(); |
| d->context(ctxt); |
| |
| return d; |
| } |
| |
| /// Compute the diff between two scopes. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first_scope the first scope to consider in computing the diff. |
| /// |
| /// @param second_scope the second scope to consider in the diff |
| /// computation. The second scope is diffed against the first scope. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return return the resulting diff |
| scope_diff_sptr |
| compute_diff(const scope_decl_sptr first_scope, |
| const scope_decl_sptr second_scope, |
| diff_context_sptr ctxt) |
| { |
| if (first_scope && second_scope) |
| ABG_ASSERT(first_scope->get_environment() |
| == second_scope->get_environment()); |
| |
| scope_diff_sptr d(new scope_diff(first_scope, second_scope, ctxt)); |
| d = compute_diff(first_scope, second_scope, d, ctxt); |
| ctxt->initialize_canonical_diff(d); |
| return d; |
| } |
| |
| //</scope_diff stuff> |
| |
| // <fn_parm_diff stuff> |
| |
| /// Constructor for the fn_parm_diff type. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref fn_parm_diff. Otherwise memory |
| /// corruption issues occur. |
| fn_parm_diff::fn_parm_diff(const function_decl::parameter_sptr first, |
| const function_decl::parameter_sptr second, |
| diff_context_sptr ctxt) |
| : decl_diff_base(first, second, ctxt), |
| priv_(new priv) |
| { |
| ABG_ASSERT(first->get_index() == second->get_index()); |
| priv_->type_diff = compute_diff(first->get_type(), |
| second->get_type(), |
| ctxt); |
| ABG_ASSERT(priv_->type_diff); |
| } |
| |
| /// Finish the building of the current instance of @ref fn_parm_diff. |
| void |
| fn_parm_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first subject of this diff node. |
| /// |
| /// @return the first function_decl::parameter_sptr subject of this |
| /// diff node. |
| const function_decl::parameter_sptr |
| fn_parm_diff::first_parameter() const |
| {return dynamic_pointer_cast<function_decl::parameter>(first_subject());} |
| |
| /// Getter for the second subject of this diff node. |
| /// |
| /// @return the second function_decl::parameter_sptr subject of this |
| /// diff node. |
| const function_decl::parameter_sptr |
| fn_parm_diff::second_parameter() const |
| {return dynamic_pointer_cast<function_decl::parameter>(second_subject());} |
| |
| /// Getter for the diff representing the changes on the type of the |
| /// function parameter involved in the current instance of @ref |
| /// fn_parm_diff. |
| /// |
| /// @return a diff_sptr representing the changes on the type of the |
| /// function parameter we are interested in. |
| diff_sptr |
| fn_parm_diff::type_diff() const |
| {return priv_->type_diff;} |
| |
| /// Build and return a textual representation of the current instance |
| /// of @ref fn_parm_diff. |
| /// |
| /// @return the string representing the current instance of |
| /// fn_parm_diff. |
| const string& |
| fn_parm_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "function_parameter_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| fn_parm_diff::has_changes() const |
| {return *first_parameter() != *second_parameter();} |
| |
| /// Check if the current diff node carries a local change. |
| /// |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| fn_parm_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_parameter(), *second_parameter(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Emit a textual report about the current fn_parm_diff instance. |
| /// |
| /// @param out the output stream to emit the textual report to. |
| /// |
| /// @param indent the indentation string to use in the report. |
| void |
| fn_parm_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Populate the vector of children nodes of the @ref diff base type |
| /// sub-object of this instance of @ref fn_parm_diff. |
| /// |
| /// The children nodes can then later be retrieved using |
| /// diff::children_nodes() |
| void |
| fn_parm_diff::chain_into_hierarchy() |
| { |
| if (type_diff()) |
| append_child_node(type_diff()); |
| } |
| |
| /// Compute the difference between two function_decl::parameter_sptr; |
| /// that is, between two function parameters. Return a resulting |
| /// fn_parm_diff_sptr that represents the changes. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param ctxt the context of the diff. |
| /// |
| /// @return fn_parm_diff_sptr the resulting diff node. |
| fn_parm_diff_sptr |
| compute_diff(const function_decl::parameter_sptr first, |
| const function_decl::parameter_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (!first || !second) |
| return fn_parm_diff_sptr(); |
| |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| fn_parm_diff_sptr result(new fn_parm_diff(first, second, ctxt)); |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| // </fn_parm_diff stuff> |
| |
| // <function_type_diff stuff> |
| |
| void |
| function_type_diff::ensure_lookup_tables_populated() |
| { |
| priv_->return_type_diff_ = |
| compute_diff(first_function_type()->get_return_type(), |
| second_function_type()->get_return_type(), |
| context()); |
| |
| string parm_name; |
| function_decl::parameter_sptr parm; |
| for (vector<deletion>::const_iterator i = |
| priv_->parm_changes_.deletions().begin(); |
| i != priv_->parm_changes_.deletions().end(); |
| ++i) |
| { |
| parm = *(first_function_type()->get_first_parm() |
| + i->index()); |
| parm_name = parm->get_name_id(); |
| // If for a reason the type name is empty we want to know and |
| // fix that. |
| ABG_ASSERT(!parm_name.empty()); |
| priv_->deleted_parms_[parm_name] = parm; |
| priv_->deleted_parms_by_id_[parm->get_index()] = parm; |
| } |
| |
| for (vector<insertion>::const_iterator i = |
| priv_->parm_changes_.insertions().begin(); |
| i != priv_->parm_changes_.insertions().end(); |
| ++i) |
| { |
| for (vector<unsigned>::const_iterator j = |
| i->inserted_indexes().begin(); |
| j != i->inserted_indexes().end(); |
| ++j) |
| { |
| parm = *(second_function_type()->get_first_parm() + *j); |
| parm_name = parm->get_name_id(); |
| // If for a reason the type name is empty we want to know and |
| // fix that. |
| ABG_ASSERT(!parm_name.empty()); |
| { |
| string_parm_map::const_iterator k = |
| priv_->deleted_parms_.find(parm_name); |
| if (k != priv_->deleted_parms_.end()) |
| { |
| if (*k->second != *parm) |
| priv_->subtype_changed_parms_[parm_name] = |
| compute_diff(k->second, parm, context()); |
| priv_->deleted_parms_.erase(parm_name); |
| } |
| else |
| priv_->added_parms_[parm_name] = parm; |
| } |
| { |
| unsigned_parm_map::const_iterator k = |
| priv_->deleted_parms_by_id_.find(parm->get_index()); |
| if (k != priv_->deleted_parms_by_id_.end()) |
| { |
| if (*k->second != *parm |
| && (k->second->get_name_id() != parm_name)) |
| priv_->changed_parms_by_id_[parm->get_index()] = |
| compute_diff(k->second, parm, context()); |
| priv_->added_parms_.erase(parm_name); |
| priv_->deleted_parms_.erase(k->second->get_name_id()); |
| priv_->deleted_parms_by_id_.erase(parm->get_index()); |
| } |
| else |
| priv_->added_parms_by_id_[parm->get_index()] = parm; |
| } |
| } |
| } |
| |
| sort_string_fn_parm_diff_sptr_map(priv_->subtype_changed_parms_, |
| priv_->sorted_subtype_changed_parms_); |
| sort_string_fn_parm_diff_sptr_map(priv_->changed_parms_by_id_, |
| priv_->sorted_changed_parms_by_id_); |
| sort_string_parm_map(priv_->deleted_parms_, |
| priv_->sorted_deleted_parms_); |
| |
| sort_string_parm_map(priv_->added_parms_, |
| priv_->sorted_added_parms_); |
| } |
| |
| /// In the vector of deleted parameters, get the one that is at a given |
| /// index. |
| /// |
| /// @param i the index of the deleted parameter to get. |
| /// |
| /// @return the parameter returned. |
| const function_decl::parameter_sptr |
| function_type_diff::deleted_parameter_at(int i) const |
| {return first_function_type()->get_parameters()[i];} |
| |
| /// Getter for the sorted vector of deleted parameters. |
| /// |
| /// @return the sorted vector of deleted parameters. |
| const vector<function_decl::parameter_sptr>& |
| function_type_diff::sorted_deleted_parms() const |
| {return priv_->sorted_deleted_parms_;} |
| |
| /// Getter for the sorted vector of added parameters . |
| /// |
| /// @return the sorted vector of added parameters. |
| const vector<function_decl::parameter_sptr>& |
| function_type_diff::sorted_added_parms() const |
| {return priv_->sorted_added_parms_;} |
| |
| /// In the vector of inserted parameters, get the one that is at a |
| /// given index. |
| /// |
| /// @param i the index of the inserted parameter to get. |
| /// |
| /// @return the parameter returned. |
| const function_decl::parameter_sptr |
| function_type_diff::inserted_parameter_at(int i) const |
| {return second_function_type()->get_parameters()[i];} |
| |
| /// Consutrctor of the @ref function_type type. |
| /// |
| /// @param first the first @ref function_type subject of the diff to |
| /// create. |
| /// |
| /// @param second the second @ref function_type subject of the diff to |
| /// create. |
| /// |
| /// @param ctxt the diff context to be used by the newly created |
| /// instance of function_type_diff. Note that this context object |
| /// must stay alive at least during the life time of the current |
| /// instance of @ref function_type_diff. Otherwise memory corruption |
| /// issues occur. |
| function_type_diff::function_type_diff(const function_type_sptr first, |
| const function_type_sptr second, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv) |
| {} |
| |
| /// Finish building the current instance of @ref function_type_diff |
| void |
| function_type_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first subject of the diff. |
| /// |
| /// @return the first function type involved in the diff. |
| const function_type_sptr |
| function_type_diff::first_function_type() const |
| {return dynamic_pointer_cast<function_type>(first_subject());} |
| |
| /// Getter for the second subject of the diff. |
| /// |
| /// @return the second function type involved in the diff. |
| const function_type_sptr |
| function_type_diff::second_function_type() const |
| {return dynamic_pointer_cast<function_type>(second_subject());} |
| |
| /// Getter for the diff of the return types of the two function types |
| /// of the current diff. |
| /// |
| /// @return the diff of the return types of the two function types of |
| /// the current diff. |
| const diff_sptr |
| function_type_diff::return_type_diff() const |
| {return priv_->return_type_diff_;} |
| |
| /// Getter for the map of function parameter changes of the current diff. |
| /// |
| /// @return a map of function parameter changes of the current diff. |
| const string_fn_parm_diff_sptr_map& |
| function_type_diff::subtype_changed_parms() const |
| {return priv_->subtype_changed_parms_;} |
| |
| /// Getter for the map of parameters that got removed. |
| /// |
| /// @return the map of parameters that got removed. |
| const string_parm_map& |
| function_type_diff::removed_parms() const |
| {return priv_->deleted_parms_;} |
| |
| /// Getter for the map of parameters that got added. |
| /// |
| /// @return the map of parameters that got added. |
| const string_parm_map& |
| function_type_diff::added_parms() const |
| {return priv_->added_parms_;} |
| |
| /// Build and return a copy of a pretty representation of the current |
| /// instance of @ref function_type_diff. |
| /// |
| /// @return a copy of the pretty representation of the current |
| /// instance of @ref function_type_diff. |
| const string& |
| function_type_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "function_type_diff[" |
| << abigail::ir::get_pretty_representation(first_function_type()) |
| << ", " |
| << abigail::ir::get_pretty_representation(second_function_type()) |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Test if the current diff node carries changes. |
| /// |
| /// @return true iff the current diff node carries changes. |
| bool |
| function_type_diff::has_changes() const |
| {return *first_function_type() != *second_function_type();} |
| |
| /// Test if the current diff node carries local changes. |
| /// |
| /// A local change is a change that is carried by this diff node, not |
| /// by any of its children nodes. |
| /// |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| function_type_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_function_type(), *second_function_type(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Build and emit a textual report about the current @ref |
| /// function_type_diff instance. |
| /// |
| /// @param out the output stream. |
| /// |
| /// @param indent the indentation string to use. |
| void |
| function_type_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref function_type_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| function_type_diff::chain_into_hierarchy() |
| { |
| if (diff_sptr d = return_type_diff()) |
| append_child_node(d); |
| |
| for (vector<fn_parm_diff_sptr>::const_iterator i = |
| priv_->sorted_subtype_changed_parms_.begin(); |
| i != priv_->sorted_subtype_changed_parms_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| |
| for (vector<fn_parm_diff_sptr>::const_iterator i = |
| priv_->sorted_changed_parms_by_id_.begin(); |
| i != priv_->sorted_changed_parms_by_id_.end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| } |
| |
| /// Compute the diff between two instances of @ref function_type. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first @ref function_type to consider for the diff. |
| /// |
| /// @param second the second @ref function_type to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff between the two @ref function_type. |
| function_type_diff_sptr |
| compute_diff(const function_type_sptr first, |
| const function_type_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (!first || !second) |
| { |
| // TODO: implement this for either first or second being NULL. |
| return function_type_diff_sptr(); |
| } |
| |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| function_type_diff_sptr result(new function_type_diff(first, second, ctxt)); |
| |
| diff_utils::compute_diff(first->get_first_parm(), |
| first->get_parameters().end(), |
| second->get_first_parm(), |
| second->get_parameters().end(), |
| result->priv_->parm_changes_); |
| |
| result->ensure_lookup_tables_populated(); |
| |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| // </function_type_diff stuff> |
| |
| // <function_decl_diff stuff> |
| |
| /// Build the lookup tables of the diff, if necessary. |
| void |
| function_decl_diff::ensure_lookup_tables_populated() |
| { |
| } |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref function_decl_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| function_decl_diff::chain_into_hierarchy() |
| { |
| if (diff_sptr d = type_diff()) |
| append_child_node(d); |
| } |
| |
| /// Constructor for function_decl_diff |
| /// |
| /// @param first the first function considered by the diff. |
| /// |
| /// @param second the second function considered by the diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref function_decl_diff. Otherwise memory |
| /// corruption issues occur. |
| function_decl_diff::function_decl_diff(const function_decl_sptr first, |
| const function_decl_sptr second, |
| diff_context_sptr ctxt) |
| : decl_diff_base(first, second, ctxt), |
| priv_(new priv) |
| { |
| } |
| |
| /// Finish building the current instance of @ref function_decl_diff. |
| void |
| function_decl_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// @return the first function considered by the diff. |
| const function_decl_sptr |
| function_decl_diff::first_function_decl() const |
| {return dynamic_pointer_cast<function_decl>(first_subject());} |
| |
| /// @return the second function considered by the diff. |
| const function_decl_sptr |
| function_decl_diff::second_function_decl() const |
| {return dynamic_pointer_cast<function_decl>(second_subject());} |
| |
| const function_type_diff_sptr |
| function_decl_diff::type_diff() const |
| {return priv_->type_diff_;} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// function_decl_diff. |
| const string& |
| function_decl_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "function_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| function_decl_diff::has_changes() const |
| {return *first_function_decl() != *second_function_decl();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| function_decl_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_function_decl(), *second_function_decl(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Serialize a report of the changes encapsulated in the current |
| /// instance of @ref function_decl_diff over to an output stream. |
| /// |
| /// @param out the output stream to serialize the report to. |
| /// |
| /// @param indent the string to use an an indentation prefix. |
| void |
| function_decl_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute the diff between two function_decl. |
| /// |
| /// Note that the two decls must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first the first function_decl to consider for the diff |
| /// |
| /// @param second the second function_decl to consider for the diff |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the computed diff |
| function_decl_diff_sptr |
| compute_diff(const function_decl_sptr first, |
| const function_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (!first || !second) |
| { |
| // TODO: implement this for either first or second being NULL. |
| return function_decl_diff_sptr(); |
| } |
| |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| function_type_diff_sptr type_diff = compute_diff(first->get_type(), |
| second->get_type(), |
| ctxt); |
| |
| function_decl_diff_sptr result(new function_decl_diff(first, second, |
| ctxt)); |
| result->priv_->type_diff_ = type_diff; |
| |
| result->ensure_lookup_tables_populated(); |
| |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| |
| // </function_decl_diff stuff> |
| |
| // <type_decl_diff stuff> |
| |
| /// Constructor for type_decl_diff. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref type_decl_diff. Otherwise memory |
| /// corruption issues occur. |
| type_decl_diff::type_decl_diff(const type_decl_sptr first, |
| const type_decl_sptr second, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt) |
| {} |
| |
| /// Finish building the current instance of @ref type_decl_diff. |
| void |
| type_decl_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the first subject of the type_decl_diff. |
| /// |
| /// @return the first type_decl involved in the diff. |
| const type_decl_sptr |
| type_decl_diff::first_type_decl() const |
| {return dynamic_pointer_cast<type_decl>(first_subject());} |
| |
| /// Getter for the second subject of the type_decl_diff. |
| /// |
| /// @return the second type_decl involved in the diff. |
| const type_decl_sptr |
| type_decl_diff::second_type_decl() const |
| {return dynamic_pointer_cast<type_decl>(second_subject());} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// type_decl_diff. |
| const string& |
| type_decl_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "type_decl_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| type_decl_diff::has_changes() const |
| {return first_type_decl() != second_type_decl();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| type_decl_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_type_decl(), *second_type_decl(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| /// Ouputs a report of the differences between of the two type_decl |
| /// involved in the type_decl_diff. |
| /// |
| /// @param out the output stream to emit the report to. |
| /// |
| /// @param indent the string to use for indentatino indent. |
| void |
| type_decl_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute a diff between two type_decl. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// This function doesn't actually compute a diff. As a type_decl is |
| /// very simple (unlike compound constructs like function_decl or |
| /// class_decl) it's easy to just compare the components of the |
| /// type_decl to know what has changed. Thus this function just |
| /// builds and return a type_decl_diff object. The |
| /// type_decl_diff::report function will just compare the components |
| /// of the the two type_decl and display where and how they differ. |
| /// |
| /// @param first a pointer to the first type_decl to |
| /// consider. |
| /// |
| /// @param second a pointer to the second type_decl to consider. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return a pointer to the resulting type_decl_diff. |
| type_decl_diff_sptr |
| compute_diff(const type_decl_sptr first, |
| const type_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| type_decl_diff_sptr result(new type_decl_diff(first, second, ctxt)); |
| |
| // We don't need to actually compute a diff here as a type_decl |
| // doesn't have complicated sub-components. type_decl_diff::report |
| // just walks the members of the type_decls and display information |
| // about the ones that have changed. On a similar note, |
| // type_decl_diff::length returns 0 if the two type_decls are equal, |
| // and 1 otherwise. |
| |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| |
| // </type_decl_diff stuff> |
| |
| // <typedef_diff stuff> |
| |
| /// Populate the vector of children node of the @ref diff base type |
| /// sub-object of this instance of @ref typedef_diff. |
| /// |
| /// The children node can then later be retrieved using |
| /// diff::children_node(). |
| void |
| typedef_diff::chain_into_hierarchy() |
| {append_child_node(underlying_type_diff());} |
| |
| /// Constructor for typedef_diff. |
| /// |
| /// @param first the first subject of the diff. |
| /// |
| /// @param second the second subject of the diff. |
| /// |
| /// @param underlying the underlying diff of the @ref typedef_diff. |
| /// That is the diff between the underlying types of @p first and @p |
| /// second. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref typedef_diff. Otherwise memory |
| /// corruption issues occur. |
| typedef_diff::typedef_diff(const typedef_decl_sptr first, |
| const typedef_decl_sptr second, |
| const diff_sptr underlying, |
| diff_context_sptr ctxt) |
| : type_diff_base(first, second, ctxt), |
| priv_(new priv(underlying)) |
| {} |
| |
| /// Finish building the current instance of @ref typedef_diff. |
| void |
| typedef_diff::finish_diff_type() |
| { |
| if (diff::priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| diff::priv_->finished_ = true; |
| } |
| |
| /// Getter for the firt typedef_decl involved in the diff. |
| /// |
| /// @return the first subject of the diff. |
| const typedef_decl_sptr |
| typedef_diff::first_typedef_decl() const |
| {return dynamic_pointer_cast<typedef_decl>(first_subject());} |
| |
| /// Getter for the second typedef_decl involved in the diff. |
| /// |
| /// @return the second subject of the diff. |
| const typedef_decl_sptr |
| typedef_diff::second_typedef_decl() const |
| {return dynamic_pointer_cast<typedef_decl>(second_subject());} |
| |
| /// Getter for the diff between the two underlying types of the |
| /// typedefs. |
| /// |
| /// @return the diff object reprensenting the difference between the |
| /// two underlying types of the typedefs. |
| const diff_sptr |
| typedef_diff::underlying_type_diff() const |
| {return priv_->underlying_type_diff_;} |
| |
| /// Setter for the diff between the two underlying types of the |
| /// typedefs. |
| /// |
| /// @param d the new diff object reprensenting the difference between |
| /// the two underlying types of the typedefs. |
| void |
| typedef_diff::underlying_type_diff(const diff_sptr d) |
| {priv_->underlying_type_diff_ = d;} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// typedef_diff. |
| const string& |
| typedef_diff::get_pretty_representation() const |
| { |
| if (diff::priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "typedef_diff[" |
| << first_subject()->get_pretty_representation() |
| << ", " |
| << second_subject()->get_pretty_representation() |
| << "]"; |
| diff::priv_->pretty_representation_ = o.str(); |
| } |
| return diff::priv_->pretty_representation_; |
| } |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| typedef_diff::has_changes() const |
| { |
| decl_base_sptr second = second_typedef_decl(); |
| return !(*first_typedef_decl() == *second); |
| } |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| typedef_diff::has_local_changes() const |
| { |
| ir::change_kind k = ir::NO_CHANGE_KIND; |
| if (!equals(*first_typedef_decl(), *second_typedef_decl(), &k)) |
| return k & ir::ALL_LOCAL_CHANGES_MASK; |
| return ir::NO_CHANGE_KIND; |
| } |
| |
| /// Reports the difference between the two subjects of the diff in a |
| /// serialized form. |
| /// |
| /// @param out the output stream to emit the report to. |
| /// |
| /// @param indent the indentation string to use. |
| void |
| typedef_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Compute a diff between two typedef_decl. |
| /// |
| /// Note that the two types must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param first a pointer to the first typedef_decl to consider. |
| /// |
| /// @param second a pointer to the second typedef_decl to consider. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return a pointer to the the resulting typedef_diff. |
| typedef_diff_sptr |
| compute_diff(const typedef_decl_sptr first, |
| const typedef_decl_sptr second, |
| diff_context_sptr ctxt) |
| { |
| if (first && second) |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| diff_sptr d = compute_diff_for_types(first->get_underlying_type(), |
| second->get_underlying_type(), |
| ctxt); |
| typedef_diff_sptr result(new typedef_diff(first, second, d, ctxt)); |
| |
| ctxt->initialize_canonical_diff(result); |
| |
| return result; |
| } |
| |
| /// Return the leaf underlying diff node of a @ref typedef_diff node. |
| /// |
| /// If the underlying diff node of a @ref typedef_diff node is itself |
| /// a @ref typedef_diff node, then recursively look at the underlying |
| /// diff nodes to get the first one that is not a a @ref typedef_diff |
| /// node. This is what a leaf underlying diff node means. |
| /// |
| /// Otherwise, if the underlying diff node of @ref typedef_diff is |
| /// *NOT* a @ref typedef_diff node, then just return the underlying |
| /// diff node. |
| /// |
| /// And if the diff node considered is not a @ref typedef_diff node, |
| /// then just return it. |
| /// |
| /// @return the leaf underlying diff node of a @p diff. |
| const diff* |
| get_typedef_diff_underlying_type_diff(const diff* diff) |
| { |
| const typedef_diff* d = dynamic_cast<const typedef_diff*>(diff); |
| if (!d) |
| return diff; |
| |
| if (const typedef_diff* deef = |
| dynamic_cast<const typedef_diff*>(d->underlying_type_diff().get())) |
| return get_typedef_diff_underlying_type_diff(deef); |
| |
| return d->underlying_type_diff().get(); |
| } |
| |
| // </typedef_diff stuff> |
| |
| // <translation_unit_diff stuff> |
| |
| /// Constructor for translation_unit_diff. |
| /// |
| /// @param first the first translation unit to consider for this diff. |
| /// |
| /// @param second the second translation unit to consider for this diff. |
| /// |
| /// @param ctxt the context of the diff. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref translation_unit_diff. Otherwise memory |
| /// corruption issues occur. |
| translation_unit_diff::translation_unit_diff(translation_unit_sptr first, |
| translation_unit_sptr second, |
| diff_context_sptr ctxt) |
| : scope_diff(first->get_global_scope(), second->get_global_scope(), ctxt), |
| priv_(new priv(first, second)) |
| { |
| } |
| |
| /// Getter for the first translation unit of this diff. |
| /// |
| /// @return the first translation unit of this diff. |
| const translation_unit_sptr |
| translation_unit_diff::first_translation_unit() const |
| {return priv_->first_;} |
| |
| /// Getter for the second translation unit of this diff. |
| /// |
| /// @return the second translation unit of this diff. |
| const translation_unit_sptr |
| translation_unit_diff::second_translation_unit() const |
| {return priv_->second_;} |
| |
| /// Return true iff the current diff node carries a change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| translation_unit_diff::has_changes() const |
| {return scope_diff::has_changes();} |
| |
| /// @return the kind of local change carried by the current diff node. |
| /// The value returned is zero if the current node carries no local |
| /// change. |
| enum change_kind |
| translation_unit_diff::has_local_changes() const |
| {return ir::NO_CHANGE_KIND;} |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the output stream to serialize the report to. |
| /// |
| /// @param indent the prefix to use as indentation for the report. |
| void |
| translation_unit_diff::report(ostream& out, const string& indent) const |
| {scope_diff::report(out, indent);} |
| |
| /// Compute the diff between two translation_units. |
| /// |
| /// Note that the two translation units must have been created in the |
| /// same @ref environment, otherwise, this function aborts. |
| /// |
| /// @param first the first translation_unit to consider. |
| /// |
| /// @param second the second translation_unit to consider. |
| /// |
| /// @param ctxt the diff context to use. If null, this function will |
| /// create a new context and set to the diff object returned. |
| /// |
| /// @return the newly created diff object. |
| translation_unit_diff_sptr |
| compute_diff(const translation_unit_sptr first, |
| const translation_unit_sptr second, |
| diff_context_sptr ctxt) |
| { |
| ABG_ASSERT(first && second); |
| |
| ABG_ASSERT(first->get_environment() == second->get_environment()); |
| |
| if (!ctxt) |
| ctxt.reset(new diff_context); |
| |
| // TODO: handle first or second having empty contents. |
| translation_unit_diff_sptr tu_diff(new translation_unit_diff(first, second, |
| ctxt)); |
| scope_diff_sptr sc_diff = dynamic_pointer_cast<scope_diff>(tu_diff); |
| |
| compute_diff(static_pointer_cast<scope_decl>(first->get_global_scope()), |
| static_pointer_cast<scope_decl>(second->get_global_scope()), |
| sc_diff, |
| ctxt); |
| |
| ctxt->initialize_canonical_diff(tu_diff); |
| |
| return tu_diff; |
| } |
| |
| // </translation_unit_diff stuff> |
| |
| // <diff_maps stuff> |
| |
| /// The private data of the @ref diff_maps type. |
| struct diff_maps::priv |
| { |
| string_diff_ptr_map type_decl_diff_map_; |
| string_diff_ptr_map enum_diff_map_; |
| string_diff_ptr_map class_diff_map_; |
| string_diff_ptr_map union_diff_map_; |
| string_diff_ptr_map typedef_diff_map_; |
| string_diff_ptr_map array_diff_map_; |
| string_diff_ptr_map reference_diff_map_; |
| string_diff_ptr_map function_type_diff_map_; |
| string_diff_ptr_map function_decl_diff_map_; |
| string_diff_ptr_map var_decl_diff_map_; |
| string_diff_ptr_map distinct_diff_map_; |
| string_diff_ptr_map fn_parm_diff_map_; |
| diff_artifact_set_map_type impacted_artifacts_map_; |
| }; // end struct diff_maps::priv |
| |
| /// Default constructor of the @ref diff_maps type. |
| diff_maps::diff_maps() |
| : priv_(new diff_maps::priv()) |
| {} |
| |
| diff_maps::~diff_maps() = default; |
| |
| /// Getter of the map that contains basic type diffs. |
| /// |
| /// @return the map that contains basic type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_type_decl_diff_map() const |
| {return priv_->type_decl_diff_map_;} |
| |
| /// Getter of the map that contains basic type diffs. |
| /// |
| /// @return the map that contains basic type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_type_decl_diff_map() |
| {return priv_->type_decl_diff_map_;} |
| |
| /// Getter of the map that contains enum type diffs. |
| /// |
| /// @return the map that contains enum type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_enum_diff_map() const |
| {return priv_->enum_diff_map_;} |
| |
| /// Getter of the map that contains enum type diffs. |
| /// |
| /// @return the map that contains enum type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_enum_diff_map() |
| {return priv_->enum_diff_map_;} |
| |
| /// Getter of the map that contains class type diffs. |
| /// |
| /// @return the map that contains class type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_class_diff_map() const |
| {return priv_->class_diff_map_;} |
| |
| /// Getter of the map that contains class type diffs. |
| /// |
| /// @return the map that contains class type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_class_diff_map() |
| {return priv_->class_diff_map_;} |
| |
| /// Getter of the map that contains union type diffs. |
| /// |
| /// @return the map that contains union type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_union_diff_map() const |
| {return priv_->union_diff_map_;} |
| |
| /// Getter of the map that contains union type diffs. |
| /// |
| /// @return the map that contains union type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_union_diff_map() |
| {return priv_->union_diff_map_;} |
| |
| /// Getter of the map that contains typedef type diffs. |
| /// |
| /// @return the map that contains typedef type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_typedef_diff_map() const |
| {return priv_->typedef_diff_map_;} |
| |
| /// Getter of the map that contains typedef type diffs. |
| /// |
| /// @return the map that contains typedef type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_typedef_diff_map() |
| {return priv_->typedef_diff_map_;} |
| |
| /// Getter of the map that contains array type diffs. |
| /// |
| /// @return the map that contains array type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_array_diff_map() const |
| {return priv_->array_diff_map_;} |
| |
| /// Getter of the map that contains array type diffs. |
| /// |
| /// @return the map that contains array type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_array_diff_map() |
| {return priv_->array_diff_map_;} |
| |
| /// Getter of the map that contains reference type diffs. |
| /// |
| /// @return the map that contains reference type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_reference_diff_map() const |
| {return priv_->reference_diff_map_;} |
| |
| /// Getter of the map that contains reference type diffs. |
| /// |
| /// @return the map that contains reference type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_reference_diff_map() |
| {{return priv_->reference_diff_map_;}} |
| |
| /// Getter of the map that contains function parameter diffs. |
| /// |
| /// @return the map that contains function parameter diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_fn_parm_diff_map() const |
| {return priv_->fn_parm_diff_map_;} |
| |
| /// Getter of the map that contains function parameter diffs. |
| /// |
| /// @return the map that contains function parameter diffs. |
| string_diff_ptr_map& |
| diff_maps::get_fn_parm_diff_map() |
| {return priv_->fn_parm_diff_map_;} |
| |
| /// Getter of the map that contains function type diffs. |
| /// |
| /// @return the map that contains function type diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_function_type_diff_map() const |
| {return priv_->function_type_diff_map_;} |
| |
| /// Getter of the map that contains function type diffs. |
| /// |
| /// @return the map that contains function type diffs. |
| string_diff_ptr_map& |
| diff_maps::get_function_type_diff_map() |
| {return priv_->function_type_diff_map_;} |
| |
| /// Getter of the map that contains function decl diffs. |
| /// |
| /// @return the map that contains function decl diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_function_decl_diff_map() const |
| {return priv_->function_decl_diff_map_;} |
| |
| /// Getter of the map that contains function decl diffs. |
| /// |
| /// @return the map that contains function decl diffs. |
| string_diff_ptr_map& |
| diff_maps::get_function_decl_diff_map() |
| {return priv_->function_decl_diff_map_;} |
| |
| /// Getter of the map that contains var decl diffs. |
| /// |
| /// @return the map that contains var decl diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_var_decl_diff_map() const |
| {return priv_->var_decl_diff_map_;} |
| |
| /// Getter of the map that contains var decl diffs. |
| /// |
| /// @return the map that contains var decl diffs. |
| string_diff_ptr_map& |
| diff_maps::get_var_decl_diff_map() |
| {return priv_->var_decl_diff_map_;} |
| |
| /// Getter of the map that contains distinct diffs. |
| /// |
| /// @return the map that contains distinct diffs. |
| const string_diff_ptr_map& |
| diff_maps::get_distinct_diff_map() const |
| {return priv_->distinct_diff_map_;} |
| |
| /// Getter of the map that contains distinct diffs. |
| /// |
| /// @return the map that contains distinct diffs. |
| string_diff_ptr_map& |
| diff_maps::get_distinct_diff_map() |
| {return priv_->distinct_diff_map_;} |
| |
| /// Insert a new diff node into the current instance of @ref diff_maps. |
| /// |
| /// @param dif the new diff node to insert into the @ref diff_maps. |
| /// |
| /// @param impacted_iface the interface (global function or variable) |
| /// currently being analysed that led to analysing the diff node @p |
| /// dif. In other words, this is the interface impacted by the diff |
| /// node @p dif. Note that this can be nil in cases where we are |
| /// directly analysing changes to a type that is not reachable from |
| /// any global function or variable. |
| /// |
| /// @return true iff the diff node could be added to the current |
| /// instance of @ref diff_maps. |
| bool |
| diff_maps::insert_diff_node(const diff *dif, |
| const type_or_decl_base_sptr& impacted_iface) |
| { |
| string n = get_pretty_representation(dif->first_subject(), |
| /*internal=*/true); |
| if (const type_decl_diff *d = is_diff_of_basic_type(dif)) |
| get_type_decl_diff_map()[n] = const_cast<type_decl_diff*>(d); |
| else if (const enum_diff *d = is_enum_diff(dif)) |
| get_enum_diff_map()[n] = const_cast<enum_diff*>(d); |
| else if (const class_diff *d = is_class_diff(dif)) |
| get_class_diff_map()[n] = const_cast<class_diff*>(d); |
| else if (const union_diff *d = is_union_diff(dif)) |
| get_union_diff_map()[n] = const_cast<union_diff*>(d); |
| else if (const typedef_diff *d = is_typedef_diff(dif)) |
| get_typedef_diff_map()[n] = const_cast<typedef_diff*>(d); |
| else if (const array_diff *d = is_array_diff(dif)) |
| get_array_diff_map()[n] = const_cast<array_diff*>(d); |
| else if (const reference_diff *d = is_reference_diff(dif)) |
| get_reference_diff_map()[n] = const_cast<reference_diff*>(d); |
| else if (const fn_parm_diff *d = is_fn_parm_diff(dif)) |
| get_fn_parm_diff_map()[n] = const_cast<fn_parm_diff*>(d); |
| else if (const function_type_diff *d = is_function_type_diff(dif)) |
| get_function_type_diff_map()[n] = const_cast<function_type_diff*>(d); |
| else if (const var_diff *d = is_var_diff(dif)) |
| get_var_decl_diff_map()[n] = const_cast<var_diff*>(d); |
| else if (const function_decl_diff *d = is_function_decl_diff(dif)) |
| get_function_decl_diff_map()[n] = const_cast<function_decl_diff*>(d); |
| else if (const distinct_diff *d = is_distinct_diff(dif)) |
| get_distinct_diff_map()[n] = const_cast<distinct_diff*>(d); |
| else if (is_base_diff(dif)) |
| // we silently drop this case. |
| return true; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| |
| // Update the map that associates this diff node to the set of |
| // interfaces it impacts. |
| |
| if (impacted_iface) |
| { |
| diff_artifact_set_map_type::iterator i = |
| priv_->impacted_artifacts_map_.find(dif); |
| |
| if (i == priv_->impacted_artifacts_map_.end()) |
| { |
| artifact_sptr_set_type set; |
| set.insert(impacted_iface); |
| priv_->impacted_artifacts_map_[dif] = set; |
| } |
| else |
| i->second.insert(impacted_iface); |
| } |
| |
| return true; |
| } |
| |
| /// Lookup the interfaces that are impacted by a given leaf diff node. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return the set of artifacts impacted by @p d. |
| artifact_sptr_set_type* |
| diff_maps::lookup_impacted_interfaces(const diff *d) const |
| { |
| diff_artifact_set_map_type::iterator i = |
| priv_->impacted_artifacts_map_.find(d); |
| |
| if (i == priv_->impacted_artifacts_map_.end()) |
| return 0; |
| |
| return &i->second; |
| } |
| |
| // |
| // </diff_maps stuff> |
| |
| /// Constructor for the @ref diff_stat type. |
| /// |
| /// @param ctxt the context of the corpus diff. Note that this |
| /// context object must stay alive at least during the life time of |
| /// the current instance of @ref corpus_diff::diff_stats. Otherwise |
| /// memory corruption issues occur. |
| corpus_diff::diff_stats::diff_stats(diff_context_sptr ctxt) |
| : priv_(new priv(ctxt)) |
| {} |
| |
| /// Getter for the number of functions removed. |
| /// |
| /// @return the number of functions removed. |
| size_t |
| corpus_diff::diff_stats::num_func_removed() const |
| {return priv_->num_func_removed;} |
| |
| /// Setter for the number of functions removed. |
| /// |
| /// @param n the new number of functions removed. |
| void |
| corpus_diff::diff_stats::num_func_removed(size_t n) |
| {priv_->num_func_removed = n;} |
| |
| /// Getter for the number of removed functions that have been filtered |
| /// out. |
| /// |
| /// @return the number of removed functions that have been filtered |
| /// out. |
| size_t |
| corpus_diff::diff_stats::num_removed_func_filtered_out() const |
| { |
| if (priv_->ctxt() && !priv_->ctxt()->show_deleted_fns()) |
| return num_func_removed(); |
| return priv_->num_removed_func_filtered_out; |
| } |
| |
| /// Setter for the number of removed functions that have been filtered |
| /// out. |
| /// |
| /// @param t the new value. |
| void |
| corpus_diff::diff_stats::num_removed_func_filtered_out(size_t t) |
| {priv_->num_removed_func_filtered_out = t;} |
| |
| /// Getter for the net number of function removed. |
| /// |
| /// This is the difference between the number of functions removed and |
| /// the number of functons removed that have been filtered out. |
| /// |
| /// @return the net number of function removed. |
| size_t |
| corpus_diff::diff_stats::net_num_func_removed() const |
| { |
| ABG_ASSERT(num_func_removed() >= num_removed_func_filtered_out()); |
| return num_func_removed() - num_removed_func_filtered_out(); |
| } |
| |
| /// Getter for the number of functions added. |
| /// |
| /// @return the number of functions added. |
| size_t |
| corpus_diff::diff_stats::num_func_added() const |
| {return priv_->num_func_added;} |
| |
| /// Setter for the number of functions added. |
| /// |
| /// @param n the new number of functions added. |
| void |
| corpus_diff::diff_stats::num_func_added(size_t n) |
| {priv_->num_func_added = n;} |
| |
| /// Getter for the number of added function that have been filtered out. |
| /// |
| /// @return the number of added function that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_added_func_filtered_out() const |
| { |
| if (priv_->ctxt() && !priv_->ctxt()->show_added_fns()) |
| return num_func_added(); |
| return priv_->num_added_func_filtered_out; |
| } |
| |
| /// Setter for the number of added function that have been filtered |
| /// out. |
| /// |
| /// @param n the new value. |
| void |
| corpus_diff::diff_stats::num_added_func_filtered_out(size_t n) |
| {priv_->num_added_func_filtered_out = n;} |
| |
| /// Getter for the net number of added functions. |
| /// |
| /// The net number of added functions is the difference between the |
| /// number of added functions and the number of added functions that |
| /// have been filtered out. |
| /// |
| /// @return the net number of added functions. |
| size_t |
| corpus_diff::diff_stats::net_num_func_added() const |
| { |
| ABG_ASSERT(num_func_added() >= num_added_func_filtered_out()); |
| return num_func_added() - num_added_func_filtered_out(); |
| } |
| |
| /// Getter for the number of functions that have a change in one of |
| /// their sub-types. |
| /// |
| /// @return the number of functions that have a change in one of their |
| /// sub-types. |
| size_t |
| corpus_diff::diff_stats::num_func_changed() const |
| {return priv_->num_func_changed;} |
| |
| /// Setter for the number of functions that have a change in one of |
| /// their sub-types. |
| /// |
| /// @@param n the new number of functions that have a change in one of |
| /// their sub-types. |
| void |
| corpus_diff::diff_stats::num_func_changed(size_t n) |
| {priv_->num_func_changed = n;} |
| |
| /// Getter for the number of functions that have a change in one of |
| /// their sub-types, and that have been filtered out. |
| /// |
| /// @return the number of functions that have a change in one of their |
| /// sub-types, and that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_changed_func_filtered_out() const |
| {return priv_->num_changed_func_filtered_out;} |
| |
| /// Setter for the number of functions that have a change in one of |
| /// their sub-types, and that have been filtered out. |
| /// |
| /// @param n the new number of functions that have a change in one of their |
| /// sub-types, and that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_changed_func_filtered_out(size_t n) |
| {priv_->num_changed_func_filtered_out = n;} |
| |
| /// Getter for the number of functions that carry virtual member |
| /// offset changes. |
| /// |
| /// @return the number of functions that carry virtual member changes. |
| size_t |
| corpus_diff::diff_stats::num_func_with_virtual_offset_changes() const |
| {return priv_->num_func_with_virt_offset_changes;} |
| |
| /// Setter for the number of functions that carry virtual member |
| /// offset changes. |
| /// |
| /// @param n the new number of functions that carry virtual member |
| /// offset. changes. |
| void |
| corpus_diff::diff_stats::num_func_with_virtual_offset_changes(size_t n) |
| {priv_->num_func_with_virt_offset_changes = n;} |
| |
| /// Getter for the number of functions that have a change in their |
| /// sub-types, minus the number of these functions that got filtered |
| /// out from the diff. |
| /// |
| /// @return for the the number of functions that have a change in |
| /// their sub-types, minus the number of these functions that got |
| /// filtered out from the diff. |
| size_t |
| corpus_diff::diff_stats::net_num_func_changed() const |
| {return num_func_changed() - num_changed_func_filtered_out();} |
| |
| /// Getter for the number of variables removed. |
| /// |
| /// @return the number of variables removed. |
| size_t |
| corpus_diff::diff_stats::num_vars_removed() const |
| {return priv_->num_vars_removed;} |
| |
| /// Setter for the number of variables removed. |
| /// |
| /// @param n the new number of variables removed. |
| void |
| corpus_diff::diff_stats::num_vars_removed(size_t n) |
| {priv_->num_vars_removed = n;} |
| |
| /// Getter for the number removed variables that have been filtered |
| /// out. |
| /// |
| /// @return the number removed variables that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_removed_vars_filtered_out() const |
| { |
| if (priv_->ctxt() && !priv_->ctxt()->show_deleted_vars()) |
| return num_vars_removed(); |
| return priv_->num_removed_vars_filtered_out; |
| } |
| |
| /// Setter for the number of removed variables that have been filtered |
| /// out. |
| /// |
| /// @param n the new value. |
| void |
| corpus_diff::diff_stats::num_removed_vars_filtered_out(size_t n) const |
| {priv_->num_removed_vars_filtered_out = n;} |
| |
| /// Getter for the net number of removed variables. |
| /// |
| /// The net number of removed variables is the difference between the |
| /// number of removed variables and the number of removed variables |
| /// that have been filtered out. |
| /// |
| /// @return the net number of removed variables. |
| size_t |
| corpus_diff::diff_stats::net_num_vars_removed() const |
| { |
| ABG_ASSERT(num_vars_removed() >= num_removed_vars_filtered_out()); |
| return num_vars_removed() - num_removed_vars_filtered_out(); |
| } |
| |
| /// Getter for the number of variables added. |
| /// |
| /// @return the number of variables added. |
| size_t |
| corpus_diff::diff_stats::num_vars_added() const |
| {return priv_->num_vars_added;} |
| |
| /// Setter for the number of variables added. |
| /// |
| /// @param n the new number of variables added. |
| void |
| corpus_diff::diff_stats::num_vars_added(size_t n) |
| {priv_->num_vars_added = n;} |
| |
| /// Getter for the number of added variables that have been filtered |
| /// out. |
| /// |
| /// @return the number of added variables that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_added_vars_filtered_out() const |
| { |
| if (priv_->ctxt() && !priv_->ctxt()->show_added_vars()) |
| return num_vars_added(); |
| return priv_->num_added_vars_filtered_out; |
| } |
| |
| /// Setter for the number of added variables that have been filtered |
| /// out. |
| /// |
| /// @param n the new value. |
| void |
| corpus_diff::diff_stats::num_added_vars_filtered_out(size_t n) |
| {priv_->num_added_vars_filtered_out = n;} |
| |
| /// Getter for the net number of added variables. |
| /// |
| /// The net number of added variables is the difference between the |
| /// number of added variables and the number of added variables that |
| /// have been filetered out. |
| /// |
| /// @return the net number of added variables. |
| size_t |
| corpus_diff::diff_stats::net_num_vars_added() const |
| { |
| ABG_ASSERT(num_vars_added() >= num_added_vars_filtered_out()); |
| return num_vars_added() - num_added_vars_filtered_out(); |
| } |
| |
| /// Getter for the number of variables that have a change in one of |
| /// their sub-types. |
| /// |
| /// @return the number of variables that have a change in one of their |
| /// sub-types. |
| size_t |
| corpus_diff::diff_stats::num_vars_changed() const |
| {return priv_->num_vars_changed;} |
| |
| /// Setter for the number of variables that have a change in one of |
| /// their sub-types. |
| /// |
| /// @param n the new number of variables that have a change in one of |
| /// their sub-types. |
| void |
| corpus_diff::diff_stats::num_vars_changed(size_t n) |
| {priv_->num_vars_changed = n;} |
| |
| /// Getter for the number of variables that have a change in one of |
| /// their sub-types, and that have been filtered out. |
| /// |
| /// @return the number of functions that have a change in one of their |
| /// sub-types, and that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_changed_vars_filtered_out() const |
| {return priv_->num_changed_vars_filtered_out;} |
| |
| /// Setter for the number of variables that have a change in one of |
| /// their sub-types, and that have been filtered out. |
| /// |
| /// @param n the new number of variables that have a change in one of their |
| /// sub-types, and that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_changed_vars_filtered_out(size_t n) |
| {priv_->num_changed_vars_filtered_out = n;} |
| |
| /// Getter for the number of variables that have a change in their |
| /// sub-types, minus the number of these variables that got filtered |
| /// out from the diff. |
| /// |
| /// @return for the the number of variables that have a change in |
| /// their sub-types, minus the number of these variables that got |
| /// filtered out from the diff. |
| size_t |
| corpus_diff::diff_stats::net_num_vars_changed() const |
| {return num_vars_changed() - num_changed_vars_filtered_out();} |
| |
| /// Getter for the number of function symbols (not referenced by any |
| /// debug info) that got removed. |
| /// |
| /// @return the number of function symbols (not referenced by any |
| /// debug info) that got removed. |
| size_t |
| corpus_diff::diff_stats::num_func_syms_removed() const |
| {return priv_->num_func_syms_removed;} |
| |
| /// Setter for the number of function symbols (not referenced by any |
| /// debug info) that got removed. |
| /// |
| /// @param n the number of function symbols (not referenced by any |
| /// debug info) that got removed. |
| void |
| corpus_diff::diff_stats::num_func_syms_removed(size_t n) |
| {priv_->num_func_syms_removed = n;} |
| |
| /// Getter for the number of removed function symbols, not referenced |
| /// by debug info, that have been filtered out. |
| /// |
| /// @return the number of removed function symbols, not referenced by |
| /// debug info, that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_removed_func_syms_filtered_out() const |
| { |
| if (priv_->ctxt() |
| && !priv_->ctxt()->show_symbols_unreferenced_by_debug_info()) |
| return num_func_syms_removed(); |
| return priv_->num_removed_func_syms_filtered_out; |
| } |
| |
| /// Setter for the number of removed function symbols, not referenced |
| /// by debug info, that have been filtered out. |
| /// |
| /// @param n the new the number of removed function symbols, not |
| /// referenced by debug info, that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_removed_func_syms_filtered_out(size_t n) |
| {priv_->num_removed_func_syms_filtered_out = n;} |
| |
| /// Getter of the net number of removed function symbols that are not |
| /// referenced by any debug info. |
| /// |
| /// This is the difference between the total number of removed |
| /// function symbols and the number of removed function symbols that |
| /// have been filteted out. Both numbers are for symbols not |
| /// referenced by debug info. |
| /// |
| /// return the net number of removed function symbols that are not |
| /// referenced by any debug info. |
| size_t |
| corpus_diff::diff_stats::net_num_removed_func_syms() const |
| { |
| ABG_ASSERT(num_func_syms_removed() >= num_removed_func_syms_filtered_out()); |
| return num_func_syms_removed() - num_removed_func_syms_filtered_out(); |
| } |
| |
| /// Getter for the number of function symbols (not referenced by any |
| /// debug info) that got added. |
| /// |
| /// @return the number of function symbols (not referenced by any |
| /// debug info) that got added. |
| size_t |
| corpus_diff::diff_stats::num_func_syms_added() const |
| {return priv_->num_func_syms_added;} |
| |
| /// Setter for the number of function symbols (not referenced by any |
| /// debug info) that got added. |
| /// |
| /// @param n the new number of function symbols (not referenced by any |
| /// debug info) that got added. |
| void |
| corpus_diff::diff_stats::num_func_syms_added(size_t n) |
| {priv_->num_func_syms_added = n;} |
| |
| /// Getter for the number of added function symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| /// |
| /// @return the number of added function symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_added_func_syms_filtered_out() const |
| { |
| if (priv_->ctxt() |
| && !(priv_->ctxt()->show_added_symbols_unreferenced_by_debug_info() |
| && priv_->ctxt()->show_symbols_unreferenced_by_debug_info())) |
| return num_func_syms_added(); |
| return priv_->num_added_func_syms_filtered_out; |
| } |
| |
| /// Setter for the number of added function symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| /// |
| /// @param n the new number of added function symbols, not referenced |
| /// by any debug info, that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_added_func_syms_filtered_out(size_t n) |
| {priv_->num_added_func_syms_filtered_out = n;} |
| |
| /// Getter of the net number of added function symbols that are not |
| /// referenced by any debug info. |
| /// |
| /// This is the difference between the total number of added |
| /// function symbols and the number of added function symbols that |
| /// have been filteted out. Both numbers are for symbols not |
| /// referenced by debug info. |
| /// |
| /// return the net number of added function symbols that are not |
| /// referenced by any debug info. |
| size_t |
| corpus_diff::diff_stats::net_num_added_func_syms() const |
| { |
| ABG_ASSERT(num_func_syms_added() >= num_added_func_syms_filtered_out()); |
| return num_func_syms_added()- num_added_func_syms_filtered_out(); |
| } |
| |
| /// Getter for the number of variable symbols (not referenced by any |
| /// debug info) that got removed. |
| /// |
| /// @return the number of variable symbols (not referenced by any |
| /// debug info) that got removed. |
| size_t |
| corpus_diff::diff_stats::num_var_syms_removed() const |
| {return priv_->num_var_syms_removed;} |
| |
| /// Setter for the number of variable symbols (not referenced by any |
| /// debug info) that got removed. |
| /// |
| /// @param n the number of variable symbols (not referenced by any |
| /// debug info) that got removed. |
| void |
| corpus_diff::diff_stats::num_var_syms_removed(size_t n) |
| {priv_->num_var_syms_removed = n;} |
| |
| /// Getter for the number of removed variable symbols, not referenced |
| /// by any debug info, that have been filtered out. |
| /// |
| /// @return the number of removed variable symbols, not referenced |
| /// by any debug info, that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_removed_var_syms_filtered_out() const |
| { |
| if (priv_->ctxt() |
| && !priv_->ctxt()->show_symbols_unreferenced_by_debug_info()) |
| return num_var_syms_removed(); |
| return priv_->num_removed_var_syms_filtered_out; |
| } |
| |
| /// Setter for the number of removed variable symbols, not referenced |
| /// by any debug info, that have been filtered out. |
| /// |
| /// @param n the number of removed variable symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_removed_var_syms_filtered_out(size_t n) |
| {priv_->num_removed_var_syms_filtered_out = n;} |
| |
| /// Getter of the net number of removed variable symbols that are not |
| /// referenced by any debug info. |
| /// |
| /// This is the difference between the total number of removed |
| /// variable symbols and the number of removed variable symbols that |
| /// have been filteted out. Both numbers are for symbols not |
| /// referenced by debug info. |
| /// |
| /// return the net number of removed variable symbols that are not |
| /// referenced by any debug info. |
| size_t |
| corpus_diff::diff_stats::net_num_removed_var_syms() const |
| { |
| ABG_ASSERT(num_var_syms_removed() >= num_removed_var_syms_filtered_out()); |
| return num_var_syms_removed() - num_removed_var_syms_filtered_out(); |
| } |
| |
| /// Getter for the number of variable symbols (not referenced by any |
| /// debug info) that got added. |
| /// |
| /// @return the number of variable symbols (not referenced by any |
| /// debug info) that got added. |
| size_t |
| corpus_diff::diff_stats::num_var_syms_added() const |
| {return priv_->num_var_syms_added;} |
| |
| /// Setter for the number of variable symbols (not referenced by any |
| /// debug info) that got added. |
| /// |
| /// @param n the new number of variable symbols (not referenced by any |
| /// debug info) that got added. |
| void |
| corpus_diff::diff_stats::num_var_syms_added(size_t n) |
| {priv_->num_var_syms_added = n;} |
| |
| /// Getter for the number of added variable symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| /// |
| /// @return the number of added variable symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_added_var_syms_filtered_out() const |
| { |
| if (priv_->ctxt() |
| && !(priv_->ctxt()->show_added_symbols_unreferenced_by_debug_info() |
| && priv_->ctxt()->show_symbols_unreferenced_by_debug_info())) |
| return num_var_syms_added(); |
| return priv_->num_added_var_syms_filtered_out; |
| } |
| |
| /// Setter for the number of added variable symbols, not referenced by |
| /// any debug info, that have been filtered out. |
| /// |
| /// @param n the new number of added variable symbols, not referenced |
| /// by any debug info, that have been filtered out. |
| void |
| corpus_diff::diff_stats::num_added_var_syms_filtered_out(size_t n) |
| {priv_->num_added_var_syms_filtered_out = n;} |
| |
| /// Getter of the net number of added variable symbols that are not |
| /// referenced by any debug info. |
| /// |
| /// This is the difference between the total number of added |
| /// variable symbols and the number of added variable symbols that |
| /// have been filteted out. Both numbers are for symbols not |
| /// referenced by debug info. |
| /// |
| /// return the net number of added variable symbols that are not |
| /// referenced by any debug info. |
| size_t |
| corpus_diff::diff_stats::net_num_added_var_syms() const |
| { |
| ABG_ASSERT(num_var_syms_added() >= num_added_var_syms_filtered_out()); |
| return num_var_syms_added() - num_added_var_syms_filtered_out(); |
| } |
| |
| /// Getter of the number of leaf type change diff nodes. |
| /// |
| /// @return the number of leaf type change diff nodes. |
| size_t |
| corpus_diff::diff_stats::num_leaf_changes() const |
| {return priv_->num_leaf_changes;} |
| |
| /// Setter of the number of leaf type change diff nodes. |
| /// |
| /// @param n the new number of leaf type change diff nodes. |
| void |
| corpus_diff::diff_stats::num_leaf_changes(size_t n) |
| {priv_->num_leaf_changes = n;} |
| |
| /// Getter of the number of leaf type change diff nodes that have been |
| /// filtered out. |
| /// |
| /// @return the number of leaf type change diff nodes that have been |
| size_t |
| corpus_diff::diff_stats::num_leaf_changes_filtered_out() const |
| {return priv_->num_leaf_changes_filtered_out;} |
| |
| /// Setter of the number of leaf type change diff nodes that have been |
| /// filtered out. |
| /// |
| /// @param n the new number of leaf type change diff nodes that have |
| /// been filtered out. |
| void |
| corpus_diff::diff_stats::num_leaf_changes_filtered_out(size_t n) |
| {priv_->num_leaf_changes_filtered_out = n;} |
| |
| /// Getter of the net number of leaf change diff nodes. |
| /// |
| /// This is the difference between the total number of leaf change |
| /// diff nodes, and the number of the leaf change diff nodes that have |
| /// been filtered out. |
| /// |
| /// A leaf change is either a type change, a function change or a |
| /// variable change. |
| size_t |
| corpus_diff::diff_stats::net_num_leaf_changes() const |
| { |
| ABG_ASSERT(num_leaf_changes() >= num_leaf_changes_filtered_out()); |
| return num_leaf_changes() - num_leaf_changes_filtered_out(); |
| } |
| |
| /// Getter for the number of leaf type change diff nodes. |
| /// |
| /// @return the number of leaf type changes diff nodes. |
| size_t |
| corpus_diff::diff_stats::num_leaf_type_changes() const |
| {return priv_->num_leaf_type_changes;} |
| |
| /// Setter for the number of leaf type change diff nodes. |
| /// |
| /// @param n the new number of leaf type change diff nodes. |
| void |
| corpus_diff::diff_stats::num_leaf_type_changes(size_t n) |
| {priv_->num_leaf_type_changes = n;} |
| |
| /// Getter for the number of filtered out leaf type change diff nodes. |
| /// |
| /// @return the number of filtered out leaf type change diff nodes. |
| size_t |
| corpus_diff::diff_stats::num_leaf_type_changes_filtered_out() const |
| {return priv_->num_leaf_type_changes_filtered_out;} |
| |
| /// Setter for the number of filtered out leaf type change diff nodes. |
| /// @param n the new number of filtered out leaf type change diff nodes. |
| void |
| corpus_diff::diff_stats::num_leaf_type_changes_filtered_out(size_t n) |
| {priv_->num_leaf_type_changes_filtered_out = n;} |
| |
| /// Getter for the net number of leaf type change diff nodes. |
| /// |
| /// This is the difference between the number of leaf type changes and |
| /// the number of filtered out leaf type changes. |
| /// |
| /// @return the net number of leaf type change diff nodes. |
| size_t |
| corpus_diff::diff_stats::net_num_leaf_type_changes() const |
| {return num_leaf_type_changes() - num_leaf_type_changes_filtered_out();} |
| |
| /// Getter for the number of leaf function change diff nodes. |
| /// |
| /// @return the number of leaf function change diff nodes. |
| size_t |
| corpus_diff::diff_stats::num_leaf_func_changes() const |
| {return priv_->num_leaf_func_changes;} |
| |
| /// Setter for the number of leaf function change diff nodes. |
| /// |
| /// @param n the new number of leaf function change diff nodes. |
| void |
| corpus_diff::diff_stats::num_leaf_func_changes(size_t n) |
| {priv_->num_leaf_func_changes = n;} |
| |
| /// Getter for the number of leaf function change diff nodes that were |
| /// filtered out. |
| /// |
| /// @return the number of leaf function change diff nodes that were |
| /// filtered out. |
| size_t |
| corpus_diff::diff_stats::num_leaf_func_changes_filtered_out() const |
| {return priv_->num_leaf_func_changes_filtered_out;} |
| |
| /// Setter for the number of leaf function change diff nodes that were |
| /// filtered out. |
| /// |
| /// @param n the new number of leaf function change diff nodes that |
| /// were filtered out. |
| void |
| corpus_diff::diff_stats::num_leaf_func_changes_filtered_out(size_t n) |
| {priv_->num_leaf_func_changes_filtered_out = n;} |
| |
| /// Getter for the net number of leaf function change diff nodes. |
| /// |
| /// This is the difference between the number of leaf function change |
| /// diff nodes and the number of filtered out leaf function change |
| /// diff nodes. |
| /// |
| /// @return the net number of leaf function change diff nodes. |
| size_t |
| corpus_diff::diff_stats::net_num_leaf_func_changes() const |
| {return num_leaf_func_changes() - num_leaf_func_changes_filtered_out();} |
| |
| /// Getter for the number of leaf variable change diff nodes. |
| /// |
| /// @return the number of leaf variable change diff nodes. |
| size_t |
| corpus_diff::diff_stats::num_leaf_var_changes() const |
| {return priv_->num_leaf_var_changes;} |
| |
| /// Setter for the number of leaf variable change diff nodes. |
| /// |
| /// @param n the number of leaf variable change diff nodes. |
| void |
| corpus_diff::diff_stats::num_leaf_var_changes(size_t n) |
| {priv_->num_leaf_var_changes = n;} |
| |
| /// Getter of the number of added types that are unreachable from the |
| /// public interface of the ABI corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| /// @return the number of added types that are unreachable from the |
| /// public interface of the ABI corpus. |
| size_t |
| corpus_diff::diff_stats::num_added_unreachable_types() const |
| {return priv_->num_added_unreachable_types;} |
| |
| /// Setter of the number of added types that are unreachable from the |
| /// public interface (global functions or variables) of the ABI |
| /// corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| /// @param n the new number of added types that are unreachable from |
| /// the public interface of the ABI corpus. |
| void |
| corpus_diff::diff_stats::num_added_unreachable_types(size_t n) |
| {priv_->num_added_unreachable_types = n;} |
| |
| /// Getter of the number of added types that are unreachable from |
| /// public interfaces and that are filtered out by suppression |
| /// specifications. |
| /// |
| /// @return the number of added types that are unreachable from public |
| /// interfaces and that are filtered out by suppression |
| /// specifications. |
| size_t |
| corpus_diff::diff_stats::num_added_unreachable_types_filtered_out() const |
| {return priv_->num_added_unreachable_types_filtered_out;} |
| |
| /// Setter of the number of added types that are unreachable from |
| /// public interfaces and that are filtered out by suppression |
| /// specifications. |
| /// |
| /// @param n the new number of added types that are unreachable from |
| /// public interfaces and that are filtered out by suppression |
| /// specifications. |
| void |
| corpus_diff::diff_stats::num_added_unreachable_types_filtered_out(size_t n) |
| {priv_->num_added_unreachable_types_filtered_out = n;} |
| |
| /// Getter of the number of added types that are unreachable from |
| /// public interfaces and that are *NOT* filtered out by suppression |
| /// specifications. |
| /// |
| /// @return the number of added types that are unreachable from public |
| /// interfaces and that are *NOT* filtered out by suppression |
| /// specifications. |
| size_t |
| corpus_diff::diff_stats::net_num_added_unreachable_types() const |
| { |
| ABG_ASSERT(num_added_unreachable_types() |
| >= |
| num_added_unreachable_types_filtered_out()); |
| |
| return (num_added_unreachable_types() |
| - |
| num_added_unreachable_types_filtered_out()); |
| } |
| |
| /// Getter of the number of removed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| /// @return the number of removed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| size_t |
| corpus_diff::diff_stats::num_removed_unreachable_types() const |
| {return priv_->num_removed_unreachable_types;} |
| |
| /// Setter of the number of removed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| ///@param n the new number of removed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| void |
| corpus_diff::diff_stats::num_removed_unreachable_types(size_t n) |
| {priv_->num_removed_unreachable_types = n;} |
| |
| /// Getter of the number of removed types that are not reachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| /// |
| /// @return the number of removed types that are not reachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| size_t |
| corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out() const |
| {return priv_->num_removed_unreachable_types_filtered_out;} |
| |
| /// Setter of the number of removed types that are not reachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| /// |
| /// @param n the new number of removed types that are not reachable |
| /// from public interfaces and that have been filtered out by |
| /// suppression specifications. |
| void |
| corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out(size_t n) |
| {priv_->num_removed_unreachable_types_filtered_out = n;} |
| |
| /// Getter of the number of removed types that are not reachable from |
| /// public interfaces and that have *NOT* been filtered out by |
| /// suppression specifications. |
| /// |
| /// @return the number of removed types that are not reachable from |
| /// public interfaces and that have *NOT* been filtered out by |
| /// suppression specifications. |
| size_t |
| corpus_diff::diff_stats::net_num_removed_unreachable_types() const |
| { |
| ABG_ASSERT(num_removed_unreachable_types() |
| >= |
| num_removed_unreachable_types_filtered_out()); |
| |
| return (num_removed_unreachable_types() |
| - |
| num_removed_unreachable_types_filtered_out()); |
| } |
| |
| /// Getter of the number of changed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| /// @return the number of changed types that are unreachable from the |
| /// public interface of the ABI corpus. |
| size_t |
| corpus_diff::diff_stats::num_changed_unreachable_types() const |
| {return priv_->num_changed_unreachable_types;} |
| |
| /// Setter of the number of changed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| /// |
| /// Public interface means the set of defined and publicly exported |
| /// functions and variables of the ABI corpus. |
| /// |
| ///@param n the new number of changed types that are unreachable from |
| /// the public interface of the ABI corpus. |
| void |
| corpus_diff::diff_stats::num_changed_unreachable_types(size_t n) |
| {priv_->num_changed_unreachable_types = n;} |
| |
| /// Getter of the number of changed types that are unreachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| /// |
| /// @return the number of changed types that are unreachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| size_t |
| corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out() const |
| {return priv_->num_changed_unreachable_types_filtered_out;} |
| |
| /// Setter of the number of changed types that are unreachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| /// |
| /// @param n the new number of changed types that are unreachable from |
| /// public interfaces and that have been filtered out by suppression |
| /// specifications. |
| void |
| corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out(size_t n) |
| {priv_->num_changed_unreachable_types_filtered_out = n;} |
| |
| /// Getter of the number of changed types that are unreachable from |
| /// public interfaces and that have *NOT* been filtered out by |
| /// suppression specifications. |
| /// |
| /// @return the number of changed types that are unreachable from |
| /// public interfaces and that have *NOT* been filtered out by |
| /// suppression specifications. |
| size_t |
| corpus_diff::diff_stats::net_num_changed_unreachable_types() const |
| { |
| ABG_ASSERT(num_changed_unreachable_types() |
| >= |
| num_changed_unreachable_types_filtered_out()); |
| |
| return (num_changed_unreachable_types() |
| - |
| num_changed_unreachable_types_filtered_out()); |
| } |
| |
| /// Getter for the number of leaf variable changes diff nodes that |
| /// have been filtered out. |
| /// |
| /// @return the number of leaf variable changes diff nodes that have |
| /// been filtered out. |
| size_t |
| corpus_diff::diff_stats::num_leaf_var_changes_filtered_out() const |
| {return priv_->num_leaf_var_changes_filtered_out;} |
| |
| /// Setter for the number of leaf variable changes diff nodes that |
| /// have been filtered out. |
| /// |
| /// @param n the number of leaf variable changes diff nodes that have |
| /// been filtered out. |
| void |
| corpus_diff::diff_stats::num_leaf_var_changes_filtered_out(size_t n) |
| {priv_->num_leaf_var_changes_filtered_out = n;} |
| |
| /// Getter for the net number of leaf variable change diff nodes. |
| /// |
| /// This the difference between the number of leaf variable change |
| /// diff nodes and the number of filtered out leaf variable change |
| /// diff nodes. |
| /// |
| /// @return the net number of leaf variable change diff nodes. |
| size_t |
| corpus_diff::diff_stats::net_num_leaf_var_changes() const |
| {return num_leaf_var_changes() - num_leaf_var_changes_filtered_out();} |
| |
| |
| // <corpus_diff stuff> |
| |
| /// Getter of the context associated with this corpus. |
| /// |
| /// @return a smart pointer to the context associate with the corpus. |
| diff_context_sptr |
| corpus_diff::priv::get_context() |
| {return ctxt_.lock();} |
| |
| /// Tests if the lookup tables are empty. |
| /// |
| /// @return true if the lookup tables are empty, false otherwise. |
| bool |
| corpus_diff::priv::lookup_tables_empty() const |
| { |
| return (deleted_fns_.empty() |
| && added_fns_.empty() |
| && changed_fns_map_.empty() |
| && deleted_vars_.empty() |
| && added_vars_.empty() |
| && changed_vars_map_.empty()); |
| } |
| |
| /// Clear the lookup tables useful for reporting an enum_diff. |
| void |
| corpus_diff::priv::clear_lookup_tables() |
| { |
| deleted_fns_.clear(); |
| added_fns_.clear(); |
| changed_fns_map_.clear(); |
| deleted_vars_.clear(); |
| added_vars_.clear(); |
| changed_vars_map_.clear(); |
| } |
| |
| /// If the lookup tables are not yet built, walk the differences and |
| /// fill the lookup tables. |
| void |
| corpus_diff::priv::ensure_lookup_tables_populated() |
| { |
| if (!lookup_tables_empty()) |
| return; |
| |
| diff_context_sptr ctxt = get_context(); |
| |
| { |
| edit_script& e = fns_edit_script_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| ABG_ASSERT(i < first_->get_functions().size()); |
| |
| function_decl* deleted_fn = first_->get_functions()[i]; |
| string n = deleted_fn->get_id(); |
| ABG_ASSERT(!n.empty()); |
| // The below is commented out because there can be several |
| // functions with the same ID in the corpus. So several |
| // functions with the same ID can be deleted. |
| // ABG_ASSERT(deleted_fns_.find(n) == deleted_fns_.end()); |
| deleted_fns_[n] = deleted_fn; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| function_decl* added_fn = second_->get_functions()[i]; |
| string n = added_fn->get_id(); |
| ABG_ASSERT(!n.empty()); |
| // The below is commented out because there can be several |
| // functions with the same ID in the corpus. So several |
| // functions with the same ID can be added. |
| // ABG_ASSERT(added_fns_.find(n) == added_fns_.end()); |
| string_function_ptr_map::const_iterator j = |
| deleted_fns_.find(n); |
| if (j != deleted_fns_.end()) |
| { |
| function_decl_sptr f(j->second, noop_deleter()); |
| function_decl_sptr s(added_fn, noop_deleter()); |
| function_decl_diff_sptr d = compute_diff(f, s, ctxt); |
| if (*j->second != *added_fn) |
| changed_fns_map_[j->first] = d; |
| deleted_fns_.erase(j); |
| } |
| else |
| added_fns_[n] = added_fn; |
| } |
| } |
| sort_string_function_decl_diff_sptr_map(changed_fns_map_, changed_fns_); |
| |
| // Now walk the allegedly deleted functions; check if their |
| // underlying symbols are deleted as well; otherwise, consider |
| // that the function in question hasn't been deleted. |
| |
| vector<string> to_delete; |
| for (string_function_ptr_map::const_iterator i = deleted_fns_.begin(); |
| i != deleted_fns_.end(); |
| ++i) |
| if (second_->lookup_function_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| deleted_fns_.erase(*i); |
| |
| // Do something similar for added functions. |
| |
| to_delete.clear(); |
| for (string_function_ptr_map::const_iterator i = added_fns_.begin(); |
| i != added_fns_.end(); |
| ++i) |
| { |
| if (first_->lookup_function_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| else if (! i->second->get_symbol()->get_version().is_empty() |
| && i->second->get_symbol()->get_version().is_default()) |
| // We are looking for a symbol that has a default version, |
| // and which seems to be newly added. Let's see if the same |
| // symbol with *no* version was already present in the |
| // former corpus. If yes, then the symbol shouldn't be |
| // considered as 'added'. |
| { |
| elf_symbol::version empty_version; |
| if (first_->lookup_function_symbol(i->second->get_symbol()->get_name(), |
| empty_version)) |
| to_delete.push_back(i->first); |
| } |
| } |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| added_fns_.erase(*i); |
| } |
| |
| { |
| edit_script& e = vars_edit_script_; |
| |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| ABG_ASSERT(i < first_->get_variables().size()); |
| |
| var_decl* deleted_var = first_->get_variables()[i]; |
| string n = deleted_var->get_id(); |
| ABG_ASSERT(!n.empty()); |
| ABG_ASSERT(deleted_vars_.find(n) == deleted_vars_.end()); |
| deleted_vars_[n] = deleted_var; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| var_decl* added_var = second_->get_variables()[i]; |
| string n = added_var->get_id(); |
| ABG_ASSERT(!n.empty()); |
| { |
| string_var_ptr_map::const_iterator k = added_vars_.find(n); |
| if ( k != added_vars_.end()) |
| { |
| ABG_ASSERT(is_member_decl(k->second) |
| && get_member_is_static(k->second)); |
| continue; |
| } |
| } |
| string_var_ptr_map::const_iterator j = |
| deleted_vars_.find(n); |
| if (j != deleted_vars_.end()) |
| { |
| if (*j->second != *added_var) |
| { |
| var_decl_sptr f(j->second, noop_deleter()); |
| var_decl_sptr s(added_var, noop_deleter()); |
| changed_vars_map_[n] = compute_diff(f, s, ctxt); |
| } |
| deleted_vars_.erase(j); |
| } |
| else |
| added_vars_[n] = added_var; |
| } |
| } |
| sort_string_var_diff_sptr_map(changed_vars_map_, |
| sorted_changed_vars_); |
| |
| // Now walk the allegedly deleted variables; check if their |
| // underlying symbols are deleted as well; otherwise consider |
| // that the variable in question hasn't been deleted. |
| |
| vector<string> to_delete; |
| for (string_var_ptr_map::const_iterator i = deleted_vars_.begin(); |
| i != deleted_vars_.end(); |
| ++i) |
| if (second_->lookup_variable_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| deleted_vars_.erase(*i); |
| |
| // Do something similar for added variables. |
| |
| to_delete.clear(); |
| for (string_var_ptr_map::const_iterator i = added_vars_.begin(); |
| i != added_vars_.end(); |
| ++i) |
| if (first_->lookup_variable_symbol(*i->second->get_symbol())) |
| to_delete.push_back(i->first); |
| else if (! i->second->get_symbol()->get_version().is_empty() |
| && i->second->get_symbol()->get_version().is_default()) |
| // We are looking for a symbol that has a default version, |
| // and which seems to be newly added. Let's see if the same |
| // symbol with *no* version was already present in the |
| // former corpus. If yes, then the symbol shouldn't be |
| // considered as 'added'. |
| { |
| elf_symbol::version empty_version; |
| if (first_->lookup_variable_symbol(i->second->get_symbol()->get_name(), |
| empty_version)) |
| to_delete.push_back(i->first); |
| } |
| |
| for (vector<string>::const_iterator i = to_delete.begin(); |
| i != to_delete.end(); |
| ++i) |
| added_vars_.erase(*i); |
| } |
| |
| // Massage the edit script for added/removed function symbols that |
| // were not referenced by any debug info and turn them into maps of |
| // {symbol_name, symbol}. |
| { |
| edit_script& e = unrefed_fn_syms_edit_script_; |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| ABG_ASSERT(i < first_->get_unreferenced_function_symbols().size()); |
| elf_symbol_sptr deleted_sym = |
| first_->get_unreferenced_function_symbols()[i]; |
| if (!second_->lookup_function_symbol(*deleted_sym)) |
| deleted_unrefed_fn_syms_[deleted_sym->get_id_string()] = deleted_sym; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| ABG_ASSERT(i < second_->get_unreferenced_function_symbols().size()); |
| elf_symbol_sptr added_sym = |
| second_->get_unreferenced_function_symbols()[i]; |
| if ((deleted_unrefed_fn_syms_.find(added_sym->get_id_string()) |
| == deleted_unrefed_fn_syms_.end())) |
| { |
| if (!first_->lookup_function_symbol(*added_sym)) |
| { |
| bool do_add = true; |
| if (! added_sym->get_version().is_empty() |
| && added_sym->get_version().is_default()) |
| { |
| // So added_seem has a default version. If |
| // the former corpus had a symbol with the |
| // same name as added_sym but with *no* |
| // version, then added_sym shouldn't be |
| // considered as a newly added symbol. |
| elf_symbol::version empty_version; |
| if (first_->lookup_function_symbol(added_sym->get_name(), |
| empty_version)) |
| do_add = false; |
| } |
| |
| if (do_add) |
| added_unrefed_fn_syms_[added_sym->get_id_string()] = |
| added_sym; |
| } |
| } |
| else |
| deleted_unrefed_fn_syms_.erase(added_sym->get_id_string()); |
| } |
| } |
| } |
| |
| // Massage the edit script for added/removed variable symbols that |
| // were not referenced by any debug info and turn them into maps of |
| // {symbol_name, symbol}. |
| { |
| edit_script& e = unrefed_var_syms_edit_script_; |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| ABG_ASSERT(i < first_->get_unreferenced_variable_symbols().size()); |
| elf_symbol_sptr deleted_sym = |
| first_->get_unreferenced_variable_symbols()[i]; |
| if (!second_->lookup_variable_symbol(*deleted_sym)) |
| deleted_unrefed_var_syms_[deleted_sym->get_id_string()] = deleted_sym; |
| } |
| |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| ABG_ASSERT(i < second_->get_unreferenced_variable_symbols().size()); |
| elf_symbol_sptr added_sym = |
| second_->get_unreferenced_variable_symbols()[i]; |
| if (deleted_unrefed_var_syms_.find(added_sym->get_id_string()) |
| == deleted_unrefed_var_syms_.end()) |
| { |
| if (!first_->lookup_variable_symbol(*added_sym)) |
| { |
| bool do_add = true; |
| if (! added_sym->get_version().is_empty() |
| && added_sym->get_version().is_default()) |
| { |
| // So added_seem has a default version. If |
| // the former corpus had a symbol with the |
| // same name as added_sym but with *no* |
| // version, then added_sym shouldn't be |
| // considered as a newly added symbol. |
| elf_symbol::version empty_version; |
| if (first_->lookup_variable_symbol(added_sym->get_name(), |
| empty_version)) |
| do_add = false; |
| } |
| |
| if (do_add) |
| added_unrefed_var_syms_[added_sym->get_id_string()] = |
| added_sym; |
| } |
| } |
| else |
| deleted_unrefed_var_syms_.erase(added_sym->get_id_string()); |
| } |
| } |
| } |
| |
| // Handle the unreachable_types_edit_script_ |
| { |
| edit_script& e = unreachable_types_edit_script_; |
| |
| // Populate the map of deleted unreachable types from the |
| // deletions of the edit script. |
| for (vector<deletion>::const_iterator it = e.deletions().begin(); |
| it != e.deletions().end(); |
| ++it) |
| { |
| unsigned i = it->index(); |
| type_base_sptr t |
| (first_->get_types_not_reachable_from_public_interfaces()[i]); |
| |
| if (!is_user_defined_type(t)) |
| continue; |
| |
| string repr = abigail::ir::get_pretty_representation(t, true); |
| deleted_unreachable_types_[repr] = t; |
| } |
| |
| // Populate the map of added and change unreachable types from the |
| // insertions of the edit script. |
| for (vector<insertion>::const_iterator it = e.insertions().begin(); |
| it != e.insertions().end(); |
| ++it) |
| { |
| for (vector<unsigned>::const_iterator iit = |
| it->inserted_indexes().begin(); |
| iit != it->inserted_indexes().end(); |
| ++iit) |
| { |
| unsigned i = *iit; |
| type_base_sptr t |
| (second_->get_types_not_reachable_from_public_interfaces()[i]); |
| |
| if (!is_user_defined_type(t)) |
| continue; |
| |
| string repr = abigail::ir::get_pretty_representation(t, true); |
| |
| // Let's see if the inserted type we are looking at was |
| // reported as deleted as well. |
| // |
| // If it's been deleted and a different version of it has |
| // now been added, it means it's been *changed*. In that |
| // case we'll compute the diff of that change and store it |
| // in the map of changed unreachable types. |
| // |
| // Otherwise, it means the type's been added so we'll add |
| // it to the set of added unreachable types. |
| |
| string_type_base_sptr_map::const_iterator j = |
| deleted_unreachable_types_.find(repr); |
| if (j != deleted_unreachable_types_.end()) |
| { |
| // So there was another type of the same pretty |
| // representation which was reported as deleted. |
| // Let's see if they are different or not ... |
| decl_base_sptr old_type = is_decl(j->second); |
| decl_base_sptr new_type = is_decl(t); |
| if (old_type != new_type) |
| { |
| // The previously added type is different from this |
| // one that is added. That means the initial type |
| // was changed. Let's compute its diff and store it |
| // as a changed type. |
| diff_sptr d = compute_diff(old_type, new_type, ctxt); |
| ABG_ASSERT(d->has_changes()); |
| changed_unreachable_types_[repr]= d; |
| } |
| |
| // In any case, the type was both deleted and added, |
| // so we cannot have it marked as being deleted. So |
| // let's remove it from the deleted types. |
| deleted_unreachable_types_.erase(j); |
| } |
| else |
| // The type wasn't previously reported as deleted, so |
| // it's really added. |
| added_unreachable_types_[repr] = t; |
| } |
| } |
| } |
| } |
| |
| /// Test if a change reports about a given @ref function_decl that is |
| /// changed in a certain way is suppressed by a given suppression |
| /// specifiation |
| /// |
| /// @param fn the @ref function_decl to consider. |
| /// |
| /// @param suppr the suppression specification to consider. |
| /// |
| /// @param k the kind of change that happened to @p fn. |
| /// |
| /// @param ctxt the context of the current diff. |
| /// |
| /// @return true iff the suppression specification @p suppr suppresses |
| /// change reports about function @p fn, if that function changes in |
| /// the way expressed by @p k. |
| static bool |
| function_is_suppressed(const function_decl* fn, |
| const suppression_sptr suppr, |
| function_suppression::change_kind k, |
| const diff_context_sptr ctxt) |
| { |
| function_suppression_sptr fn_suppr = is_function_suppression(suppr); |
| if (!fn_suppr) |
| return false; |
| return fn_suppr->suppresses_function(fn, k, ctxt); |
| } |
| |
| /// Test if a change reports about a given @ref var_decl that is |
| /// changed in a certain way is suppressed by a given suppression |
| /// specifiation |
| /// |
| /// @param fn the @ref var_decl to consider. |
| /// |
| /// @param suppr the suppression specification to consider. |
| /// |
| /// @param k the kind of change that happened to @p fn. |
| /// |
| /// @param ctxt the context of the current diff. |
| /// |
| /// @return true iff the suppression specification @p suppr suppresses |
| /// change reports about variable @p fn, if that variable changes in |
| /// the way expressed by @p k. |
| static bool |
| variable_is_suppressed(const var_decl* var, |
| const suppression_sptr suppr, |
| variable_suppression::change_kind k, |
| const diff_context_sptr ctxt) |
| { |
| variable_suppression_sptr var_suppr = is_variable_suppression(suppr); |
| if (!var_suppr) |
| return false; |
| return var_suppr->suppresses_variable(var, k, ctxt); |
| } |
| |
| /// Apply suppression specifications for this corpus diff to the set |
| /// of added/removed functions/variables, as well as to types not |
| /// reachable from global functions/variables. |
| void |
| corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types() |
| { |
| diff_context_sptr ctxt = get_context(); |
| |
| const suppressions_type& suppressions = ctxt->suppressions(); |
| for (suppressions_type::const_iterator i = suppressions.begin(); |
| i != suppressions.end(); |
| ++i) |
| { |
| // Added/Deleted functions. |
| if (function_suppression_sptr fn_suppr = is_function_suppression(*i)) |
| { |
| // Added functions |
| for (string_function_ptr_map::const_iterator e = added_fns_.begin(); |
| e != added_fns_.end(); |
| ++e) |
| if (function_is_suppressed(e->second, fn_suppr, |
| function_suppression::ADDED_FUNCTION_CHANGE_KIND, |
| ctxt)) |
| suppressed_added_fns_[e->first] = e->second; |
| |
| // Deleted functions. |
| for (string_function_ptr_map::const_iterator e = deleted_fns_.begin(); |
| e != deleted_fns_.end(); |
| ++e) |
| if (function_is_suppressed(e->second, fn_suppr, |
| function_suppression::DELETED_FUNCTION_CHANGE_KIND, |
| ctxt)) |
| suppressed_deleted_fns_[e->first] = e->second; |
| |
| // Added function symbols not referenced by any debug info |
| for (string_elf_symbol_map::const_iterator e = |
| added_unrefed_fn_syms_.begin(); |
| e != added_unrefed_fn_syms_.end(); |
| ++e) |
| if (fn_suppr->suppresses_function_symbol(e->second, |
| function_suppression::ADDED_FUNCTION_CHANGE_KIND, |
| ctxt)) |
| suppressed_added_unrefed_fn_syms_[e->first] = e->second; |
| |
| // Removed function symbols not referenced by any debug info |
| for (string_elf_symbol_map::const_iterator e = |
| deleted_unrefed_fn_syms_.begin(); |
| e != deleted_unrefed_fn_syms_.end(); |
| ++e) |
| if (fn_suppr->suppresses_function_symbol(e->second, |
| function_suppression::DELETED_FUNCTION_CHANGE_KIND, |
| ctxt)) |
| suppressed_deleted_unrefed_fn_syms_[e->first] = e->second; |
| } |
| // Added/Delete virtual member functions changes that might be |
| // suppressed by a type_suppression that matches the enclosing |
| // class of the virtual member function. |
| else if (type_suppression_sptr type_suppr = is_type_suppression(*i)) |
| { |
| // Added virtual functions |
| for (string_function_ptr_map::const_iterator e = added_fns_.begin(); |
| e != added_fns_.end(); |
| ++e) |
| if (is_member_function(e->second) |
| && get_member_function_is_virtual(e->second)) |
| { |
| function_decl *f = e->second; |
| class_decl_sptr c = |
| is_class_type(is_method_type(f->get_type())->get_class_type()); |
| ABG_ASSERT(c); |
| if (type_suppr->suppresses_type(c, ctxt)) |
| suppressed_added_fns_[e->first] = e->second; |
| } |
| // Deleted virtual functions |
| for (string_function_ptr_map::const_iterator e = deleted_fns_.begin(); |
| e != deleted_fns_.end(); |
| ++e) |
| if (is_member_function(e->second) |
| && get_member_function_is_virtual(e->second)) |
| { |
| function_decl *f = e->second; |
| class_decl_sptr c = |
| is_class_type(is_method_type(f->get_type())->get_class_type()); |
| ABG_ASSERT(c); |
| if (type_suppr->suppresses_type(c, ctxt)) |
| suppressed_deleted_fns_[e->first] = e->second; |
| } |
| |
| // Apply this type suppression to deleted types |
| // non-reachable from a public interface. |
| for (string_type_base_sptr_map::const_iterator e = |
| deleted_unreachable_types_.begin(); |
| e != deleted_unreachable_types_.end(); |
| ++e) |
| if (type_suppr->suppresses_type(e->second, ctxt)) |
| suppressed_deleted_unreachable_types_[e->first] = e->second; |
| |
| // Apply this type suppression to added types |
| // non-reachable from a public interface. |
| for (string_type_base_sptr_map::const_iterator e = |
| added_unreachable_types_.begin(); |
| e != added_unreachable_types_.end(); |
| ++e) |
| if (type_suppr->suppresses_type(e->second, ctxt)) |
| suppressed_added_unreachable_types_[e->first] = e->second; |
| } |
| // Added/Deleted variables |
| else if (variable_suppression_sptr var_suppr = |
| is_variable_suppression(*i)) |
| { |
| // Added variables |
| for (string_var_ptr_map::const_iterator e = added_vars_.begin(); |
| e != added_vars_.end(); |
| ++e) |
| if (variable_is_suppressed(e->second, var_suppr, |
| variable_suppression::ADDED_VARIABLE_CHANGE_KIND, |
| ctxt)) |
| suppressed_added_vars_[e->first] = e->second; |
| |
| //Deleted variables |
| for (string_var_ptr_map::const_iterator e = deleted_vars_.begin(); |
| e != deleted_vars_.end(); |
| ++e) |
| if (variable_is_suppressed(e->second, var_suppr, |
| variable_suppression::DELETED_VARIABLE_CHANGE_KIND, |
| ctxt)) |
| suppressed_deleted_vars_[e->first] = e->second; |
| |
| // Added variable symbols not referenced by any debug info |
| for (string_elf_symbol_map::const_iterator e = |
| added_unrefed_var_syms_.begin(); |
| e != added_unrefed_var_syms_.end(); |
| ++e) |
| if (var_suppr->suppresses_variable_symbol(e->second, |
| variable_suppression::ADDED_VARIABLE_CHANGE_KIND, |
| ctxt)) |
| suppressed_added_unrefed_var_syms_[e->first] = e->second; |
| |
| // Removed variable symbols not referenced by any debug info |
| for (string_elf_symbol_map::const_iterator e = |
| deleted_unrefed_var_syms_.begin(); |
| e != deleted_unrefed_var_syms_.end(); |
| ++e) |
| if (var_suppr->suppresses_variable_symbol(e->second, |
| variable_suppression::DELETED_VARIABLE_CHANGE_KIND, |
| ctxt)) |
| suppressed_deleted_unrefed_var_syms_[e->first] = e->second; |
| } |
| } |
| } |
| |
| /// Test if the change reports for a given deleted function have |
| /// been deleted. |
| /// |
| /// @param fn the function to consider. |
| /// |
| /// @return true iff the change reports for a give given deleted |
| /// function have been deleted. |
| bool |
| corpus_diff::priv::deleted_function_is_suppressed(const function_decl* fn) const |
| { |
| if (!fn) |
| return false; |
| |
| string_function_ptr_map::const_iterator i = |
| suppressed_deleted_fns_.find(fn->get_id()); |
| |
| return (i != suppressed_deleted_fns_.end()); |
| } |
| |
| /// Test if an added type that is unreachable from public interface |
| /// has been suppressed by a suppression specification. |
| /// |
| /// @param t the added unreachable type to be considered. |
| /// |
| /// @return true iff @p t has been suppressed by a suppression |
| /// specification. |
| bool |
| corpus_diff::priv::added_unreachable_type_is_suppressed(const type_base *t)const |
| { |
| if (!t) |
| return false; |
| |
| string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true); |
| string_type_base_sptr_map::const_iterator i = |
| suppressed_added_unreachable_types_.find(repr); |
| if (i == suppressed_added_unreachable_types_.end()) |
| return false; |
| |
| return true; |
| } |
| |
| /// Test if a deleted type that is unreachable from public interface |
| /// has been suppressed by a suppression specification. |
| /// |
| /// @param t the deleted unreachable type to be considered. |
| /// |
| /// @return true iff @p t has been suppressed by a suppression |
| /// specification. |
| bool |
| corpus_diff::priv::deleted_unreachable_type_is_suppressed(const type_base *t) const |
| { |
| if (!t) |
| return false; |
| |
| string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true); |
| string_type_base_sptr_map::const_iterator i = |
| suppressed_deleted_unreachable_types_.find(repr); |
| if (i == suppressed_deleted_unreachable_types_.end()) |
| return false; |
| |
| return true; |
| } |
| |
| /// Test if the change reports for a give given added function has |
| /// been deleted. |
| /// |
| /// @param fn the function to consider. |
| /// |
| /// @return true iff the change reports for a give given added |
| /// function has been deleted. |
| bool |
| corpus_diff::priv::added_function_is_suppressed(const function_decl* fn) const |
| { |
| if (!fn) |
| return false; |
| |
| string_function_ptr_map::const_iterator i = |
| suppressed_added_fns_.find(fn->get_id()); |
| |
| return (i != suppressed_added_fns_.end()); |
| } |
| |
| /// Test if the change reports for a give given deleted variable has |
| /// been deleted. |
| /// |
| /// @param var the variable to consider. |
| /// |
| /// @return true iff the change reports for a give given deleted |
| /// variable has been deleted. |
| bool |
| corpus_diff::priv::deleted_variable_is_suppressed(const var_decl* var) const |
| { |
| if (!var) |
| return false; |
| |
| string_var_ptr_map::const_iterator i = |
| suppressed_deleted_vars_.find(var->get_id()); |
| |
| return (i != suppressed_deleted_vars_.end()); |
| } |
| |
| /// Test if the change reports for a given added variable have been |
| /// suppressed. |
| /// |
| /// @param var the variable to consider. |
| /// |
| /// @return true iff the change reports for a given deleted |
| /// variable has been deleted. |
| bool |
| corpus_diff::priv::added_variable_is_suppressed(const var_decl* var) const |
| { |
| if (!var) |
| return false; |
| |
| string_var_ptr_map::const_iterator i = |
| suppressed_added_vars_.find(var->get_id()); |
| |
| return (i != suppressed_added_vars_.end()); |
| } |
| |
| /// Test if the change reports for a given deleted function symbol |
| /// (that is not referenced by any debug info) has been suppressed. |
| /// |
| /// @param var the function to consider. |
| /// |
| /// @return true iff the change reports for a given deleted function |
| /// symbol has been suppressed. |
| bool |
| corpus_diff::priv::deleted_unrefed_fn_sym_is_suppressed(const elf_symbol* s) const |
| { |
| if (!s) |
| return false; |
| |
| string_elf_symbol_map::const_iterator i = |
| suppressed_deleted_unrefed_fn_syms_.find(s->get_id_string()); |
| |
| return (i != suppressed_deleted_unrefed_fn_syms_.end()); |
| } |
| |
| /// Test if the change reports for a given added function symbol |
| /// (that is not referenced by any debug info) has been suppressed. |
| /// |
| /// @param var the function to consider. |
| /// |
| /// @return true iff the change reports for a given added function |
| /// symbol has been suppressed. |
| bool |
| corpus_diff::priv::added_unrefed_fn_sym_is_suppressed(const elf_symbol* s) const |
| { |
| if (!s) |
| return false; |
| |
| string_elf_symbol_map::const_iterator i = |
| suppressed_added_unrefed_fn_syms_.find(s->get_id_string()); |
| |
| return (i != suppressed_added_unrefed_fn_syms_.end()); |
| } |
| |
| /// Test if the change reports for a given deleted variable symbol |
| /// (that is not referenced by any debug info) has been suppressed. |
| /// |
| /// @param var the variable to consider. |
| /// |
| /// @return true iff the change reports for a given deleted variable |
| /// symbol has been suppressed. |
| bool |
| corpus_diff::priv::deleted_unrefed_var_sym_is_suppressed(const elf_symbol* s) const |
| { |
| if (!s) |
| return false; |
| |
| string_elf_symbol_map::const_iterator i = |
| suppressed_deleted_unrefed_var_syms_.find(s->get_id_string()); |
| |
| return (i != suppressed_deleted_unrefed_var_syms_.end()); |
| } |
| |
| /// Test if the change reports for a given added variable symbol |
| /// (that is not referenced by any debug info) has been suppressed. |
| /// |
| /// @param var the variable to consider. |
| /// |
| /// @return true iff the change reports for a given added variable |
| /// symbol has been suppressed. |
| bool |
| corpus_diff::priv::added_unrefed_var_sym_is_suppressed(const elf_symbol* s) const |
| { |
| if (!s) |
| return false; |
| |
| string_elf_symbol_map::const_iterator i = |
| suppressed_added_unrefed_var_syms_.find(s->get_id_string()); |
| |
| return (i != suppressed_added_unrefed_var_syms_.end()); |
| } |
| |
| #ifdef do_count_diff_map_changes |
| #undef do_count_diff_map_changes |
| #endif |
| #define do_count_diff_map_changes(diff_map, n_changes, n_filtered) \ |
| { \ |
| string_diff_ptr_map::const_iterator i; \ |
| for (i = diff_map.begin(); \ |
| i != diff_map.end(); \ |
| ++i) \ |
| { \ |
| if (const var_diff* d = is_var_diff(i->second)) \ |
| if (is_data_member(d->first_var())) \ |
| continue; \ |
| \ |
| if (i->second->has_local_changes()) \ |
| ++n_changes; \ |
| if (!i->second->get_canonical_diff()->to_be_reported()) \ |
| ++n_filtered; \ |
| } \ |
| } |
| |
| /// Count the number of leaf changes as well as the number of the |
| /// changes that have been filtered out. |
| /// |
| /// @param num_changes out parameter. This is set to the total number |
| /// of leaf changes. |
| /// |
| /// @param num_filtered out parameter. This is set to the number of |
| /// leaf changes that have been filtered out. |
| void |
| corpus_diff::priv::count_leaf_changes(size_t &num_changes, size_t &num_filtered) |
| { |
| count_leaf_type_changes(num_changes, num_filtered); |
| |
| // Now count the non-type changes. |
| do_count_diff_map_changes(leaf_diffs_.get_function_decl_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_var_decl_diff_map(), |
| num_changes, num_filtered); |
| } |
| |
| /// Count the number of leaf *type* changes as well as the number of |
| /// the leaf type changes that have been filtered out. |
| /// |
| /// @param num_changes out parameter. This is set to the total number |
| /// of leaf type changes. |
| /// |
| /// @param num_filtered out parameter. This is set to the number of |
| /// leaf type changes that have been filtered out. |
| void |
| corpus_diff::priv::count_leaf_type_changes(size_t &num_changes, |
| size_t &num_filtered) |
| { |
| do_count_diff_map_changes(leaf_diffs_.get_type_decl_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_enum_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_class_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_union_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_typedef_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_array_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_distinct_diff_map(), |
| num_changes, num_filtered); |
| do_count_diff_map_changes(leaf_diffs_.get_fn_parm_diff_map(), |
| num_changes, num_filtered); |
| } |
| |
| /// Count the number of types not reachable from the interface (i.e, |
| /// not reachable from global functions or variables). |
| /// |
| /// @param num_added this is set to the number of added types not |
| /// reachable from the interface. |
| /// |
| /// @param num_deleted this is set to the number of deleted types not |
| /// reachable from the interface. |
| /// |
| /// @param num_changed this is set to the number of changed types not |
| /// reachable from the interface. |
| /// |
| /// @param num_filtered_added this is set to the number of added types |
| /// not reachable from the interface and that have been filtered out |
| /// by suppression specifications. |
| /// |
| /// @param num_filtered_deleted this is set to the number of deleted |
| /// types not reachable from the interface and that have been filtered |
| /// out by suppression specifications. |
| /// |
| /// @param num_filtered_changed this is set to the number of changed |
| /// types not reachable from the interface and that have been filtered |
| /// out by suppression specifications. |
| void |
| corpus_diff::priv::count_unreachable_types(size_t &num_added, |
| size_t &num_deleted, |
| size_t &num_changed, |
| size_t &num_filtered_added, |
| size_t &num_filtered_deleted, |
| size_t &num_filtered_changed) |
| { |
| num_added = added_unreachable_types_.size(); |
| num_deleted = deleted_unreachable_types_.size(); |
| num_changed = changed_unreachable_types_.size(); |
| num_filtered_added = suppressed_added_unreachable_types_.size(); |
| num_filtered_deleted = suppressed_deleted_unreachable_types_.size(); |
| |
| for (vector<diff_sptr>::const_iterator i = |
| changed_unreachable_types_sorted().begin(); |
| i != changed_unreachable_types_sorted().end(); |
| ++i) |
| if (!(*i)->to_be_reported()) |
| ++num_filtered_changed; |
| } |
| |
| /// Get the sorted vector of diff nodes representing changed |
| /// unreachable types. |
| /// |
| /// Upon the first invocation of this method, if the vector is empty, |
| /// this function gets the diff nodes representing changed |
| /// unreachable, sort them, and return the sorted vector. |
| /// |
| /// @return the sorted vector of diff nodes representing changed |
| /// unreachable types. |
| const vector<diff_sptr>& |
| corpus_diff::priv::changed_unreachable_types_sorted() const |
| { |
| if (changed_unreachable_types_sorted_.empty()) |
| if (!changed_unreachable_types_.empty()) |
| sort_string_diff_sptr_map(changed_unreachable_types_, |
| changed_unreachable_types_sorted_); |
| |
| return changed_unreachable_types_sorted_; |
| } |
| |
| /// Compute the diff stats. |
| /// |
| /// To know the number of functions that got filtered out, this |
| /// function applies the categorizing filters to the diff sub-trees of |
| /// each function changes diff, prior to calculating the stats. |
| /// |
| /// @param num_removed the number of removed functions. |
| /// |
| /// @param num_added the number of added functions. |
| /// |
| /// @param num_changed the number of changed functions. |
| /// |
| /// @param num_filtered_out the number of changed functions that are |
| /// got filtered out from the report |
| void |
| corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat) |
| { |
| stat.num_func_removed(deleted_fns_.size()); |
| stat.num_removed_func_filtered_out(suppressed_deleted_fns_.size()); |
| stat.num_func_added(added_fns_.size()); |
| stat.num_added_func_filtered_out(suppressed_added_fns_.size()); |
| stat.num_func_changed(changed_fns_map_.size()); |
| |
| stat.num_vars_removed(deleted_vars_.size()); |
| stat.num_removed_vars_filtered_out(suppressed_deleted_vars_.size()); |
| stat.num_vars_added(added_vars_.size()); |
| stat.num_added_vars_filtered_out(suppressed_added_vars_.size()); |
| stat.num_vars_changed(changed_vars_map_.size()); |
| |
| diff_context_sptr ctxt = get_context(); |
| |
| // Walk the changed function diff nodes to apply the categorization |
| // filters. |
| diff_sptr diff; |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_fns_.begin(); |
| i != changed_fns_.end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| ctxt->maybe_apply_filters(diff); |
| } |
| |
| // Walk the changed variable diff nodes to apply the categorization |
| // filters. |
| for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin(); |
| i != sorted_changed_vars_.end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| ctxt->maybe_apply_filters(diff); |
| } |
| |
| // walk the changed unreachable types to apply categorization |
| // filters |
| for (diff_sptrs_type::const_iterator i = |
| changed_unreachable_types_sorted().begin(); |
| i != changed_unreachable_types_sorted().end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| ctxt->maybe_apply_filters(diff); |
| } |
| |
| categorize_redundant_changed_sub_nodes(); |
| |
| // Walk the changed function diff nodes to count the number of |
| // filtered-out functions and the number of functions with virtual |
| // offset changes. |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_fns_.begin(); |
| i != changed_fns_.end(); |
| ++i) |
| { |
| if ((*i)->is_filtered_out()) |
| { |
| stat.num_changed_func_filtered_out |
| (stat.num_changed_func_filtered_out() + 1); |
| |
| if ((*i)->has_local_changes()) |
| stat.num_leaf_func_changes_filtered_out |
| (stat.num_leaf_func_changes_filtered_out() + 1); |
| } |
| else |
| { |
| if ((*i)->get_category() & VIRTUAL_MEMBER_CHANGE_CATEGORY) |
| stat.num_func_with_virtual_offset_changes |
| (stat.num_func_with_virtual_offset_changes() + 1); |
| } |
| |
| if ((*i)->has_local_changes()) |
| stat.num_leaf_func_changes |
| (stat.num_leaf_func_changes() + 1); |
| } |
| |
| // Walk the changed variables diff nodes to count the number of |
| // filtered-out variables. |
| for (var_diff_sptrs_type ::const_iterator i = sorted_changed_vars_.begin(); |
| i != sorted_changed_vars_.end(); |
| ++i) |
| { |
| if ((*i)->is_filtered_out()) |
| { |
| stat.num_changed_vars_filtered_out |
| (stat.num_changed_vars_filtered_out() + 1); |
| |
| if ((*i)->has_local_changes()) |
| stat.num_leaf_var_changes_filtered_out |
| (stat.num_leaf_var_changes_filtered_out() + 1); |
| } |
| if ((*i)->has_local_changes()) |
| stat.num_leaf_var_changes |
| (stat.num_leaf_var_changes() + 1); |
| } |
| |
| stat.num_func_syms_added(added_unrefed_fn_syms_.size()); |
| stat.num_added_func_syms_filtered_out(suppressed_added_unrefed_fn_syms_.size()); |
| stat.num_func_syms_removed(deleted_unrefed_fn_syms_.size()); |
| stat.num_removed_func_syms_filtered_out(suppressed_deleted_unrefed_fn_syms_.size()); |
| stat.num_var_syms_added(added_unrefed_var_syms_.size()); |
| stat.num_added_var_syms_filtered_out(suppressed_added_unrefed_var_syms_.size()); |
| stat.num_var_syms_removed(deleted_unrefed_var_syms_.size()); |
| stat.num_removed_var_syms_filtered_out(suppressed_deleted_unrefed_var_syms_.size()); |
| |
| // Walk the general leaf type diff nodes to count them |
| { |
| size_t num_type_changes = 0, num_type_filtered = 0; |
| count_leaf_type_changes(num_type_changes, num_type_filtered); |
| |
| stat.num_leaf_type_changes(num_type_changes); |
| stat.num_leaf_type_changes_filtered_out(num_type_filtered); |
| } |
| |
| // Walk the general leaf artefacts diff nodes to count them |
| { |
| size_t num_changes = 0, num_filtered = 0; |
| count_leaf_changes(num_changes, num_filtered); |
| |
| stat.num_leaf_changes(num_changes); |
| stat.num_leaf_changes_filtered_out(num_filtered); |
| } |
| |
| // Walk the unreachable types to count them |
| { |
| size_t num_added_unreachable_types = 0, |
| num_changed_unreachable_types = 0, |
| num_deleted_unreachable_types = 0, |
| num_added_unreachable_types_filtered = 0, |
| num_changed_unreachable_types_filtered = 0, |
| num_deleted_unreachable_types_filtered = 0; |
| |
| count_unreachable_types(num_added_unreachable_types, |
| num_deleted_unreachable_types, |
| num_changed_unreachable_types, |
| num_added_unreachable_types_filtered, |
| num_deleted_unreachable_types_filtered, |
| num_changed_unreachable_types_filtered); |
| |
| stat.num_added_unreachable_types(num_added_unreachable_types); |
| stat.num_removed_unreachable_types(num_deleted_unreachable_types); |
| stat.num_changed_unreachable_types(num_changed_unreachable_types); |
| stat.num_added_unreachable_types_filtered_out |
| (num_added_unreachable_types_filtered); |
| stat.num_removed_unreachable_types_filtered_out |
| (num_deleted_unreachable_types_filtered); |
| stat.num_changed_unreachable_types_filtered_out |
| (num_changed_unreachable_types_filtered); |
| } |
| } |
| |
| /// Emit the summary of the functions & variables that got |
| /// removed/changed/added. |
| /// |
| /// TODO: This should be handled by the reporters, just like what is |
| /// done for reporter_base::diff_to_be_reported. |
| /// |
| /// @param out the output stream to emit the stats to. |
| /// |
| /// @param indent the indentation string to use in the summary. |
| void |
| corpus_diff::priv::emit_diff_stats(const diff_stats& s, |
| ostream& out, |
| const string& indent) |
| { |
| /// Report added/removed/changed functions. |
| size_t net_num_leaf_changes = |
| s.net_num_func_removed() + |
| s.net_num_func_added() + |
| s.net_num_leaf_func_changes() + |
| s.net_num_vars_removed() + |
| s.net_num_vars_added() + |
| s.net_num_leaf_var_changes() + |
| s.net_num_leaf_type_changes(); |
| |
| if (!sonames_equal_) |
| out << indent << "ELF SONAME changed\n"; |
| |
| if (!architectures_equal_) |
| out << indent << "ELF architecture changed\n"; |
| |
| diff_context_sptr ctxt = get_context(); |
| |
| if (ctxt->show_leaf_changes_only()) |
| { |
| out << "Leaf changes summary: "; |
| out << net_num_leaf_changes << " artifact"; |
| if (net_num_leaf_changes > 1) |
| out << "s"; |
| out << " changed"; |
| |
| if (size_t num_filtered = s.num_leaf_changes_filtered_out()) |
| out << " (" << num_filtered << " filtered out)"; |
| out << "\n"; |
| |
| out << indent << "Changed leaf types summary: " |
| << s.net_num_leaf_type_changes(); |
| if (s.num_leaf_type_changes_filtered_out()) |
| out << " (" << s.num_leaf_type_changes_filtered_out() |
| << " filtered out)"; |
| out << " leaf type"; |
| if (s.num_leaf_type_changes() > 1) |
| out << "s"; |
| out << " changed\n"; |
| |
| // function changes summary |
| out << indent << "Removed/Changed/Added functions summary: "; |
| out << s.net_num_func_removed() << " Removed"; |
| if (s.num_removed_func_filtered_out()) |
| out << " (" |
| << s.num_removed_func_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_leaf_func_changes() << " Changed"; |
| if (s.num_leaf_func_changes_filtered_out()) |
| out << " (" |
| << s.num_leaf_func_changes_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_func_added()<< " Added "; |
| if (s.net_num_func_added() <= 1) |
| out << "function"; |
| else |
| out << "functions"; |
| if (s.num_added_func_filtered_out()) |
| out << " (" << s.num_added_func_filtered_out() << " filtered out)"; |
| out << "\n"; |
| |
| // variables changes summary |
| out << indent << "Removed/Changed/Added variables summary: "; |
| out << s.net_num_vars_removed() << " Removed"; |
| if (s.num_removed_vars_filtered_out()) |
| out << " (" << s.num_removed_vars_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_leaf_var_changes() << " Changed"; |
| if (s.num_leaf_var_changes_filtered_out()) |
| out << " (" |
| << s.num_leaf_var_changes_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_vars_added() << " Added "; |
| if (s.net_num_vars_added() <= 1) |
| out << "variable"; |
| else |
| out << "variables"; |
| if (s.num_added_vars_filtered_out()) |
| out << " (" << s.num_added_vars_filtered_out() |
| << " filtered out)"; |
| out << "\n"; |
| } |
| else // if (ctxt->show_leaf_changes_only()) |
| { |
| size_t total_nb_function_changes = s.num_func_removed() |
| + s.num_func_changed() + s.num_func_added(); |
| |
| // function changes summary |
| out << indent << "Functions changes summary: "; |
| out << s.net_num_func_removed() << " Removed"; |
| if (s.num_removed_func_filtered_out()) |
| out << " (" |
| << s.num_removed_func_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_func_changed() << " Changed"; |
| if (s.num_changed_func_filtered_out()) |
| out << " (" << s.num_changed_func_filtered_out() << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_func_added() << " Added"; |
| if (s.num_added_func_filtered_out()) |
| out << " (" << s.num_added_func_filtered_out() << " filtered out)"; |
| if (total_nb_function_changes <= 1) |
| out << " function"; |
| else |
| out << " functions"; |
| out << "\n"; |
| |
| // variables changes summary |
| size_t total_nb_variable_changes = s.num_vars_removed() |
| + s.num_vars_changed() + s.num_vars_added(); |
| |
| out << indent << "Variables changes summary: "; |
| out << s.net_num_vars_removed() << " Removed"; |
| if (s.num_removed_vars_filtered_out()) |
| out << " (" << s.num_removed_vars_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.num_vars_changed() - s.num_changed_vars_filtered_out() << " Changed"; |
| if (s.num_changed_vars_filtered_out()) |
| out << " (" << s.num_changed_vars_filtered_out() << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_vars_added() << " Added"; |
| if (s.num_added_vars_filtered_out()) |
| out << " (" << s.num_added_vars_filtered_out() |
| << " filtered out)"; |
| if (total_nb_variable_changes <= 1) |
| out << " variable"; |
| else |
| out << " variables"; |
| out << "\n"; |
| } |
| |
| // Show statistics about types not reachable from global |
| // functions/variables. |
| if (ctxt->show_unreachable_types()) |
| { |
| size_t total_nb_unreachable_type_changes = |
| s.num_removed_unreachable_types() |
| + s.num_changed_unreachable_types() |
| + s.num_added_unreachable_types(); |
| |
| // Show summary of unreachable types |
| out << indent << "Unreachable types summary: " |
| << s.net_num_removed_unreachable_types() |
| << " removed"; |
| if (s.num_removed_unreachable_types_filtered_out()) |
| out << " (" << s.num_removed_unreachable_types_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_changed_unreachable_types() |
| << " changed"; |
| if (s.num_changed_unreachable_types_filtered_out()) |
| out << " (" << s.num_changed_unreachable_types_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| |
| out << s.net_num_added_unreachable_types() |
| << " added"; |
| if (s.num_added_unreachable_types_filtered_out()) |
| out << " (" << s.num_added_unreachable_types_filtered_out() |
| << " filtered out)"; |
| if (total_nb_unreachable_type_changes <= 1) |
| out << " type"; |
| else |
| out << " types"; |
| out << "\n"; |
| } |
| |
| if (ctxt->show_symbols_unreferenced_by_debug_info() |
| && (s.num_func_syms_removed() |
| || s.num_func_syms_added() |
| || s.num_var_syms_removed() |
| || s.num_var_syms_added())) |
| { |
| // function symbols changes summary. |
| |
| if (!ctxt->show_added_symbols_unreferenced_by_debug_info() |
| && s.num_func_syms_removed() == 0 |
| && s.num_func_syms_added() != 0) |
| // If the only unreferenced function symbol change is function |
| // syms that got added, but we were forbidden to show function |
| // syms being added, do nothing. |
| ; |
| else |
| { |
| out << indent |
| << "Function symbols changes summary: " |
| << s.net_num_removed_func_syms() << " Removed"; |
| if (s.num_removed_func_syms_filtered_out()) |
| out << " (" << s.num_removed_func_syms_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| out << s.net_num_added_func_syms() << " Added"; |
| if (s.num_added_func_syms_filtered_out()) |
| out << " (" << s.num_added_func_syms_filtered_out() |
| << " filtered out)"; |
| out << " function symbol"; |
| if (s.num_func_syms_added() + s.num_func_syms_removed() > 1) |
| out << "s"; |
| out << " not referenced by debug info\n"; |
| } |
| |
| // variable symbol changes summary. |
| |
| if (!ctxt->show_added_symbols_unreferenced_by_debug_info() |
| && s.num_var_syms_removed() == 0 |
| && s.num_var_syms_added() != 0) |
| // If the only unreferenced variable symbol change is variable |
| // syms that got added, but we were forbidden to show variable |
| // syms being added, do nothing. |
| ; |
| else |
| { |
| out << indent |
| << "Variable symbols changes summary: " |
| << s.net_num_removed_var_syms() << " Removed"; |
| if (s.num_removed_var_syms_filtered_out()) |
| out << " (" << s.num_removed_var_syms_filtered_out() |
| << " filtered out)"; |
| out << ", "; |
| out << s.net_num_added_var_syms() << " Added"; |
| if (s.num_added_var_syms_filtered_out()) |
| out << " (" << s.num_added_var_syms_filtered_out() |
| << " filtered out)"; |
| out << " variable symbol"; |
| if (s.num_var_syms_added() + s.num_var_syms_removed() > 1) |
| out << "s"; |
| out << " not referenced by debug info\n"; |
| } |
| } |
| } |
| |
| /// Walk the changed functions and variables diff nodes to categorize |
| /// redundant nodes. |
| void |
| corpus_diff::priv::categorize_redundant_changed_sub_nodes() |
| { |
| diff_sptr diff; |
| |
| diff_context_sptr ctxt = get_context(); |
| |
| ctxt->forget_visited_diffs(); |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_fns_.begin(); |
| i!= changed_fns_.end(); |
| ++i) |
| { |
| diff = *i; |
| categorize_redundancy(diff); |
| } |
| |
| for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin(); |
| i!= sorted_changed_vars_.end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| categorize_redundancy(diff); |
| } |
| |
| for (diff_sptrs_type::const_iterator i = |
| changed_unreachable_types_sorted().begin(); |
| i!= changed_unreachable_types_sorted().end(); |
| ++i) |
| { |
| diff_sptr diff = *i; |
| categorize_redundancy(diff); |
| } |
| } |
| |
| /// Walk the changed functions and variables diff nodes and clear the |
| /// redundancy categorization they might carry. |
| void |
| corpus_diff::priv::clear_redundancy_categorization() |
| { |
| diff_sptr diff; |
| for (function_decl_diff_sptrs_type::const_iterator i = changed_fns_.begin(); |
| i!= changed_fns_.end(); |
| ++i) |
| { |
| diff = *i; |
| abigail::comparison::clear_redundancy_categorization(diff); |
| } |
| |
| for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin(); |
| i!= sorted_changed_vars_.end(); |
| ++i) |
| { |
| diff = *i; |
| abigail::comparison::clear_redundancy_categorization(diff); |
| } |
| } |
| |
| /// If the user asked to dump the diff tree node (for changed |
| /// variables and functions) on the error output stream, then just do |
| /// that. |
| /// |
| /// This function is used for debugging purposes. |
| void |
| corpus_diff::priv::maybe_dump_diff_tree() |
| { |
| diff_context_sptr ctxt = get_context(); |
| |
| if (!ctxt->dump_diff_tree() |
| || ctxt->error_output_stream() == 0) |
| return; |
| |
| if (!changed_fns_.empty()) |
| { |
| *ctxt->error_output_stream() << "changed functions diff tree: \n\n"; |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_fns_.begin(); |
| i != changed_fns_.end(); |
| ++i) |
| { |
| diff_sptr d = *i; |
| print_diff_tree(d, *ctxt->error_output_stream()); |
| } |
| } |
| |
| if (!sorted_changed_vars_.empty()) |
| { |
| *ctxt->error_output_stream() << "\nchanged variables diff tree: \n\n"; |
| for (var_diff_sptrs_type::const_iterator i = |
| sorted_changed_vars_.begin(); |
| i != sorted_changed_vars_.end(); |
| ++i) |
| { |
| diff_sptr d = *i; |
| print_diff_tree(d, *ctxt->error_output_stream()); |
| } |
| } |
| |
| if (!changed_unreachable_types_sorted().empty()) |
| { |
| *ctxt->error_output_stream() << "\nchanged unreachable " |
| "types diff tree: \n\n"; |
| for (vector<diff_sptr>::const_iterator i = |
| changed_unreachable_types_sorted().begin(); |
| i != changed_unreachable_types_sorted().end(); |
| ++i) |
| { |
| diff_sptr d = *i; |
| print_diff_tree(d, *ctxt->error_output_stream()); |
| } |
| } |
| } |
| |
| /// Populate the vector of children node of the @ref corpus_diff type. |
| /// |
| /// The children node can then later be retrieved using |
| /// corpus_diff::children_node(). |
| void |
| corpus_diff::chain_into_hierarchy() |
| { |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_functions_sorted().begin(); |
| i != changed_functions_sorted().end(); |
| ++i) |
| if (diff_sptr d = *i) |
| append_child_node(d); |
| } |
| |
| /// Constructor for @ref corpus_diff. |
| /// |
| /// @param first the first corpus of the diff. |
| /// |
| /// @param second the second corpus of the diff. |
| /// |
| /// @param ctxt the diff context to use. Note that this context |
| /// object must stay alive at least during the life time of the |
| /// current instance of @ref corpus_diff. Otherwise memory corruption |
| /// issues occur. |
| corpus_diff::corpus_diff(corpus_sptr first, |
| corpus_sptr second, |
| diff_context_sptr ctxt) |
| : priv_(new priv(first, second, ctxt)) |
| {} |
| |
| corpus_diff::~corpus_diff() = default; |
| |
| /// Finish building the current instance of @ref corpus_diff. |
| void |
| corpus_diff::finish_diff_type() |
| { |
| if (priv_->finished_) |
| return; |
| chain_into_hierarchy(); |
| priv_->finished_ = true; |
| } |
| |
| /// @return the first corpus of the diff. |
| corpus_sptr |
| corpus_diff::first_corpus() const |
| {return priv_->first_;} |
| |
| /// @return the second corpus of the diff. |
| corpus_sptr |
| corpus_diff::second_corpus() const |
| {return priv_->second_;} |
| |
| /// @return the children nodes of the current instance of corpus_diff. |
| const vector<diff*>& |
| corpus_diff::children_nodes() const |
| {return priv_->children_;} |
| |
| /// Append a new child node to the vector of children nodes for the |
| /// current instance of @ref corpus_diff node. |
| /// |
| /// Note that the vector of children nodes for the current instance of |
| /// @ref corpus_diff node must remain sorted, using |
| /// diff_less_than_functor. |
| /// |
| /// @param d the new child node. Note that the life time of the |
| /// object held by @p d will thus equal the life time of the current |
| /// instance of @ref corpus_diff. |
| void |
| corpus_diff::append_child_node(diff_sptr d) |
| { |
| ABG_ASSERT(d); |
| |
| diff_less_than_functor is_less_than; |
| bool inserted = false; |
| for (vector<diff*>::iterator i = priv_->children_.begin(); |
| i != priv_->children_.end(); |
| ++i) |
| // Look for the point where to insert the diff child node. |
| if (!is_less_than(d.get(), *i)) |
| { |
| context()->keep_diff_alive(d); |
| priv_->children_.insert(i, d.get()); |
| // As we have just inserted 'd' into the vector, the iterator |
| // 'i' is invalidated. We must *NOT* use it anymore. |
| inserted = true; |
| break; |
| } |
| |
| if (!inserted) |
| { |
| context()->keep_diff_alive(d); |
| // We didn't insert anything to the vector, presumably b/c it was |
| // empty or had one element that was "less than" 'd'. We can thus |
| // just append 'd' to the end of the vector. |
| priv_->children_.push_back(d.get()); |
| } |
| } |
| |
| /// @return the bare edit script of the functions changed as recorded |
| /// by the diff. |
| edit_script& |
| corpus_diff::function_changes() const |
| {return priv_->fns_edit_script_;} |
| |
| /// @return the bare edit script of the variables changed as recorded |
| /// by the diff. |
| edit_script& |
| corpus_diff::variable_changes() const |
| {return priv_->vars_edit_script_;} |
| |
| /// Test if the soname of the underlying corpus has changed. |
| /// |
| /// @return true iff the soname has changed. |
| bool |
| corpus_diff::soname_changed() const |
| {return !priv_->sonames_equal_;} |
| |
| /// Test if the architecture of the underlying corpus has changed. |
| /// |
| /// @return true iff the architecture has changed. |
| bool |
| corpus_diff::architecture_changed() const |
| {return !priv_->architectures_equal_;} |
| |
| /// Getter for the deleted functions of the diff. |
| /// |
| /// @return the the deleted functions of the diff. |
| const string_function_ptr_map& |
| corpus_diff::deleted_functions() const |
| {return priv_->deleted_fns_;} |
| |
| /// Getter for the added functions of the diff. |
| /// |
| /// @return the added functions of the diff. |
| const string_function_ptr_map& |
| corpus_diff::added_functions() |
| {return priv_->added_fns_;} |
| |
| /// Getter for the functions which signature didn't change, but which |
| /// do have some indirect changes in their parms. |
| /// |
| /// @return a non-sorted map of functions which signature didn't |
| /// change, but which do have some indirect changes in their parms. |
| /// The key of the map is a unique identifier for the function; it's |
| /// usually made of the name and version of the underlying ELF symbol |
| /// of the function for corpora that were built from ELF files. |
| const string_function_decl_diff_sptr_map& |
| corpus_diff::changed_functions() |
| {return priv_->changed_fns_map_;} |
| |
| /// Getter for a sorted vector of functions which signature didn't |
| /// change, but which do have some indirect changes in their parms. |
| /// |
| /// @return a sorted vector of functions which signature didn't |
| /// change, but which do have some indirect changes in their parms. |
| const function_decl_diff_sptrs_type& |
| corpus_diff::changed_functions_sorted() |
| {return priv_->changed_fns_;} |
| |
| /// Getter for the variables that got deleted from the first subject |
| /// of the diff. |
| /// |
| /// @return the map of deleted variable. |
| const string_var_ptr_map& |
| corpus_diff::deleted_variables() const |
| {return priv_->deleted_vars_;} |
| |
| /// Getter for the added variables of the diff. |
| /// |
| /// @return the map of added variable. |
| const string_var_ptr_map& |
| corpus_diff::added_variables() const |
| {return priv_->added_vars_;} |
| |
| /// Getter for the non-sorted map of variables which signature didn't |
| /// change but which do have some indirect changes in some sub-types. |
| /// |
| /// @return the non-sorted map of changed variables. |
| const string_var_diff_sptr_map& |
| corpus_diff::changed_variables() |
| {return priv_->changed_vars_map_;} |
| |
| /// Getter for the sorted vector of variables which signature didn't |
| /// change but which do have some indirect changes in some sub-types. |
| /// |
| /// @return a sorted vector of changed variables. |
| const var_diff_sptrs_type& |
| corpus_diff::changed_variables_sorted() |
| {return priv_->sorted_changed_vars_;} |
| |
| /// Getter for function symbols not referenced by any debug info and |
| /// that got deleted. |
| /// |
| /// @return a map of elf function symbols not referenced by any debug |
| /// info and that got deleted. |
| const string_elf_symbol_map& |
| corpus_diff::deleted_unrefed_function_symbols() const |
| {return priv_->deleted_unrefed_fn_syms_;} |
| |
| /// Getter for function symbols not referenced by any debug info and |
| /// that got added. |
| /// |
| /// @return a map of elf function symbols not referenced by any debug |
| /// info and that got added. |
| const string_elf_symbol_map& |
| corpus_diff::added_unrefed_function_symbols() const |
| {return priv_->added_unrefed_fn_syms_;} |
| |
| /// Getter for variable symbols not referenced by any debug info and |
| /// that got deleted. |
| /// |
| /// @return a map of elf variable symbols not referenced by any debug |
| /// info and that got deleted. |
| const string_elf_symbol_map& |
| corpus_diff::deleted_unrefed_variable_symbols() const |
| {return priv_->deleted_unrefed_var_syms_;} |
| |
| /// Getter for variable symbols not referenced by any debug info and |
| /// that got added. |
| /// |
| /// @return a map of elf variable symbols not referenced by any debug |
| /// info and that got added. |
| const string_elf_symbol_map& |
| corpus_diff::added_unrefed_variable_symbols() const |
| {return priv_->added_unrefed_var_syms_;} |
| |
| /// Getter for a map of deleted types that are not reachable from |
| /// global functions/variables. |
| /// |
| /// @return a map that associates pretty representation of deleted |
| /// unreachable types and said types. |
| const string_type_base_sptr_map& |
| corpus_diff::deleted_unreachable_types() const |
| {return priv_->deleted_unreachable_types_;} |
| |
| /// Getter of a sorted vector of deleted types that are not reachable |
| /// from global functions/variables. |
| /// |
| /// @return a sorted vector of deleted types that are not reachable |
| /// from global functions/variables. The types are lexicographically |
| /// sorted by considering their pretty representation. |
| const vector<type_base_sptr>& |
| corpus_diff::deleted_unreachable_types_sorted() const |
| { |
| if (priv_->deleted_unreachable_types_sorted_.empty()) |
| if (!priv_->deleted_unreachable_types_.empty()) |
| sort_string_type_base_sptr_map(priv_->deleted_unreachable_types_, |
| priv_->deleted_unreachable_types_sorted_); |
| |
| return priv_->deleted_unreachable_types_sorted_; |
| } |
| |
| /// Getter for a map of added types that are not reachable from global |
| /// functions/variables. |
| /// |
| /// @return a map that associates pretty representation of added |
| /// unreachable types and said types. |
| const string_type_base_sptr_map& |
| corpus_diff::added_unreachable_types() const |
| {return priv_->added_unreachable_types_;} |
| |
| /// Getter of a sorted vector of added types that are not reachable |
| /// from global functions/variables. |
| /// |
| /// @return a sorted vector of added types that are not reachable from |
| /// global functions/variables. The types are lexicographically |
| /// sorted by considering their pretty representation. |
| const vector<type_base_sptr>& |
| corpus_diff::added_unreachable_types_sorted() const |
| { |
| if (priv_->added_unreachable_types_sorted_.empty()) |
| if (!priv_->added_unreachable_types_.empty()) |
| sort_string_type_base_sptr_map(priv_->added_unreachable_types_, |
| priv_->added_unreachable_types_sorted_); |
| |
| return priv_->added_unreachable_types_sorted_; |
| } |
| |
| /// Getter for a map of changed types that are not reachable from |
| /// global functions/variables. |
| /// |
| /// @return a map that associates pretty representation of changed |
| /// unreachable types and said types. |
| const string_diff_sptr_map& |
| corpus_diff::changed_unreachable_types() const |
| {return priv_->changed_unreachable_types_;} |
| |
| /// Getter of a sorted vector of changed types that are not reachable |
| /// from global functions/variables. |
| /// |
| /// @return a sorted vector of changed types that are not reachable |
| /// from global functions/variables. The diffs are lexicographically |
| /// sorted by considering their pretty representation. |
| const vector<diff_sptr>& |
| corpus_diff::changed_unreachable_types_sorted() const |
| {return priv_->changed_unreachable_types_sorted();} |
| |
| /// Getter of the diff context of this diff |
| /// |
| /// @return the diff context for this diff. |
| const diff_context_sptr |
| corpus_diff::context() const |
| {return priv_->get_context();} |
| |
| /// @return the pretty representation for the current instance of @ref |
| /// corpus_diff |
| const string& |
| corpus_diff::get_pretty_representation() const |
| { |
| if (priv_->pretty_representation_.empty()) |
| { |
| std::ostringstream o; |
| o << "corpus_diff[" |
| << first_corpus()->get_path() |
| << ", " |
| << second_corpus()->get_path() |
| << "]"; |
| priv_->pretty_representation_ = o.str(); |
| } |
| return priv_->pretty_representation_; |
| } |
| /// Return true iff the current @ref corpus_diff node carries a |
| /// change. |
| /// |
| /// @return true iff the current diff node carries a change. |
| bool |
| corpus_diff::has_changes() const |
| { |
| return (soname_changed() |
| || architecture_changed() |
| || !(priv_->deleted_fns_.empty() |
| && priv_->added_fns_.empty() |
| && priv_->changed_fns_map_.empty() |
| && priv_->deleted_vars_.empty() |
| && priv_->added_vars_.empty() |
| && priv_->changed_vars_map_.empty() |
| && priv_->added_unrefed_fn_syms_.empty() |
| && priv_->deleted_unrefed_fn_syms_.empty() |
| && priv_->added_unrefed_var_syms_.empty() |
| && priv_->deleted_unrefed_var_syms_.empty() |
| && priv_->deleted_unreachable_types_.empty() |
| && priv_->added_unreachable_types_.empty() |
| && priv_->changed_unreachable_types_.empty())); |
| } |
| |
| /// Test if the current instance of @ref corpus_diff carries changes |
| /// that we are sure are incompatible. By incompatible change we mean |
| /// a change that "breaks" the ABI of the corpus we are looking at. |
| /// |
| /// In concrete terms, this function considers the following changes |
| /// as being ABI incompatible for sure: |
| /// |
| /// - a soname change |
| /// - if exported functions or variables got removed |
| /// |
| /// Note that subtype changes *can* represent changes that break ABI |
| /// too. But they also can be changes that are OK, ABI-wise. |
| /// |
| /// It's up to the user to provide suppression specifications to say |
| /// explicitely which subtype change is OK. The remaining sub-type |
| /// changes are then considered to be ABI incompatible. But to test |
| /// if such ABI incompatible subtype changes are present you need to |
| /// use the function @ref corpus_diff::has_net_subtype_changes() |
| /// |
| /// @return true iff the current instance of @ref corpus_diff carries |
| /// changes that we are sure are ABI incompatible. |
| bool |
| corpus_diff::has_incompatible_changes() const |
| { |
| const diff_stats& stats = const_cast<corpus_diff*>(this)-> |
| apply_filters_and_suppressions_before_reporting(); |
| |
| return (soname_changed() || architecture_changed() |
| || stats.net_num_func_removed() != 0 |
| || (stats.num_func_with_virtual_offset_changes() != 0 |
| // If all reports about functions with sub-type changes |
| // have been suppressed, then even those about functions |
| // that are virtual don't matter anymore because the |
| // user willingly requested to shut them down |
| && stats.net_num_func_changed() != 0) |
| || stats.net_num_vars_removed() != 0 |
| || stats.net_num_removed_func_syms() != 0 |
| || stats.net_num_removed_var_syms() != 0 |
| || stats.net_num_removed_unreachable_types() != 0 |
| || stats.net_num_changed_unreachable_types() != 0); |
| } |
| |
| /// Test if the current instance of @ref corpus_diff carries subtype |
| /// changes whose reports are not suppressed by any suppression |
| /// specification. In effect, these are deemed incompatible ABI |
| /// changes. |
| /// |
| /// @return true iff the the current instance of @ref corpus_diff |
| /// carries subtype changes that are deemed incompatible ABI changes. |
| bool |
| corpus_diff::has_net_subtype_changes() const |
| { |
| const diff_stats& stats = const_cast<corpus_diff*>(this)-> |
| apply_filters_and_suppressions_before_reporting(); |
| |
| return (stats.net_num_func_changed() != 0 |
| || stats.net_num_vars_changed() != 0 |
| || stats.net_num_removed_unreachable_types() != 0 |
| || stats.net_num_changed_unreachable_types() != 0); |
| } |
| |
| /// Test if the current instance of @ref corpus_diff carries changes |
| /// whose reports are not suppressed by any suppression specification. |
| /// In effect, these are deemed incompatible ABI changes. |
| /// |
| /// @return true iff the the current instance of @ref corpus_diff |
| /// carries subtype changes that are deemed incompatible ABI changes. |
| bool |
| corpus_diff::has_net_changes() const |
| {return context()->get_reporter()->diff_has_net_changes(this);} |
| |
| /// Apply the different filters that are registered to be applied to |
| /// the diff tree; that includes the categorization filters. Also, |
| /// apply the suppression interpretation filters. |
| /// |
| /// After the filters are applied, this function calculates some |
| /// statistics about the changes carried by the current instance of |
| /// @ref corpus_diff. These statistics are represented by an instance |
| /// of @ref corpus_diff::diff_stats. |
| /// |
| /// This member function is called by the reporting function |
| /// corpus_diff::report(). |
| /// |
| /// Note that for a given instance of corpus_diff, this function |
| /// applies the filters and suppressions only the first time it is |
| /// invoked. Subsequent invocations just return the instance of |
| /// corpus_diff::diff_stats that was cached after the first |
| /// invocation. |
| /// |
| /// @return a reference to the statistics about the changes carried by |
| /// the current instance of @ref corpus_diff. |
| const corpus_diff::diff_stats& |
| corpus_diff::apply_filters_and_suppressions_before_reporting() |
| { |
| if (priv_->diff_stats_) |
| return *priv_->diff_stats_; |
| |
| apply_suppressions(this); |
| priv_->diff_stats_.reset(new diff_stats(context())); |
| mark_leaf_diff_nodes(); |
| priv_->apply_filters_and_compute_diff_stats(*priv_->diff_stats_); |
| return *priv_->diff_stats_; |
| } |
| |
| /// A visitor that marks leaf diff nodes by storing them in the |
| /// instance of @ref diff_maps returned by |
| /// corpus_diff::get_leaf_diffs() invoked on the current instance of |
| /// corpus_diff. |
| struct leaf_diff_node_marker_visitor : public diff_node_visitor |
| { |
| /// This is called when the visitor visits a diff node. |
| /// |
| /// It basically tests if the diff node being visited is a leaf diff |
| /// node - that is, it contains local changes. If it does, then the |
| /// node is added to the set of maps that hold leaf diffs in the |
| /// current corpus_diff. |
| /// |
| /// Note that only leaf nodes that are reachable from public |
| /// interfaces (global functions or variables) are collected by this |
| /// visitor. |
| /// |
| /// @param d the diff node being visited. |
| virtual void |
| visit_begin(diff *d) |
| { |
| if (d->has_local_changes() |
| // A leaf basic (or class/union) type name change makes no |
| // sense when showing just leaf changes. It only makes sense |
| // when it can explain the details about a non-leaf change. |
| // In other words, it doesn't make sense to say that an "int" |
| // became "unsigned int". But it does make sense to say that |
| // a typedef changed because its underlying type was 'int' and |
| // is now an "unsigned int". |
| && !filtering::has_basic_or_class_type_name_change(d) |
| // Similarly, a *local* change describing a type that changed |
| // its nature doesn't make sense. |
| && !is_distinct_diff(d) |
| // Similarly, a pointer (or reference or array), a typedef or |
| // qualified type change in itself doesn't make sense. It |
| // would rather make sense to show that pointer change as part |
| // of the variable change whose pointer type changed, for |
| // instance. |
| && !is_pointer_diff(d) |
| && !is_reference_diff(d) |
| && !is_qualified_type_diff(d) |
| && !is_typedef_diff(d) |
| && !is_array_diff(d) |
| // Similarly a parameter change in itself doesn't make sense. |
| // It should have already been reported as part of the change |
| // of the function it belongs to. |
| && !is_fn_parm_diff(d) |
| // An anonymous class or union diff doesn't make sense on its |
| // own. It must have been described already by the diff of |
| // the enclosing struct or union if 'd' is from an anonymous |
| // data member, or from a typedef change if 'd' is from a |
| // typedef change which underlying type is an anonymous |
| // struct/union. |
| && !is_anonymous_class_or_union_diff(d) |
| // Don't show decl-only-ness changes either. |
| && !filtering::has_decl_only_def_change(d) |
| // Sometime, we can encounter artifacts of bogus DWARF that |
| // yield a diff node for a decl-only class (and empty class |
| // with the is_declaration flag set) that carries a non-zero |
| // size! And of course at some point that non-zero size |
| // changes. We need to be able to detect that. |
| && !filtering::is_decl_only_class_with_size_change(d)) |
| { |
| diff_context_sptr ctxt = d->context(); |
| const corpus_diff *corpus_diff_node = ctxt->get_corpus_diff().get(); |
| ABG_ASSERT(corpus_diff_node); |
| |
| if (diff *iface_diff = get_current_topmost_iface_diff()) |
| { |
| type_or_decl_base_sptr iface = iface_diff->first_subject(); |
| // So, this diff node that is reachable from a global |
| // function or variable carries a leaf change. Let's add |
| // it to the set of of leaf diffs of corpus_diff_node. |
| const_cast<corpus_diff*>(corpus_diff_node)-> |
| get_leaf_diffs().insert_diff_node(d, iface); |
| } |
| } |
| } |
| }; // end struct leaf_diff_node_marker_visitor |
| |
| /// Walks the diff nodes associated to the current corpus diff and |
| /// mark those that carry local changes. They are said to be leaf |
| /// diff nodes. |
| /// |
| /// The marked nodes are available from the |
| /// corpus_diff::get_leaf_diffs() function. |
| void |
| corpus_diff::mark_leaf_diff_nodes() |
| { |
| if (!has_changes()) |
| return; |
| |
| if (!context()->show_leaf_changes_only()) |
| return; |
| |
| leaf_diff_node_marker_visitor v; |
| context()->forget_visited_diffs(); |
| bool s = context()->visiting_a_node_twice_is_forbidden(); |
| context()->forbid_visiting_a_node_twice(true); |
| context()->forbid_visiting_a_node_twice_per_interface(true); |
| traverse(v); |
| context()->forbid_visiting_a_node_twice(s); |
| context()->forbid_visiting_a_node_twice_per_interface(false); |
| } |
| |
| /// Get the set of maps that contain leaf nodes. A leaf node being a |
| /// node with a local change. |
| /// |
| /// @return the set of maps that contain leaf nodes. A leaf node |
| /// being a node with a local change. |
| diff_maps& |
| corpus_diff::get_leaf_diffs() |
| {return priv_->leaf_diffs_;} |
| |
| /// Get the set of maps that contain leaf nodes. A leaf node being a |
| /// node with a local change. |
| /// |
| /// @return the set of maps that contain leaf nodes. A leaf node |
| /// being a node with a local change. |
| const diff_maps& |
| corpus_diff::get_leaf_diffs() const |
| {return priv_->leaf_diffs_;} |
| |
| /// Report the diff in a serialized form. |
| /// |
| /// @param out the stream to serialize the diff to. |
| /// |
| /// @param indent the prefix to use for the indentation of this |
| /// serialization. |
| void |
| corpus_diff::report(ostream& out, const string& indent) const |
| { |
| context()->get_reporter()->report(*this, out, indent); |
| } |
| |
| /// Traverse the diff sub-tree under the current instance corpus_diff. |
| /// |
| /// @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 |
| corpus_diff::traverse(diff_node_visitor& v) |
| { |
| finish_diff_type(); |
| |
| v.visit_begin(this); |
| |
| if (!v.visit(this, true)) |
| { |
| v.visit_end(this); |
| return false; |
| } |
| |
| for (function_decl_diff_sptrs_type::const_iterator i = |
| changed_functions_sorted().begin(); |
| i != changed_functions_sorted().end(); |
| ++i) |
| { |
| if (diff_sptr d = *i) |
| { |
| const diff_context_sptr &ctxt = context(); |
| if (ctxt->visiting_a_node_twice_is_forbidden_per_interface()) |
| ctxt->forget_visited_diffs(); |
| |
| v.set_current_topmost_iface_diff(d.get()); |
| |
| if (!d->traverse(v)) |
| { |
| v.visit_end(this); |
| v.set_current_topmost_iface_diff(0); |
| return false; |
| } |
| } |
| } |
| |
| for (var_diff_sptrs_type::const_iterator i = |
| changed_variables_sorted().begin(); |
| i != changed_variables_sorted().end(); |
| ++i) |
| { |
| if (diff_sptr d = *i) |
| { |
| const diff_context_sptr &ctxt = context(); |
| if (ctxt->visiting_a_node_twice_is_forbidden_per_interface()) |
| ctxt->forget_visited_diffs(); |
| |
| v.set_current_topmost_iface_diff(d.get()); |
| |
| if (!d->traverse(v)) |
| { |
| v.visit_end(this); |
| v.set_current_topmost_iface_diff(0); |
| return false; |
| } |
| } |
| } |
| |
| v.set_current_topmost_iface_diff(0); |
| |
| // Traverse the changed unreachable type diffs. These diffs are on |
| // types that are not reachable from global functions or variables. |
| for (vector<diff_sptr>::const_iterator i = |
| changed_unreachable_types_sorted().begin(); |
| i != changed_unreachable_types_sorted().end(); |
| ++i) |
| { |
| if (diff_sptr d = *i) |
| { |
| const diff_context_sptr &ctxt = context(); |
| if (ctxt->visiting_a_node_twice_is_forbidden_per_interface()) |
| ctxt->forget_visited_diffs(); |
| |
| if (!d->traverse(v)) |
| { |
| v.visit_end(this); |
| return false; |
| } |
| } |
| } |
| |
| v.visit_end(this); |
| return true; |
| } |
| |
| /// Compute the diff between two instances of @ref corpus. |
| /// |
| /// Note that the two corpora must have been created in the same @ref |
| /// environment, otherwise, this function aborts. |
| /// |
| /// @param f the first @ref corpus to consider for the diff. |
| /// |
| /// @param s the second @ref corpus to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff between the two @ref corpus. |
| corpus_diff_sptr |
| compute_diff(const corpus_sptr f, |
| const corpus_sptr s, |
| diff_context_sptr ctxt) |
| { |
| typedef corpus::functions::const_iterator fns_it_type; |
| typedef corpus::variables::const_iterator vars_it_type; |
| typedef elf_symbols::const_iterator symbols_it_type; |
| typedef diff_utils::deep_ptr_eq_functor eq_type; |
| typedef vector<type_base_wptr>::const_iterator type_base_wptr_it_type; |
| |
| ABG_ASSERT(f && s); |
| |
| // We can only compare two corpora that were built out of the same |
| // environment. |
| ABG_ASSERT(f->get_environment() == s->get_environment()); |
| |
| if (!ctxt) |
| ctxt.reset(new diff_context); |
| |
| corpus_diff_sptr r(new corpus_diff(f, s, ctxt)); |
| |
| ctxt->set_corpus_diff(r); |
| |
| r->priv_->sonames_equal_ = f->get_soname() == s->get_soname(); |
| |
| r->priv_->architectures_equal_ = |
| f->get_architecture_name() == s->get_architecture_name(); |
| |
| // Compute the diff of publicly defined and exported functions |
| diff_utils::compute_diff<fns_it_type, eq_type>(f->get_functions().begin(), |
| f->get_functions().end(), |
| s->get_functions().begin(), |
| s->get_functions().end(), |
| r->priv_->fns_edit_script_); |
| |
| // Compute the diff of publicly defined and exported variables. |
| diff_utils::compute_diff<vars_it_type, eq_type> |
| (f->get_variables().begin(), f->get_variables().end(), |
| s->get_variables().begin(), s->get_variables().end(), |
| r->priv_->vars_edit_script_); |
| |
| // Compute the diff of function elf symbols not referenced by debug |
| // info. |
| diff_utils::compute_diff<symbols_it_type, eq_type> |
| (f->get_unreferenced_function_symbols().begin(), |
| f->get_unreferenced_function_symbols().end(), |
| s->get_unreferenced_function_symbols().begin(), |
| s->get_unreferenced_function_symbols().end(), |
| r->priv_->unrefed_fn_syms_edit_script_); |
| |
| // Compute the diff of variable elf symbols not referenced by debug |
| // info. |
| diff_utils::compute_diff<symbols_it_type, eq_type> |
| (f->get_unreferenced_variable_symbols().begin(), |
| f->get_unreferenced_variable_symbols().end(), |
| s->get_unreferenced_variable_symbols().begin(), |
| s->get_unreferenced_variable_symbols().end(), |
| r->priv_->unrefed_var_syms_edit_script_); |
| |
| if (ctxt->show_unreachable_types()) |
| // Compute the diff of types not reachable from public functions |
| // or global variables that are exported. |
| diff_utils::compute_diff<type_base_wptr_it_type, eq_type> |
| (f->get_types_not_reachable_from_public_interfaces().begin(), |
| f->get_types_not_reachable_from_public_interfaces().end(), |
| s->get_types_not_reachable_from_public_interfaces().begin(), |
| s->get_types_not_reachable_from_public_interfaces().end(), |
| r->priv_->unreachable_types_edit_script_); |
| |
| r->priv_->ensure_lookup_tables_populated(); |
| |
| return r; |
| } |
| |
| // </corpus stuff> |
| |
| /// Compute the diff between two instances of @ref corpus_group. |
| /// |
| /// Note that the two corpus_diff must have been created in the same |
| /// @ref environment, otherwise, this function aborts. |
| /// |
| /// @param f the first @ref corpus_group to consider for the diff. |
| /// |
| /// @param s the second @ref corpus_group to consider for the diff. |
| /// |
| /// @param ctxt the diff context to use. |
| /// |
| /// @return the resulting diff between the two @ref corpus_group. |
| corpus_diff_sptr |
| compute_diff(const corpus_group_sptr& f, |
| const corpus_group_sptr& s, |
| diff_context_sptr ctxt) |
| { |
| |
| corpus_sptr c1 = f; |
| corpus_sptr c2 = s; |
| |
| return compute_diff(c1, c2, ctxt); |
| } |
| |
| // <corpus_group stuff> |
| |
| // </corpus_group stuff> |
| // <diff_node_visitor stuff> |
| |
| /// The private data of the @diff_node_visitor type. |
| struct diff_node_visitor::priv |
| { |
| diff* topmost_interface_diff; |
| visiting_kind kind; |
| |
| priv() |
| : topmost_interface_diff(), |
| kind() |
| {} |
| |
| priv(visiting_kind k) |
| : topmost_interface_diff(), |
| kind(k) |
| {} |
| }; // end struct diff_node_visitor |
| |
| /// Default constructor of the @ref diff_node_visitor type. |
| diff_node_visitor::diff_node_visitor() |
| : priv_(new priv) |
| {} |
| |
| diff_node_visitor::~diff_node_visitor() = default; |
| |
| /// Constructor of the @ref diff_node_visitor type. |
| /// |
| /// @param k how the visiting has to be performed. |
| diff_node_visitor::diff_node_visitor(visiting_kind k) |
| : priv_(new priv(k)) |
| {} |
| |
| /// Getter for the visiting policy of the traversing code while |
| /// invoking this visitor. |
| /// |
| /// @return the visiting policy used by the traversing code when |
| /// invoking this visitor. |
| visiting_kind |
| diff_node_visitor::get_visiting_kind() const |
| {return priv_->kind;} |
| |
| /// Setter for the visiting policy of the traversing code while |
| /// invoking this visitor. |
| /// |
| /// @param v a bit map representing the new visiting policy used by |
| /// the traversing code when invoking this visitor. |
| void |
| diff_node_visitor::set_visiting_kind(visiting_kind v) |
| {priv_->kind = v;} |
| |
| /// Setter for the visiting policy of the traversing code while |
| /// invoking this visitor. This one makes a logical or between the |
| /// current policy and the bitmap given in argument and assigns the |
| /// current policy to the result. |
| /// |
| /// @param v a bitmap representing the visiting policy to or with |
| /// the current policy. |
| void |
| diff_node_visitor::or_visiting_kind(visiting_kind v) |
| {priv_->kind = priv_->kind | v;} |
| |
| /// Setter of the diff current topmost interface which is impacted by |
| /// the current diff node being visited. |
| /// |
| /// @param d the current topmost interface diff impacted. |
| void |
| diff_node_visitor::set_current_topmost_iface_diff(diff* d) |
| {priv_->topmost_interface_diff = d;} |
| |
| /// Getter of the diff current topmost interface which is impacted by |
| /// the current diff node being visited. |
| /// |
| /// @return the current topmost interface diff impacted. |
| diff* |
| diff_node_visitor::get_current_topmost_iface_diff() const |
| {return priv_->topmost_interface_diff;} |
| |
| /// This is called by the traversing code on a @ref diff node just |
| /// before visiting it. That is, before visiting it and its children |
| /// node. |
| /// |
| /// @param d the diff node to visit. |
| void |
| diff_node_visitor::visit_begin(diff* /*p*/) |
| {} |
| |
| /// This is called by the traversing code on a @ref diff node just |
| /// after visiting it. That is after visiting it and its children |
| /// nodes. |
| /// |
| /// @param d the diff node that got visited. |
| void |
| diff_node_visitor::visit_end(diff* /*p*/) |
| {} |
| |
| /// This is called by the traversing code on a @ref corpus_diff node |
| /// just before visiting it. That is, before visiting it and its |
| /// children node. |
| /// |
| /// @param p the corpus_diff node to visit. |
| /// |
| void |
| diff_node_visitor::visit_begin(corpus_diff* /*p*/) |
| {} |
| |
| /// This is called by the traversing code on a @ref corpus_diff node |
| /// just after visiting it. That is after visiting it and its children |
| /// nodes. |
| /// |
| /// @param d the diff node that got visited. |
| void |
| diff_node_visitor::visit_end(corpus_diff* /*d*/) |
| {} |
| |
| /// Default visitor implementation |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(diff*, bool) |
| {return true;} |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(distinct_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(var_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(pointer_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(reference_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(qualified_type_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(enum_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(class_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(base_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(scope_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(function_decl_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(type_decl_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(typedef_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(translation_unit_diff* dif, bool pre) |
| { |
| diff* d = dif; |
| visit(d, pre); |
| |
| return true; |
| } |
| |
| /// Default visitor implementation. |
| /// |
| /// @return true |
| bool |
| diff_node_visitor::visit(corpus_diff*, bool) |
| {return true;} |
| |
| // </diff_node_visitor stuff> |
| |
| // <redundant diff node marking> |
| |
| // </redundant diff node marking> |
| |
| // <diff tree category propagation> |
| |
| /// A visitor to propagate the category of a node up to its parent |
| /// nodes. This visitor doesn't touch the REDUNDANT_CATEGORY or the |
| /// SUPPRESSED_CATEGORY because those are propagated using other |
| /// specific visitors. |
| struct category_propagation_visitor : public diff_node_visitor |
| { |
| virtual void |
| visit_end(diff* d) |
| { |
| // Has this diff node 'd' been already visited ? |
| bool already_visited = d->context()->diff_has_been_visited(d); |
| |
| // The canonical diff node of the class of equivalence of the diff |
| // node 'd'. |
| diff* canonical = d->get_canonical_diff(); |
| |
| // If this class of equivalence of diff node is being visited for |
| // the first time, then update its canonical node's category too. |
| bool update_canonical = !already_visited && canonical; |
| |
| for (vector<diff*>::const_iterator i = d->children_nodes().begin(); |
| i != d->children_nodes().end(); |
| ++i) |
| { |
| // If we are visiting the class of equivalence of 'd' for the |
| // first time, then let's look at the children of 'd' and |
| // propagate their categories to 'd'. |
| // |
| // If the class of equivalence of 'd' has already been |
| // visited, then let's look at the canonical diff nodes of the |
| // children of 'd' and propagate their categories to 'd'. |
| diff* diff = already_visited |
| ? (*i)->get_canonical_diff() |
| : *i; |
| |
| ABG_ASSERT(diff); |
| |
| diff_category c = diff->get_category(); |
| // Do not propagate redundant and suppressed categories. Those |
| // are propagated in a specific pass elsewhere. |
| c &= ~(REDUNDANT_CATEGORY |
| | SUPPRESSED_CATEGORY |
| | PRIVATE_TYPE_CATEGORY); |
| // Also, if a (class) type has got a harmful name change, do not |
| // propagate harmless name changes coming from its sub-types |
| // (i.e, data members) to the class itself. |
| if (filtering::has_harmful_name_change(d)) |
| c &= ~HARMLESS_DECL_NAME_CHANGE_CATEGORY; |
| |
| d->add_to_category(c); |
| if (!already_visited && canonical) |
| if (update_canonical) |
| canonical->add_to_category(c); |
| } |
| } |
| };// end struct category_propagation_visitor |
| |
| /// Visit all the nodes of a given sub-tree. For each node that has a |
| /// particular category set, propagate that category set up to its |
| /// parent nodes. |
| /// |
| /// @param diff_tree the diff sub-tree to walk for categorization |
| /// purpose; |
| void |
| propagate_categories(diff* diff_tree) |
| { |
| category_propagation_visitor v; |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(true); |
| diff_tree->context()->forget_visited_diffs(); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Visit all the nodes of a given sub-tree. For each node that has a |
| /// particular category set, propagate that category set up to its |
| /// parent nodes. |
| /// |
| /// @param diff_tree the diff sub-tree to walk for categorization |
| /// purpose; |
| void |
| propagate_categories(diff_sptr diff_tree) |
| {propagate_categories(diff_tree.get());} |
| |
| /// Visit all the nodes of a given corpus tree. For each node that |
| /// has a particular category set, propagate that category set up to |
| /// its parent nodes. |
| /// |
| /// @param diff_tree the corpus_diff tree to walk for categorization |
| /// purpose; |
| void |
| propagate_categories(corpus_diff* diff_tree) |
| { |
| category_propagation_visitor v; |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Visit all the nodes of a given corpus tree. For each node that |
| /// has a particular category set, propagate that category set up to |
| /// its parent nodes. |
| /// |
| /// @param diff_tree the corpus_diff tree to walk for categorization |
| /// purpose; |
| void |
| propagate_categories(corpus_diff_sptr diff_tree) |
| {propagate_categories(diff_tree.get());} |
| |
| /// A tree node visitor that knows how to categorizes a given diff |
| /// node in the SUPPRESSED_CATEGORY category and how to propagate that |
| /// categorization. |
| struct suppression_categorization_visitor : public diff_node_visitor |
| { |
| |
| /// Before visiting the children of the diff node, check if the node |
| /// is suppressed by a suppression specification. If it is, mark |
| /// the node as belonging to the SUPPRESSED_CATEGORY category. |
| /// |
| /// @param p the diff node to visit. |
| virtual void |
| visit_begin(diff* d) |
| { |
| bool is_private_type = false; |
| if (d->is_suppressed(is_private_type)) |
| { |
| diff_category c = is_private_type |
| ? PRIVATE_TYPE_CATEGORY |
| : SUPPRESSED_CATEGORY; |
| d->add_to_local_and_inherited_categories(c); |
| |
| // If a node was suppressed, all the other nodes of its class |
| // of equivalence are suppressed too. |
| diff *canonical_diff = d->get_canonical_diff(); |
| if (canonical_diff != d) |
| canonical_diff->add_to_category(c); |
| } |
| } |
| |
| /// After visiting the children nodes of a given diff node, |
| /// propagate the SUPPRESSED_CATEGORY from the children nodes to the |
| /// diff node, if need be. |
| /// |
| /// That is, if all children nodes carry a suppressed change the |
| /// current node should be marked as suppressed as well. |
| /// |
| /// In practice, this might be too strong of a condition. If the |
| /// current node carries a local change (i.e, a change not carried |
| /// by any of its children node) and if that change is not |
| /// suppressed, then the current node should *NOT* be suppressed. |
| /// |
| /// But right now, the IR doesn't let us know about local vs |
| /// children-carried changes. So we cannot be that precise yet. |
| virtual void |
| visit_end(diff* d) |
| { |
| bool has_non_suppressed_child = false; |
| bool has_non_empty_child = false; |
| bool has_suppressed_child = false; |
| bool has_non_private_child = false; |
| bool has_private_child = false; |
| |
| if (// A node to which we can propagate the "SUPPRESSED_CATEGORY" |
| // (or the PRIVATE_TYPE_CATEGORY for the same matter) |
| // category from its children is a node which: |
| // |
| // 1/ hasn't been suppressed already |
| // |
| // 2/ and has no local change (unless it's a pointer, |
| // reference or qualified diff node). |
| // |
| // Note that qualified type and typedef diff nodes are a bit |
| // special. The local changes of the underlying type are |
| // considered local for the qualified/typedef type, just like |
| // for pointer/reference types. But then the qualified or |
| // typedef type itself can have local changes of its own, and |
| // those changes are of the kind LOCAL_NON_TYPE_CHANGE_KIND. |
| // So a qualified type which have local changes that are |
| // *NOT* of LOCAL_NON_TYPE_CHANGE_KIND (or that has no local |
| // changes at all) and which is in the PRIVATE_TYPE_CATEGORY |
| // or SUPPRESSED_CATEGORY can see these categories be |
| // propagated. |
| // |
| // Note that all pointer/reference diff node changes are |
| // potentially considered local, i.e, local changes of the |
| // pointed-to-type are considered local to the pointer itself. |
| // |
| // Similarly, changes local to the type of function parameters, |
| // variables (and data members) and classes (that are not of |
| // LOCAL_NON_TYPE_CHANGE_KIND kind) and that have been |
| // suppressed can propagate their SUPPRESSED_CATEGORY-ness to |
| // those kinds of diff node. |
| !(d->get_category() & SUPPRESSED_CATEGORY) |
| && (!d->has_local_changes() |
| || is_pointer_diff(d) |
| || is_reference_diff(d) |
| || (is_qualified_type_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_typedef_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_function_decl_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_fn_parm_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_function_type_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_var_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))) |
| || (is_class_diff(d) |
| && (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))))) |
| { |
| // Note that we handle private diff nodes differently from |
| // generally suppressed diff nodes. E.g, it's not because a |
| // type is private (and suppressed because of that; i.e, in |
| // the category PRIVATE_TYPE_CATEGORY) that a typedef to that |
| // type should also be private and so suppressed. Private |
| // diff nodes thus have different propagation rules than |
| // generally suppressed rules. |
| for (vector<diff*>::const_iterator i = d->children_nodes().begin(); |
| i != d->children_nodes().end(); |
| ++i) |
| { |
| diff* child = *i; |
| if (child->has_changes()) |
| { |
| has_non_empty_child = true; |
| if (child->get_class_of_equiv_category() & SUPPRESSED_CATEGORY) |
| has_suppressed_child = true; |
| else if (child->get_class_of_equiv_category() |
| & PRIVATE_TYPE_CATEGORY) |
| // Propagation of the PRIVATE_TYPE_CATEGORY is going |
| // to be handled later below. |
| ; |
| else |
| has_non_suppressed_child = true; |
| |
| if (child->get_class_of_equiv_category() |
| & PRIVATE_TYPE_CATEGORY) |
| has_private_child = true; |
| else if (child->get_class_of_equiv_category() |
| & SUPPRESSED_CATEGORY) |
| // Propagation of the SUPPRESSED_CATEGORY has been |
| // handled above already. |
| ; |
| else |
| has_non_private_child = true; |
| } |
| } |
| |
| if (has_non_empty_child |
| && has_suppressed_child |
| && !has_non_suppressed_child) |
| { |
| d->add_to_category(SUPPRESSED_CATEGORY); |
| // If a node was suppressed, all the other nodes of its class |
| // of equivalence are suppressed too. |
| diff *canonical_diff = d->get_canonical_diff(); |
| if (canonical_diff != d) |
| canonical_diff->add_to_category(SUPPRESSED_CATEGORY); |
| } |
| |
| // Note that the private-ness of a an underlying type won't be |
| // propagated to its parent typedef, by virtue of the big "if" |
| // clause at the beginning of this function. So we don't have |
| // to handle that case here. So the idiom of defining |
| // typedefs of private (opaque) types will be respected; |
| // meaning that changes to opaque underlying type will be |
| // flagged as private and the typedef will be flagged private |
| // as well, unless the typedef itself has local non-type |
| // changes. In the later case, changes to the typedef will be |
| // emitted because the typedef won't inherit the privateness |
| // of its underlying type. So in practise, the typedef |
| // remains public for the purpose of change reporting. |
| if (has_non_empty_child |
| && has_private_child |
| && !has_non_private_child) |
| { |
| d->add_to_category(PRIVATE_TYPE_CATEGORY); |
| // If a node was suppressed, all the other nodes of its class |
| // of equivalence are suppressed too. |
| diff *canonical_diff = d->get_canonical_diff(); |
| if (canonical_diff != d) |
| canonical_diff->add_to_category(PRIVATE_TYPE_CATEGORY); |
| } |
| |
| // If the underlying type of a typedef is private and carries |
| // changes (that are implicitely suppressed because it's |
| // private) then the typedef must be suppressed too, so that |
| // those changes to the underlying type are not seen. |
| if (is_typedef_diff(d) |
| && !d->has_local_changes() |
| && has_private_child |
| && has_non_empty_child) |
| { |
| d->add_to_category(SUPPRESSED_CATEGORY|PRIVATE_TYPE_CATEGORY); |
| // If a node was suppressed, all the other nodes of its class |
| // of equivalence are suppressed too. |
| diff *canonical_diff = d->get_canonical_diff(); |
| if (canonical_diff != d) |
| canonical_diff->add_to_category |
| (SUPPRESSED_CATEGORY|PRIVATE_TYPE_CATEGORY); |
| } |
| |
| if (const function_decl_diff *fn_diff = is_function_decl_diff(d)) |
| if (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)) |
| { |
| // d is a function diff that carries a local *type* |
| // change (that means it's a change to the function |
| // type). Let's see if the child function type diff |
| // node is suppressed. That would mean that we are |
| // instructed to show details of a diff that is deemed |
| // suppressed; this means the suppression conflicts with |
| // a local type change. In that case, let's follow what |
| // the user asked and suppress the function altogether, |
| if (function_type_diff_sptr fn_type_diff = fn_diff->type_diff()) |
| if (fn_type_diff->is_suppressed()) |
| { |
| d->add_to_category(SUPPRESSED_CATEGORY); |
| // If a node was suppressed, all the other nodes |
| // of its class of equivalence are suppressed too. |
| diff *canonical_diff = d->get_canonical_diff(); |
| if (canonical_diff != d) |
| canonical_diff->add_to_category(SUPPRESSED_CATEGORY); |
| } |
| } |
| } |
| } |
| }; //end struct suppression_categorization_visitor |
| |
| /// Walk a given diff-sub tree and appply the suppressions carried by |
| /// the context. If the suppression applies to a given node than |
| /// categorize the node into the SUPPRESSED_CATEGORY category and |
| /// propagate that categorization. |
| /// |
| /// @param diff_tree the diff-sub tree to apply the suppressions to. |
| void |
| apply_suppressions(diff* diff_tree) |
| { |
| if (diff_tree && !diff_tree->context()->suppressions().empty()) |
| { |
| // Apply suppressions to functions and variables that have |
| // changed sub-types. |
| suppression_categorization_visitor v; |
| diff_tree->context()->forget_visited_diffs(); |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(true); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| } |
| |
| /// Walk a given diff-sub tree and appply the suppressions carried by |
| /// the context. If the suppression applies to a given node than |
| /// categorize the node into the SUPPRESSED_CATEGORY category and |
| /// propagate that categorization. |
| /// |
| /// @param diff_tree the diff-sub tree to apply the suppressions to. |
| void |
| apply_suppressions(diff_sptr diff_tree) |
| {apply_suppressions(diff_tree.get());} |
| |
| /// Walk a @ref corpus_diff tree and appply the suppressions carried |
| /// by the context. If the suppression applies to a given node then |
| /// categorize the node into the SUPPRESSED_CATEGORY category and |
| /// propagate that categorization. |
| /// |
| /// @param diff_tree the diff tree to apply the suppressions to. |
| void |
| apply_suppressions(const corpus_diff* diff_tree) |
| { |
| if (diff_tree && !diff_tree->context()->suppressions().empty()) |
| { |
| // First, visit the children trees of changed constructs: |
| // changed functions, variables, as well as sub-types of these, |
| // and apply suppression specifications to these ... |
| suppression_categorization_visitor v; |
| diff_tree->context()->forget_visited_diffs(); |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(true); |
| const_cast<corpus_diff*>(diff_tree)->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| |
| // ... then also visit the set of added and removed functions, |
| // variables, symbols, and types not reachable from global |
| // functions and variables. |
| diff_tree->priv_-> |
| apply_supprs_to_added_removed_fns_vars_unreachable_types(); |
| } |
| } |
| |
| /// Walk a diff tree and appply the suppressions carried by the |
| /// context. If the suppression applies to a given node than |
| /// categorize the node into the SUPPRESSED_CATEGORY category and |
| /// propagate that categorization. |
| /// |
| /// @param diff_tree the diff tree to apply the suppressions to. |
| void |
| apply_suppressions(corpus_diff_sptr diff_tree) |
| {apply_suppressions(diff_tree.get());} |
| |
| // </diff tree category propagation> |
| |
| // <diff tree printing stuff> |
| |
| /// A visitor to print (to an output stream) a pretty representation |
| /// of a @ref diff sub-tree or of a complete @ref corpus_diff tree. |
| struct diff_node_printer : public diff_node_visitor |
| { |
| ostream& out_; |
| unsigned level_; |
| |
| /// Emit a certain number of spaces to the output stream associated |
| /// to this diff_node_printer. |
| /// |
| /// @param level half of the numver of spaces to emit. |
| void |
| do_indent(unsigned level) |
| { |
| for (unsigned i = 0; i < level; ++i) |
| out_ << " "; |
| } |
| |
| diff_node_printer(ostream& out) |
| : diff_node_visitor(DO_NOT_MARK_VISITED_NODES_AS_VISITED), |
| out_(out), |
| level_(0) |
| {} |
| |
| virtual void |
| visit_begin(diff*) |
| { |
| ++level_; |
| } |
| |
| virtual void |
| visit_end(diff*) |
| { |
| --level_; |
| } |
| |
| virtual void |
| visit_begin(corpus_diff*) |
| { |
| ++level_; |
| } |
| |
| virtual void |
| visit_end(corpus_diff*) |
| { |
| --level_; |
| } |
| |
| virtual bool |
| visit(diff* d, bool pre) |
| { |
| if (!pre) |
| // We are post-visiting the diff node D. Which means, we have |
| // printed a pretty representation for it already. So do |
| // nothing now. |
| return true; |
| |
| do_indent(level_); |
| out_ << d->get_pretty_representation(); |
| out_ << "\n"; |
| do_indent(level_); |
| out_ << "{\n"; |
| do_indent(level_ + 1); |
| out_ << "category: "<< d->get_category() << "\n"; |
| do_indent(level_ + 1); |
| out_ << "@: " << std::hex << d << std::dec << "\n"; |
| do_indent(level_ + 1); |
| out_ << "@-canonical: " << std::hex |
| << d->get_canonical_diff() |
| << std::dec << "\n"; |
| do_indent(level_); |
| out_ << "}\n"; |
| |
| return true; |
| } |
| |
| virtual bool |
| visit(corpus_diff* d, bool pre) |
| { |
| if (!pre) |
| // We are post-visiting the diff node D. Which means, we have |
| // printed a pretty representation for it already. So do |
| // nothing now. |
| return true; |
| |
| // indent |
| for (unsigned i = 0; i < level_; ++i) |
| out_ << ' '; |
| out_ << d->get_pretty_representation(); |
| out_ << '\n'; |
| return true; |
| } |
| }; // end struct diff_printer_visitor |
| |
| // </ diff tree printing stuff> |
| |
| /// Emit a textual representation of a @ref diff sub-tree to an |
| /// output stream. |
| /// |
| /// @param diff_tree the sub-tree to emit the textual representation |
| /// for. |
| /// |
| /// @param out the output stream to emit the textual representation |
| /// for @p diff_tree to. |
| void |
| print_diff_tree(diff* diff_tree, ostream& out) |
| { |
| diff_node_printer p(out); |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(p); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Emit a textual representation of a @ref corpus_diff tree to an |
| /// output stream. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to emit the textual |
| /// representation for. |
| /// |
| /// @param out the output stream to emit the textual representation |
| /// for @p diff_tree to. |
| void |
| print_diff_tree(corpus_diff* diff_tree, std::ostream& out) |
| { |
| diff_node_printer p(out); |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(p); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Emit a textual representation of a @ref diff sub-tree to an |
| /// output stream. |
| /// |
| /// @param diff_tree the sub-tree to emit the textual representation |
| /// for. |
| /// |
| /// @param out the output stream to emit the textual representation |
| /// for @p diff_tree to. |
| void |
| print_diff_tree(diff_sptr diff_tree, |
| std::ostream& o) |
| {print_diff_tree(diff_tree.get(), o);} |
| |
| /// Emit a textual representation of a @ref corpus_diff tree to an |
| /// output stream. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to emit the textual |
| /// representation for. |
| /// |
| /// @param out the output stream to emit the textual representation |
| /// for @p diff_tree to. |
| void |
| print_diff_tree(corpus_diff_sptr diff_tree, |
| std::ostream& o) |
| {print_diff_tree(diff_tree.get(), o);} |
| |
| // <redundancy_marking_visitor> |
| |
| /// A tree visitor to categorize nodes with respect to the |
| /// REDUNDANT_CATEGORY. That is, detect if a node is redundant (is |
| /// present on several spots of the tree) and mark such nodes |
| /// appropriatly. This visitor also takes care of propagating the |
| /// REDUNDANT_CATEGORY of a given node to its parent nodes as |
| /// appropriate. |
| struct redundancy_marking_visitor : public diff_node_visitor |
| { |
| bool skip_children_nodes_; |
| |
| redundancy_marking_visitor() |
| : skip_children_nodes_() |
| {} |
| |
| virtual void |
| visit_begin(diff* d) |
| { |
| if (d->to_be_reported()) |
| { |
| // A diff node that carries a change and that has been already |
| // traversed elsewhere is considered redundant. So let's mark |
| // it as such and let's not traverse it; that is, let's not |
| // visit its children. |
| if ((d->context()->diff_has_been_visited(d) |
| || d->get_canonical_diff()->is_traversing()) |
| && d->has_changes()) |
| { |
| // But if two diff nodes are redundant sibbling that carry |
| // changes of base types, do not mark them as being |
| // redundant. This is to avoid marking nodes as redundant |
| // in this case: |
| // |
| // int foo(int a, int b); |
| // compared with: |
| // float foo(float a, float b); (in C). |
| // |
| // In this case, we want to report all the occurences of |
| // the int->float change because logically, they are at |
| // the same level in the diff tree. |
| |
| bool redundant_with_sibling_node = false; |
| const diff* p = d->parent_node(); |
| |
| // If this is a child node of a fn_parm_diff, look through |
| // the fn_parm_diff node to get the function diff node. |
| if (p && dynamic_cast<const fn_parm_diff*>(p)) |
| p = p->parent_node(); |
| |
| if (p) |
| for (vector<diff*>::const_iterator s = |
| p->children_nodes().begin(); |
| s != p->children_nodes().end(); |
| ++s) |
| { |
| if (*s == d) |
| continue; |
| diff* sib = *s; |
| // If this is a fn_parm_diff, look through the |
| // fn_parm_diff node to get at the real type node. |
| if (fn_parm_diff* f = dynamic_cast<fn_parm_diff*>(*s)) |
| sib = f->type_diff().get(); |
| if (sib == d) |
| continue; |
| if (sib->get_canonical_diff() == d->get_canonical_diff() |
| // Sibbling diff nodes that carry base type |
| // changes ar to be marked as redundant. |
| && (is_base_diff(sib) || is_distinct_diff(sib))) |
| { |
| redundant_with_sibling_node = true; |
| break; |
| } |
| } |
| if (!redundant_with_sibling_node |
| // Changes to basic types should never be considered |
| // redundant. For instance, if a member of integer |
| // type is changed into a char type in both a struct A |
| // and a struct B, we want to see both changes. |
| && !has_basic_type_change_only(d) |
| // The same goes for distinct type changes |
| && !filtering::is_mostly_distinct_diff(d) |
| // Functions with similar *local* changes are never marked |
| // redundant because otherwise one could miss important |
| // similar local changes that are applied to different |
| // functions. |
| && !is_function_type_diff_with_local_changes(d) |
| // Changes involving variadic parameters of functions |
| // should never be marked redundant because we want to see |
| // them all. |
| && !is_diff_of_variadic_parameter(d) |
| && !is_diff_of_variadic_parameter_type(d) |
| // If the canonical diff itself has been filtered out, |
| // then this one is not marked redundant, unless the |
| // canonical diff was already redundant. |
| && (!d->get_canonical_diff()->is_filtered_out() |
| || (d->get_canonical_diff()->get_category() |
| & REDUNDANT_CATEGORY)) |
| // If the *same* diff node (not one that is merely |
| // equivalent to this one) has already been visited |
| // the do not mark it as beind redundant. It's only |
| // the other nodes that are equivalent to this one |
| // that must be marked redundant. |
| && d->context()->diff_has_been_visited(d) != d |
| // If the diff node is a function parameter and is not |
| // a reference/pointer (to a non basic or a non |
| // distinct type diff) then do not mark it as |
| // redundant. |
| // |
| // Children nodes of base class diff nodes are never |
| // redundant either, we want to see them all. |
| && (is_reference_or_ptr_diff_to_non_basic_nor_distinct_types(d) |
| || (!is_child_node_of_function_parm_diff(d) |
| && !is_child_node_of_base_diff(d)))) |
| { |
| d->add_to_category(REDUNDANT_CATEGORY); |
| // As we said in preamble, as this node is marked as |
| // being redundant, let's not visit its children. |
| // This is not an optimization; it's needed for |
| // correctness. In the case of a diff node involving |
| // a class type that refers to himself, visiting the |
| // children nodes might cause them to be wrongly |
| // marked as redundant. |
| set_visiting_kind(get_visiting_kind() |
| | SKIP_CHILDREN_VISITING_KIND); |
| skip_children_nodes_ = true; |
| } |
| } |
| } |
| else |
| { |
| // If the node is not to be reported, do not look at it children. |
| set_visiting_kind(get_visiting_kind() | SKIP_CHILDREN_VISITING_KIND); |
| skip_children_nodes_ = true; |
| } |
| } |
| |
| virtual void |
| visit_begin(corpus_diff*) |
| { |
| } |
| |
| virtual void |
| visit_end(diff* d) |
| { |
| if (skip_children_nodes_) |
| // When visiting this node, we decided to skip its children |
| // node. Now that we are done visiting the node, lets stop |
| // avoiding the children nodes visiting for the other tree |
| // nodes. |
| { |
| set_visiting_kind(get_visiting_kind() & (~SKIP_CHILDREN_VISITING_KIND)); |
| skip_children_nodes_ = false; |
| } |
| else |
| { |
| // Propagate the redundancy categorization of the children nodes |
| // to this node. But if this node has local changes, then it |
| // doesn't inherit redundancy from its children nodes. |
| if (!(d->get_category() & REDUNDANT_CATEGORY) |
| && (!d->has_local_changes_to_be_reported() |
| // By default, pointer, reference and qualified types |
| // consider that a local changes to their underlying |
| // type is always a local change for themselves. |
| // |
| // This is as if those types don't have local changes |
| // in the same sense as other types. So we always |
| // propagate redundancy to them, regardless of if they |
| // have local changes or not. |
| // |
| // We also propagate redundancy to typedef types if |
| // these /only/ carry changes to their underlying |
| // type. |
| // |
| // Note that changes to the underlying type of a |
| // typedef is considered local of |
| // LOCAL_TYPE_CHANGE_KIND kind. The other changes to the |
| // typedef itself are considered local of |
| // LOCAL_NON_TYPE_CHANGE_KIND kind. |
| || is_pointer_diff(d) |
| || is_qualified_type_diff(d) |
| || (is_typedef_diff(d) |
| && (!(d->has_local_changes() |
| & LOCAL_NON_TYPE_CHANGE_KIND))))) |
| { |
| bool has_non_redundant_child = false; |
| bool has_non_empty_child = false; |
| for (vector<diff*>::const_iterator i = |
| d->children_nodes().begin(); |
| i != d->children_nodes().end(); |
| ++i) |
| { |
| if ((*i)->has_changes()) |
| { |
| has_non_empty_child = true; |
| // Let's see if the current child node '*i' is |
| // "non-redundant". |
| // |
| // A non-redundant node would be a node that |
| // carries a change to be reported and has not |
| // been marked as being redundant. |
| if ((*i)->to_be_reported() |
| && ((*i)->get_category() & REDUNDANT_CATEGORY) == 0) |
| has_non_redundant_child = true; |
| } |
| if (has_non_redundant_child) |
| break; |
| } |
| |
| // A diff node for which at least a child node carries a |
| // change, and for which all the children are redundant is |
| // deemed redundant too, unless it has local changes. |
| if (has_non_empty_child |
| && !has_non_redundant_child) |
| d->add_to_category(REDUNDANT_CATEGORY); |
| } |
| } |
| } |
| |
| virtual void |
| visit_end(corpus_diff*) |
| { |
| } |
| |
| virtual bool |
| visit(diff*, bool) |
| {return true;} |
| |
| virtual bool |
| visit(corpus_diff*, bool) |
| { |
| return true; |
| } |
| };// end struct redundancy_marking_visitor |
| |
| /// A visitor of @ref diff nodes that clears the REDUNDANT_CATEGORY |
| /// category out of the nodes. |
| struct redundancy_clearing_visitor : public diff_node_visitor |
| { |
| bool |
| visit(corpus_diff*, bool) |
| {return true;} |
| |
| bool |
| visit(diff* d, bool) |
| { |
| // clear the REDUNDANT_CATEGORY out of the current node. |
| diff_category c = d->get_category(); |
| c &= ~REDUNDANT_CATEGORY; |
| d->set_category(c); |
| return true; |
| } |
| }; // end struct redundancy_clearing_visitor |
| |
| /// Walk a given @ref diff sub-tree to categorize each of the nodes |
| /// with respect to the REDUNDANT_CATEGORY. |
| /// |
| /// @param diff_tree the @ref diff sub-tree to walk. |
| void |
| categorize_redundancy(diff* diff_tree) |
| { |
| if (diff_tree->context()->show_redundant_changes()) |
| return; |
| redundancy_marking_visitor v; |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Walk a given @ref diff sub-tree to categorize each of the nodes |
| /// with respect to the REDUNDANT_CATEGORY. |
| /// |
| /// @param diff_tree the @ref diff sub-tree to walk. |
| void |
| categorize_redundancy(diff_sptr diff_tree) |
| {categorize_redundancy(diff_tree.get());} |
| |
| /// Walk a given @ref corpus_diff tree to categorize each of the nodes |
| /// with respect to the REDUNDANT_CATEGORY. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to walk. |
| void |
| categorize_redundancy(corpus_diff* diff_tree) |
| { |
| redundancy_marking_visitor v; |
| diff_tree->context()->forget_visited_diffs(); |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| } |
| |
| /// Walk a given @ref corpus_diff tree to categorize each of the nodes |
| /// with respect to the REDUNDANT_CATEGORY. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to walk. |
| void |
| categorize_redundancy(corpus_diff_sptr diff_tree) |
| {categorize_redundancy(diff_tree.get());} |
| |
| // </redundancy_marking_visitor> |
| |
| /// Walk a given @ref diff sub-tree to clear the REDUNDANT_CATEGORY |
| /// out of the category of the nodes. |
| /// |
| /// @param diff_tree the @ref diff sub-tree to walk. |
| void |
| clear_redundancy_categorization(diff* diff_tree) |
| { |
| redundancy_clearing_visitor v; |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| diff_tree->context()->forget_visited_diffs(); |
| } |
| |
| /// Walk a given @ref diff sub-tree to clear the REDUNDANT_CATEGORY |
| /// out of the category of the nodes. |
| /// |
| /// @param diff_tree the @ref diff sub-tree to walk. |
| void |
| clear_redundancy_categorization(diff_sptr diff_tree) |
| {clear_redundancy_categorization(diff_tree.get());} |
| |
| /// Walk a given @ref corpus_diff tree to clear the REDUNDANT_CATEGORY |
| /// out of the category of the nodes. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to walk. |
| void |
| clear_redundancy_categorization(corpus_diff* diff_tree) |
| { |
| redundancy_clearing_visitor v; |
| bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden(); |
| diff_tree->context()->forbid_visiting_a_node_twice(false); |
| diff_tree->traverse(v); |
| diff_tree->context()->forbid_visiting_a_node_twice(s); |
| diff_tree->context()->forget_visited_diffs(); |
| } |
| |
| /// Walk a given @ref corpus_diff tree to clear the REDUNDANT_CATEGORY |
| /// out of the category of the nodes. |
| /// |
| /// @param diff_tree the @ref corpus_diff tree to walk. |
| void |
| clear_redundancy_categorization(corpus_diff_sptr diff_tree) |
| {clear_redundancy_categorization(diff_tree.get());} |
| |
| /// Apply the @ref diff tree filters that have been associated to the |
| /// context of the a given @ref corpus_diff tree. As a result, the |
| /// nodes of the @diff tree are going to be categorized into one of |
| /// several of the categories of @ref diff_category. |
| /// |
| /// @param diff_tree the @ref corpus_diff instance which @ref diff are |
| /// to be categorized. |
| void |
| apply_filters(corpus_diff_sptr diff_tree) |
| { |
| diff_tree->context()->maybe_apply_filters(diff_tree); |
| propagate_categories(diff_tree); |
| } |
| |
| /// Test if a diff node represents the difference between a variadic |
| /// parameter type and something else. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a diff node that represents the |
| /// difference between a variadic parameter type and something else. |
| bool |
| is_diff_of_variadic_parameter_type(const diff* d) |
| { |
| if (!d) |
| return false; |
| |
| type_base_sptr t = is_type(d->first_subject()); |
| if (t && t->get_environment()->is_variadic_parameter_type(t)) |
| return true; |
| |
| t = is_type(d->second_subject()); |
| if (t && t->get_environment()->is_variadic_parameter_type(t)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a diff node represents the difference between a variadic |
| /// parameter type and something else. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a diff node that represents the |
| /// difference between a variadic parameter type and something else. |
| bool |
| is_diff_of_variadic_parameter_type(const diff_sptr& d) |
| {return is_diff_of_variadic_parameter_type(d.get());} |
| |
| /// Test if a diff node represents the difference between a variadic |
| /// parameter and something else. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a diff node that represents the |
| /// difference between a variadic parameter and something else. |
| bool |
| is_diff_of_variadic_parameter(const diff* d) |
| { |
| fn_parm_diff* diff = |
| dynamic_cast<fn_parm_diff*>(const_cast<abigail::comparison::diff*>(d)); |
| return (diff && is_diff_of_variadic_parameter_type(diff->type_diff())); |
| } |
| |
| /// Test if a diff node represents the difference between a variadic |
| /// parameter and something else. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a diff node that represents the |
| /// difference between a variadic parameter and something else. |
| bool |
| is_diff_of_variadic_parameter(const diff_sptr& d) |
| {return is_diff_of_variadic_parameter(d.get());} |
| |
| /// Test if a diff node represents a diff between two basic types. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a diff between two basic types. |
| const type_decl_diff* |
| is_diff_of_basic_type(const diff *d) |
| {return dynamic_cast<const type_decl_diff*>(d);} |
| |
| /// Test if a diff node represents a diff between two basic types, or |
| /// between pointers, references or qualified type to basic types. |
| /// |
| /// @param diff the diff node to consider. |
| /// |
| /// @param allow_indirect_type if true, then this function looks into |
| /// pointer, reference or qualified diff types to see if they "point |
| /// to" basic types. |
| /// |
| /// @return true iff @p d is a diff between two basic types. |
| const type_decl_diff* |
| is_diff_of_basic_type(const diff* diff, bool allow_indirect_type) |
| { |
| if (allow_indirect_type) |
| diff = peel_pointer_or_qualified_type_diff(diff); |
| return is_diff_of_basic_type(diff); |
| } |
| |
| /// If a diff node is about changes between two typedef types, get the |
| /// diff node about changes between the underlying types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not typedefs. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not a typedef diff node. |
| const diff* |
| peel_typedef_diff(const diff* dif) |
| { |
| const typedef_diff *d = 0; |
| while ((d = is_typedef_diff(dif))) |
| dif = d->underlying_type_diff().get(); |
| return dif; |
| } |
| |
| /// If a diff node is about changes between two pointer types, get the |
| /// diff node about changes between the underlying (pointed-to) types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not pointers. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not a pointer diff node. |
| const diff* |
| peel_pointer_diff(const diff* dif) |
| { |
| const pointer_diff *d = 0; |
| while ((d = is_pointer_diff(dif))) |
| dif = d->underlying_type_diff().get(); |
| return dif; |
| } |
| |
| /// If a diff node is about changes between two reference types, get |
| /// the diff node about changes between the underlying (pointed-to) |
| /// types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not references. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not a reference diff node. |
| const diff* |
| peel_reference_diff(const diff* dif) |
| { |
| const reference_diff *d = 0; |
| while ((d = is_reference_diff(dif))) |
| dif = d->underlying_type_diff().get(); |
| return dif; |
| } |
| |
| /// If a diff node is about changes between two qualified types, get |
| /// the diff node about changes between the underlying (non-qualified) |
| /// types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not qualified. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not a qualified diff node. |
| const diff* |
| peel_qualified_diff(const diff* dif) |
| { |
| const qualified_type_diff *d = 0; |
| while ((d = is_qualified_type_diff(dif))) |
| dif = d->underlying_type_diff().get(); |
| return dif; |
| } |
| |
| /// If a diff node is about changes between two pointer, reference or |
| /// qualified types, get the diff node about changes between the |
| /// underlying types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not pointer, |
| /// reference or qualified. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not a pointer, reference or qualified diff node. |
| const diff* |
| peel_pointer_or_qualified_type_diff(const diff*dif) |
| { |
| while (true) |
| { |
| if (const pointer_diff *d = is_pointer_diff(dif)) |
| dif = peel_pointer_diff(d); |
| else if (const reference_diff *d = is_reference_diff(dif)) |
| dif = peel_reference_diff(d); |
| else if (const qualified_type_diff *d = is_qualified_type_diff(dif)) |
| dif = peel_qualified_diff(d); |
| else |
| break; |
| } |
| return dif; |
| } |
| |
| /// If a diff node is about changes between two typedefs or qualified |
| /// types, get the diff node about changes between the underlying |
| /// types. |
| /// |
| /// Note that this function walks the tree of underlying diff nodes |
| /// returns the first diff node about types that are not typedef or |
| /// qualified types. |
| /// |
| /// @param dif the dif node to consider. |
| /// |
| /// @return the underlying diff node of @p dif, or just return @p dif |
| /// if it's not typedef or qualified diff node. |
| const diff* |
| peel_typedef_or_qualified_type_diff(const diff *dif) |
| { |
| while (true) |
| { |
| if (const typedef_diff *d = is_typedef_diff(dif)) |
| dif = peel_typedef_diff(d); |
| else if (const qualified_type_diff *d = is_qualified_type_diff(dif)) |
| dif = peel_qualified_diff(d); |
| else |
| break; |
| } |
| return dif; |
| } |
| |
| /// Test if a diff node represents a diff between two class or union |
| /// types. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return iff @p is a diff between two class or union types then |
| /// return the instance of @ref class_or_union_diff that @p derives |
| /// from. Otherwise, return nil. |
| const class_or_union_diff* |
| is_diff_of_class_or_union_type(const diff *d) |
| {return dynamic_cast<const class_or_union_diff*>(d);} |
| |
| /// Test if a given diff node carries *only* a local type change. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p has a change and that change is a local type |
| /// change. |
| static bool |
| has_local_type_change_only(const diff *d) |
| { |
| if (enum change_kind k = d->has_local_changes()) |
| if ((k & LOCAL_NON_TYPE_CHANGE_KIND) == 0 |
| && (k & LOCAL_TYPE_CHANGE_KIND) != 0) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a diff node is a decl diff that only carries a basic type |
| /// change on its type diff sub-node. |
| /// |
| ///Note that that pointers/references/qualified types diffs to basic |
| /// type diffs are considered as having basic type change only. |
| /// |
| /// @param d the diff node to consider. |
| /// |
| /// @return true iff @p d is a decl diff that only carries a basic |
| /// type change on its type diff sub-node. |
| bool |
| has_basic_type_change_only(const diff *d) |
| { |
| if (is_diff_of_basic_type(d, true) && d->has_changes()) |
| return true; |
| else if (const var_diff * v = dynamic_cast<const var_diff*>(d)) |
| return (has_local_type_change_only(v) |
| && is_diff_of_basic_type(v->type_diff().get(), true)); |
| else if (const fn_parm_diff * p = dynamic_cast<const fn_parm_diff*>(d)) |
| return (has_local_type_change_only(p) |
| && is_diff_of_basic_type(p->type_diff().get(), true)); |
| else if (const function_decl_diff* f = |
| dynamic_cast<const function_decl_diff*>(d)) |
| return (has_local_type_change_only(f) |
| && f->type_diff() |
| && is_diff_of_basic_type(f->type_diff()->return_type_diff().get(), |
| true)); |
| return false; |
| } |
| }// end namespace comparison |
| } // end namespace abigail |