blob: eab5fc1a9327bd2c53750fb8f471baac41a9f286 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2016-2020 Red Hat, Inc.
//
// Author: Dodji Seketeli
/// @file
///
/// This contains the implementation of the suppression engine of
/// libabigail.
#include <algorithm>
#include "abg-internal.h"
#include <memory>
#include <limits>
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS
#include "abg-ini.h"
#include "abg-comp-filter.h"
#include "abg-suppression.h"
#include "abg-tools-utils.h"
ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>
#include "abg-suppression-priv.h"
namespace abigail
{
namespace suppr
{
using std::dynamic_pointer_cast;
using regex::regex_t_sptr;
// <parsing stuff>
// section parsing
/// Check if a section has at least one of the given properties.
///
/// @param names pointer to the start of an array of names.
///
/// @param count number of names in the array.
///
/// @return whether at least of one the properties was found.
bool
check_sufficient_props(const char *const * names, size_t count,
const ini::config::section& section)
{
for (const char *const * name = names; name < names + count; ++name)
if (section.find_property(*name))
return true;
// TODO: Possibly give reason for failure in a message here.
return false;
}
// </parsing stuff>
// <suppression_base stuff>
/// Constructor for @ref suppression_base
///
/// @param a label for the suppression. This represents just a
/// comment.
suppression_base::suppression_base(const string& label)
: priv_(new priv(label))
{}
/// Constructor for @ref suppression_base
///
/// @param a label for the suppression. This represents just a
/// comment.
///
/// @param file_name_regex_str the regular expression that denotes the
/// file name to match.
///
/// @param file_name_not_regex_str the regular expression that denotes
/// the file name to *NOT* match.
suppression_base::suppression_base(const string& label,
const string& file_name_regex_str,
const string& file_name_not_regex_str)
: priv_(new priv(label,
file_name_regex_str,
file_name_not_regex_str))
{
}
/// Tests if the current suppression specification is to avoid adding
/// the matched ABI artifact to the internal representation or not.
///
/// @return true iff the current suppression specification is to avoid
/// adding the matched ABI artifact to the internal representation.
bool
suppression_base::get_drops_artifact_from_ir() const
{return priv_->drops_artifact_;}
/// Set the flag that says whether the current suppression
/// specification is to avoid adding the matched ABI artifact to the
/// internal representation or not.
///
/// @param f the flag to set to true iff the current suppression
/// specification is to avoid adding the matched ABI artifact to the
/// internal representation.
void
suppression_base::set_drops_artifact_from_ir(bool f)
{priv_->drops_artifact_ = f;}
/// Test is the suppression specification is artificial.
///
/// Artificial means that the suppression was automatically generated
/// by libabigail, rather than being constructed from a suppression
/// file provided by the user.
///
/// @return TRUE iff the suppression specification is artificial.
bool
suppression_base::get_is_artificial() const
{return priv_->is_artificial_;}
/// Set a flag saying if the suppression specification is artificial
/// or not.
///
/// Artificial means that the suppression was automatically generated
/// by libabigail, rather than being constructed from a suppression
/// file provided by the user.
void
suppression_base::set_is_artificial(bool f)
{priv_->is_artificial_ = f;}
/// Getter for the label associated to this suppression specification.
///
/// @return the label.
const string
suppression_base::get_label() const
{return priv_->label_;}
/// Setter for the label associated to this suppression specification.
///
/// @param label the new label.
void
suppression_base::set_label(const string& label)
{priv_->label_ = label;}
/// Setter for the "file_name_regex" property of the current instance
/// of @ref suppression_base.
///
/// The "file_name_regex" property is a regular expression string that
/// designates the file name that contains the ABI artifact this
/// suppression should apply to.
///
/// @param regexp the new regular expression string.
void
suppression_base::set_file_name_regex_str(const string& regexp)
{priv_->file_name_regex_str_ = regexp;}
/// Getter for the "file_name_regex" property of the current instance
/// of @ref suppression_base.
///
/// The "file_name_regex" property is a regular expression string that
/// designates the file name that contains the ABI artifacts this
/// suppression should apply to.
///
/// @return the regular expression string.
const string&
suppression_base::get_file_name_regex_str() const
{return priv_->file_name_regex_str_;}
/// Setter for the "file_name_not_regex" property of the current
/// instance of @ref suppression_base.
///
/// The current suppression specification should apply to ABI
/// artifacts of a file which name does *NOT* match the regular
/// expression string designated by the "file_name_not_regex"
/// property.
///
/// @param regexp the new regular expression string.
void
suppression_base::set_file_name_not_regex_str(const string& regexp)
{priv_->file_name_not_regex_str_ = regexp;}
/// Getter for the "file_name_not_regex" property of the current
/// instance of @ref suppression_base.
///
/// The current suppression specification should apply to ABI
/// artifacts of a file which name does *NOT* match the regular
/// expression string designated by the "file_name_not_regex"
/// property.
///
/// @return the regular expression string.
const string&
suppression_base::get_file_name_not_regex_str() const
{return priv_->file_name_not_regex_str_;}
/// Test if the current suppression has a property related to file
/// name.
///
/// @return true iff the current suppression has either a
/// file_name_regex or a file_name_not_regex property.
bool
suppression_base::has_file_name_related_property() const
{
return (!(get_file_name_regex_str().empty()
&& get_file_name_not_regex_str().empty()));
}
/// Setter of the "soname_regex_str property of the current instance
/// of @ref suppression_base.
///
/// The "soname_regex_str" is a regular expression string that
/// designates the soname of the shared library that contains the ABI
/// artifacts this suppression should apply to.
///
/// @param regexp the new regular expression string.
void
suppression_base::set_soname_regex_str(const string& regexp)
{priv_->soname_regex_str_ = regexp;}
/// Getter of the "soname_regex_str property of the current instance
/// of @ref suppression_base.
///
/// The "soname_regex_str" is a regular expression string that
/// designates the soname of the shared library that contains the ABI
/// artifacts this suppression should apply to.
///
/// @return the regular expression string.
const string&
suppression_base::get_soname_regex_str() const
{return priv_->soname_regex_str_;}
/// Setter of the "soname_not_regex_str property of the current
/// instance of @ref suppression_base.
///
/// The current suppression specification should apply to ABI
/// artifacts of a shared library which SONAME does *NOT* match the
/// regular expression string designated by the "soname_not_regex"
/// property.
///
/// @param regexp the new regular expression string.
void
suppression_base::set_soname_not_regex_str(const string& regexp)
{priv_->soname_not_regex_str_ = regexp;}
/// Getter of the "soname_not_regex_str property of the current
/// instance of @ref suppression_base.
///
/// The current suppression specification should apply to ABI
/// artifacts of a shared library which SONAME does *NOT* match the
/// regular expression string designated by the "soname_not_regex"
/// property.
///
/// @return the regular expression string.
const string&
suppression_base::get_soname_not_regex_str() const
{return priv_->soname_not_regex_str_;}
/// Test if the current suppression has a property related to SONAMEs.
///
/// @return true iff the current suppression has either a soname_regex
/// or a soname_not_regex property.
bool
suppression_base::has_soname_related_property() const
{
return (!(get_soname_regex_str().empty()
&& get_soname_not_regex_str().empty()));
}
/// Check if the SONAMEs of the two binaries being compared match the
/// content of the properties "soname_regexp" and "soname_not_regexp"
/// of the current suppression specification.
///
/// @param suppr the suppression specification
///
/// @param ctxt the context of the comparison.
///
/// @return false if the regular expression contained in the property
/// soname_regexp or in the property "soname_not_regexp" does *NOT*
/// match at least one of the SONAMEs of the two binaries being
/// compared. Return true otherwise.
static bool
sonames_of_binaries_match(const suppression_base& suppr,
const diff_context& ctxt)
{
// Check if the sonames of the binaries match
string first_soname = ctxt.get_corpus_diff()->first_corpus()->get_soname(),
second_soname = ctxt.get_corpus_diff()->second_corpus()->get_soname();
if (!suppr.has_soname_related_property())
return false;
if (!suppr.priv_->matches_soname(first_soname)
&& !suppr.priv_->matches_soname(second_soname))
return false;
return true;
}
/// Check if the names of the two binaries being compared match the
/// content of the properties "file_name_regexp" and
/// "file_name_not_regexp".
///
/// @param suppr the current suppression specification.
///
/// @param ctxt the context of the comparison.
///
/// @return false if the regular expression contained in the property
/// file_name_regexp or in the property "file_name_not_regexp" does
/// *NOT* match at least one of the names of the two binaries being
/// compared. Return true otherwise.
static bool
names_of_binaries_match(const suppression_base& suppr,
const diff_context &ctxt)
{
// Check if the file names of the binaries match
string first_binary_path = ctxt.get_corpus_diff()->first_corpus()->get_path(),
second_binary_path = ctxt.get_corpus_diff()->second_corpus()->get_path();
if (!suppr.has_file_name_related_property())
return false;
if (!suppr.priv_->matches_binary_name(first_binary_path)
&& !suppr.priv_->matches_binary_name(second_binary_path))
return false;
return true;
}
suppression_base::~suppression_base()
{}
static type_suppression_sptr
read_type_suppression(const ini::config::section& section);
static function_suppression_sptr
read_function_suppression(const ini::config::section& section);
static variable_suppression_sptr
read_variable_suppression(const ini::config::section& section);
static file_suppression_sptr
read_file_suppression(const ini::config::section& section);
/// Read a vector of suppression specifications from the sections of
/// an ini::config.
///
/// Note that this function needs to be updated each time a new kind
/// of suppression specification is added.
///
/// @param config the config to read from.
///
/// @param suppressions out parameter. The vector of suppressions to
/// append the newly read suppressions to.
static void
read_suppressions(const ini::config& config,
suppressions_type& suppressions)
{
suppression_sptr s;
for (ini::config::sections_type::const_iterator i =
config.get_sections().begin();
i != config.get_sections().end();
++i)
if ((s = read_type_suppression(**i))
|| (s = read_function_suppression(**i))
|| (s = read_variable_suppression(**i))
|| (s = read_file_suppression(**i)))
suppressions.push_back(s);
}
/// Read suppressions specifications from an input stream.
///
/// @param input the input stream to read from.
///
/// @param suppressions the vector of suppressions to append the newly
/// read suppressions to.
void
read_suppressions(std::istream& input,
suppressions_type& suppressions)
{
if (ini::config_sptr config = ini::read_config(input))
read_suppressions(*config, suppressions);
}
/// Read suppressions specifications from an input file on disk.
///
/// @param input the path to the input file to read from.
///
/// @param suppressions the vector of suppressions to append the newly
/// read suppressions to.
void
read_suppressions(const string& file_path,
suppressions_type& suppressions)
{
if (ini::config_sptr config = ini::read_config(file_path))
read_suppressions(*config, suppressions);
}
// </suppression_base stuff>
// <type_suppression stuff>
/// Constructor for @ref type_suppression.
///
/// @param label the label of the suppression. This is just a free
/// form comment explaining what the suppression is about.
///
/// @param type_name_regexp the regular expression describing the
/// types about which diff reports should be suppressed. If it's an
/// empty string, the parameter is ignored.
///
/// @param type_name the name of the type about which diff reports
/// should be suppressed. If it's an empty string, the parameter is
/// ignored.
///
/// Note that parameter @p type_name_regexp and @p type_name_regexp
/// should not necessarily be populated. It usually is either one or
/// the other that the user wants.
type_suppression::type_suppression(const string& label,
const string& type_name_regexp,
const string& type_name)
: suppression_base(label),
priv_(new priv(type_name_regexp,
type_name,
/*consider_type_kind=*/false,
/*type_kind=*/CLASS_TYPE_KIND,
/*consider_reach_kind=*/false,
/*reach_kind=*/DIRECT_REACH_KIND))
{}
type_suppression::~type_suppression()
{}
/// Setter for the "type_name_regex" property of the type suppression
/// specification.
///
/// This sets a regular expression that specifies the family of types
/// about which diff reports should be suppressed.
///
/// @param name_regex_str the new regular expression to set.
void
type_suppression::set_type_name_regex_str(const string& name_regex_str)
{priv_->type_name_regex_str_ = name_regex_str;}
/// Getter for the "type_name_regex" property of the type suppression
/// specification.
///
/// This returns a regular expression string that specifies the family
/// of types about which diff reports should be suppressed.
///
/// @return the regular expression string.
const string&
type_suppression::get_type_name_regex_str() const
{return priv_->type_name_regex_str_;}
/// Setter for the "type_name_not_regex_str" property of the type
/// suppression specification.
///
/// This returns a regular expression string that specifies the family
/// of types that should be kept after suppression.
///
/// @param r the new regexp string.
void
type_suppression::set_type_name_not_regex_str(const string& r)
{priv_->set_type_name_not_regex_str(r);}
/// Getter for the "type_name_not_regex_str" property of the type
/// suppression specification.
///
/// This returns a regular expression string that specifies the family
/// of types that should be kept after suppression.
///
/// @return the new regexp string.
const string&
type_suppression::get_type_name_not_regex_str() const
{return priv_->get_type_name_not_regex_str();}
/// Setter for the name of the type about which diff reports should be
/// suppressed.
///
/// @param name the new type name.
void
type_suppression::set_type_name(const string& name)
{priv_->type_name_ = name;}
/// Getter for the name of the type about which diff reports should be
/// suppressed.
///
/// @param return the type name.
const string&
type_suppression::get_type_name() const
{return priv_->type_name_;}
/// Getter of the property that says whether to consider the kind of
/// type this suppression is about.
///
/// @return the boolean value of the property.
bool
type_suppression::get_consider_type_kind() const
{return priv_->consider_type_kind_;}
/// Setter of the property that says whether to consider the kind of
/// type this suppression is about.
///
/// @param f the new boolean value of the property.
void
type_suppression::set_consider_type_kind(bool f)
{priv_->consider_type_kind_ = f;}
/// Setter of the kind of type this suppression is about.
///
/// Note that this will be considered during evaluation of the
/// suppression only if type_suppression::get_consider_type_kind()
/// returns true.
///
/// @param k the new kind of type this suppression is about.
void
type_suppression::set_type_kind(type_kind k)
{priv_->type_kind_ = k;}
/// Getter of the kind of type this suppression is about.
///
/// Note that this will be considered during evaluation of the
/// suppression only if type_suppression::get_consider_type_kind()
/// returns true.
///
/// @return the kind of type this suppression is about.
type_suppression::type_kind
type_suppression::get_type_kind() const
{return priv_->type_kind_;}
/// Test if the current type suppression specification
/// suggests to consider how the matching diff node is reached.
///
/// @return true if the current type suppression specification
/// suggests to consider how the matching diff node is reached.
bool
type_suppression::get_consider_reach_kind() const
{return priv_->consider_reach_kind_;}
/// Set a flag saying if the current type suppression specification
/// suggests to consider how the matching diff node is reached.
///
/// @param f the new value of the flag. It's true iff the current
/// type suppression specification suggests to consider how the
/// matching diff node is reached.
void
type_suppression::set_consider_reach_kind(bool f)
{priv_->consider_reach_kind_ = f;}
/// Getter of the way the diff node matching the current suppression
/// specification is to be reached.
///
/// @return the way the diff node matching the current suppression
/// specification is to be reached.
type_suppression::reach_kind
type_suppression::get_reach_kind() const
{return priv_->reach_kind_;}
/// Setter of the way the diff node matching the current suppression
/// specification is to be reached.
///
/// @param p the way the diff node matching the current suppression
/// specification is to be reached.
void
type_suppression::set_reach_kind(reach_kind k)
{priv_->reach_kind_ = k;}
/// Setter for the vector of data member insertion ranges that
/// specifies where a data member is inserted as far as this
/// suppression specification is concerned.
///
/// @param r the new insertion range vector.
void
type_suppression::set_data_member_insertion_ranges(const insertion_ranges& r)
{priv_->insertion_ranges_ = r;}
/// Getter for the vector of data member insertion range that
/// specifiers where a data member is inserted as far as this
/// suppression specification is concerned.
///
/// @return the vector of insertion ranges.
const type_suppression::insertion_ranges&
type_suppression::get_data_member_insertion_ranges() const
{return priv_->insertion_ranges_;}
/// Getter for the vector of data member insertion range that
/// specifiers where a data member is inserted as far as this
/// suppression specification is concerned.
///
/// @return the vector of insertion ranges.
type_suppression::insertion_ranges&
type_suppression::get_data_member_insertion_ranges()
{return priv_->insertion_ranges_;}
/// Getter for the array of source location paths of types that should
/// *NOT* be suppressed.
///
/// @return the set of source locations of types that should *NOT* be
/// supressed.
const unordered_set<string>&
type_suppression::get_source_locations_to_keep() const
{return priv_->source_locations_to_keep_;}
/// Getter for the array of source location paths of types that should
/// *NOT* be suppressed.
///
/// @return the array of source locations of types that should *NOT*
/// be supressed.
unordered_set<string>&
type_suppression::get_source_locations_to_keep()
{return priv_->source_locations_to_keep_;}
/// Setter for the array of source location paths of types that should
/// *NOT* be suppressed.
///
/// @param l the new array.
void
type_suppression::set_source_locations_to_keep
(const unordered_set<string>& l)
{priv_->source_locations_to_keep_ = l;}
/// Getter of the regular expression string that designates the source
/// location paths of types that should not be suppressed.
///
/// @return the regular expression string.
const string&
type_suppression::get_source_location_to_keep_regex_str() const
{return priv_->source_location_to_keep_regex_str_;}
/// Setter of the regular expression string that designates the source
/// location paths of types that should not be suppressed.
///
/// @param r the new regular expression.
void
type_suppression::set_source_location_to_keep_regex_str(const string& r)
{priv_->source_location_to_keep_regex_str_ = r;}
/// Getter of the vector of the changed enumerators that are supposed
/// to be suppressed. Note that this will be "valid" only if the type
/// suppression has the 'type_kind = enum' property.
///
/// @return the vector of the changed enumerators that are supposed to
/// be suppressed.
const vector<string>&
type_suppression::get_changed_enumerator_names() const
{return priv_->changed_enumerator_names_;}
/// Setter of the vector of changed enumerators that are supposed to
/// be suppressed. Note that this will be "valid" only if the type
/// suppression has the 'type_kind = enum' property.
///
/// @param n the vector of the changed enumerators that are supposed
/// to be suppressed.
void
type_suppression::set_changed_enumerator_names(const vector<string>& n)
{priv_->changed_enumerator_names_ = n;}
/// Evaluate this suppression specification on a given diff node and
/// say if the diff node should be suppressed or not.
///
/// @param diff the diff node to evaluate this suppression
/// specification against.
///
/// @return true if @p diff should be suppressed.
bool
type_suppression::suppresses_diff(const diff* diff) const
{
const type_diff_base* d = is_type_diff(diff);
if (!d)
{
// So the diff we are looking at is not a type diff. However,
// there are cases where a type suppression can suppress changes
// on functions.
// Typically, if a virtual member function's virtual index (its
// index in the vtable of a class) changes and if the current
// type suppression is meant to suppress change reports about
// the enclosing class of the virtual member function, then this
// type suppression should suppress reports about that function
// change.
const function_decl_diff* d = is_function_decl_diff(diff);
if (d)
{
// Let's see if 'd' carries a virtual member function
// change.
if (comparison::filtering::has_virtual_mem_fn_change(d))
{
function_decl_sptr f = d->first_function_decl();
class_decl_sptr fc =
is_class_type(is_method_type(f->get_type())->get_class_type());
ABG_ASSERT(fc);
if (suppresses_type(fc, diff->context()))
return true;
}
}
return false;
}
// If the suppression should consider the way the diff node has been
// reached, then do it now.
if (get_consider_reach_kind())
{
if (get_reach_kind() == POINTER_REACH_KIND)
{
if (const pointer_diff* ptr_diff = is_pointer_diff(diff))
{
d = is_type_diff(ptr_diff->underlying_type_diff().get());
if (!d)
// This might be of, e.g, distinct_diff type.
return false;
d = is_type_diff(peel_qualified_diff(d));
}
else
return false;
}
else if (get_reach_kind() == REFERENCE_REACH_KIND)
{
if (const reference_diff* ref_diff = is_reference_diff(diff))
{
d = is_type_diff(ref_diff->underlying_type_diff().get());
if (!d)
// This might be of, e.g, distinct_diff type.
return false;
d = is_type_diff(peel_qualified_diff(d));
}
else
return false;
}
else if (get_reach_kind() == REFERENCE_OR_POINTER_REACH_KIND)
{
if (const pointer_diff* ptr_diff = is_pointer_diff(diff))
{
d = is_type_diff(ptr_diff->underlying_type_diff().get());
ABG_ASSERT(d);
d = is_type_diff(peel_qualified_diff(d));
}
else if (const reference_diff* ref_diff = is_reference_diff(diff))
{
d = is_type_diff(ref_diff->underlying_type_diff().get());
ABG_ASSERT(d);
d = is_type_diff(peel_qualified_diff(d));
}
else
return false;
}
}
type_base_sptr ft, st;
ft = is_type(d->first_subject());
st = is_type(d->second_subject());
ABG_ASSERT(ft && st);
if (!suppresses_type(ft, d->context())
&& !suppresses_type(st, d->context()))
{
// A private type suppression specification considers that a
// type can be private and yet some typedefs of that type can be
// public -- depending on, e.g, if the typedef is defined in a
// public header or not. So if we are in the context of a
// private type suppression let's *NOT* peel typedefs away.
if (!is_private_type_suppr_spec(*this))
{
ft = peel_typedef_type(ft);
st = peel_typedef_type(st);
}
if (!suppresses_type(ft, d->context())
&& !suppresses_type(st, d->context()))
return false;
d = is_type_diff(get_typedef_diff_underlying_type_diff(d));
}
// Now let's consider class diffs in the context of a suppr spec
// that contains properties like "has_data_member_inserted_*".
const class_diff* klass_diff = dynamic_cast<const class_diff*>(d);
if (klass_diff)
{
// We are looking at a class diff ...
if (!get_data_member_insertion_ranges().empty())
{
// ... and the suppr spec contains a
// "has_data_member_inserted_*" clause ...
if (klass_diff->deleted_data_members().empty()
&& (klass_diff->first_class_decl()->get_size_in_bits()
<= klass_diff->second_class_decl()->get_size_in_bits()))
{
// That "has_data_member_inserted_*" clause doesn't hold
// if the class has deleted data members or shrunk.
const class_decl_sptr& first_type_decl =
klass_diff->first_class_decl();
for (string_decl_base_sptr_map::const_iterator m =
klass_diff->inserted_data_members().begin();
m != klass_diff->inserted_data_members().end();
++m)
{
decl_base_sptr member = m->second;
size_t dm_offset = get_data_member_offset(member);
bool matched = false;
for (insertion_ranges::const_iterator i =
get_data_member_insertion_ranges().begin();
i != get_data_member_insertion_ranges().end();
++i)
{
type_suppression::insertion_range_sptr range = *i;
uint64_t range_begin_val = 0, range_end_val = 0;
if (!type_suppression::insertion_range::eval_boundary
(range->begin(), first_type_decl, range_begin_val))
break;
if (!type_suppression::insertion_range::eval_boundary
(range->end(), first_type_decl, range_end_val))
break;
uint64_t range_begin = range_begin_val;
uint64_t range_end = range_end_val;
if (insertion_range::boundary_value_is_end(range_begin)
&& insertion_range::boundary_value_is_end(range_end))
{
// This idiom represents the predicate
// "has_data_member_inserted_at = end"
if (dm_offset >
get_data_member_offset(get_last_data_member
(first_type_decl)))
{
// So the data member was added after
// last data member of the klass. That
// matches the suppr spec
// "has_data_member_inserted_at = end".
matched = true;
continue;
}
}
if (range_begin > range_end)
// Wrong suppr spec. Ignore it.
continue;
if (dm_offset < range_begin || dm_offset > range_end)
// The offset of the added data member doesn't
// match the insertion range specified. So
// the diff object won't be suppressed.
continue;
// If we reached this point, then all the
// insertion range constraints have been
// satisfied. So
matched = true;
}
if (!matched)
return false;
}
}
else
return false;
}
}
const enum_diff* enum_dif = dynamic_cast<const enum_diff*>(d);
if (// We are looking at an enum diff node which ...
enum_dif
//... carries no deleted enumerator ... "
&& enum_dif->deleted_enumerators().empty()
// ... carries no size change ...
&& (enum_dif->first_enum()->get_size_in_bits()
== enum_dif->second_enum()->get_size_in_bits())
// ... and yet carries some changed enumerators!
&& !enum_dif->changed_enumerators().empty())
{
// Make sure that all changed enumerators are listed in the
// vector of enumerator names returned by the
// get_changed_enumerator_names() member function.
bool matched = true;
for (string_changed_enumerator_map::const_iterator i =
enum_dif->changed_enumerators().begin();
i != enum_dif->changed_enumerators().end();
++i)
{
matched &= true;
if (std::find(get_changed_enumerator_names().begin(),
get_changed_enumerator_names().end(),
i->first) == get_changed_enumerator_names().end())
{
matched &= false;
break;
}
}
if (!matched)
return false;
}
return true;
}
/// Test if the current instance of @ref type_suppression suppresses a
/// change reports about a given type.
///
/// @param type the type to consider.
///
/// @param ctxt the context of comparison we are involved with.
///
/// @return true iff the suppression specification suppresses type @p
/// type.
bool
type_suppression::suppresses_type(const type_base_sptr& type,
const diff_context_sptr& ctxt) const
{
if (ctxt)
{
// Check if the names of the binaries match the suppression
if (!names_of_binaries_match(*this, *ctxt))
if (has_file_name_related_property())
return false;
// Check if the sonames of the binaries match the suppression
if (!sonames_of_binaries_match(*this, *ctxt))
if (has_soname_related_property())
return false;
}
return suppresses_type(type);
}
/// Test if an instance of @ref type_suppression matches a given type.
///
/// This function does not take the name of the type into account
/// while testing if the type matches the type_suppression.
///
/// @param s the suppression to evaluate.
///
/// @param type the type to consider.
///
/// @return true iff the suppression specification matches type @p
/// type without taking its name into account.
static bool
suppression_matches_type_no_name(const type_suppression& s,
const type_base_sptr &type)
{
// If the suppression should consider type kind then, well, check
// for that.
if (s.get_consider_type_kind())
{
type_suppression::type_kind tk = s.get_type_kind();
bool matches = true;
switch (tk)
{
case type_suppression::UNKNOWN_TYPE_KIND:
case type_suppression::CLASS_TYPE_KIND:
if (!is_class_type(type))
matches = false;
break;
case type_suppression::STRUCT_TYPE_KIND:
{
class_decl_sptr klass = is_class_type(type);
if (!klass || !klass->is_struct())
matches = false;
}
break;
case type_suppression::UNION_TYPE_KIND:
if (!is_union_type(type))
matches = false;
break;
case type_suppression::ENUM_TYPE_KIND:
if (!is_enum_type(type))
matches = false;
break;
case type_suppression::ARRAY_TYPE_KIND:
if (!is_array_type(type))
matches = false;
break;
case type_suppression::TYPEDEF_TYPE_KIND:
if (!is_typedef(type))
matches = false;
break;
case type_suppression::BUILTIN_TYPE_KIND:
if (!is_type_decl(type))
matches = false;
break;
}
if (!matches)
return false;
}
// Check if there is a source location related match.
if (!suppression_matches_type_location(s, type))
return false;
return true;
}
/// Test if a type suppression specification matches a type name.
///
/// @param s the type suppression to consider.
///
/// @param type_name the type name to consider.
///
/// @return true iff the type designated by its name @p type_name is
/// matched by the type suppression specification @p s.
bool
suppression_matches_type_name(const type_suppression& s,
const string& type_name)
{
if (!s.get_type_name().empty()
|| s.priv_->get_type_name_regex()
|| s.priv_->get_type_name_not_regex())
{
// Check if there is an exact type name match.
if (!s.get_type_name().empty())
{
if (s.get_type_name() != type_name)
return false;
}
else
{
// Now check if there is a regular expression match.
//
// If the qualified name of the considered type doesn't match
// the regular expression of the type name, then this
// suppression doesn't apply.
if (const regex_t_sptr& type_name_regex =
s.priv_->get_type_name_regex())
{
if (!regex::match(type_name_regex, type_name))
return false;
}
if (const regex_t_sptr type_name_not_regex =
s.priv_->get_type_name_not_regex())
{
if (regex::match(type_name_not_regex, type_name))
return false;
}
}
}
return true;
}
/// Test if a type suppression matches a type in a particular scope.
///
/// @param s the type suppression to consider.
///
/// @param type_scope the scope of the type to consider.
///
/// @param type the type to consider.
///
/// @return true iff the supression @p s matches type @p type in scope
/// @p type_scope.
bool
suppression_matches_type_name(const suppr::type_suppression& s,
const scope_decl* type_scope,
const type_base_sptr& type)
{
string type_name = build_qualified_name(type_scope, type);
return suppression_matches_type_name(s, type_name);
}
/// Test if a type suppression matches a source location.
///
/// @param s the type suppression to consider.
///
/// @param loc the location to consider.
///
/// @return true iff the suppression @p s matches location @p loc.
bool
suppression_matches_type_location(const type_suppression& s,
const location& loc)
{
if (loc)
{
// Check if there is a source location related match.
string loc_path, loc_path_base;
unsigned loc_line = 0, loc_column = 0;
loc.expand(loc_path, loc_line, loc_column);
if (regex_t_sptr regexp = s.priv_->get_source_location_to_keep_regex())
if (regex::match(regexp, loc_path))
return false;
tools_utils::base_name(loc_path, loc_path_base);
if (s.get_source_locations_to_keep().find(loc_path_base)
!= s.get_source_locations_to_keep().end())
return false;
if (s.get_source_locations_to_keep().find(loc_path)
!= s.get_source_locations_to_keep().end())
return false;
}
else
{
if (!s.get_source_locations_to_keep().empty()
|| s.priv_->get_source_location_to_keep_regex())
// The user provided a "source_location_not_regexp" or
// a "source_location_not_in" property that was not
// triggered. This means the current type suppression
// doesn't suppress the type given.
return false;
}
return true;
}
/// Test if a type suppression matches a type.
///
/// @param s the type suppression to consider.
///
/// @param type the type to consider.
///
/// @return true iff the suppression @p s matches type @p type.
bool
suppression_matches_type_location(const type_suppression& s,
const type_base_sptr& type)
{
location loc = get_location(type);
if (loc)
return suppression_matches_type_location(s, loc);
else
{
// The type had no source location.
//
// In the case where this type suppression was automatically
// generated to suppress types not defined in public
// headers, then this might mean that the type is not
// defined in the public headers. Otherwise, why does it
// not have a source location?
if (s.get_is_artificial())
{
if (class_decl_sptr cl = is_class_type(type))
{
if (cl->get_is_declaration_only())
// We tried hard above to get the definition of
// the declaration. If we reach this place, it
// means the class has no definition at this point.
ABG_ASSERT(!cl->get_definition_of_declaration());
if (s.get_label() == get_private_types_suppr_spec_label())
// So this looks like what really amounts to an
// opaque type. So it's not defined in the public
// headers. So we want to filter it out.
return true;
}
}
if (!s.get_source_locations_to_keep().empty()
|| s.priv_->get_source_location_to_keep_regex())
// The user provided a "source_location_not_regexp" or
// a "source_location_not_in" property that was not
// triggered. This means the current type suppression
// doesn't suppress the type given.
return false;
}
return true;
}
/// Test if a type suppression matches a type name and location.
///
/// @param s the type suppression to consider.
///
/// @param type_name the name of the type to consider.
///
/// @param type_location the location of the type to consider.
///
/// @return true iff suppression @p s matches a type named @p
/// type_name with a location @p type_location.
bool
suppression_matches_type_name_or_location(const type_suppression& s,
const string& type_name,
const location& type_location)
{
if (!suppression_matches_type_name(s, type_name))
return false;
if (!suppression_matches_type_location(s, type_location))
return false;
return true;
}
/// Test if the current instance of @ref type_suppression matches a
/// given type.
///
/// @param type the type to consider.
///
/// @return true iff the suppression specification suppresses type @p
/// type.
bool
type_suppression::suppresses_type(const type_base_sptr& type) const
{
if (!suppression_matches_type_no_name(*this, type))
return false;
if (!suppression_matches_type_name(*this, get_name(type)))
return false;
return true;
}
/// Test if the current instance of @ref type_suppression matches a
/// given type in a given scope.
///
/// @param type the type to consider.
///
/// @param type_scope the scope of type @p type.
///
/// @return true iff the suppression specification suppresses type @p
/// type from scope @p type_scope.
bool
type_suppression::suppresses_type(const type_base_sptr& type,
const scope_decl* type_scope) const
{
if (!suppression_matches_type_no_name(*this, type))
return false;
if (!suppression_matches_type_name(*this, type_scope, type))
return false;
return true;
}
/// The private data of type_suppression::insertion_range
struct type_suppression::insertion_range::priv
{
boundary_sptr begin_;
boundary_sptr end_;
priv()
{}
priv(boundary_sptr begin, boundary_sptr end)
: begin_(begin), end_(end)
{}
}; // end struct type_suppression::insertion_range::priv
/// Default Constructor of @ref type_suppression::insertion_range.
type_suppression::insertion_range::insertion_range()
: priv_(new priv)
{}
/// Constructor of @ref type_suppression::insertion_range.
///
/// @param begin the start of the range. A range boundary that is an
/// instance of @ref interger_boundary with a negative value means the
/// maximum possible value.
///
/// @param end the end of the range. A range boundary that is an
/// instance of @ref interger_boundary with a negative value means the
/// maximum possible value.
type_suppression::insertion_range::insertion_range(boundary_sptr begin,
boundary_sptr end)
: priv_(new priv(begin, end))
{}
/// Getter for the beginning of the range.
///
/// @return the beginning of the range. A range boundary that is an
/// instance of @ref interger_boundary with a negative value means the
/// maximum possible value.
type_suppression::insertion_range::boundary_sptr
type_suppression::insertion_range::begin() const
{return priv_->begin_;}
/// Getter for the end of the range.
///
/// @return the end of the range. A range boundary that is an
/// instance of @ref interger_boundary with a negative value means the
/// maximum possible value.
type_suppression::insertion_range::boundary_sptr
type_suppression::insertion_range::end() const
{return priv_->end_;}
/// Create an integer boundary.
///
/// The return value of this function is to be used as a boundary for
/// an instance of @ref type_suppression::insertion_range. That
/// boundary evaluates to an integer value.
///
/// @param value the value of the integer boundary.
///
/// @return the resulting integer boundary.
type_suppression::insertion_range::integer_boundary_sptr
type_suppression::insertion_range::create_integer_boundary(int value)
{return integer_boundary_sptr(new integer_boundary(value));}
/// Create a function call expression boundary.
///
/// The return value of this function is to be used as a boundary for
/// an instance of @ref type_suppression::insertion_range. The value
/// of that boundary is actually a function call expression that
/// itself evalutates to an integer value, in the context of a @ref
/// class_decl.
///
/// @param expr the function call expression to create the boundary from.
///
/// @return the resulting function call expression boundary.
type_suppression::insertion_range::fn_call_expr_boundary_sptr
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::function_call_expr_sptr expr)
{return fn_call_expr_boundary_sptr(new fn_call_expr_boundary(expr));}
/// Create a function call expression boundary.
///
/// The return value of this function is to be used as a boundary for
/// an instance of @ref type_suppression::insertion_range. The value
/// of that boundary is actually a function call expression that
/// itself evalutates to an integer value, in the context of a @ref
/// class_decl.
///
/// @param s a string representing the expression the function call
/// expression to create the boundary from.
///
/// @return the resulting function call expression boundary.
type_suppression::insertion_range::fn_call_expr_boundary_sptr
type_suppression::insertion_range::create_fn_call_expr_boundary(const string& s)
{
fn_call_expr_boundary_sptr result, nil;
ini::function_call_expr_sptr expr;
if (ini::read_function_call_expr(s, expr) && expr)
result.reset(new fn_call_expr_boundary(expr));
return result;
}
/// Evaluate an insertion range boundary to get a resulting integer
/// value.
///
/// @param boundary the boundary to evaluate.
///
/// @param context the context of evualuation. It's a @ref class_decl
/// to take into account during the evaluation, if there is a need for
/// it.
///
/// @return true iff the evaluation was successful and @p value
/// contains the resulting value.
bool
type_suppression::insertion_range::eval_boundary(boundary_sptr boundary,
class_decl_sptr context,
uint64_t& value)
{
if (integer_boundary_sptr b = is_integer_boundary(boundary))
{
value = b->as_integer();
return true;
}
else if (fn_call_expr_boundary_sptr b = is_fn_call_expr_boundary(boundary))
{
ini::function_call_expr_sptr fn_call = b->as_function_call_expr();
if ((fn_call->get_name() == "offset_of"
|| fn_call->get_name() == "offset_after")
&& fn_call->get_arguments().size() == 1)
{
string member_name = fn_call->get_arguments()[0];
for (class_decl::data_members::const_iterator it =
context->get_data_members().begin();
it != context->get_data_members().end();
++it)
{
if (!get_data_member_is_laid_out(**it))
continue;
if ((*it)->get_name() == member_name)
{
if (fn_call->get_name() == "offset_of")
value = get_data_member_offset(*it);
else if (fn_call->get_name() == "offset_after")
{
if (!get_next_data_member_offset(context, *it, value))
{
value = get_data_member_offset(*it) +
(*it)->get_type()->get_size_in_bits();
}
}
else
// We should not reach this point.
abort();
return true;
}
}
}
}
return false;
}
/// Test if a given value supposed to be inside an insertion range
/// represents the end of the range.
///
/// @param value the value to test for.
///
/// @return true iff @p value represents the end of the insertion
/// range.
bool
type_suppression::insertion_range::boundary_value_is_end(uint64_t value)
{
return value == std::numeric_limits<uint64_t>::max();
}
/// Tests if a given instance of @ref
/// type_suppression::insertion_range::boundary is actually an integer boundary.
///
/// @param b the boundary to test.
///
/// @return a pointer to the instance of
/// type_suppression::insertion_range::integer_boundary if @p b is
/// actually an integer boundary. Otherwise, return a null pointer.
type_suppression::insertion_range::integer_boundary_sptr
is_integer_boundary(type_suppression::insertion_range::boundary_sptr b)
{return dynamic_pointer_cast<type_suppression::insertion_range::integer_boundary>(b);}
/// Tests if a given instance of @ref
/// type_suppression::insertion_range::boundary is actually an function call expression boundary.
///
/// @param b the boundary to test.
///
/// @return a pointer to the instance of
/// type_suppression::insertion_range::fn_call_expr_boundary if @p b
/// is actually an function call expression boundary. Otherwise,
/// return a null pointer.
type_suppression::insertion_range::fn_call_expr_boundary_sptr
is_fn_call_expr_boundary(type_suppression::insertion_range::boundary_sptr b)
{return dynamic_pointer_cast<type_suppression::insertion_range::fn_call_expr_boundary>(b);}
/// The private data type of @ref
/// type_suppression::insertion_range::boundary.
struct type_suppression::insertion_range::boundary::priv
{
priv()
{}
}; // end struct type_suppression::insertion_range::boundary::priv
/// Default constructor of @ref
/// type_suppression::insertion_range::boundary
type_suppression::insertion_range::boundary::boundary()
: priv_(new priv())
{}
/// Destructor of @ref type_suppression::insertion_range::boundary.
type_suppression::insertion_range::boundary::~boundary()
{}
/// Private data type for @ref
/// type_suppression::insertion_range::integer_boundary.
struct type_suppression::insertion_range::integer_boundary::priv
{
uint64_t value_;
priv()
: value_()
{}
priv(uint64_t value)
: value_(value)
{}
}; // end type_suppression::insertion_range::integer_boundary::priv
/// Converting constructor of
/// type_suppression::insertion_range::integer_boundary.
///
/// @param value the integer value of the newly created integer boundary.
type_suppression::insertion_range::integer_boundary::integer_boundary(uint64_t value)
: priv_(new priv(value))
{}
/// Return the integer value of the current instance of @ref
/// type_suppression::insertion_range::integer_boundary.
///
/// @return the integer value of the current boundary.
uint64_t
type_suppression::insertion_range::integer_boundary::as_integer() const
{return priv_->value_;}
/// Converts the current boundary into an integer value.
///
/// @return the integer value of the current boundary.
type_suppression::insertion_range::integer_boundary::operator uint64_t() const
{return as_integer();}
/// Destructor of @ref type_suppression::insertion_range::integer_boundary.
type_suppression::insertion_range::integer_boundary::~integer_boundary()
{}
/// Private data type of type @ref
/// type_suppression::insertion_range::fn_call_expr_boundary.
struct type_suppression::insertion_range::fn_call_expr_boundary::priv
{
ini::function_call_expr_sptr expr_;
priv()
{}
priv(ini::function_call_expr_sptr expr)
: expr_(expr)
{}
}; // end struct type_suppression::insertion_range::fn_call_expr_boundary::priv
/// Converting constructor for @ref
/// type_suppression::insertion_range::fn_call_expr_boundary.
///
/// @param expr the function call expression to build this boundary
/// from.
type_suppression::insertion_range::fn_call_expr_boundary::
fn_call_expr_boundary(ini::function_call_expr_sptr expr)
: priv_(new priv(expr))
{}
/// Returns the function call expression value of the current boundary.
///
/// @return the function call expression value of the current boundary;
ini::function_call_expr_sptr
type_suppression::insertion_range::fn_call_expr_boundary::as_function_call_expr() const
{return priv_->expr_;}
/// Converts the current boundary to its function call expression value.
///
/// @return the function call expression value of the current boundary.
type_suppression::insertion_range::fn_call_expr_boundary::operator ini::function_call_expr_sptr () const
{return as_function_call_expr();}
/// Destructor of @ref
/// type_suppression::insertion_range::fn_call_expr_boundary.
type_suppression::insertion_range::fn_call_expr_boundary::~fn_call_expr_boundary()
{}
/// Test if an instance of @ref suppression is an instance of @ref
/// type_suppression.
///
/// @param suppr the instance of @ref suppression to test for.
///
/// @return if @p suppr is an instance of @ref type_suppression, then
/// return the sub-object of the @p suppr of type @ref
/// type_suppression, otherwise return a nil pointer.
type_suppression_sptr
is_type_suppression(suppression_sptr suppr)
{return dynamic_pointer_cast<type_suppression>(suppr);}
// </type_suppression stuff>
/// Parse the value of the "type_kind" property in the "suppress_type"
/// section.
///
/// @param input the input string representing the value of the
/// "type_kind" property.
///
/// @return the @ref type_kind enumerator parsed.
static type_suppression::type_kind
read_type_kind_string(const string& input)
{
if (input == "class")
return type_suppression::CLASS_TYPE_KIND;
else if (input == "struct")
return type_suppression::STRUCT_TYPE_KIND;
else if (input == "union")
return type_suppression::UNION_TYPE_KIND;
else if (input == "enum")
return type_suppression::ENUM_TYPE_KIND;
else if (input == "array")
return type_suppression::ARRAY_TYPE_KIND;
else if (input == "typedef")
return type_suppression::TYPEDEF_TYPE_KIND;
else if (input == "builtin")
return type_suppression::BUILTIN_TYPE_KIND;
else
return type_suppression::UNKNOWN_TYPE_KIND;
}
/// Parse the value of the "accessed_through" property in the
/// "suppress_type" section.
///
/// @param input the input string representing the value of the
/// "accessed_through" property.
///
/// @return the @ref type_suppression::reach_kind enumerator parsed.
static type_suppression::reach_kind
read_suppression_reach_kind(const string& input)
{
if (input == "direct")
return type_suppression::DIRECT_REACH_KIND;
else if (input == "pointer")
return type_suppression::POINTER_REACH_KIND;
else if (input == "reference")
return type_suppression::REFERENCE_REACH_KIND;
else if (input == "reference-or-pointer")
return type_suppression::REFERENCE_OR_POINTER_REACH_KIND;
else
return type_suppression::DIRECT_REACH_KIND;
}
/// Read a type suppression from an instance of ini::config::section
/// and build a @ref type_suppression as a result.
///
/// @param section the section of the ini config to read.
///
/// @return the resulting @ref type_suppression upon successful
/// parsing, or nil.
static type_suppression_sptr
read_type_suppression(const ini::config::section& section)
{
type_suppression_sptr result;
if (section.get_name() != "suppress_type")
return result;
static const char *const sufficient_props[] = {
"file_name_regexp",
"file_name_not_regexp",
"soname_regexp",
"soname_not_regexp",
"name",
"name_regexp",
"name_not_regexp",
"type_kind",
"source_location_not_in",
"source_location_not_regexp",
};
if (!check_sufficient_props(sufficient_props,
sizeof(sufficient_props)/sizeof(char*),
section))
return result;
ini::simple_property_sptr drop_artifact =
is_simple_property(section.find_property("drop_artifact"));
if (!drop_artifact)
drop_artifact = is_simple_property(section.find_property("drop"));
string drop_artifact_str = drop_artifact
? drop_artifact->get_value()->as_string()
: "";
ini::simple_property_sptr label =
is_simple_property(section.find_property("label"));
string label_str = label ? label->get_value()->as_string() : "";
ini::simple_property_sptr file_name_regex_prop =
is_simple_property(section.find_property("file_name_regexp"));
string file_name_regex_str =
file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr file_name_not_regex_prop =
is_simple_property(section.find_property("file_name_not_regexp"));
string file_name_not_regex_str =
file_name_not_regex_prop
? file_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr soname_regex_prop =
is_simple_property(section.find_property("soname_regexp"));
string soname_regex_str =
soname_regex_prop ? soname_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr soname_not_regex_prop =
is_simple_property(section.find_property("soname_not_regexp"));
string soname_not_regex_str =
soname_not_regex_prop
? soname_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_regex_prop =
is_simple_property(section.find_property("name_regexp"));
string name_regex_str = name_regex_prop
? name_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_not_regex_prop =
is_simple_property(section.find_property("name_not_regexp"));
string name_not_regex_str = name_not_regex_prop
? name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_prop =
is_simple_property(section.find_property("name"));
string name_str = name_prop
? name_prop->get_value()->as_string()
: "";
ini::property_sptr srcloc_not_in_prop =
section.find_property("source_location_not_in");
unordered_set<string> srcloc_not_in;
if (srcloc_not_in_prop)
{
if (ini::simple_property_sptr p = is_simple_property(srcloc_not_in_prop))
srcloc_not_in.insert(p->get_value()->as_string());
else
{
ini::list_property_sptr list_property =
is_list_property(srcloc_not_in_prop);
if (list_property)
{
vector<string>::const_iterator i;
for (i = list_property->get_value()->get_content().begin();
i != list_property->get_value()->get_content().end();
++i)
srcloc_not_in.insert(*i);
}
}
}
ini::simple_property_sptr srcloc_not_regexp_prop =
is_simple_property(section.find_property("source_location_not_regexp"));
string srcloc_not_regexp_str;
if (srcloc_not_regexp_prop)
srcloc_not_regexp_str = srcloc_not_regexp_prop->get_value()->as_string();
bool consider_type_kind = false;
type_suppression::type_kind type_kind = type_suppression::UNKNOWN_TYPE_KIND;
if (ini::simple_property_sptr type_kind_prop =
is_simple_property(section.find_property("type_kind")))
{
consider_type_kind = true;
type_kind =
read_type_kind_string(type_kind_prop->get_value()->as_string());
}
bool consider_reach_kind = false;
type_suppression::reach_kind reach_kind = type_suppression::DIRECT_REACH_KIND;
if (ini::simple_property_sptr reach_kind_prop =
is_simple_property(section.find_property("accessed_through")))
{
consider_reach_kind = true;
reach_kind =
read_suppression_reach_kind(reach_kind_prop->get_value()->as_string());
}
// Support has_data_member_inserted_at
vector<type_suppression::insertion_range_sptr> insert_ranges;
bool consider_data_member_insertion = false;
if (ini::simple_property_sptr prop =
is_simple_property(section.find_property("has_data_member_inserted_at")))
{
// So this property has the form:
// has_data_member_inserted_at = <one-string-property-value>
string ins_point = prop->get_value()->as_string();
type_suppression::insertion_range::boundary_sptr begin, end;
if (ins_point == "end")
begin = type_suppression::insertion_range::create_integer_boundary(-1);
else if (isdigit(ins_point[0]))
begin = type_suppression::insertion_range::create_integer_boundary
(atoi(ins_point.c_str()));
else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr =
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(ins_point)))
begin = expr;
else
return result;
end = type_suppression::insertion_range::create_integer_boundary(-1);
type_suppression::insertion_range_sptr insert_range
(new type_suppression::insertion_range(begin, end));
insert_ranges.push_back(insert_range);
consider_data_member_insertion = true;
}
// Support has_data_member_inserted_between
if (ini::tuple_property_sptr prop =
is_tuple_property(section.find_property
("has_data_member_inserted_between")))
{
// ensures that this has the form:
// has_data_member_inserted_between = {0 , end};
// and not (for instance):
// has_data_member_inserted_between = {{0 , end}, {1, foo}}
//
// This means that the tuple_property_value contains just one
// value, which is a list_property that itself contains 2
// values.
type_suppression::insertion_range::boundary_sptr begin, end;
ini::tuple_property_value_sptr v = prop->get_value();
if (v
&& v->get_value_items().size() == 1
&& is_list_property_value(v->get_value_items()[0])
&& is_list_property_value(v->get_value_items()[0])->get_content().size() == 2)
{
ini::list_property_value_sptr val =
is_list_property_value(v->get_value_items()[0]);
ABG_ASSERT(val);
string str = val->get_content()[0];
if (str == "end")
begin =
type_suppression::insertion_range::create_integer_boundary(-1);
else if (isdigit(str[0]))
begin = type_suppression::insertion_range::create_integer_boundary
(atoi(str.c_str()));
else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr =
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str)))
begin = expr;
else
return result;
str = val->get_content()[1];
if (str == "end")
end =
type_suppression::insertion_range::create_integer_boundary(-1);
else if (isdigit(str[0]))
end = type_suppression::insertion_range::create_integer_boundary
(atoi(str.c_str()));
else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr =
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str)))
end = expr;
else
return result;
type_suppression::insertion_range_sptr insert_range
(new type_suppression::insertion_range(begin, end));
insert_ranges.push_back(insert_range);
consider_data_member_insertion = true;
}
else
// the 'has_data_member_inserted_between' property has a wrong
// value type, so let's discard the endire [suppress_type]
// section.
return result;
}
// Support has_data_members_inserted_between
// The syntax looks like:
//
// has_data_members_inserted_between = {{8, 24}, {32, 64}, {128, end}}
//
// So we expect a tuple property, with potentially several pairs (as
// part of the value); each pair designating a range. Note that
// each pair (range) is a list property value.
if (ini::tuple_property_sptr prop =
is_tuple_property(section.find_property
("has_data_members_inserted_between")))
{
bool is_well_formed = true;
for (vector<ini::property_value_sptr>::const_iterator i =
prop->get_value()->get_value_items().begin();
is_well_formed && i != prop->get_value()->get_value_items().end();
++i)
{
ini::tuple_property_value_sptr tuple_value =
is_tuple_property_value(*i);
if (!tuple_value
|| tuple_value->get_value_items().size() != 1
|| !is_list_property_value(tuple_value->get_value_items()[0]))
{
is_well_formed = false;
break;
}
ini::list_property_value_sptr list_value =
is_list_property_value(tuple_value->get_value_items()[0]);
if (list_value->get_content().size() != 2)
{
is_well_formed = false;
break;
}
type_suppression::insertion_range::boundary_sptr begin, end;
string str = list_value->get_content()[0];
if (str == "end")
begin =
type_suppression::insertion_range::create_integer_boundary(-1);
else if (isdigit(str[0]))
begin =
type_suppression::insertion_range::create_integer_boundary
(atoi(str.c_str()));
else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr =
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str)))
begin = expr;
else
return result;
str = list_value->get_content()[1];
if (str == "end")
end =
type_suppression::insertion_range::create_integer_boundary(-1);
else if (isdigit(str[0]))
end = type_suppression::insertion_range::create_integer_boundary
(atoi(str.c_str()));
else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr =
type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str)))
end = expr;
else
return result;
type_suppression::insertion_range_sptr insert_range
(new type_suppression::insertion_range(begin, end));
insert_ranges.push_back(insert_range);
consider_data_member_insertion = true;
}
if (!is_well_formed)
return result;
}
/// Support 'changed_enumerators = foo, bar, baz'
///
/// Note that this constraint is valid only if we have:
/// 'type_kind = enum'.
///
/// If the current type is an enum and if it carries changed
/// enumerators listed in the changed_enumerators property value
/// then it should be suppressed.
ini::property_sptr changed_enumerators_prop =
section.find_property("changed_enumerators");
vector<string> changed_enumerator_names;
if (changed_enumerators_prop)
{
if (ini::list_property_sptr p =
is_list_property(changed_enumerators_prop))
changed_enumerator_names =
p->get_value()->get_content();
else if (ini::simple_property_sptr p =
is_simple_property(changed_enumerators_prop))
changed_enumerator_names.push_back(p->get_value()->as_string());
}
result.reset(new type_suppression(label_str, name_regex_str, name_str));
if (consider_type_kind)
{
result->set_consider_type_kind(true);
result->set_type_kind(type_kind);
}
if (consider_reach_kind)
{
result->set_consider_reach_kind(true);
result->set_reach_kind(reach_kind);
}
if (consider_data_member_insertion)
result->set_data_member_insertion_ranges(insert_ranges);
if (!name_not_regex_str.empty())
result->set_type_name_not_regex_str(name_not_regex_str);
if (!file_name_regex_str.empty())
result->set_file_name_regex_str(file_name_regex_str);
if (!file_name_not_regex_str.empty())
result->set_file_name_not_regex_str(file_name_not_regex_str);
if (!soname_regex_str.empty())
result->set_soname_regex_str(soname_regex_str);
if (!soname_not_regex_str.empty())
result->set_soname_not_regex_str(soname_not_regex_str);
if (!srcloc_not_in.empty())
result->set_source_locations_to_keep(srcloc_not_in);
if (!srcloc_not_regexp_str.empty())
result->set_source_location_to_keep_regex_str(srcloc_not_regexp_str);
if ((drop_artifact_str == "yes" || drop_artifact_str == "true")
&& ((!name_regex_str.empty()
|| !name_str.empty()
|| !srcloc_not_regexp_str.empty()
|| !srcloc_not_in.empty())))
result->set_drops_artifact_from_ir(true);
if (result->get_type_kind() == type_suppression::ENUM_TYPE_KIND
&& !changed_enumerator_names.empty())
result->set_changed_enumerator_names(changed_enumerator_names);
return result;
}
// <function_suppression stuff>
/// Constructor for the @ref the function_suppression::parameter_spec
/// type.
///
/// @param i the index of the parameter designated by this specification.
///
/// @param tn the type name of the parameter designated by this specification.
///
/// @param tn_regex a regular expression that defines a set of type
/// names for the parameter designated by this specification. Note
/// that at evaluation time, this regular expression is taken in
/// account only if the parameter @p tn is empty.
function_suppression::parameter_spec::parameter_spec(size_t i,
const string& tn,
const string& tn_regex)
: priv_(new priv(i, tn, tn_regex))
{}
/// Getter for the index of the parameter designated by this
/// specification.
///
/// @return the index of the parameter designated by this
/// specification.
size_t
function_suppression::parameter_spec::get_index() const
{return priv_->index_;}
/// Setter for the index of the parameter designated by this
/// specification.
///
/// @param i the new index to set.
void
function_suppression::parameter_spec::set_index(size_t i)
{priv_->index_ = i;}
/// Getter for the type name of the parameter designated by this specification.
///
/// @return the type name of the parameter.
const string&
function_suppression::parameter_spec::get_parameter_type_name() const
{return priv_->type_name_;}
/// Setter for the type name of the parameter designated by this
/// specification.
///
/// @param tn new parameter type name to set.
void
function_suppression::parameter_spec::set_parameter_type_name(const string& tn)
{priv_->type_name_ = tn;}
/// Getter for the regular expression that defines a set of type names
/// for the parameter designated by this specification.
///
/// Note that at evaluation time, this regular expression is taken in
/// account only if the name of the parameter as returned by
/// function_suppression::parameter_spec::get_parameter_type_name() is
/// empty.
///
/// @return the regular expression or the parameter type name.
const string&
function_suppression::parameter_spec::get_parameter_type_name_regex_str() const
{return priv_->type_name_regex_str_;}
/// Setter for the regular expression that defines a set of type names
/// for the parameter designated by this specification.
///
/// Note that at evaluation time, this regular expression is taken in
/// account only if the name of the parameter as returned by
/// function_suppression::parameter_spec::get_parameter_type_name() is
/// empty.
///
/// @param type_name_regex_str the new type name regular expression to
/// set.
void
function_suppression::parameter_spec::set_parameter_type_name_regex_str
(const string& type_name_regex_str)
{priv_->type_name_regex_str_ = type_name_regex_str;}
/// Default constructor for the @ref function_suppression type.
///
/// It defines no suppression for now. Suppressions have to be
/// specified by using the various accessors of the @ref
/// function_suppression type.
function_suppression::function_suppression()
: suppression_base(/*label=*/""), priv_(new priv)
{}
/// Constructor for the @ref function_suppression type.
///
/// @param label an informative text string that the evalution code
/// might use to designate this function suppression specification in
/// error messages. This parameter might be empty, in which case it's
/// ignored at evaluation time.
///
/// @param the name of the function the user wants the current
/// specification to designate. This parameter might be empty, in
/// which case it's ignored at evaluation time.
///
/// @param nr if @p name is empty this parameter is a regular
/// expression for a family of names of functions the user wants the
/// current specification to designate. If @p name is not empty, this
/// parameter is ignored at specification evaluation time. This
/// parameter might be empty, in which case it's ignored at evaluation
/// time.
///
/// @param ret_tn the name of the return type of the function the user
/// wants this specification to designate. This parameter might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param ret_tr if @p ret_tn is empty, then this is a regular
/// expression for a family of return type names for functions the
/// user wants the current specification to designate. If @p ret_tn
/// is not empty, then this parameter is ignored at specification
/// evaluation time. This parameter might be empty, in which case
/// it's ignored at evaluation time.
///
/// @param ps a vector of parameter specifications to specify
/// properties of the parameters of the functions the user wants this
/// specification to designate. This parameter might be empty, in
/// which case it's ignored at evaluation time.
///
/// @param sym_n the name of symbol of the function the user wants
/// this specification to designate. This parameter might be empty,
/// in which case it's ignored at evaluation time.
///
/// @param sym_nr if the parameter @p sym_n is empty, then this
/// parameter is a regular expression for a family of names of symbols
/// of functions the user wants this specification to designate. If
/// the parameter @p sym_n is not empty, then this parameter is
/// ignored at specification evaluation time. This parameter might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param sym_v the name of the version of the symbol of the function
/// the user wants this specification to designate. This parameter
/// might be empty, in which case it's ignored at evaluation time.
///
/// @param sym_vr if the parameter @p sym_v is empty, then this
/// parameter is a regular expression for a family of versions of
/// symbols of functions the user wants the current specification to
/// designate. If the parameter @p sym_v is non empty, then this
/// parameter is ignored. This parameter might be empty, in which
/// case it's ignored at evaluation time.
function_suppression::function_suppression(const string& label,
const string& name,
const string& nr,
const string& ret_tn,
const string& ret_tr,
parameter_specs_type& ps,
const string& sym_n,
const string& sym_nr,
const string& sym_v,
const string& sym_vr)
: suppression_base(label),
priv_(new priv(name, nr, ret_tn, ret_tr, ps,
sym_n, sym_nr, sym_v, sym_vr))
{}
function_suppression::~function_suppression()
{}
/// Parses a string containing the content of the "change-kind"
/// property and returns the an instance of @ref
/// function_suppression::change_kind as a result.
///
/// @param s the string to parse.
///
/// @return the resulting @ref function_suppression::change_kind.
function_suppression::change_kind
function_suppression::parse_change_kind(const string& s)
{
if (s == "function-subtype-change")
return FUNCTION_SUBTYPE_CHANGE_KIND;
else if (s == "added-function")
return ADDED_FUNCTION_CHANGE_KIND;
else if (s == "deleted-function")
return DELETED_FUNCTION_CHANGE_KIND;
else if (s == "all")
return ALL_CHANGE_KIND;
else
return UNDEFINED_CHANGE_KIND;
}
/// Getter of the "change-kind" property.
///
/// @param returnthe "change-kind" property.
function_suppression::change_kind
function_suppression::get_change_kind() const
{return priv_->change_kind_;}
/// Setter of the "change-kind" property.
///
/// @param k the new value of the change_kind property.
void
function_suppression::set_change_kind(change_kind k)
{priv_->change_kind_ = k;}
/// Getter for the name of the function the user wants the current
/// specification to designate. This might be empty, in which case
/// it's ignored at evaluation time.
///
/// @return the name of the function.
const string&
function_suppression::get_name() const
{return priv_->name_;}
/// Setter for the name of the function the user wants the current
/// specification to designate. This might be empty, in which case
/// it's ignored at evaluation time.
///
/// @param n the new function name to set.
void
function_suppression::set_name(const string& n)
{priv_->name_ = n;}
/// Getter for a regular expression for a family of names of functions
/// the user wants the current specification to designate.
///
/// @return the regular expression for the possible names of the
/// function(s).
const string&
function_suppression::get_name_regex_str() const
{return priv_->name_regex_str_;}
/// Setter for a regular expression for a family of names of functions
/// the user wants the current specification to designate.
///
/// @param r the new the regular expression for the possible names of
/// the function(s).
void
function_suppression::set_name_regex_str(const string& r)
{priv_->name_regex_str_ = r;}
/// Getter for a regular expression of a family of names of functions
/// the user wants the current specification to designate the negation
/// of.
///
/// @return the regular expression for the possible names of the
/// function(s).
const string&
function_suppression::get_name_not_regex_str() const
{return priv_->name_not_regex_str_;}
/// Setter for a regular expression for a family of names of functions
/// the user wants the current specification to designate the negation
/// of.
///
/// @param r the new the regular expression for the possible names of
/// the function(s).
void
function_suppression::set_name_not_regex_str(const string& r)
{priv_->name_not_regex_str_ = r;}
/// Getter for the name of the return type of the function the user
/// wants this specification to designate. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @return the name of the return type of the function.
const string&
function_suppression::get_return_type_name() const
{return priv_->return_type_name_;}
/// Setter for the name of the return type of the function the user
/// wants this specification to designate. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param tr the new name of the return type of the function to set.
void
function_suppression::set_return_type_name(const string& tr)
{priv_->return_type_name_ = tr;}
/// Getter for a regular expression for a family of return type names
/// for functions the user wants the current specification to
/// designate.
///
/// If the name of the return type of the function as returned by
/// function_suppression::get_return_type_name() is not empty, then
/// this property is ignored at specification evaluation time. This
/// property might be empty, in which case it's ignored at evaluation
/// time.
///
/// @return the regular expression for the possible names of the
/// return types of the function(s).
const string&
function_suppression::get_return_type_regex_str() const
{return priv_->return_type_regex_str_;}
/// Setter for a regular expression for a family of return type names
/// for functions the user wants the current specification to
/// designate.
///
/// If the name of the return type of the function as returned by
/// function_suppression::get_return_type_name() is not empty, then
/// this property is ignored at specification evaluation time. This
/// property might be empty, in which case it's ignored at evaluation
/// time.
///
/// @param r the new regular expression for the possible names of the
/// return types of the function(s) to set.
void
function_suppression::set_return_type_regex_str(const string& r)
{priv_->return_type_regex_str_ = r;}
/// Getter for a vector of parameter specifications to specify
/// properties of the parameters of the functions the user wants this
/// specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the specifications of the parameters of the function(s).
const function_suppression::parameter_specs_type&
function_suppression::get_parameter_specs() const
{return priv_->parm_specs_;}
/// Setter for a vector of parameter specifications to specify
/// properties of the parameters of the functions the user wants this
/// specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param p the new specifications of the parameters of the
/// function(s) to set.
void
function_suppression::set_parameter_specs(parameter_specs_type& p)
{priv_->parm_specs_ = p;}
/// Append a specification of a parameter of the function specification.
///
/// @param p the parameter specification to add.
void
function_suppression::append_parameter_specs(const parameter_spec_sptr p)
{priv_->parm_specs_.push_back(p);}
/// Getter for the name of symbol of the function the user wants this
/// specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return name of the symbol of the function.
const string&
function_suppression::get_symbol_name() const
{return priv_->symbol_name_;}
/// Setter for the name of symbol of the function the user wants this
/// specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return name of the symbol of the function.
void
function_suppression::set_symbol_name(const string& n)
{priv_->symbol_name_ = n;}
/// Getter for a regular expression for a family of names of symbols
/// of functions the user wants this specification to designate.
///
/// If the symbol name as returned by
/// function_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the regular expression for a family of names of symbols of
/// functions to designate.
const string&
function_suppression::get_symbol_name_regex_str() const
{return priv_->symbol_name_regex_str_;}
/// Setter for a regular expression for a family of names of symbols
/// of functions the user wants this specification to designate.
///
/// If the symbol name as returned by
/// function_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param r the new regular expression for a family of names of
/// symbols of functions to set.
void
function_suppression::set_symbol_name_regex_str(const string& r)
{priv_->symbol_name_regex_str_ = r;}
/// Getter for a regular expression for a family of names of symbols
/// of functions the user wants this specification to designate.
///
/// If a symbol name is matched by this regular expression, then the
/// suppression specification will *NOT* suppress the symbol.
///
/// If the symbol name as returned by
/// function_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the regular expression string for a family of names of
/// symbols that is to be *NOT* suppressed by this suppression specification.
const string&
function_suppression::get_symbol_name_not_regex_str() const
{return priv_->symbol_name_not_regex_str_;}
/// Setter for a regular expression for a family of names of symbols
/// of functions the user wants this specification to designate.
///
/// If a symbol name is matched by this regular expression, then the
/// suppression specification will *NOT* suppress the symbol.
///
/// If the symbol name as returned by
/// function_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param the new regular expression string for a family of names of
/// symbols that is to be *NOT* suppressed by this suppression
/// specification.
void
function_suppression::set_symbol_name_not_regex_str(const string& r)
{priv_->symbol_name_not_regex_str_ = r;}
/// Getter for the name of the version of the symbol of the function
/// the user wants this specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the symbol version of the function.
const string&
function_suppression::get_symbol_version() const
{return priv_->symbol_version_;}
/// Setter for the name of the version of the symbol of the function
/// the user wants this specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param v the new symbol version of the function.
void
function_suppression::set_symbol_version(const string& v)
{priv_->symbol_version_ = v;}
/// Getter for a regular expression for a family of versions of
/// symbols of functions the user wants the current specification to
/// designate.
///
/// If the symbol version as returned by
/// function_suppression::get_symbol_version() is non empty, then this
/// property is ignored. This property might be empty, in which case
/// it's ignored at evaluation time.
///
/// @return the regular expression for the versions of symbols of
/// functions to designate.
const string&
function_suppression::get_symbol_version_regex_str() const
{return priv_->symbol_version_regex_str_;}
/// Setter for a regular expression for a family of versions of
/// symbols of functions the user wants the current specification to
/// designate.
///
/// If the symbol version as returned by
/// function_suppression::get_symbol_version() is non empty, then this
/// property is ignored. This property might be empty, in which case
/// it's ignored at evaluation time.
///
/// @param the new regular expression for the versions of symbols of
/// functions to designate.
void
function_suppression::set_symbol_version_regex_str(const string& r)
{priv_->symbol_version_regex_str_ = r;}
/// Getter for the "allow_other_aliases" property of the function
/// suppression specification.
///
/// @return the value of the "allow_other_aliases" property.
bool
function_suppression::get_allow_other_aliases() const
{return priv_->allow_other_aliases_;}
/// Setter for the "allow_other_aliases" property of the function
/// suppression specification.
///
/// @param f the new value of the property.
void
function_suppression::set_allow_other_aliases(bool f)
{priv_->allow_other_aliases_ = f;}
/// Evaluate this suppression specification on a given diff node and
/// say if the diff node should be suppressed or not.
///
/// @param diff the diff node to evaluate this suppression
/// specification against.
///
/// @return true if @p diff should be suppressed.
bool
function_suppression::suppresses_diff(const diff* diff) const
{
const function_decl_diff* d = is_function_decl_diff(diff);
if (!d)
return false;
function_decl_sptr ff = is_function_decl(d->first_function_decl()),
sf = is_function_decl(d->second_function_decl());
ABG_ASSERT(ff && sf);
return (suppresses_function(ff,
FUNCTION_SUBTYPE_CHANGE_KIND,
diff->context())
|| suppresses_function(sf,
FUNCTION_SUBTYPE_CHANGE_KIND,
diff->context()));
}
/// Evaluate the current function suppression specification on a given
/// @ref function_decl and say if a report about a change involving this
/// @ref function_decl should be suppressed or not.
///
/// @param fn the @ref function_decl to evaluate this suppression
/// specification against.
///
/// @param k the kind of function change @p fn is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the function @p
/// fn should be suppressed.
bool
function_suppression::suppresses_function(const function_decl* fn,
change_kind k,
const diff_context_sptr ctxt) const
{
if (!(get_change_kind() & k))
return false;
// Check if the name and soname of the binaries match the current
// suppr spec
if (ctxt)
{
// Check if the name of the binaries match the current suppr spec
if (!names_of_binaries_match(*this, *ctxt))
if (has_file_name_related_property())
return false;
// Check if the soname of the binaries match the current suppr spec
if (!sonames_of_binaries_match(*this, *ctxt))
if (has_soname_related_property())
return false;
}
string fname = fn->get_qualified_name();
// Check if the "name" property matches.
if (!get_name().empty())
{
if (get_name() != fn->get_qualified_name())
return false;
if (get_allow_other_aliases()
&& fn->get_symbol()
&& fn->get_symbol()->get_alias_from_name(fname))
{
// So we are in a case of a languages in which the symbol
// name is the same as the function name and we want to
// allow the removal of change reports on an aliased
// function only if the suppression condition matches the
// names of all aliases.
string symbol_name;
elf_symbol_sptr sym = fn->get_symbol();
ABG_ASSERT(sym);
symbol_name = sym->get_name();
if (sym->has_aliases() && sym->get_alias_from_name(fname))
{
for (elf_symbol_sptr a = sym->get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
if (a->get_name() != symbol_name)
// There is an alias which name is different from
// the function (symbol) name given in the
// suppression condition.
return false;
}
}
}
// check if the "name_regexp" property matches.
const regex_t_sptr name_regex = priv_->get_name_regex();
if (name_regex)
{
if (!regex::match(name_regex, fname))
return false;
if (get_allow_other_aliases()
&& fn->get_symbol()
&& fn->get_symbol()->get_alias_from_name(fname))
{
// So we are in a case of a languages in which the symbol
// name is the same as the function name and we want to
// allow the removal of change reports on an aliased
// function only if the suppression condition matches *all*
// the aliases.
string symbol_name;
elf_symbol_sptr sym = fn->get_symbol();
ABG_ASSERT(sym);
symbol_name = sym->get_name();
if (sym->has_aliases())
{
for (elf_symbol_sptr a = sym->get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
if (!regex::match(name_regex, a->get_name()))
return false;
}
}
}
// check if the "name_not_regexp" property matches.
const regex_t_sptr name_not_regex = priv_->get_name_not_regex();
if (name_not_regex)
{
if (regex::match(name_not_regex, fname))
return false;
if (get_allow_other_aliases()
&& fn->get_symbol()
&& fn->get_symbol()->get_alias_from_name(fname))
{
// So we are in a case of a languages in which the symbol
// name is the same as the function name and we want to
// allow the removal of change reports on an aliased
// function only if the suppression condition matches *all*
// the aliases.
string symbol_name;
elf_symbol_sptr sym = fn->get_symbol();
ABG_ASSERT(sym);
symbol_name = sym->get_name();
if (sym->has_aliases())
{
for (elf_symbol_sptr a = sym->get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
if (regex::match(name_regex, a->get_name()))
return false;
}
}
}
// Check if the "return_type_name" or "return_type_regexp"
// properties matches.
string fn_return_type_name = fn->get_type()->get_return_type()
? static_cast<string>
((get_type_declaration(fn->get_type()->get_return_type())
->get_qualified_name()))
: "";
if (!get_return_type_name().empty())
{
if (fn_return_type_name != get_return_type_name())
return false;
}
else
{
const regex_t_sptr return_type_regex = priv_->get_return_type_regex();
if (return_type_regex
&& !regex::match(return_type_regex, fn_return_type_name))
return false;
}
// Check if the "symbol_name", "symbol_name_regexp", and
// "symbol_name_not_regexp" properties match.
string fn_sym_name, fn_sym_version;
elf_symbol_sptr sym = fn->get_symbol();
if (sym)
{
fn_sym_name = sym->get_name();
fn_sym_version = sym->get_version().str();
}
if (sym && !get_symbol_name().empty())
{
if (fn_sym_name != get_symbol_name())
return false;
if (sym && get_allow_other_aliases())
{
// In this case, we want to allow the suppression of change
// reports about an aliased symbol only if the suppression
// condition matches the name of all aliases.
if (sym->has_aliases())
{
for (elf_symbol_sptr a = sym->get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
if (a->get_name() != fn_sym_name)
return false;
}
}
}
else if (sym)
{
const regex_t_sptr symbol_name_regex = priv_->get_symbol_name_regex();
if (symbol_name_regex && !regex::match(symbol_name_regex, fn_sym_name))
return false;
const regex_t_sptr symbol_name_not_regex =
priv_->get_symbol_name_not_regex();
if (symbol_name_not_regex
&& regex::match(symbol_name_not_regex, fn_sym_name))
return false;
if (get_allow_other_aliases())
{
// In this case, we want to allow the suppression of change
// reports about an aliased symbol only if the suppression
// condition matches the name of all aliases.
if (sym->has_aliases())
{
for (elf_symbol_sptr a = sym->get_next_alias();
a && !a->is_main_symbol();
a = a->get_next_alias())
{
if (symbol_name_regex
&& !regex::match(symbol_name_regex, a->get_name()))
return false;
if (symbol_name_not_regex
&& regex::match(symbol_name_not_regex, a->get_name()))
return false;
}
}
}
}
// Check if the "symbol_version" and "symbol_version_regexp"
// properties match.
if (sym && !get_symbol_version().empty())
{
if (fn_sym_version != get_symbol_version())
return false;
}
else if (sym)
{
const regex_t_sptr symbol_version_regex =
priv_->get_symbol_version_regex();
if (symbol_version_regex
&& !regex::match(symbol_version_regex, fn_sym_version))
return false;
}
// Check the 'parameter' property.
if (!get_parameter_specs().empty())
{
function_type_sptr fn_type = fn->get_type();
type_base_sptr parm_type;
for (parameter_specs_type::const_iterator p =
get_parameter_specs().begin();
p != get_parameter_specs().end();
++p)
{
size_t index = (*p)->get_index();
function_decl::parameter_sptr fn_parm =
fn_type->get_parm_at_index_from_first_non_implicit_parm(index);
if (!fn_parm)
return false;
string fn_parm_type_qualified_name;
if (fn_parm)
{
parm_type = fn_parm->get_type();
fn_parm_type_qualified_name =
get_type_declaration(parm_type)->get_qualified_name();
}
const string& tn = (*p)->get_parameter_type_name();
if (!tn.empty())
{
if (tn != fn_parm_type_qualified_name)
return false;
}
else
{
const regex_t_sptr parm_type_name_regex =
(*p)->priv_->get_type_name_regex();
if (parm_type_name_regex)
{
if (!regex::match(parm_type_name_regex,
fn_parm_type_qualified_name))
return false;
}
}
}
}
return true;
}
/// Evaluate the current function suppression specification on a given
/// @ref function_decl and say if a report about a change involving this
/// @ref function_decl should be suppressed or not.
///
/// @param fn the @ref function_decl to evaluate this suppression
/// specification against.
///
/// @param k the kind of function change @p fn is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the function @p
/// fn should be suppressed.
bool
function_suppression::suppresses_function(const function_decl_sptr fn,
change_kind k,
const diff_context_sptr ctxt) const
{return suppresses_function(fn.get(), k, ctxt);}
/// Evaluate the current function suppression specification on a given
/// @ref elf_symbol and say if a report about a change involving this
/// @ref elf_symbol should be suppressed or not.
///
/// @param sym the @ref elf_symbol to evaluate this suppression
/// specification against.
///
/// @param k the kind of function change @p sym is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the symbol @p
/// sym should be suppressed.
bool
function_suppression::suppresses_function_symbol(const elf_symbol* sym,
change_kind k,
const diff_context_sptr ctxt)
{
if (!sym)
return false;
if (!(get_change_kind() & k))
return false;
if (!sym->is_function())
return false;
ABG_ASSERT(k & function_suppression::ADDED_FUNCTION_CHANGE_KIND
|| k & function_suppression::DELETED_FUNCTION_CHANGE_KIND);
// Check if the name and soname of the binaries match the current
// suppr spect
if (ctxt)
{
// Check if the name of the binaries match the current
// suppr spect
if (!names_of_binaries_match(*this, *ctxt))
if (has_file_name_related_property())
return false;
// Check if the soname of the binaries match the current
// suppr spect
if (!sonames_of_binaries_match(*this, *ctxt))
if (has_soname_related_property())
return false;
}
string sym_name = sym->get_name(), sym_version = sym->get_version().str();
bool no_symbol_name = false, no_symbol_version = false;
// Consider the symbol name.
if (!get_symbol_name().empty())
{
if (sym_name != get_symbol_name())
return false;
}
else if (!get_symbol_name_regex_str().empty())
{
const regex_t_sptr symbol_name_regex = priv_->get_symbol_name_regex();
if (symbol_name_regex && !regex::match(symbol_name_regex, sym_name))
return false;
}
else
no_symbol_name = true;
// Consider the symbol version
if (!get_symbol_version().empty())
{
if (sym_version != get_symbol_version())
return false;
}
else if (!get_symbol_version_regex_str().empty())
{
const regex_t_sptr symbol_version_regex =
priv_->get_symbol_version_regex();
if (symbol_version_regex
&& !regex::match(symbol_version_regex, sym_version))
return false;
}
else
no_symbol_version = true;
if (no_symbol_name && no_symbol_version)
return false;
return true;
}
/// Evaluate the current function suppression specification on a given
/// @ref elf_symbol and say if a report about a change involving this
/// @ref elf_symbol should be suppressed or not.
///
/// @param sym the @ref elf_symbol to evaluate this suppression
/// specification against.
///
/// @param k the kind of function change @p sym is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the symbol @p
/// sym should be suppressed.
bool
function_suppression::suppresses_function_symbol(const elf_symbol_sptr sym,
change_kind k,
const diff_context_sptr ctxt)
{return suppresses_function_symbol(sym.get(), k, ctxt);}
/// Test if an instance of @ref suppression is an instance of @ref
/// function_suppression.
///
/// @param suppr the instance of @ref suppression to test for.
///
/// @return if @p suppr is an instance of @ref function_suppression, then
/// return the sub-object of the @p suppr of type @ref
/// function_suppression, otherwise return a nil pointer.
function_suppression_sptr
is_function_suppression(const suppression_sptr suppr)
{return dynamic_pointer_cast<function_suppression>(suppr);}
/// The bitwise 'and' operator for the enum @ref
/// function_suppression::change_kind.
///
/// @param l the first operand of the 'and' operator.
///
/// @param r the second operand of the 'and' operator.
///
/// @return the result of 'and' operation on @p l and @p r.
function_suppression::change_kind
operator&(function_suppression::change_kind l,
function_suppression::change_kind r)
{
return static_cast<function_suppression::change_kind>
(static_cast<unsigned>(l) & static_cast<unsigned>(r));
}
/// The bitwise 'or' operator for the enum @ref
/// function_suppression::change_kind.
///
/// @param l the first operand of the 'or' operator.
///
/// @param r the second operand of the 'or' operator.
///
/// @return the result of 'or' operation on @p l and @p r.
function_suppression::change_kind
operator|(function_suppression::change_kind l,
function_suppression::change_kind r)
{
return static_cast<function_suppression::change_kind>
(static_cast<unsigned>(l) | static_cast<unsigned>(r));
}
/// Test whether if a given function suppression matches a function
/// designated by a regular expression that describes its name.
///
/// @param s the suppression specification to evaluate to see if it
/// matches a given function name.
///
/// @param fn_name the name of the function of interest. Note that
/// this name must be *non* qualified.
///
/// @return true iff the suppression specification @p s matches the
/// function whose name is @p fn_name.
bool
suppression_matches_function_name(const suppr::function_suppression& s,
const string& fn_name)
{
if (regex_t_sptr regexp = s.priv_->get_name_regex())
{
if (!regex::match(regexp, fn_name))
return false;
}
else if (regex_t_sptr regexp = s.priv_->get_name_not_regex())
{
if (regex::match(regexp, fn_name))
return false;
}
else if (s.priv_->name_.empty())
return false;
else // if (!s.priv_->name_.empty())
{
if (s.priv_->name_ != fn_name)
return false;
}
return true;
}
/// Test whether if a given function suppression matches a function
/// designated by a regular expression that describes its linkage
/// name (symbol name).
///
/// @param s the suppression specification to evaluate to see if it
/// matches a given function linkage name
///
/// @param fn_linkage_name the linkage name of the function of interest.
///
/// @return true iff the suppression specification @p s matches the
/// function whose linkage name is @p fn_linkage_name.
bool
suppression_matches_function_sym_name(const suppr::function_suppression& s,
const string& fn_linkage_name)
{
if (regex_t_sptr regexp = s.priv_->get_symbol_name_regex())
{
if (!regex::match(regexp, fn_linkage_name))
return false;
}
else if (regex_t_sptr regexp = s.priv_->get_symbol_name_not_regex())
{
if (regex::match(regexp, fn_linkage_name))
return false;
}
else if (s.priv_->symbol_name_.empty())
return false;
else // if (!s.priv_->symbol_name_.empty())
{
if (s.priv_->symbol_name_ != fn_linkage_name)
return false;
}
return true;
}
/// Test if a variable suppression matches a variable denoted by its name.
///
/// @param s the variable suppression to consider.
///
/// @param var_name the name of the variable to consider.
///
/// @return true if the variable is matches by the suppression
/// specification.
bool
suppression_matches_variable_name(const suppr::variable_suppression& s,
const string& var_name)
{
if (regex_t_sptr regexp = s.priv_->get_name_regex())
{
if (!regex::match(regexp, var_name))
return false;
}
else if (regex_t_sptr regexp = s.priv_->get_name_not_regex())
{
if (regex::match(regexp, var_name))
return false;
}
else if (s.priv_->name_.empty())
return false;
else // if (!s.priv_->name_.empty())
{
if (s.priv_->name_ != var_name)
return false;
}
return true;
}
/// Test if a variable suppression matches a variable denoted by its
/// symbol name.
///
/// @param s the variable suppression to consider.
///
/// @param var_linkage_name the name of the variable to consider.
///
/// @return true if the variable is matches by the suppression
/// specification.
bool
suppression_matches_variable_sym_name(const suppr::variable_suppression& s,
const string& var_linkage_name)
{
if (regex_t_sptr regexp = s.priv_->get_symbol_name_regex())
{
if (!regex::match(regexp, var_linkage_name))
return false;
}
else if (regex_t_sptr regexp =
s.priv_->get_symbol_name_not_regex())
{
if (regex::match(regexp, var_linkage_name))
return false;
}
else if (s.priv_->symbol_name_.empty())
return false;
else // if (!s.priv_->symbol_name_.empty())
{
if (s.priv_->symbol_name_ != var_linkage_name)
return false;
}
return true;
}
/// Test if a type suppression matches a type designated by its fully
/// qualified name.
///
/// @param s the type suppression to consider.
///
/// @param type_name the name of the type to consider.
///
/// @return true iff the suppression s matches the type denoted by
/// name @p type_name.
bool
suppression_matches_type(const suppr::type_suppression& s,
const string& type_name)
{
if (regex_t_sptr regexp = s.priv_->get_type_name_regex())
{
if (!regex::match(regexp, type_name))
return false;
}
else if (!s.get_type_name().empty())
{
if (s.get_type_name() != type_name)
return false;
}
else
return false;
return true;
}
/// Parse a string containing a parameter spec, build an instance of
/// function_suppression::parameter_spec from it and return a pointer
/// to that object.
///
/// @return a shared pointer pointer to the newly built instance of
/// function_suppression::parameter_spec. If the parameter
/// specification could not be parsed, return a nil object.
static function_suppression::parameter_spec_sptr
read_parameter_spec_from_string(const string& str)
{
string::size_type cur = 0;
function_suppression::parameter_spec_sptr result;
// skip leading white spaces.
for (; cur < str.size(); ++cur)
if (!isspace(str[cur]))
break;
// look for the parameter index
string index_str;
if (str[cur] == '\'')
{
++cur;
for (; cur < str.size(); ++cur)
if (!isdigit(str[cur]))
break;
else
index_str += str[cur];
}
// skip white spaces.
for (; cur < str.size(); ++cur)
if (!isspace(str[cur]))
break;
bool is_regex = false;
if (str[cur] == '/')
{
is_regex = true;
++cur;
}
// look for the type name (regex)
string type_name;
for (; cur < str.size(); ++cur)
if (!isspace(str[cur]))
{
if (is_regex && str[cur] == '/')
break;
type_name += str[cur];
}
if (is_regex && str[cur] == '/')
++cur;
if (!index_str.empty() || !type_name.empty())
{
std::string type_name_regex;
if (is_regex)
{
type_name_regex = type_name;
type_name.clear();
}
function_suppression::parameter_spec* p =
new function_suppression::parameter_spec(atoi(index_str.c_str()),
type_name, type_name_regex);
result.reset(p);
}
return result;
}
/// Parse function suppression specification, build a resulting @ref
/// function_suppression type and return a shared pointer to that
/// object.
///
/// @return a shared pointer to the newly built @ref
/// function_suppression. If the function suppression specification
/// could not be parsed then a nil shared pointer is returned.
static function_suppression_sptr
read_function_suppression(const ini::config::section& section)
{
function_suppression_sptr result;
if (section.get_name() != "suppress_function")
return result;
static const char *const sufficient_props[] = {
"label",
"file_name_regexp",
"file_name_not_regexp",
"soname_regexp",
"soname_not_regexp",
"name",
"name_regexp",
"name_not_regexp",
"parameter",
"return_type_name",
"return_type_regexp",
"symbol_name",
"symbol_name_regexp",
"symbol_name_not_regexp",
"symbol_version",
"symbol_version_regexp",
};
if (!check_sufficient_props(sufficient_props,
sizeof(sufficient_props)/sizeof(char*),
section))
return result;
ini::simple_property_sptr drop_artifact =
is_simple_property(section.find_property("drop_artifact"));
if (!drop_artifact)
drop_artifact = is_simple_property(section.find_property("drop"));
string drop_artifact_str = drop_artifact
? drop_artifact->get_value()->as_string()
: "";
ini::simple_property_sptr change_kind_prop =
is_simple_property(section.find_property("change_kind"));
string change_kind_str = change_kind_prop
? change_kind_prop->get_value()->as_string()
: "";
ini::simple_property_sptr label_prop =
is_simple_property(section.find_property("label"));
string label_str = label_prop
? label_prop->get_value()->as_string()
: "";
ini::simple_property_sptr file_name_regex_prop =
is_simple_property(section.find_property("file_name_regexp"));
string file_name_regex_str =
file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr file_name_not_regex_prop =
is_simple_property(section.find_property("file_name_not_regexp"));
string file_name_not_regex_str =
file_name_not_regex_prop
? file_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr soname_regex_prop =
is_simple_property(section.find_property("soname_regexp"));
string soname_regex_str =
soname_regex_prop ? soname_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr soname_not_regex_prop =
is_simple_property(section.find_property("soname_not_regexp"));
string soname_not_regex_str =
soname_not_regex_prop
? soname_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_prop =
is_simple_property(section.find_property("name"));
string name = name_prop
? name_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_regex_prop =
is_simple_property(section.find_property("name_regexp"));
string name_regex_str = name_regex_prop
? name_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_not_regex_prop =
is_simple_property(section.find_property("name_not_regexp"));
string name_not_regex_str = name_not_regex_prop
? name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr return_type_name_prop =
is_simple_property(section.find_property("return_type_name"));
string return_type_name = return_type_name_prop
? return_type_name_prop->get_value()->as_string()
: "";
ini::simple_property_sptr return_type_regex_prop =
is_simple_property(section.find_property("return_type_regexp"));
string return_type_regex_str = return_type_regex_prop
? return_type_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_name_prop =
is_simple_property(section.find_property("symbol_name"));
string sym_name = sym_name_prop
? sym_name_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_name_regex_prop =
is_simple_property(section.find_property("symbol_name_regexp"));
string sym_name_regex_str = sym_name_regex_prop
? sym_name_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_name_not_regex_prop =
is_simple_property(section.find_property("symbol_name_not_regexp"));
string sym_name_not_regex_str = sym_name_not_regex_prop
? sym_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_ver_prop =
is_simple_property(section.find_property("symbol_version"));
string sym_version = sym_ver_prop
? sym_ver_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_ver_regex_prop =
is_simple_property(section.find_property("symbol_version_regexp"));
string sym_ver_regex_str = sym_ver_regex_prop
? sym_ver_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr allow_other_aliases_prop =
is_simple_property(section.find_property("allow_other_aliases"));
string allow_other_aliases = allow_other_aliases_prop
? allow_other_aliases_prop->get_value()->as_string()
: "";
function_suppression::parameter_spec_sptr parm;
function_suppression::parameter_specs_type parms;
for (ini::config::properties_type::const_iterator p =
section.get_properties().begin();
p != section.get_properties().end();
++p)
if ((*p)->get_name() == "parameter")
{
ini::simple_property_sptr prop = is_simple_property(*p);
ABG_ASSERT(prop);
if ((parm = read_parameter_spec_from_string
(prop->get_value()->as_string())))
parms.push_back(parm);
}
result.reset(new function_suppression(label_str,
name,
name_regex_str,
return_type_name,
return_type_regex_str,
parms,
sym_name,
sym_name_regex_str,
sym_version,
sym_ver_regex_str));
if ((drop_artifact_str == "yes" || drop_artifact_str == "true")
&& (!name.empty()
|| !name_regex_str.empty()
|| !name_not_regex_str.empty()
|| !sym_name.empty()
|| !sym_name_regex_str.empty()
|| !sym_name_not_regex_str.empty()))
result->set_drops_artifact_from_ir(true);
if (!change_kind_str.empty())
result->set_change_kind
(function_suppression::parse_change_kind(change_kind_str));
if (!allow_other_aliases.empty())
result->set_allow_other_aliases(allow_other_aliases == "yes"
|| allow_other_aliases == "true");
if (!name_not_regex_str.empty())
result->set_name_not_regex_str(name_not_regex_str);
if (!sym_name_not_regex_str.empty())
result->set_symbol_name_not_regex_str(sym_name_not_regex_str);
if (!file_name_regex_str.empty())
result->set_file_name_regex_str(file_name_regex_str);
if (!file_name_not_regex_str.empty())
result->set_file_name_not_regex_str(file_name_not_regex_str);
if (!soname_regex_str.empty())
result->set_soname_regex_str(soname_regex_str);
if (!soname_not_regex_str.empty())
result->set_soname_not_regex_str(soname_not_regex_str);
return result;
}
// </function_suppression stuff>
// <variable_suppression stuff>
/// Constructor for the @ref variable_suppression type.
///
/// @param label an informative text string that the evalution code
/// might use to designate this variable suppression specification in
/// error messages. This parameter might be empty, in which case it's
/// ignored at evaluation time.
///
/// @param name the name of the variable the user wants the current
/// specification to designate. This parameter might be empty, in
/// which case it's ignored at evaluation time.
///
/// @param name_regex_str if @p name is empty, this parameter is a
/// regular expression for a family of names of variables the user
/// wants the current specification to designate. If @p name is not
/// empty, then this parameter is ignored at evaluation time. This
/// parameter might be empty, in which case it's ignored at evaluation
/// time.
///
/// @param symbol_name the name of the symbol of the variable the user
/// wants the current specification to designate. This parameter
/// might be empty, in which case it's ignored at evaluation time.
///
/// @param symbol_name_str if @p symbol_name is empty, this parameter
/// is a regular expression for a family of names of symbols of
/// variables the user wants the current specification to designate.
/// If @p symbol_name is not empty, then this parameter is ignored at
/// evaluation time. This parameter might be empty, in which case
/// it's ignored at evaluation time.
///
/// @param symbol_version the version of the symbol of the variable
/// the user wants the current specification to designate. This
/// parameter might be empty, in which case it's ignored at evaluation
/// time.
///
/// @param symbol_version_regex if @p symbol_version is empty, then
/// this parameter is a regular expression for a family of versions of
/// symbol for the variables the user wants the current specification
/// to designate. If @p symbol_version is not empty, then this
/// parameter is ignored at evaluation time. This parameter might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param type_name the name of the type of the variable the user
/// wants the current specification to designate. This parameter
/// might be empty, in which case it's ignored at evaluation time.
///
/// @param type_name_regex_str if @p type_name is empty, then this
/// parameter is a regular expression for a family of type names of
/// variables the user wants the current specification to designate.
/// If @p type_name is not empty, then this parameter is ignored at
/// evluation time. This parameter might be empty, in which case it's
/// ignored at evaluation time.
variable_suppression::variable_suppression(const string& label,
const string& name,
const string& name_regex_str,
const string& symbol_name,
const string& symbol_name_regex_str,
const string& symbol_version,
const string& symbol_version_regex,
const string& type_name,
const string& type_name_regex_str)
: suppression_base(label),
priv_(new priv(name, name_regex_str,
symbol_name, symbol_name_regex_str,
symbol_version, symbol_version_regex,
type_name, type_name_regex_str))
{}
/// Virtual destructor for the @erf variable_suppression type.
/// variable_suppression type.
variable_suppression::~variable_suppression()
{}
/// Parses a string containing the content of the "change-kind"
/// property and returns the an instance of @ref
/// variable_suppression::change_kind as a result.
///
/// @param s the string to parse.
///
/// @return the resulting @ref variable_suppression::change_kind.
variable_suppression::change_kind
variable_suppression::parse_change_kind(const string& s)
{
if (s == "variable-subtype-change")
return VARIABLE_SUBTYPE_CHANGE_KIND;
else if (s == "added-variable")
return ADDED_VARIABLE_CHANGE_KIND;
else if (s == "deleted-variable")
return DELETED_VARIABLE_CHANGE_KIND;
else if (s == "all")
return ALL_CHANGE_KIND;
else
return UNDEFINED_CHANGE_KIND;
}
/// Getter of the "change_king" property.
///
/// @return the value of the "change_kind" property.
variable_suppression::change_kind
variable_suppression::get_change_kind() const
{return priv_->change_kind_;}
/// Setter of the "change_kind" property.
///
/// @param k the new value of of the change_kind.
void
variable_suppression::set_change_kind(change_kind k)
{priv_->change_kind_ = k;}
/// Getter for the name of the variable the user wants the current
/// specification to designate. This property might be empty, in
/// which case it's ignored at evaluation time.
///
/// @return the name of the variable.
const string&
variable_suppression::get_name() const
{return priv_->name_;}
/// Setter for the name of the variable the user wants the current
/// specification to designate. This property might be empty, in
/// which case it's ignored at evaluation time.
///
/// @param n the new name of the variable to set.
void
variable_suppression::set_name(const string& n)
{priv_->name_ = n;}
/// Getter for the regular expression for a family of names of
/// variables the user wants the current specification to designate.
/// If the variable name as returned by
/// variable_suppression::get_name() is not empty, then this property
/// is ignored at evaluation time. This property might be empty, in
/// which case it's ignored at evaluation time.
///
/// @return the regular expression for the variable name.
const string&
variable_suppression::get_name_regex_str() const
{return priv_->name_regex_str_;}
/// Setter for the regular expression for a family of names of
/// variables the user wants the current specification to designate.
/// If the variable name as returned by
/// variable_suppression::get_name() is not empty, then this property
/// is ignored at evaluation time. This property might be empty, in
/// which case it's ignored at evaluation time.
///
/// @param r the new regular expression for the variable name.
void
variable_suppression::set_name_regex_str(const string& r)
{priv_->name_regex_str_ = r;}
/// Getter for the "name_not_regexp" property of the specification.
///
/// @return the value of the "name_not_regexp" property.
const string&
variable_suppression::get_name_not_regex_str() const
{return priv_->name_not_regex_str_;}
/// Setter for the "name_not_regexp" property of the specification.
///
/// @param r the new value of the "name_not_regexp" property.
void
variable_suppression::set_name_not_regex_str(const string& r)
{priv_->name_not_regex_str_ = r;}
/// Getter for the name of the symbol of the variable the user wants
/// the current specification to designate.
///
/// This property might be empty, in which case it is ignored at
/// evaluation time.
///
/// @return the name of the symbol of the variable.
const string&
variable_suppression::get_symbol_name() const
{return priv_->symbol_name_;}
/// Setter for the name of the symbol of the variable the user wants
/// the current specification to designate.
///
/// This property might be empty, in which case it is ignored at
/// evaluation time.
///
/// @param n the new name of the symbol of the variable.
void
variable_suppression::set_symbol_name(const string& n)
{priv_->symbol_name_ = n;}
/// Getter of the regular expression for a family of symbol names of
/// the variables this specification is about to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time. Otherwise, it is taken in account iff the
/// property returned by variable_suppression::get_symbol_name() is
/// empty.
///
/// @return the regular expression for a symbol name of the variable.
const string&
variable_suppression::get_symbol_name_regex_str() const
{return priv_->symbol_name_regex_str_;}
/// Setter of the regular expression for a family of symbol names of
/// the variables this specification is about to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time. Otherwise, it is taken in account iff the
/// property returned by variable_suppression::get_symbol_name() is
/// empty.
///
/// @param r the regular expression for a symbol name of the variable.
void
variable_suppression::set_symbol_name_regex_str(const string& r)
{priv_->symbol_name_regex_str_ = r;}
/// Getter for a regular expression for a family of names of symbols
/// of variables the user wants this specification to designate.
///
/// If a symbol name is matched by this regular expression, then the
/// suppression specification will *NOT* suppress the symbol.
///
/// If the symbol name as returned by
/// variable_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the regular expression string for a family of names of
/// symbols that is to be *NOT* suppressed by this suppression specification.
const string&
variable_suppression::get_symbol_name_not_regex_str() const
{return priv_->symbol_name_not_regex_str_;}
/// Setter for a regular expression for a family of names of symbols
/// of variables the user wants this specification to designate.
///
/// If a symbol name is matched by this regular expression, then the
/// suppression specification will *NOT* suppress the symbol.
///
/// If the symbol name as returned by
/// variable_suppression::get_symbol_name() is not empty, then this
/// property is ignored at specification evaluation time.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param the new regular expression string for a family of names of
/// symbols that is to be *NOT* suppressed by this suppression
/// specification.
void
variable_suppression::set_symbol_name_not_regex_str(const string& r)
{priv_->symbol_name_not_regex_str_ = r;}
/// Getter for the version of the symbol of the variable the user
/// wants the current specification to designate. This property might
/// be empty, in which case it's ignored at evaluation time.
///
/// @return the symbol version of the variable.
const string&
variable_suppression::get_symbol_version() const
{return priv_->symbol_version_;}
/// Setter for the version of the symbol of the variable the user
/// wants the current specification to designate. This property might
/// be empty, in which case it's ignored at evaluation time.
///
/// @return the new symbol version of the variable.
void
variable_suppression::set_symbol_version(const string& v)
{priv_->symbol_version_ = v;}
/// Getter of the regular expression for a family of versions of
/// symbol for the variables the user wants the current specification
/// to designate. If @p symbol_version is not empty, then this
/// property is ignored at evaluation time. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @return the regular expression of the symbol version of the
/// variable.
const string&
variable_suppression::get_symbol_version_regex_str() const
{return priv_->symbol_version_regex_str_;}
/// Setter of the regular expression for a family of versions of
/// symbol for the variables the user wants the current specification
/// to designate. If @p symbol_version is not empty, then this
/// property is ignored at evaluation time. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param v the new regular expression of the symbol version of the
/// variable.
void
variable_suppression::set_symbol_version_regex_str(const string& r)
{priv_->symbol_version_regex_str_ = r;}
/// Getter for the name of the type of the variable the user wants the
/// current specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @return the name of the variable type.
const string&
variable_suppression::get_type_name() const
{return priv_->type_name_;}
/// Setter for the name of the type of the variable the user wants the
/// current specification to designate.
///
/// This property might be empty, in which case it's ignored at
/// evaluation time.
///
/// @param n the new name of the variable type.
void
variable_suppression::set_type_name(const string& n)
{priv_->type_name_ = n;}
/// Getter for the regular expression for a family of type names of
/// variables the user wants the current specification to designate.
///
/// If the type name as returned by
/// variable_suppression::get_type_name() is not empty, then this
/// property is ignored at evaluation time. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @return the regular expression of the variable type name.
const string&
variable_suppression::get_type_name_regex_str() const
{return priv_->type_name_regex_str_;}
/// Setter for the regular expression for a family of type names of
/// variables the user wants the current specification to designate.
///
/// If the type name as returned by
/// variable_suppression::get_type_name() is not empty, then this
/// property is ignored at evaluation time. This property might be
/// empty, in which case it's ignored at evaluation time.
///
/// @param r the regular expression of the variable type name.
void
variable_suppression::set_type_name_regex_str(const string& r)
{priv_->type_name_regex_str_ = r;}
/// Evaluate this suppression specification on a given diff node and
/// say if the diff node should be suppressed or not.
///
/// @param diff the diff node to evaluate this suppression
/// specification against.
///
/// @return true if @p diff should be suppressed.
bool
variable_suppression::suppresses_diff(const diff* diff) const
{
const var_diff* d = is_var_diff(diff);
if (!d)
return false;
var_decl_sptr fv = is_var_decl(is_decl(d->first_subject())),
sv = is_var_decl(is_decl(d->second_subject()));
ABG_ASSERT(fv && sv);
return (suppresses_variable(fv,
VARIABLE_SUBTYPE_CHANGE_KIND,
diff->context())
|| suppresses_variable(sv,
VARIABLE_SUBTYPE_CHANGE_KIND,
diff->context()));
}
/// Evaluate the current variable suppression specification on a given
/// @ref var_decl and say if a report about a change involving this
/// @ref var_decl should be suppressed or not.
///
/// @param var the @ref var_decl to evaluate this suppression
/// specification against.
///
/// @param k the kind of variable change @p var is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the variable @p
/// var should be suppressed.
bool
variable_suppression::suppresses_variable(const var_decl* var,
change_kind k,
const diff_context_sptr ctxt) const
{
if (!(get_change_kind() & k))
return false;
// Check if the name and soname of the binaries match
if (ctxt)
{
// Check if the name of the binaries match the current
// suppr spec
if (!names_of_binaries_match(*this, *ctxt))
if (has_file_name_related_property())
return false;
// Check if the soname of the binaries match the current suppr
// spec
if (!sonames_of_binaries_match(*this, *ctxt))
if (has_soname_related_property())
return false;
}
string var_name = var->get_qualified_name();
// Check for "name" property match.
if (!get_name().empty())
{
if (get_name() != var_name)
return false;
}
else
{
// If the "name" property is empty, then consider checking for the
// "name_regex" and "name_not_regex" properties match
if (get_name().empty())
{
const regex_t_sptr name_regex = priv_->get_name_regex();
if (name_regex && !regex::match(name_regex, var_name))
return false;
const regex_t_sptr name_not_regex = priv_->get_name_not_regex();
if (name_not_regex && regex::match(name_not_regex, var_name))
return false;
}
}
// Check for the symbol_name, symbol_name_regex and
// symbol_name_not_regex property match.
string var_sym_name = var->get_symbol() ? var->get_symbol()->get_name() : "";
if (!get_symbol_name().empty())
{
if (get_symbol_name() != var_sym_name)
return false;
}
else
{
const regex_t_sptr sym_name_regex = priv_->get_symbol_name_regex();
if (sym_name_regex && !regex::match(sym_name_regex, var_sym_name))
return false;
const regex_t_sptr sym_name_not_regex =
priv_->get_symbol_name_not_regex();
if (sym_name_not_regex && regex::match(sym_name_not_regex, var_sym_name))
return false;
}
// Check for symbol_version and symbol_version_regexp property match
string var_sym_version =
var->get_symbol() ? var->get_symbol()->get_version().str() : "";
if (!get_symbol_version().empty())
{
if (get_symbol_version() != var_sym_version)
return false;
}
else
{
const regex_t_sptr symbol_version_regex =
priv_->get_symbol_version_regex();
if (symbol_version_regex
&& !regex::match(symbol_version_regex, var_sym_version))
return false;
}
// Check for the "type_name" and type_name_regex properties match.
string var_type_name =
get_type_declaration(var->get_type())->get_qualified_name();
if (!get_type_name().empty())
{
if (get_type_name() != var_type_name)
return false;
}
else
{
if (get_type_name().empty())
{
const regex_t_sptr type_name_regex = priv_->get_type_name_regex();
if (type_name_regex && !regex::match(type_name_regex, var_type_name))
return false;
}
}
return true;
}
/// Evaluate the current variable suppression specification on a given
/// @ref var_decl and say if a report about a change involving this
/// @ref var_decl should be suppressed or not.
///
/// @param var the @ref var_decl to evaluate this suppression
/// specification against.
///
/// @param k the kind of variable change @p var is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the variable @p
/// var should be suppressed.
bool
variable_suppression::suppresses_variable(const var_decl_sptr var,
change_kind k,
const diff_context_sptr ctxt) const
{return suppresses_variable(var.get(), k, ctxt);}
/// Evaluate the current variable suppression specification on a given
/// @ref elf_symbol and say if a report about a change involving this
/// @ref elf_symbol should be suppressed or not.
///
/// @param sym the @ref elf_symbol to evaluate this suppression
/// specification against.
///
/// @param k the kind of variable change @p sym is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the symbol @p
/// sym should be suppressed.
bool
variable_suppression::suppresses_variable_symbol(const elf_symbol* sym,
change_kind k,
const diff_context_sptr ctxt) const
{
if (!sym)
return false;
if (!(get_change_kind() & k))
return false;
if (!sym->is_variable())
return false;
ABG_ASSERT(k & ADDED_VARIABLE_CHANGE_KIND
|| k & DELETED_VARIABLE_CHANGE_KIND);
// Check if the name and soname of the binaries match the current
// suppr spec.
if (ctxt)
{
// Check if the name of the binaries match the current suppr
// spec
if (!names_of_binaries_match(*this, *ctxt))
if (has_file_name_related_property())
return false;
// Check if the soname of the binaries match the current suppr spec
if (!sonames_of_binaries_match(*this, *ctxt))
if (has_soname_related_property())
return false;
}
string sym_name = sym->get_name(), sym_version = sym->get_version().str();
bool no_symbol_name = false, no_symbol_version = false;
// Consider the symbol name
if (!get_name().empty())
{
if (get_name() != sym_name)
return false;
}
else if (!get_symbol_name().empty())
{
if (get_symbol_name() != sym_name)
return false;
}
else if (!get_symbol_name_regex_str().empty())
{
const regex_t_sptr sym_name_regex = priv_->get_symbol_name_regex();
if (sym_name_regex && !regex::match(sym_name_regex, sym_name))
return false;
}
else
no_symbol_name = true;
// Consider the symbol version.
if (!get_symbol_version().empty())
{
if (get_symbol_version() != sym_version)
return false;
}
else if (!get_symbol_version_regex_str().empty())
{
const regex_t_sptr symbol_version_regex =
priv_->get_symbol_version_regex();
if (symbol_version_regex
&& !regex::match(symbol_version_regex, sym_version))
return false;
}
else
no_symbol_version = true;
if (no_symbol_name && no_symbol_version)
return false;
return true;
}
/// Evaluate the current variable suppression specification on a given
/// @ref elf_symbol and say if a report about a change involving this
/// @ref elf_symbol should be suppressed or not.
///
/// @param sym the @ref elf_symbol to evaluate this suppression
/// specification against.
///
/// @param k the kind of variable change @p sym is supposed to have.
///
/// @param ctxt the context of the current diff.
///
/// @return true iff a report about a change involving the symbol @p
/// sym should be suppressed.
bool
variable_suppression::suppresses_variable_symbol(const elf_symbol_sptr sym,
change_kind k,
const diff_context_sptr ctxt) const
{return suppresses_variable_symbol(sym.get(), k, ctxt);}
/// Test if an instance of @ref suppression is an instance of @ref
/// variable_suppression.
///
/// @param suppr the instance of @ref suppression to test for.
///
/// @return if @p suppr is an instance of @ref variable_suppression, then
/// return the sub-object of the @p suppr of type @ref
/// variable_suppression, otherwise return a nil pointer.
variable_suppression_sptr
is_variable_suppression(const suppression_sptr s)
{return dynamic_pointer_cast<variable_suppression>(s);}
/// The bitwise 'and' operator for the enum @ref
/// variable_suppression::change_kind.
///
/// @param l the first operand of the 'and' operator.
///
/// @param r the second operand of the 'and' operator.
///
/// @return the result of 'and' operation on @p l and @p r.
variable_suppression::change_kind
operator&(variable_suppression::change_kind l,
variable_suppression::change_kind r)
{
return static_cast<variable_suppression::change_kind>
(static_cast<unsigned>(l) & static_cast<unsigned>(r));
}
/// The bitwise 'or' operator for the enum @ref
/// variable_suppression::change_kind.
///
/// @param l the first operand of the 'or' operator.
///
/// @param r the second operand of the 'or' operator.
///
/// @return the result of 'or' operation on @p l and @p r.
variable_suppression::change_kind
operator|(variable_suppression::change_kind l,
variable_suppression::change_kind r)
{
return static_cast<variable_suppression::change_kind>
(static_cast<unsigned>(l) | static_cast<unsigned>(r));
}
/// Parse variable suppression specification, build a resulting @ref
/// variable_suppression type and return a shared pointer to that
/// object.
///
/// @return a shared pointer to the newly built @ref
/// variable_suppression. If the variable suppression specification
/// could not be parsed then a nil shared pointer is returned.
static variable_suppression_sptr
read_variable_suppression(const ini::config::section& section)
{
variable_suppression_sptr result;
if (section.get_name() != "suppress_variable")
return result;
static const char *const sufficient_props[] = {
"label",
"file_name_regexp",
"file_name_not_regexp",
"soname_regexp",
"soname_not_regexp",
"name",
"name_regexp",
"name_not_regexp",
"symbol_name",
"symbol_name_regexp",
"symbol_name_not_regexp",
"symbol_version",
"symbol_version_regexp",
"type_name",
"type_name_regexp",
};
if (!check_sufficient_props(sufficient_props,
sizeof(sufficient_props)/sizeof(char*),
section))
return result;
ini::simple_property_sptr drop_artifact =
is_simple_property(section.find_property("drop_artifact"));
if (!drop_artifact)
drop_artifact = is_simple_property(section.find_property("drop"));
string drop_artifact_str = drop_artifact
? drop_artifact->get_value()->as_string()
: "";
ini::simple_property_sptr change_kind_prop =
is_simple_property(section.find_property("change_kind"));
string change_kind_str = change_kind_prop
? change_kind_prop->get_value()->as_string()
: "";
ini::simple_property_sptr label_prop =
is_simple_property(section.find_property("label"));
string label_str = (label_prop
? label_prop->get_value()->as_string()
: "");
ini::simple_property_sptr file_name_regex_prop =
is_simple_property(section.find_property("file_name_regexp"));
string file_name_regex_str =
file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr file_name_not_regex_prop =
is_simple_property(section.find_property("file_name_not_regexp"));
string file_name_not_regex_str =
file_name_not_regex_prop
? file_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr soname_regex_prop =
is_simple_property(section.find_property("soname_regexp"));
string soname_regex_str =
soname_regex_prop ? soname_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr soname_not_regex_prop =
is_simple_property(section.find_property("soname_not_regexp"));
string soname_not_regex_str =
soname_not_regex_prop
? soname_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr name_prop =
is_simple_property(section.find_property("name"));
string name_str = (name_prop
? name_prop->get_value()->as_string()
: "");
ini::simple_property_sptr name_regex_prop =
is_simple_property(section.find_property("name_regexp"));
string name_regex_str = (name_regex_prop
? name_regex_prop->get_value()->as_string()
: "");
ini::simple_property_sptr name_not_regex_prop =
is_simple_property(section.find_property("name_not_regexp"));
string name_not_regex_str = name_not_regex_prop
? name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_name_prop =
is_simple_property(section.find_property("symbol_name"));
string symbol_name = (sym_name_prop
? sym_name_prop->get_value()->as_string()
: "");
ini::simple_property_sptr sym_name_regex_prop =
is_simple_property(section.find_property("symbol_name_regexp"));
string symbol_name_regex_str = sym_name_regex_prop
? sym_name_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_name_not_regex_prop =
is_simple_property(section.find_property("symbol_name_not_regexp"));
string symbol_name_not_regex_str = sym_name_not_regex_prop
? sym_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_version_prop =
is_simple_property(section.find_property("symbol_version"));
string symbol_version = sym_version_prop
? sym_version_prop->get_value()->as_string()
: "";
ini::simple_property_sptr sym_version_regex_prop =
is_simple_property(section.find_property("symbol_version_regexp"));
string symbol_version_regex_str = sym_version_regex_prop
? sym_version_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr type_name_prop =
is_simple_property(section.find_property("type_name"));
string type_name_str = type_name_prop
? type_name_prop->get_value()->as_string()
: "";
ini::simple_property_sptr type_name_regex_prop =
is_simple_property(section.find_property("type_name_regexp"));
string type_name_regex_str = type_name_regex_prop
? type_name_regex_prop->get_value()->as_string()
: "";
result.reset(new variable_suppression(label_str,
name_str,
name_regex_str,
symbol_name,
symbol_name_regex_str,
symbol_version,
symbol_version_regex_str,
type_name_str,
type_name_regex_str));
if ((drop_artifact_str == "yes" || drop_artifact_str == "true")
&& (!name_str.empty()
|| !name_regex_str.empty()
|| !name_not_regex_str.empty()
|| !symbol_name.empty()
|| !symbol_name_regex_str.empty()
|| !symbol_name_not_regex_str.empty()))
result->set_drops_artifact_from_ir(true);
if (!name_not_regex_str.empty())
result->set_name_not_regex_str(name_not_regex_str);
if (!symbol_name_not_regex_str.empty())
result->set_symbol_name_not_regex_str(symbol_name_not_regex_str);
if (!change_kind_str.empty())
result->set_change_kind
(variable_suppression::parse_change_kind(change_kind_str));
if (!file_name_regex_str.empty())
result->set_file_name_regex_str(file_name_regex_str);
if (!file_name_not_regex_str.empty())
result->set_file_name_not_regex_str(file_name_not_regex_str);
if (!soname_regex_str.empty())
result->set_soname_regex_str(soname_regex_str);
if (!soname_not_regex_str.empty())
result->set_soname_not_regex_str(soname_not_regex_str);
return result;
}
// </variable_suppression stuff>
// <file_suppression stuff>
/// Constructor for the the @ref file_suppression type.
///
/// @param label the label of the suppression directive.
///
/// @param fname_regex_str the regular expression string that
/// designates the file name that instances of @ref file_suppression
/// should match.
///
/// @param fname_not_regex_str the regular expression string that
/// designates the file name that instances of @ref file_suppression
/// shoult *NOT* match. In other words, this file_suppression should
/// be activated if its file name does not match the regular
/// expression @p fname_not_regex_str.
file_suppression::file_suppression(const string& label,
const string& fname_regex_str,
const string& fname_not_regex_str)
: suppression_base(label,
fname_regex_str,
fname_not_regex_str)
{}
/// Test if instances of this @ref file_suppression suppresses a
/// certain instance of @ref diff.
///
/// This function always returns false because, obviously, a
/// file_suppression is meants to prevents Abigail tools from loading
/// some files. It is not meant to act on instance of @ref diff.
/// @return false.
bool
file_suppression::suppresses_diff(const diff*) const
{return false;}
/// Test if a instances of this @ref file_suppression suppresses a
/// given file.
///
/// @param file_path the file path to test against.
///
/// @return true iff this file_suppression matches the file path @p
/// file_path.
bool
file_suppression::suppresses_file(const string& file_path)
{
if (file_path.empty())
return false;
string fname;
tools_utils::base_name(file_path, fname);
bool has_regexp = false;
if (regex_t_sptr regexp = suppression_base::priv_->get_file_name_regex())
{
has_regexp = true;
if (!regex::match(regexp, fname))
return false;
}
if (regex_t_sptr regexp = suppression_base::priv_->get_file_name_not_regex())
{
has_regexp = true;
if (regex::match(regexp, fname))
return false;
}
if (!has_regexp)
return false;
return true;
}
/// Destructor of @ref file_suppression.
file_suppression::~file_suppression()
{
}
/// Read a file suppression from an instance of ini::config::section
/// and build a @ref type_suppression as a result.
///
/// @param section the section (from an ini file) to read the file
/// suppression from.
///
/// @return file_suppression_sptr.
static file_suppression_sptr
read_file_suppression(const ini::config::section& section)
{
file_suppression_sptr result;
if (section.get_name() != "suppress_file")
return result;
static const char *const sufficient_props[] = {
"file_name_regexp",
"file_name_not_regexp",
"soname_regexp",
"soname_not_regexp",
};
if (!check_sufficient_props(sufficient_props,
sizeof(sufficient_props)/sizeof(char*),
section))
return result;
ini::simple_property_sptr label_prop =
is_simple_property(section.find_property("label"));
string label_str = (label_prop
? label_prop->get_value()->as_string()
: "");
ini::simple_property_sptr file_name_regex_prop =
is_simple_property(section.find_property("file_name_regexp"));
string file_name_regex_str =
file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr file_name_not_regex_prop =
is_simple_property(section.find_property("file_name_not_regexp"));
string file_name_not_regex_str =
file_name_not_regex_prop
? file_name_not_regex_prop->get_value()->as_string()
: "";
ini::simple_property_sptr soname_regex_prop =
is_simple_property(section.find_property("soname_regexp"));
string soname_regex_str =
soname_regex_prop ? soname_regex_prop->get_value()->as_string() : "";
ini::simple_property_sptr soname_not_regex_prop =
is_simple_property(section.find_property("soname_not_regexp"));
string soname_not_regex_str =
soname_not_regex_prop
? soname_not_regex_prop->get_value()->as_string()
: "";
result.reset(new file_suppression(label_str,
file_name_regex_str,
file_name_not_regex_str));
if (!soname_regex_str.empty())
{
result->set_soname_regex_str(soname_regex_str);
result->set_drops_artifact_from_ir(true);
}
if (!soname_not_regex_str.empty())
{
result->set_soname_not_regex_str(soname_not_regex_str);
result->set_drops_artifact_from_ir(true);
}
return result;
}
/// Test if a given suppression specification is a file suppression
/// specification.
///
/// @param s the instance of @ref suppression_base to test.
///
/// @return the instance of @ref file_suppression that @p s points to,
/// iff s is an instance of @ref file_suppression. Otherwise, returns
/// nil.
file_suppression_sptr
is_file_suppression(const suppression_sptr s)
{return dynamic_pointer_cast<file_suppression>(s);}
/// Test if a given file path is "suppressed" by at least one file
/// suppression specification among a vector of suppression
/// specifications.
///
/// @param file_path the file path to test.
///
/// @param sprs the vector of suppressions to use to test if one of
/// them at lease matches the file path @p file_path.
///
/// @return a pointer to the first instance of @ref file_suppression
/// that matches @p file_path, or nil if no file suppression matches.
file_suppression_sptr
file_is_suppressed(const string& file_path,
const suppressions_type& sprs)
{
for (suppressions_type::const_iterator i = sprs.begin(); i != sprs.end(); ++i)
if (file_suppression_sptr s = is_file_suppression(*i))
if (s->suppresses_file(file_path))
return s;
return file_suppression_sptr();
}
/// Test if a given SONAME is matched by a given suppression
/// specification.
///
/// @param soname the SONAME to consider.
///
/// @param suppr the suppression specification to consider.
///
/// @return true iff a given SONAME is matched by a given suppression
/// specification.
bool
suppression_matches_soname(const string& soname,
const suppression_base& suppr)
{
return suppr.priv_->matches_soname(soname);
}
/// Test if a given SONAME or file name is matched by a given
/// suppression specification.
///
/// @param soname the SONAME to consider.
///
/// @param filename the file name to consider.
///
/// @param suppr the suppression specification to consider.
///
/// @return true iff either @p soname or @p filename is matched by the
/// suppression specification @p suppr.
bool
suppression_matches_soname_or_filename(const string& soname,
const string& filename,
const suppression_base& suppr)
{
return (suppression_matches_soname(soname, suppr)
|| suppr.priv_->matches_binary_name(filename));
}
/// @return the name of the artificial private type suppression
/// specification that is auto-generated by libabigail to suppress
/// change reports about types that are not defined in public headers.
const char*
get_private_types_suppr_spec_label()
{
static const char *PRIVATE_TYPES_SUPPR_SPEC_NAME =
"Artificial private types suppression specification";
return PRIVATE_TYPES_SUPPR_SPEC_NAME;
}
/// Test if a type suppression specification represents a private type
/// suppression automatically generated by libabigail from the user
/// telling us where public headers are.
///
/// @param s the suppression specification we are looking at.
///
/// @return true iff @p s is a private type suppr spec.
bool
is_private_type_suppr_spec(const type_suppression& s)
{return s.get_label() == get_private_types_suppr_spec_label();}
/// Test if a type suppression specification represents a private type
/// suppression automatically generated by libabigail from the user
/// telling us where public headers are.
///
/// @param s the suppression specification we are looking at.
///
/// @return true iff @p s is a private type suppr spec.
bool
is_private_type_suppr_spec(const suppression_sptr& s)
{
type_suppression_sptr type_suppr = is_type_suppression(s);
return (type_suppr
&& type_suppr->get_label() == get_private_types_suppr_spec_label());
}
// </file_suppression stuff>
}// end namespace suppr
} // end namespace abigail