| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- mode: C++ -*- |
| // |
| // Copyright (C) 2013-2021 Red Hat, Inc. |
| |
| /// @file |
| /// |
| /// This file contains the definitions of the entry points to |
| /// de-serialize an instance of @ref abigail::translation_unit from an |
| /// ABI Instrumentation file in libabigail native XML format. This |
| /// native XML format is named "abixml". |
| |
| #include "config.h" |
| #include <assert.h> |
| #include <libxml/xmlreader.h> |
| #include <libxml/xmlstring.h> |
| #include <cerrno> |
| #include <cstdlib> |
| #include <cstring> |
| #include <deque> |
| #include <memory> |
| #include <sstream> |
| #include <unordered_map> |
| |
| #include "abg-suppression-priv.h" |
| |
| #include "abg-internal.h" |
| #include "abg-tools-utils.h" |
| |
| // <headers defining libabigail's API go under here> |
| ABG_BEGIN_EXPORT_DECLARATIONS |
| |
| #include "abg-libxml-utils.h" |
| #include "abg-reader.h" |
| #include "abg-corpus.h" |
| #include "abg-symtab-reader.h" |
| |
| ABG_END_EXPORT_DECLARATIONS |
| // </headers defining libabigail's API> |
| |
| namespace abigail |
| { |
| |
| using xml::xml_char_sptr; |
| |
| /// The namespace for the native XML file format reader. |
| namespace xml_reader |
| { |
| using std::string; |
| using std::deque; |
| using std::shared_ptr; |
| using std::unordered_map; |
| using std::dynamic_pointer_cast; |
| using std::vector; |
| using std::istream; |
| |
| static bool read_is_declaration_only(xmlNodePtr, bool&); |
| static bool read_is_artificial(xmlNodePtr, bool&); |
| static bool read_tracking_non_reachable_types(xmlNodePtr, bool&); |
| static bool read_is_non_reachable_type(xmlNodePtr, bool&); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| static bool read_type_id_string(xmlNodePtr, string&); |
| #endif |
| |
| class read_context; |
| |
| /// This abstracts the context in which the current ABI |
| /// instrumentation dump is being de-serialized. It carries useful |
| /// information needed during the de-serialization, but that does not |
| /// make sense to be stored in the final resulting in-memory |
| /// representation of ABI Corpus. |
| class read_context |
| { |
| public: |
| |
| typedef unordered_map<string, |
| vector<type_base_sptr> >::const_iterator |
| const_types_map_it; |
| |
| typedef unordered_map<string, |
| vector<type_base_sptr> >::iterator |
| types_map_it; |
| |
| typedef unordered_map<string, |
| shared_ptr<function_tdecl> >::const_iterator |
| const_fn_tmpl_map_it; |
| |
| typedef unordered_map<string, |
| shared_ptr<class_tdecl> >::const_iterator |
| const_class_tmpl_map_it; |
| |
| typedef unordered_map<string, xmlNodePtr> string_xml_node_map; |
| |
| typedef unordered_map<xmlNodePtr, decl_base_sptr> xml_node_decl_base_sptr_map; |
| |
| private: |
| string m_path; |
| environment* m_env; |
| unordered_map<string, vector<type_base_sptr> > m_types_map; |
| unordered_map<string, shared_ptr<function_tdecl> > m_fn_tmpl_map; |
| unordered_map<string, shared_ptr<class_tdecl> > m_class_tmpl_map; |
| vector<type_base_sptr> m_types_to_canonicalize; |
| string_xml_node_map m_id_xml_node_map; |
| xml_node_decl_base_sptr_map m_xml_node_decl_map; |
| xml::reader_sptr m_reader; |
| xmlNodePtr m_corp_node; |
| deque<shared_ptr<decl_base> > m_decls_stack; |
| corpus_sptr m_corpus; |
| corpus_group_sptr m_corpus_group; |
| corpus::exported_decls_builder* m_exported_decls_builder; |
| suppr::suppressions_type m_supprs; |
| bool m_tracking_non_reachable_types; |
| bool m_drop_undefined_syms; |
| |
| read_context(); |
| |
| public: |
| read_context(xml::reader_sptr reader, |
| environment* env) |
| : m_env(env), |
| m_reader(reader), |
| m_corp_node(), |
| m_exported_decls_builder(), |
| m_tracking_non_reachable_types(), |
| m_drop_undefined_syms() |
| {} |
| |
| /// Getter for the flag that tells us if we are tracking types that |
| /// are not reachable from global functions and variables. |
| /// |
| /// @return true iff we are tracking types that are not reachable |
| /// from global functions and variables. |
| bool |
| tracking_non_reachable_types() const |
| {return m_tracking_non_reachable_types;} |
| |
| /// Setter for the flag that tells us if we are tracking types that |
| /// are not reachable from global functions and variables. |
| /// |
| /// @param f the new value of the flag. |
| /// from global functions and variables. |
| void |
| tracking_non_reachable_types(bool f) |
| {m_tracking_non_reachable_types = f;} |
| |
| /// Getter for the flag that tells us if we are dropping functions |
| /// and variables that have undefined symbols. |
| /// |
| /// @return true iff we are dropping functions and variables that have |
| /// undefined symbols. |
| bool |
| drop_undefined_syms() const |
| {return m_drop_undefined_syms;} |
| |
| /// Setter for the flag that tells us if we are dropping functions |
| /// and variables that have undefined symbols. |
| /// |
| /// @param f the new value of the flag. |
| void |
| drop_undefined_syms(bool f) |
| {m_drop_undefined_syms = f;} |
| |
| /// Getter of the path to the ABI file. |
| /// |
| /// @return the path to the native xml abi file. |
| const string& |
| get_path() const |
| {return m_path;} |
| |
| /// Setter of the path to the ABI file. |
| /// |
| /// @param the new path to the native ABI file. |
| void |
| set_path(const string& s) |
| {m_path = s;} |
| |
| /// Getter for the environment of this reader. |
| /// |
| /// @return the environment of this reader. |
| const environment* |
| get_environment() const |
| {return m_env;} |
| |
| /// Getter for the environment of this reader. |
| /// |
| /// @return the environment of this reader. |
| environment* |
| get_environment() |
| {return m_env;} |
| |
| /// Setter for the environment of this reader. |
| /// |
| /// @param env the environment of this reader. |
| void |
| set_environment(environment* env) |
| {m_env = env;} |
| |
| xml::reader_sptr |
| get_reader() const |
| {return m_reader;} |
| |
| /// Getter of the current XML node in the corpus element sub-tree |
| /// that needs to be processed. |
| /// |
| /// @return the current XML node in the corpus element sub-tree that |
| /// needs to be processed. |
| xmlNodePtr |
| get_corpus_node() const |
| {return m_corp_node;} |
| |
| /// Setter of the current XML node in the corpus element sub-tree |
| /// that needs to be processed. |
| /// |
| /// @param node set the current XML node in the corpus element |
| /// sub-tree that needs to be processed. |
| void |
| set_corpus_node(xmlNodePtr node) |
| {m_corp_node = node;} |
| |
| const string_xml_node_map& |
| get_id_xml_node_map() const |
| {return m_id_xml_node_map;} |
| |
| string_xml_node_map& |
| get_id_xml_node_map() |
| {return m_id_xml_node_map;} |
| |
| void |
| clear_id_xml_node_map() |
| {get_id_xml_node_map().clear();} |
| |
| const xml_node_decl_base_sptr_map& |
| get_xml_node_decl_map() const |
| {return m_xml_node_decl_map;} |
| |
| xml_node_decl_base_sptr_map& |
| get_xml_node_decl_map() |
| {return m_xml_node_decl_map;} |
| |
| void |
| map_xml_node_to_decl(xmlNodePtr node, |
| decl_base_sptr decl) |
| { |
| if (node) |
| get_xml_node_decl_map()[node]= decl; |
| } |
| |
| decl_base_sptr |
| get_decl_for_xml_node(xmlNodePtr node) const |
| { |
| xml_node_decl_base_sptr_map::const_iterator i = |
| get_xml_node_decl_map().find(node); |
| |
| if (i != get_xml_node_decl_map().end()) |
| return i->second; |
| |
| return decl_base_sptr(); |
| } |
| |
| void |
| clear_xml_node_decl_map() |
| {get_xml_node_decl_map().clear();} |
| |
| void |
| map_id_and_node (const string& id, |
| xmlNodePtr node) |
| { |
| if (!node) |
| return; |
| |
| string_xml_node_map::iterator i = get_id_xml_node_map().find(id); |
| if (i != get_id_xml_node_map().end()) |
| { |
| bool is_declaration = false; |
| read_is_declaration_only(node, is_declaration); |
| if (is_declaration) |
| i->second = node; |
| } |
| else |
| get_id_xml_node_map()[id] = node; |
| } |
| |
| xmlNodePtr |
| get_xml_node_from_id(const string& id) const |
| { |
| string_xml_node_map::const_iterator i = get_id_xml_node_map().find(id); |
| if (i != get_id_xml_node_map().end()) |
| return i->second; |
| return 0; |
| } |
| |
| scope_decl_sptr |
| get_scope_for_node(xmlNodePtr node, |
| access_specifier& access); |
| |
| // This is defined later, after build_type() is declared, because it |
| // uses it. |
| type_base_sptr |
| build_or_get_type_decl(const string& id, |
| bool add_decl_to_scope); |
| |
| /// Return the first type already seen, that is identified by a |
| /// given ID. |
| /// |
| /// Note that for a type to be "identified" by id, the function |
| /// key_type_decl must have been previously called with that type |
| /// and with id. |
| /// |
| /// @param id the id to consider. |
| /// |
| /// @return the type identified by the unique id id, or a null |
| /// pointer if no type has ever been associated with id before. |
| type_base_sptr |
| get_type_decl(const string& id) const |
| { |
| const_types_map_it i = m_types_map.find(id); |
| if (i == m_types_map.end()) |
| return type_base_sptr(); |
| type_base_sptr result = i->second[0]; |
| return result; |
| } |
| |
| /// Return the vector of types already seen, that are identified by |
| /// a given ID. |
| /// |
| /// Note that for a type to be "identified" by id, the function |
| /// key_type_decl must have been previously called with that type |
| /// and with id. |
| /// |
| /// @param id the id to consider. |
| /// |
| /// @return thevector of types already seen, that are identified by |
| /// a given ID, or 0 if no type has ever been associated with @p id |
| /// before. |
| const vector<type_base_sptr>* |
| get_all_type_decls(const string& id) const |
| { |
| const_types_map_it i = m_types_map.find(id); |
| if (i == m_types_map.end()) |
| return 0; |
| else |
| return &i->second; |
| } |
| |
| /// Return the function template that is identified by a unique ID. |
| /// |
| /// Note that for a function template to be identified by id, the |
| /// function key_fn_tmpl_decl must have been previously called with |
| /// that function template and with id. |
| /// |
| /// @param id the ID to consider. |
| /// |
| /// @return the function template identified by id, or a null |
| /// pointer if no function template has ever been associated with |
| /// id before. |
| shared_ptr<function_tdecl> |
| get_fn_tmpl_decl(const string& id) const |
| { |
| const_fn_tmpl_map_it i = m_fn_tmpl_map.find(id); |
| if (i == m_fn_tmpl_map.end()) |
| return shared_ptr<function_tdecl>(); |
| return i->second; |
| } |
| |
| /// Return the class template that is identified by a unique ID. |
| /// |
| /// Note that for a class template to be identified by id, the |
| /// function key_class_tmpl_decl must have been previously called |
| /// with that class template and with id. |
| /// |
| /// @param id the ID to consider. |
| /// |
| /// @return the class template identified by id, or a null pointer |
| /// if no class template has ever been associated with id before. |
| shared_ptr<class_tdecl> |
| get_class_tmpl_decl(const string& id) const |
| { |
| const_class_tmpl_map_it i = m_class_tmpl_map.find(id); |
| if (i == m_class_tmpl_map.end()) |
| return shared_ptr<class_tdecl>(); |
| return i->second; |
| } |
| |
| /// Return the current lexical scope. |
| scope_decl* |
| get_cur_scope() const |
| { |
| shared_ptr<decl_base> cur_decl = get_cur_decl(); |
| |
| if (dynamic_cast<scope_decl*>(cur_decl.get())) |
| // The current decl is a scope_decl, so it's our lexical scope. |
| return dynamic_pointer_cast<scope_decl>(cur_decl).get(); |
| else if (cur_decl) |
| // The current decl is not a scope_decl, so our lexical scope is |
| // the scope of this decl. |
| return cur_decl->get_scope(); |
| else |
| // We have no scope set. |
| return 0; |
| } |
| |
| decl_base_sptr |
| get_cur_decl() const |
| { |
| if (m_decls_stack.empty()) |
| return shared_ptr<decl_base>(static_cast<decl_base*>(0)); |
| return m_decls_stack.back(); |
| } |
| |
| translation_unit* |
| get_translation_unit() |
| { |
| const global_scope* global = 0; |
| for (deque<shared_ptr<decl_base> >::reverse_iterator i = |
| m_decls_stack.rbegin(); |
| i != m_decls_stack.rend(); |
| ++i) |
| if (decl_base_sptr d = *i) |
| if ((global = get_global_scope(d))) |
| break; |
| |
| if (global) |
| return global->get_translation_unit(); |
| |
| return 0; |
| } |
| |
| /// Test if a given type is from the current translation unit. |
| /// |
| /// @param type the type to consider. |
| /// |
| /// @return true iff the type is from the current translation unit. |
| bool |
| type_is_from_translation_unit(type_base_sptr type) |
| { |
| decl_base_sptr d = get_type_declaration(type); |
| if (d) |
| return (ir::get_translation_unit(d) == get_translation_unit()); |
| else if (function_type_sptr fn_type = is_function_type(type)) |
| return bool(lookup_function_type(fn_type, *get_translation_unit())); |
| else |
| return false; |
| } |
| |
| void |
| push_decl(decl_base_sptr d) |
| { |
| m_decls_stack.push_back(d); |
| } |
| |
| decl_base_sptr |
| pop_decl() |
| { |
| if (m_decls_stack.empty()) |
| return decl_base_sptr(); |
| |
| shared_ptr<decl_base> t = get_cur_decl(); |
| m_decls_stack.pop_back(); |
| return t; |
| } |
| |
| /// Pop all decls until a give scope is popped. |
| /// |
| /// @param scope the scope to pop. |
| /// |
| /// @return true if the scope was popped, false otherwise. Note |
| /// that if the scope wasn't found, it might mean that many other |
| /// decls were popped. |
| bool |
| pop_scope(scope_decl_sptr scope) |
| { |
| decl_base_sptr d; |
| do |
| { |
| d = pop_decl(); |
| scope_decl_sptr s = dynamic_pointer_cast<scope_decl>(d); |
| if (s == scope) |
| break; |
| } |
| while (d); |
| |
| if (!d) |
| return false; |
| |
| return dynamic_pointer_cast<scope_decl>(d) == scope; |
| } |
| |
| /// like @ref pop_scope, but if the scope couldn't be popped, the |
| /// function aborts the execution of the process. |
| /// |
| /// @param scope the scope to pop. |
| void |
| pop_scope_or_abort(scope_decl_sptr scope) |
| {ABG_ASSERT(pop_scope(scope));} |
| |
| void |
| clear_decls_stack() |
| {m_decls_stack.clear();} |
| |
| void |
| clear_type_map() |
| {m_types_map.clear();} |
| |
| /// Clean the vector of types to canonicalize after the translation |
| /// unit has been read. |
| void |
| clear_types_to_canonicalize() |
| {m_types_to_canonicalize.clear();} |
| |
| |
| /// Test if two types are equal, without comparing them structurally. |
| /// |
| /// This either tests that type pointers are equal, or it tests |
| /// their names. This is because it might be two early to compare |
| /// types structurally because we are not necessarily done building |
| /// them yet. |
| /// |
| /// @param t1 the first type to compare. |
| /// |
| /// @param t2 the second type to compare. |
| /// |
| /// @return true iff the types are equal. |
| bool |
| types_equal(type_base_sptr t1, type_base_sptr t2) |
| { |
| if (t1.get() == t2.get()) |
| return true; |
| |
| // We are going to test qualified names only if both types have |
| // already been added to their scope. |
| bool qualified = (get_type_scope(t1) && get_type_scope(t2)); |
| |
| return (get_type_name(t1, qualified) |
| == get_type_name(t2, qualified)); |
| } |
| |
| /// Associate an ID with a type. |
| /// |
| /// @param type the type to associate witht he ID. |
| /// |
| /// @param id the ID to associate to the type. |
| /// |
| /// @return true upon successful completion. |
| bool |
| key_type_decl(shared_ptr<type_base> type, const string& id) |
| { |
| if (!type) |
| return false; |
| |
| m_types_map[id].push_back(type); |
| |
| return true; |
| } |
| |
| /// Associate an ID to a function template. |
| /// |
| /// @param fn_tmpl_decl the function template to consider. |
| /// |
| /// @param id the ID to associate to the function template. |
| /// |
| /// @return true upon successful completion, false otherwise. Note |
| /// that the function returns false if an ID was previously |
| /// associated to the function template. |
| bool |
| key_fn_tmpl_decl(shared_ptr<function_tdecl> fn_tmpl_decl, |
| const string& id) |
| { |
| ABG_ASSERT(fn_tmpl_decl); |
| |
| const_fn_tmpl_map_it i = m_fn_tmpl_map.find(id); |
| if (i != m_fn_tmpl_map.end()) |
| return false; |
| |
| m_fn_tmpl_map[id] = fn_tmpl_decl; |
| return true; |
| } |
| |
| /// Associate an ID to a class template. |
| /// |
| /// @param class_tmpl_decl the class template to consider. |
| /// |
| /// @param id the ID to associate to the class template. |
| /// |
| /// @return true upon successful completion, false otherwise. Note |
| /// that the function returns false if an ID was previously |
| /// associated to the class template. |
| bool |
| key_class_tmpl_decl(shared_ptr<class_tdecl> class_tmpl_decl, |
| const string& id) |
| { |
| ABG_ASSERT(class_tmpl_decl); |
| |
| const_class_tmpl_map_it i = m_class_tmpl_map.find(id); |
| if (i != m_class_tmpl_map.end()) |
| return false; |
| |
| m_class_tmpl_map[id] = class_tmpl_decl; |
| return true; |
| } |
| |
| /// This function must be called on each declaration that is created during |
| /// the parsing. It adds the declaration to the current scope, and updates |
| /// the state of the parsing context accordingly. |
| /// |
| /// @param decl the newly created declaration. |
| void |
| push_decl_to_current_scope(decl_base_sptr decl, |
| bool add_to_current_scope) |
| { |
| ABG_ASSERT(decl); |
| |
| if (add_to_current_scope) |
| add_decl_to_scope(decl, get_cur_scope()); |
| if (!decl->get_translation_unit()) |
| decl->set_translation_unit(get_translation_unit()); |
| ABG_ASSERT(decl->get_translation_unit()); |
| push_decl(decl); |
| } |
| |
| /// This function must be called on each type decl that is created |
| /// during the parsing. It adds the type decl to the current scope |
| /// and associates a unique ID to it. |
| /// |
| /// @param t type_decl |
| /// |
| /// @param id the unique ID to be associated to t |
| /// |
| /// @return true upon successful completion. |
| /// |
| bool |
| push_and_key_type_decl(shared_ptr<type_base> t, const string& id, |
| bool add_to_current_scope) |
| { |
| shared_ptr<decl_base> decl = dynamic_pointer_cast<decl_base>(t); |
| ABG_ASSERT(decl); |
| |
| push_decl_to_current_scope(decl, add_to_current_scope); |
| if (!t->get_translation_unit()) |
| t->set_translation_unit(get_translation_unit()); |
| ABG_ASSERT(t->get_translation_unit()); |
| key_type_decl(t, id); |
| return true; |
| } |
| |
| const corpus_sptr |
| get_corpus() const |
| {return m_corpus;} |
| |
| corpus_sptr |
| get_corpus() |
| {return m_corpus;} |
| |
| void |
| set_corpus(corpus_sptr c) |
| {m_corpus = c;} |
| |
| /// Getter of the current corpus group. |
| /// |
| /// @return the current corpus group.n |
| const corpus_group_sptr& |
| get_corpus_group() const |
| {return m_corpus_group;} |
| |
| /// Getter of the current corpus group. |
| /// |
| /// @return the current corpus group. |
| corpus_group_sptr& |
| get_corpus_group() |
| {return m_corpus_group;} |
| |
| /// Setter of the corpus_group |
| /// |
| /// @param group the new corpus group. |
| void |
| set_corpus_group(const corpus_group_sptr& group) |
| {m_corpus_group = group;} |
| |
| /// Getter for the object that determines if a given declaration |
| /// ought to be put in the set of exported decls of the current |
| /// corpus. |
| /// |
| /// @return the exported decls builder. |
| corpus::exported_decls_builder* |
| get_exported_decls_builder() |
| {return m_exported_decls_builder;} |
| |
| /// Setter for the object that determines if a given declaration |
| /// ought to be put in the set of exported decls of the current |
| /// corpus. |
| /// |
| /// @param d the new exported decls builder. |
| /// |
| /// @return the exported decls builder. |
| void |
| set_exported_decls_builder(corpus::exported_decls_builder* d) |
| {m_exported_decls_builder = d;} |
| |
| /// Getter of the vector of the suppression specifications |
| /// associated to the current read context. |
| /// |
| /// @return the vector of suppression specifications. |
| suppr::suppressions_type& |
| get_suppressions() |
| {return m_supprs;} |
| |
| /// Getter of the vector of the suppression specifications |
| /// associated to the current read context. |
| /// |
| /// @return the vector of suppression specifications. |
| const suppr::suppressions_type& |
| get_suppressions() const |
| {return const_cast<read_context*>(this)->get_suppressions();} |
| |
| /// Test if there are suppression specifications (associated to the |
| /// current corpus) that match a given SONAME or file name. |
| /// |
| /// @param soname the SONAME to consider. |
| /// |
| /// @param the file name to consider. |
| /// |
| /// @return true iff there are suppression specifications (associated to the |
| /// current corpus) that match the SONAME denoted by @p soname or |
| /// the file name denoted by @p filename. |
| bool |
| corpus_is_suppressed_by_soname_or_filename(const string& soname, |
| const string& filename) |
| { |
| using suppr::suppressions_type; |
| using suppr::file_suppression_sptr; |
| using suppr::is_file_suppression; |
| |
| for (suppressions_type::const_iterator s = get_suppressions().begin(); |
| s != get_suppressions().end(); |
| ++s) |
| if (file_suppression_sptr suppr = is_file_suppression(*s)) |
| if (suppr::suppression_matches_soname_or_filename(soname, filename, |
| *suppr)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Add a given function to the set of exported functions of the |
| /// current corpus, if the function satisfies the different |
| /// constraints requirements. |
| /// |
| /// @param fn the function to consider. |
| void |
| maybe_add_fn_to_exported_decls(function_decl* fn) |
| { |
| if (fn) |
| if (corpus::exported_decls_builder* b = get_exported_decls_builder()) |
| b->maybe_add_fn_to_exported_fns(fn); |
| } |
| |
| /// Add a given variable to the set of exported functions of the |
| /// current corpus, if the function satisfies the different |
| /// constraints requirements. |
| /// |
| /// @param var the variable to consider. |
| void |
| maybe_add_var_to_exported_decls(var_decl* var) |
| { |
| if (var && var->get_scope()) |
| if (corpus::exported_decls_builder* b = get_exported_decls_builder()) |
| b->maybe_add_var_to_exported_vars(var); |
| } |
| |
| /// Add a given variable to the set of exported functions of the |
| /// current corpus, if the function satisfies the different |
| /// constraints requirements. |
| /// |
| /// @param var the variable to consider. |
| void |
| maybe_add_var_to_exported_decls(const var_decl_sptr &var) |
| {return maybe_add_var_to_exported_decls(var.get());} |
| |
| /// Clear all the data that must absolutely be cleared at the end of |
| /// the parsing of a translation unit. |
| void |
| clear_per_translation_unit_data() |
| { |
| } |
| |
| /// Clear all the data that must absolutely be cleared at the end of |
| /// the parsing of an ABI corpus. |
| void |
| clear_per_corpus_data() |
| { |
| clear_type_map(); |
| clear_types_to_canonicalize(); |
| clear_xml_node_decl_map(); |
| clear_id_xml_node_map(); |
| clear_decls_stack(); |
| } |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| /// Perform a debugging routine for the "self-comparison" mode. |
| /// |
| /// This is done when this command is on: |
| /// |
| /// "abidw --debug-abidiff". |
| /// |
| /// Consider a type 't' built from an XML element from the abixml |
| /// reader and that has just been canonicalized. |
| /// |
| /// This function checks if the canonical type of 't' is the same as |
| /// the canonical type of the type which was saved into the abixml |
| /// with the same "type-id" as the one of 't'. |
| /// |
| /// Note that at abixml saving time, a debugging file was saved on |
| /// disk to record the mapping of canonical type pointers and their |
| /// type-ids. Right before reading the abixml again, that file was |
| /// read again and the mapping was loaded in the map returned by |
| /// environment::get_type_id_canonical_type_map(). |
| void |
| maybe_check_abixml_canonical_type_stability(type_base_sptr& t) |
| { |
| if (!m_env->self_comparison_debug_is_on() |
| || m_env->get_type_id_canonical_type_map().empty()) |
| return ; |
| |
| if (class_decl_sptr c = is_class_type(t)) |
| if (odr_is_relevant(*c) && c->get_is_declaration_only()) |
| // Declaration-only classes don't have canonical types in |
| // environments where ODR is relevant (like in C++). |
| return; |
| |
| // Let's get the type-id of this type as recorded in the |
| // originating abixml file. |
| string type_id = |
| m_env->get_type_id_from_pointer(reinterpret_cast<uintptr_t>(t.get())); |
| |
| if (!type_id.empty()) |
| { |
| // Now let's get the canonical type that initially led to the |
| // serialization of a type with this type-id, when the abixml |
| // was being serialized. |
| auto j = m_env->get_type_id_canonical_type_map().find(type_id); |
| if (j == m_env->get_type_id_canonical_type_map().end()) |
| std::cerr << "error: no type with type-id: '" |
| << type_id |
| << "' could be read back from the typeid file\n"; |
| else if (j->second |
| != reinterpret_cast<uintptr_t>(t->get_canonical_type().get())) |
| // So thecanonical type of 't' (at abixml de-serialization |
| // time) is different from the canonical type that led to |
| // the serialization of 't' at abixml serialization time. |
| // Report this because it needs further debugging. |
| std::cerr << "error: canonical type for type '" |
| << t->get_pretty_representation(/*internal=*/false, |
| /*qualified=*/false) |
| << "' of type-id '" << type_id |
| << "' changed from '" << std::hex |
| << j->second << "' to '" << std::hex |
| << reinterpret_cast<uintptr_t>(t->get_canonical_type().get()) |
| << std::dec |
| << "'\n"; |
| } |
| } |
| #endif |
| |
| /// Test if a type should be canonicalized early. If so, |
| /// canonicalize it right away. Otherwise, schedule it for late |
| /// canonicalizing; that is, schedule it so that it's going to be |
| /// canonicalized when the translation unit is fully read. |
| /// |
| /// @param t the type to consider for canonicalizing. |
| void |
| maybe_canonicalize_type(type_base_sptr t, |
| bool force_delay = false) |
| { |
| if (!t) |
| return; |
| |
| if (t->get_canonical_type()) |
| return; |
| |
| // If this class has some non-canonicalized sub type, then wait |
| // for the when we've read all the translation unit to |
| // canonicalize all of its non-canonicalized sub types and then we |
| // can canonicalize this one. |
| // |
| // Also, if this is a declaration-only class, wait for the end of |
| // the translation unit reading so that we have its definition and |
| // then we'll use that for canonicalizing it. |
| if (!force_delay |
| && !type_has_non_canonicalized_subtype(t) |
| && !is_class_type(t) |
| && !is_union_type(t) |
| // Below are types that *must* be canonicalized only after |
| // they are added to their context; but then this function |
| // might be called to early, before they are actually added to |
| // their context. |
| // |
| // TODO: make sure this function is called after types are |
| // added to their context, so that we can try to |
| // early-canonicalize some of these types, reducing the size |
| // of the set of types to put on the side, waiting for being |
| // canonicalized. |
| && !is_method_type(t) |
| && !is_reference_type(t) |
| && !is_pointer_type(t) |
| && !is_array_type(t) |
| && !is_qualified_type(t) |
| && !is_typedef(t) |
| && !is_enum_type(t) |
| && !is_function_type(t)) |
| { |
| canonicalize(t); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| maybe_check_abixml_canonical_type_stability(t); |
| #endif |
| } |
| else |
| { |
| // We do not want to try to canonicalize a class type that |
| // hasn't been properly added to its context. |
| if (class_decl_sptr c = is_class_type(t)) |
| ABG_ASSERT(c->get_scope()); |
| |
| schedule_type_for_late_canonicalizing(t); |
| } |
| } |
| |
| /// Schedule a type for being canonicalized after the current |
| /// translation unit is read. |
| /// |
| /// @param t the type to consider for canonicalization. |
| void |
| schedule_type_for_late_canonicalizing(type_base_sptr t) |
| {m_types_to_canonicalize.push_back(t);} |
| |
| /// Perform the canonicalizing of types that ought to be done after |
| /// the current translation unit is read. This function is called |
| /// when the current corpus is fully built. |
| void |
| perform_late_type_canonicalizing() |
| { |
| for (vector<type_base_sptr>::iterator i = m_types_to_canonicalize.begin(); |
| i != m_types_to_canonicalize.end(); |
| ++i) |
| { |
| canonicalize(*i); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| maybe_check_abixml_canonical_type_stability(*i); |
| #endif |
| } |
| } |
| |
| /// 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_sptr& s, |
| const string& fn_name) const |
| { |
| if (!s) |
| return false; |
| return suppression_matches_function_name(*s, fn_name); |
| } |
| |
| /// Tests if a suppression specification can match ABI artifacts |
| /// coming from the ABI corpus being analyzed. |
| /// |
| /// This tests if the suppression matches the soname of and binary |
| /// name of the corpus being analyzed. |
| /// |
| /// @param s the suppression specification to consider. |
| bool |
| suppression_can_match(const suppr::suppression_base& s) const |
| { |
| corpus_sptr corp = get_corpus(); |
| |
| if (!s.priv_->matches_soname(corp->get_soname())) |
| if (s.has_soname_related_property()) |
| // The suppression has some SONAME related properties, but |
| // none of them match the SONAME of the current binary. So |
| // the suppression cannot match the current binary. |
| return false; |
| |
| if (!s.priv_->matches_binary_name(corp->get_path())) |
| if (s.has_file_name_related_property()) |
| // The suppression has some file_name related properties, but |
| // none of them match the file name of the current binary. So |
| // the suppression cannot match the current binary. |
| return false; |
| |
| return true; |
| } |
| |
| /// 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) const |
| { |
| if (!s.get_drops_artifact_from_ir() |
| || !suppression_can_match(s)) |
| return false; |
| |
| return suppr::suppression_matches_function_name(s, fn_name); |
| } |
| |
| /// 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_sptr& s, |
| const string& fn_linkage_name) const |
| { |
| if (!s) |
| return false; |
| return suppression_matches_function_sym_name(*s, fn_linkage_name); |
| } |
| |
| /// 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) const |
| { |
| if (!s.get_drops_artifact_from_ir() |
| || !suppression_can_match(s)) |
| return false; |
| |
| return suppr::suppression_matches_function_sym_name(s, fn_linkage_name); |
| } |
| |
| /// Test whether if a given variable suppression specification |
| /// matches a variable denoted by its name. |
| /// |
| /// @param s the variable suppression specification to consider. |
| /// |
| /// @param var_name the name of the variable to consider. |
| /// |
| /// @return true iff the suppression specification @p s matches the |
| /// variable whose name is @p var_name. |
| bool |
| suppression_matches_variable_name(const suppr::variable_suppression& s, |
| const string& var_name) const |
| { |
| if (!s.get_drops_artifact_from_ir() |
| || !suppression_can_match(s)) |
| return false; |
| |
| return suppr::suppression_matches_variable_name(s, var_name); |
| } |
| |
| /// Test whether if a given variable suppression specification |
| /// matches a variable denoted by its linkage name. |
| /// |
| /// @param s the variable suppression specification to consider. |
| /// |
| /// @param var_linkage_name the linkage name of the variable to consider. |
| /// |
| /// @return true iff variable suppression specification @p s matches |
| /// the variable denoted by linkage name @p var_linkage_name. |
| bool |
| suppression_matches_variable_sym_name(const suppr::variable_suppression& s, |
| const string& var_linkage_name) const |
| { |
| if (!s.get_drops_artifact_from_ir() |
| || !suppression_can_match(s)) |
| return false; |
| |
| return suppr::suppression_matches_variable_sym_name(s, var_linkage_name); |
| } |
| |
| /// Test if a given type suppression specification matches a type |
| /// designated by its name and location. |
| /// |
| /// @param s the suppression specification to consider. |
| /// |
| /// @param type_name the fully qualified type name to consider. |
| /// |
| /// @param type_location the type location to consider. |
| /// |
| /// @return true iff the type suppression specification matches a |
| /// type of a given name and location. |
| bool |
| suppression_matches_type_name_or_location(const suppr::type_suppression& s, |
| const string& type_name, |
| const location& type_location) const |
| { |
| if (!suppression_can_match(s)) |
| return false; |
| |
| return suppr::suppression_matches_type_name_or_location(s, type_name, |
| type_location); |
| } |
| };// end class read_context |
| |
| static int advance_cursor(read_context&); |
| static bool |
| read_translation_unit(read_context&, translation_unit&, xmlNodePtr); |
| static translation_unit_sptr |
| get_or_read_and_add_translation_unit(read_context&, xmlNodePtr); |
| static translation_unit_sptr read_translation_unit_from_input(read_context&); |
| static bool read_symbol_db_from_input(read_context&, |
| string_elf_symbols_map_sptr&, |
| string_elf_symbols_map_sptr&); |
| static bool read_location(const read_context&, xmlNodePtr, location&); |
| static bool read_artificial_location(const read_context&, |
| xmlNodePtr, location&); |
| static bool maybe_set_artificial_location(const read_context&, |
| xmlNodePtr, |
| type_or_decl_base_sptr); |
| static bool read_visibility(xmlNodePtr, decl_base::visibility&); |
| static bool read_binding(xmlNodePtr, decl_base::binding&); |
| static bool read_access(xmlNodePtr, access_specifier&); |
| static bool read_size_and_alignment(xmlNodePtr, size_t&, size_t&); |
| static bool read_static(xmlNodePtr, bool&); |
| static bool read_offset_in_bits(xmlNodePtr, size_t&); |
| static bool read_cdtor_const(xmlNodePtr, bool&, bool&, bool&); |
| static bool read_is_virtual(xmlNodePtr, bool&); |
| static bool read_is_struct(xmlNodePtr, bool&); |
| static bool read_is_anonymous(xmlNodePtr, bool&); |
| static bool read_elf_symbol_type(xmlNodePtr, elf_symbol::type&); |
| static bool read_elf_symbol_binding(xmlNodePtr, elf_symbol::binding&); |
| static bool read_elf_symbol_visibility(xmlNodePtr, |
| elf_symbol::visibility&); |
| |
| static namespace_decl_sptr |
| build_namespace_decl(read_context&, const xmlNodePtr, bool); |
| |
| // <build a c++ class from an instance of xmlNodePtr> |
| // |
| // Note that whenever a new function to build a type is added here, |
| // you should make sure to call it from the build_type function, which |
| // should be the last function of the list of declarated function |
| // below. |
| |
| static elf_symbol_sptr |
| build_elf_symbol(read_context&, const xmlNodePtr, bool); |
| |
| static elf_symbol_sptr |
| build_elf_symbol_from_reference(read_context&, const xmlNodePtr); |
| |
| static string_elf_symbols_map_sptr |
| build_elf_symbol_db(read_context&, const xmlNodePtr, bool); |
| |
| static function_decl::parameter_sptr |
| build_function_parameter (read_context&, const xmlNodePtr); |
| |
| static function_decl_sptr |
| build_function_decl(read_context&, const xmlNodePtr, |
| class_or_union_sptr, bool); |
| |
| static function_decl_sptr |
| build_function_decl_if_not_suppressed(read_context&, const xmlNodePtr, |
| class_or_union_sptr, bool); |
| |
| static bool |
| function_is_suppressed(const read_context& ctxt, |
| xmlNodePtr node); |
| |
| static var_decl_sptr |
| build_var_decl_if_not_suppressed(read_context&, const xmlNodePtr, bool); |
| |
| static var_decl_sptr |
| build_var_decl(read_context&, const xmlNodePtr, bool); |
| |
| static bool |
| variable_is_suppressed(const read_context& ctxt, |
| xmlNodePtr node); |
| |
| static shared_ptr<type_decl> |
| build_type_decl(read_context&, const xmlNodePtr, bool); |
| |
| static qualified_type_def_sptr |
| build_qualified_type_decl(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<pointer_type_def> |
| build_pointer_type_def(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<reference_type_def> |
| build_reference_type_def(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<function_type> |
| build_function_type(read_context&, const xmlNodePtr, bool); |
| |
| static array_type_def::subrange_sptr |
| build_subrange_type(read_context&, const xmlNodePtr); |
| |
| static array_type_def_sptr |
| build_array_type_def(read_context&, const xmlNodePtr, bool); |
| |
| static enum_type_decl_sptr |
| build_enum_type_decl(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<typedef_decl> |
| build_typedef_decl(read_context&, const xmlNodePtr, bool); |
| |
| static class_decl_sptr |
| build_class_decl(read_context&, const xmlNodePtr, bool); |
| |
| static union_decl_sptr |
| build_union_decl(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<function_tdecl> |
| build_function_tdecl(read_context&, const xmlNodePtr, bool); |
| |
| static shared_ptr<class_tdecl> |
| build_class_tdecl(read_context&, const xmlNodePtr, bool); |
| |
| static type_tparameter_sptr |
| build_type_tparameter(read_context&, const xmlNodePtr, |
| unsigned, template_decl_sptr); |
| |
| static type_composition_sptr |
| build_type_composition(read_context&, const xmlNodePtr, |
| unsigned, template_decl_sptr); |
| |
| static non_type_tparameter_sptr |
| build_non_type_tparameter(read_context&, const xmlNodePtr, |
| unsigned, template_decl_sptr); |
| |
| static template_tparameter_sptr |
| build_template_tparameter(read_context&, const xmlNodePtr, |
| unsigned, template_decl_sptr); |
| |
| static template_parameter_sptr |
| build_template_parameter(read_context&, const xmlNodePtr, |
| unsigned, template_decl_sptr); |
| |
| // Please make this build_type function be the last one of the list. |
| // Note that it should call each type-building function above. So |
| // please make sure to update it accordingly, whenever a new |
| // type-building function is added here. |
| static shared_ptr<type_base> |
| build_type(read_context&, const xmlNodePtr, bool); |
| // </build a c++ class from an instance of xmlNodePtr> |
| |
| static type_or_decl_base_sptr handle_element_node(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_type_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_namespace_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_qualified_type_decl(read_context&, |
| xmlNodePtr, bool); |
| static decl_base_sptr handle_pointer_type_def(read_context&, |
| xmlNodePtr, bool); |
| static decl_base_sptr handle_reference_type_def(read_context&, |
| xmlNodePtr, bool); |
| static type_base_sptr handle_function_type(read_context&, |
| xmlNodePtr, bool); |
| static decl_base_sptr handle_array_type_def(read_context&, |
| xmlNodePtr, bool); |
| static decl_base_sptr handle_enum_type_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_typedef_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_var_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_function_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_class_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_union_decl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_function_tdecl(read_context&, xmlNodePtr, bool); |
| static decl_base_sptr handle_class_tdecl(read_context&, xmlNodePtr, bool); |
| |
| /// Get the IR node representing the scope for a given XML node. |
| /// |
| /// This function might trigger the building of a full sub-tree of IR. |
| /// |
| /// @param node the XML for which to return the scope decl. If its |
| /// parent XML node has no corresponding IR node, that IR node is constructed. |
| /// |
| /// @param access the access specifier of the node in its scope, if |
| /// applicable. If the node doesn't have any access specifier |
| /// provided in its scope, then the parameter is set to no_access. |
| /// |
| /// @return the IR node representing the scope of the IR node for the |
| /// XML node given in argument. |
| scope_decl_sptr |
| read_context::get_scope_for_node(xmlNodePtr node, |
| access_specifier& access) |
| { |
| scope_decl_sptr nil, scope; |
| if (!node) |
| return nil; |
| |
| xmlNodePtr parent = node->parent; |
| access = no_access; |
| if (parent |
| && (xmlStrEqual(parent->name, BAD_CAST("data-member")) |
| || xmlStrEqual(parent->name, BAD_CAST("member-type")) |
| || xmlStrEqual(parent->name, BAD_CAST("member-function")) |
| || xmlStrEqual(parent->name, BAD_CAST("member-template")))) |
| { |
| read_access(parent, access); |
| parent = parent->parent; |
| } |
| |
| xml_node_decl_base_sptr_map::const_iterator i = |
| get_xml_node_decl_map().find(parent); |
| if (i == get_xml_node_decl_map().end()) |
| { |
| if (xmlStrEqual(parent->name, BAD_CAST("abi-instr"))) |
| { |
| translation_unit_sptr tu = |
| get_or_read_and_add_translation_unit(*this, parent); |
| return tu->get_global_scope(); |
| } |
| |
| access_specifier a = no_access; |
| scope_decl_sptr parent_scope = get_scope_for_node(parent, a); |
| push_decl(parent_scope); |
| scope = dynamic_pointer_cast<scope_decl> |
| (handle_element_node(*this, parent, /*add_decl_to_scope=*/true)); |
| ABG_ASSERT(scope); |
| pop_scope_or_abort(parent_scope); |
| } |
| else |
| scope = dynamic_pointer_cast<scope_decl>(i->second); |
| |
| return scope; |
| } |
| |
| /// Get the type declaration IR node that matches a given XML type node ID. |
| /// |
| /// If no IR node has been built for this ID, this function builds the |
| /// type declaration IR node and returns it. Subsequent invocation of |
| /// this function with this ID will just return that ID previously returned. |
| /// |
| /// @param id the XML node ID to consider. |
| /// |
| /// @return the type declaration for the ID given in parameter. |
| type_base_sptr |
| read_context::build_or_get_type_decl(const string& id, |
| bool add_decl_to_scope) |
| { |
| type_base_sptr t = get_type_decl(id); |
| |
| if (!t) |
| { |
| xmlNodePtr n = get_xml_node_from_id(id); |
| ABG_ASSERT(n); |
| |
| scope_decl_sptr scope; |
| access_specifier access = no_access; |
| if (add_decl_to_scope) |
| { |
| scope = get_scope_for_node(n, access); |
| /// In some cases, if for instance the scope of 'n' is a |
| /// namespace, get_scope_for_node() can trigger the building |
| /// of what is underneath of the namespace, if that has not |
| /// already been done. So after that, the IR node for 'n' |
| /// might have been built; let's try to see if we are in |
| /// that case. Otherwise, we'll just build the IR node for |
| /// 'n' ourselves. |
| if ((t = get_type_decl(id))) |
| return t; |
| ABG_ASSERT(scope); |
| push_decl(scope); |
| } |
| |
| t = build_type(*this, n, add_decl_to_scope); |
| ABG_ASSERT(t); |
| if (is_member_type(t) && access != no_access) |
| { |
| ABG_ASSERT(add_decl_to_scope); |
| decl_base_sptr d = get_type_declaration(t); |
| ABG_ASSERT(d); |
| set_member_access_specifier(d, access); |
| } |
| map_xml_node_to_decl(n, get_type_declaration(t)); |
| |
| if (add_decl_to_scope) |
| pop_scope_or_abort(scope); |
| |
| maybe_canonicalize_type(t, !add_decl_to_scope); |
| } |
| return t; |
| } |
| |
| /// Moves the xmlTextReader cursor to the next xml node in the input |
| /// document. Return 1 of the parsing was successful, 0 if no input |
| /// xml token is left, or -1 in case of error. |
| /// |
| /// @param ctxt the read context |
| /// |
| static int |
| advance_cursor(read_context& ctxt) |
| { |
| xml::reader_sptr reader = ctxt.get_reader(); |
| return xmlTextReaderRead(reader.get()); |
| } |
| |
| /// Walk an entire XML sub-tree to build a map where the key is the |
| /// the value of the 'id' attribute (for type definitions) and the key |
| /// is the xml node containing the 'id' attribute. |
| /// |
| /// @param ctxt the context of the reader. |
| /// |
| /// @param node the XML sub-tree node to walk. It must be an element |
| /// node. |
| static void |
| walk_xml_node_to_map_type_ids(read_context& ctxt, |
| xmlNodePtr node) |
| { |
| xmlNodePtr n = node; |
| |
| if (!n || n->type != XML_ELEMENT_NODE) |
| return; |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "id")) |
| { |
| string id = CHAR_STR(s); |
| ctxt.map_id_and_node(id, n); |
| } |
| |
| for (n = xmlFirstElementChild(n); n; n = xmlNextElementSibling(n)) |
| walk_xml_node_to_map_type_ids(ctxt, n); |
| } |
| |
| static bool |
| read_translation_unit(read_context& ctxt, translation_unit& tu, xmlNodePtr node) |
| { |
| tu.set_corpus(ctxt.get_corpus().get()); |
| |
| xml::xml_char_sptr addrsize_str = |
| XML_NODE_GET_ATTRIBUTE(node, "address-size"); |
| if (addrsize_str) |
| { |
| char address_size = atoi(reinterpret_cast<char*>(addrsize_str.get())); |
| tu.set_address_size(address_size); |
| } |
| |
| xml::xml_char_sptr path_str = XML_NODE_GET_ATTRIBUTE(node, "path"); |
| if (path_str) |
| tu.set_path(reinterpret_cast<char*>(path_str.get())); |
| |
| xml::xml_char_sptr comp_dir_path_str = |
| XML_NODE_GET_ATTRIBUTE(node, "comp-dir-path"); |
| if (comp_dir_path_str) |
| tu.set_compilation_dir_path(reinterpret_cast<char*> |
| (comp_dir_path_str.get())); |
| |
| xml::xml_char_sptr language_str = XML_NODE_GET_ATTRIBUTE(node, "language"); |
| if (language_str) |
| tu.set_language(string_to_translation_unit_language |
| (reinterpret_cast<char*>(language_str.get()))); |
| |
| |
| // We are at global scope, as we've just seen the top-most |
| // "abi-instr" element. |
| ctxt.push_decl(tu.get_global_scope()); |
| ctxt.map_xml_node_to_decl(node, tu.get_global_scope()); |
| |
| if (ctxt.get_id_xml_node_map().empty() |
| || !ctxt.get_corpus()) |
| walk_xml_node_to_map_type_ids(ctxt, node); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| handle_element_node(ctxt, n, /*add_decl_to_scope=*/true); |
| |
| ctxt.pop_scope_or_abort(tu.get_global_scope()); |
| |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return false; |
| |
| ctxt.clear_per_translation_unit_data(); |
| |
| return true; |
| } |
| |
| /// Read a given xml node representing a tranlsation unit. |
| /// |
| /// If the current corpus already contains a translation unit of the |
| /// path of the xml node we need to look at, then return that |
| /// translation unit. Otherwise, read the translation unit, build a |
| /// @ref translation_unit out of it, add it to the current corpus and |
| /// return it. |
| /// |
| /// @param ctxt the read context. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @return the resulting translation unit. |
| static translation_unit_sptr |
| get_or_read_and_add_translation_unit(read_context& ctxt, xmlNodePtr node) |
| { |
| corpus_sptr corp = ctxt.get_corpus(); |
| |
| translation_unit_sptr tu; |
| string tu_path; |
| xml::xml_char_sptr path_str = XML_NODE_GET_ATTRIBUTE(node, "path"); |
| |
| if (path_str) |
| { |
| tu_path = reinterpret_cast<char*>(path_str.get()); |
| ABG_ASSERT(!tu_path.empty()); |
| |
| if (corp) |
| tu = corp->find_translation_unit(tu_path); |
| |
| if (tu) |
| return tu; |
| } |
| |
| tu.reset(new translation_unit(ctxt.get_environment(), tu_path)); |
| if (corp) |
| corp->add(tu); |
| |
| if (read_translation_unit(ctxt, *tu, node)) |
| return tu; |
| |
| return translation_unit_sptr(); |
| } |
| |
| /// Parse the input XML document containing a translation_unit, |
| /// represented by an 'abi-instr' element node, associated to the current |
| /// context. |
| /// |
| /// @param ctxt the current input context |
| /// |
| /// @return the translation unit resulting from the parsing upon |
| /// successful completion, or nil. |
| static translation_unit_sptr |
| read_translation_unit_from_input(read_context& ctxt) |
| { |
| translation_unit_sptr tu, nil; |
| |
| xmlNodePtr node = ctxt.get_corpus_node(); |
| if (!node) |
| { |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return nil; |
| |
| // The document must start with the abi-instr node. |
| int status = 1; |
| while (status == 1 |
| && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT) |
| status = advance_cursor (ctxt); |
| |
| if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("abi-instr"))) |
| return nil; |
| |
| node = xmlTextReaderExpand(reader.get()); |
| if (!node) |
| return nil; |
| } |
| else |
| { |
| if (!xmlStrEqual(node->name, BAD_CAST("abi-instr"))) |
| return nil; |
| } |
| |
| tu = get_or_read_and_add_translation_unit(ctxt, node); |
| |
| // We are not in the mode where the current corpus node came from a |
| // local invocation of xmlTextReaderExpand. So let's set |
| // ctxt.get_corpus_node to the next child element node of the corpus |
| // that needs to be processed. |
| ctxt.set_corpus_node(xmlNextElementSibling(node)); |
| |
| return tu; |
| } |
| |
| /// Parse the input XML document that may contain function symbol and |
| /// variable symbol databases. |
| /// |
| /// A function symbols database is an XML element named |
| /// "elf-function-symbols" and a variable symbols database is an XML |
| /// element named "elf-variable-symbols." They contains "elf-symbol" |
| /// XML elements. |
| /// |
| /// @param ctxt the read_context to use for the parsing. |
| /// |
| /// @param fn_symdb any resulting function symbol database object, if |
| /// elf-function-symbols was present. |
| /// |
| /// @param var_symdb any resulting variable symbol database object, if |
| /// elf-variable-symbols was present. |
| /// |
| /// @return true upon successful parsing, false otherwise. |
| static bool |
| read_symbol_db_from_input(read_context& ctxt, |
| string_elf_symbols_map_sptr& fn_symdb, |
| string_elf_symbols_map_sptr& var_symdb) |
| { |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return false; |
| |
| if (!ctxt.get_corpus_node()) |
| for (;;) |
| { |
| int status = 1; |
| while (status == 1 |
| && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT) |
| status = advance_cursor (ctxt); |
| |
| if (status != 1) |
| return false; |
| |
| bool has_fn_syms = false, has_var_syms = false; |
| if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("elf-function-symbols"))) |
| has_fn_syms = true; |
| else if (xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("elf-variable-symbols"))) |
| has_var_syms = true; |
| else |
| break; |
| |
| xmlNodePtr node = xmlTextReaderExpand(reader.get()); |
| if (!node) |
| return false; |
| |
| if (has_fn_syms) |
| fn_symdb = build_elf_symbol_db(ctxt, node, true); |
| else if (has_var_syms) |
| var_symdb = build_elf_symbol_db(ctxt, node, false); |
| |
| xmlTextReaderNext(reader.get()); |
| } |
| else |
| for (xmlNodePtr n = ctxt.get_corpus_node(); n; n = xmlNextElementSibling(n)) |
| { |
| bool has_fn_syms = false, has_var_syms = false; |
| if (xmlStrEqual(n->name, BAD_CAST("elf-function-symbols"))) |
| has_fn_syms = true; |
| else if (xmlStrEqual(n->name, BAD_CAST("elf-variable-symbols"))) |
| has_var_syms = true; |
| else |
| { |
| ctxt.set_corpus_node(n); |
| break; |
| } |
| |
| if (has_fn_syms) |
| fn_symdb = build_elf_symbol_db(ctxt, n, true); |
| else if (has_var_syms) |
| var_symdb = build_elf_symbol_db(ctxt, n, false); |
| else |
| break; |
| } |
| |
| return true; |
| } |
| |
| /// From an "elf-needed" XML_ELEMENT node, build a vector of strings |
| /// representing the vector of the dependencies needed by a given |
| /// corpus. |
| /// |
| /// @param node the XML_ELEMENT node of name "elf-needed". |
| /// |
| /// @param needed the output vector of string to populate with the |
| /// vector of dependency names found on the xml node @p node. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| build_needed(xmlNode* node, vector<string>& needed) |
| { |
| if (!node || !xmlStrEqual(node->name,BAD_CAST("elf-needed"))) |
| return false; |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (!xmlStrEqual(n->name, BAD_CAST("dependency"))) |
| continue; |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "name")) |
| xml::xml_char_sptr_to_string(s, name); |
| |
| if (!name.empty()) |
| needed.push_back(name); |
| } |
| |
| return true; |
| } |
| |
| /// Move to the next xml element node and expext it to be named |
| /// "elf-needed". Then read the sub-tree to made of that node and |
| /// extracts a vector of needed dependencies name from it. |
| /// |
| /// @param ctxt the read context used to the xml reading. |
| /// |
| /// @param needed the resulting vector of dependency names. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| read_elf_needed_from_input(read_context& ctxt, |
| vector<string>& needed) |
| { |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return false; |
| |
| xmlNodePtr node = ctxt.get_corpus_node(); |
| |
| if (!node) |
| { |
| int status = 1; |
| while (status == 1 |
| && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT) |
| status = advance_cursor (ctxt); |
| |
| if (status != 1) |
| return false; |
| |
| if (!xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("elf-needed"))) |
| return false; |
| |
| node = xmlTextReaderExpand(reader.get()); |
| if (!node) |
| return false; |
| } |
| else |
| { |
| if (!xmlStrEqual(node->name, BAD_CAST("elf-needed"))) |
| return false; |
| } |
| |
| bool result = build_needed(node, needed); |
| |
| ctxt.set_corpus_node(xmlNextElementSibling(node)); |
| |
| return result; |
| } |
| |
| /// Add suppressions specifications to the set of suppressions to be |
| /// used during the construction of the ABI internal representation |
| /// (the ABI corpus) from ELF and DWARF. |
| /// |
| /// During the construction of the ABI corpus, ABI artifacts that |
| /// match the a given suppression specification are dropped on the |
| /// floor; that is, they are discarded and won't be part of the final |
| /// ABI corpus. This is a way to reduce the amount of data held by |
| /// the final ABI corpus. |
| /// |
| /// Note that the suppression specifications provided to this function |
| /// are only considered during the construction of the ABI corpus. |
| /// For instance, they are not taken into account during e.g |
| /// comparisons of two ABI corpora that might happen later. If you |
| /// want to apply suppression specifications to the comparison (or |
| /// reporting) of ABI corpora please refer to the documentation of the |
| /// @ref diff_context type to learn how to set suppressions that are |
| /// to be used in that context. |
| /// |
| /// @param ctxt the context that is going to be used by functions that |
| /// read types and declarations information to construct and ABI |
| /// corpus. |
| /// |
| /// @param supprs the suppression specifications to be applied during |
| /// the construction of the ABI corpus. |
| void |
| add_read_context_suppressions(read_context& ctxt, |
| const suppr::suppressions_type& supprs) |
| { |
| for (suppr::suppressions_type::const_iterator i = supprs.begin(); |
| i != supprs.end(); |
| ++i) |
| if ((*i)->get_drops_artifact_from_ir()) |
| ctxt.get_suppressions().push_back(*i); |
| } |
| |
| /// Configure the @ref read_context so that types not reachable from |
| /// public interface are taken into account when the abixml file is |
| /// read. |
| /// |
| /// @param ctxt the @read_context to consider. |
| /// |
| /// @param flag if yes, then types not reachable from public interface |
| /// are taken into account when the abixml file is read. |
| void |
| consider_types_not_reachable_from_public_interfaces(read_context& ctxt, |
| bool flag) |
| {ctxt.tracking_non_reachable_types(flag);} |
| |
| /// Read the "version" attribute from the current XML element which is |
| /// supposed to be a corpus or a corpus group and set the format |
| /// version to the corpus object accordingly. |
| /// |
| /// Note that this is a subroutine of read_corpus_from_input and |
| /// read_corpus_group_from_input. |
| /// |
| /// @param reader the XML reader to consider. That reader must be |
| /// set to an XML element representing a corpus or a corpus group. |
| /// |
| /// @param corp output parameter. The corpus object which format |
| /// version string is going to be set according to the value of the |
| /// "version" attribute found on the current XML element. |
| static void |
| handle_version_attribute(xml::reader_sptr& reader, corpus& corp) |
| { |
| string version_string; |
| if (xml_char_sptr s = XML_READER_GET_ATTRIBUTE(reader, "version")) |
| xml::xml_char_sptr_to_string(s, version_string); |
| |
| vector<string> v; |
| if (version_string.empty()) |
| { |
| v.push_back("1"); |
| v.push_back("0"); |
| } |
| else |
| tools_utils::split_string(version_string, ".", v); |
| corp.set_format_major_version_number(v[0]); |
| corp.set_format_minor_version_number(v[1]); |
| } |
| |
| /// Parse the input XML document containing an ABI corpus, represented |
| /// by an 'abi-corpus' element node, associated to the current |
| /// context. |
| /// |
| /// @param ctxt the current input context. |
| /// |
| /// @return the corpus resulting from the parsing |
| corpus_sptr |
| read_corpus_from_input(read_context& ctxt) |
| { |
| corpus_sptr nil; |
| |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return nil; |
| |
| // This is to remember to call xmlTextReaderNext if we ever call |
| // xmlTextReaderExpand. |
| bool call_reader_next = false; |
| |
| xmlNodePtr node = ctxt.get_corpus_node(); |
| if (!node) |
| { |
| // The document must start with the abi-corpus node. |
| int status = 1; |
| while (status == 1 |
| && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT) |
| status = advance_cursor (ctxt); |
| |
| if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("abi-corpus"))) |
| return nil; |
| |
| ctxt.set_corpus(std::make_shared<corpus>(ctxt.get_environment(), "")); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| if (ctxt.get_environment()->self_comparison_debug_is_on()) |
| ctxt.get_environment()-> |
| set_self_comparison_debug_input(ctxt.get_corpus()); |
| #endif |
| |
| if (!ctxt.get_corpus_group()) |
| ctxt.clear_per_corpus_data(); |
| |
| corpus& corp = *ctxt.get_corpus(); |
| ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get()); |
| |
| handle_version_attribute(reader, corp); |
| |
| xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path"); |
| string path; |
| |
| if (path_str) |
| { |
| path = reinterpret_cast<char*>(path_str.get()); |
| corp.set_path(path); |
| } |
| |
| xml::xml_char_sptr architecture_str = |
| XML_READER_GET_ATTRIBUTE(reader, "architecture"); |
| if (architecture_str) |
| corp.set_architecture_name |
| (reinterpret_cast<char*>(architecture_str.get())); |
| |
| xml::xml_char_sptr soname_str = |
| XML_READER_GET_ATTRIBUTE(reader, "soname"); |
| string soname; |
| |
| if (soname_str) |
| { |
| soname = reinterpret_cast<char*>(soname_str.get()); |
| corp.set_soname(soname); |
| } |
| |
| // Apply suppression specifications here to honour: |
| // |
| // [suppress_file] |
| // (soname_regexp |
| // |soname_not_regexp |
| // |file_name_regexp |
| // |file_name_not_regexp) = <soname-or-file-name> |
| if ((!soname.empty() || !path.empty()) |
| && ctxt.corpus_is_suppressed_by_soname_or_filename(soname, path)) |
| return nil; |
| |
| node = xmlTextReaderExpand(reader.get()); |
| if (!node) |
| return nil; |
| |
| call_reader_next = true; |
| } |
| else |
| { |
| ctxt.set_corpus(std::make_shared<corpus>(ctxt.get_environment(), "")); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| if (ctxt.get_environment()->self_comparison_debug_is_on()) |
| ctxt.get_environment()-> |
| set_self_comparison_debug_input(ctxt.get_corpus()); |
| #endif |
| |
| if (!ctxt.get_corpus_group()) |
| ctxt.clear_per_corpus_data(); |
| |
| corpus& corp = *ctxt.get_corpus(); |
| ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get()); |
| |
| xml::xml_char_sptr path_str = XML_NODE_GET_ATTRIBUTE(node, "path"); |
| if (path_str) |
| corp.set_path(reinterpret_cast<char*>(path_str.get())); |
| |
| xml::xml_char_sptr architecture_str = |
| XML_NODE_GET_ATTRIBUTE(node, "architecture"); |
| if (architecture_str) |
| corp.set_architecture_name |
| (reinterpret_cast<char*>(architecture_str.get())); |
| |
| xml::xml_char_sptr soname_str = |
| XML_NODE_GET_ATTRIBUTE(node, "soname"); |
| if (soname_str) |
| corp.set_soname(reinterpret_cast<char*>(soname_str.get())); |
| } |
| |
| // If the corpus element node has children nodes, make |
| // ctxt.get_corpus_node() returns the first child element node of |
| // the corpus element that *needs* to be processed. |
| if (node->children) |
| { |
| xmlNodePtr n = xmlFirstElementChild(node); |
| ctxt.set_corpus_node(n); |
| } |
| |
| corpus& corp = *ctxt.get_corpus(); |
| |
| walk_xml_node_to_map_type_ids(ctxt, node); |
| |
| // Read the needed element |
| vector<string> needed; |
| read_elf_needed_from_input(ctxt, needed); |
| if (!needed.empty()) |
| corp.set_needed(needed); |
| |
| string_elf_symbols_map_sptr fn_sym_db, var_sym_db; |
| |
| // Read the symbol databases. |
| read_symbol_db_from_input(ctxt, fn_sym_db, var_sym_db); |
| // Note that it's possible that both fn_sym_db and var_sym_db are nil, |
| // due to potential suppression specifications. That's fine. |
| corp.set_symtab(symtab_reader::symtab::load(fn_sym_db, var_sym_db)); |
| |
| ctxt.get_environment()->canonicalization_is_done(false); |
| |
| // Read the translation units. |
| while (read_translation_unit_from_input(ctxt)) |
| ; |
| |
| if (ctxt.tracking_non_reachable_types()) |
| { |
| bool is_tracking_non_reachable_types = false; |
| read_tracking_non_reachable_types(node, is_tracking_non_reachable_types); |
| |
| ABG_ASSERT |
| (corp.recording_types_reachable_from_public_interface_supported() |
| == is_tracking_non_reachable_types); |
| } |
| |
| |
| ctxt.perform_late_type_canonicalizing(); |
| |
| ctxt.get_environment()->canonicalization_is_done(true); |
| |
| corp.set_origin(corpus::NATIVE_XML_ORIGIN); |
| |
| if (call_reader_next) |
| { |
| // This is the necessary counter-part of the xmlTextReaderExpand() |
| // call at the beginning of the function. |
| xmlTextReaderNext(reader.get()); |
| // The call above invalidates the xml node returned by |
| // xmlTextReaderExpand, which is can still be accessed via |
| // ctxt.set_corpus_node. |
| ctxt.set_corpus_node(0); |
| } |
| else |
| { |
| node = ctxt.get_corpus_node(); |
| node = xmlNextElementSibling(node); |
| if (!node) |
| { |
| node = ctxt.get_corpus_node(); |
| if (node) |
| node = xmlNextElementSibling(node->parent); |
| } |
| ctxt.set_corpus_node(node); |
| } |
| |
| return ctxt.get_corpus(); |
| } |
| |
| /// Parse the input XML document containing an ABI corpus group, |
| /// represented by an 'abi-corpus-group' element node, associated to |
| /// the current context. |
| /// |
| /// @param ctxt the current input context. |
| /// |
| /// @return the corpus group resulting from the parsing |
| corpus_group_sptr |
| read_corpus_group_from_input(read_context& ctxt) |
| { |
| corpus_group_sptr nil; |
| |
| xml::reader_sptr reader = ctxt.get_reader(); |
| if (!reader) |
| return nil; |
| |
| // The document must start with the abi-corpus-group node. |
| int status = 1; |
| while (status == 1 |
| && XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT) |
| status = advance_cursor (ctxt); |
| |
| if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(), |
| BAD_CAST("abi-corpus-group"))) |
| return nil; |
| |
| if (!ctxt.get_corpus_group()) |
| { |
| corpus_group_sptr g(new corpus_group(ctxt.get_environment(), |
| ctxt.get_path())); |
| ctxt.set_corpus_group(g); |
| } |
| |
| corpus_group_sptr group = ctxt.get_corpus_group(); |
| |
| handle_version_attribute(reader, *group); |
| |
| xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path"); |
| if (path_str) |
| group->set_path(reinterpret_cast<char*>(path_str.get())); |
| |
| xmlNodePtr node = xmlTextReaderExpand(reader.get()); |
| if (!node) |
| return nil; |
| |
| node = xmlFirstElementChild(node); |
| ctxt.set_corpus_node(node); |
| |
| corpus_sptr corp; |
| while ((corp = read_corpus_from_input(ctxt))) |
| ctxt.get_corpus_group()->add_corpus(corp); |
| |
| xmlTextReaderNext(reader.get()); |
| |
| return ctxt.get_corpus_group(); |
| } |
| |
| /// De-serialize an ABI corpus group from an input XML document which |
| /// root node is 'abi-corpus-group'. |
| /// |
| /// @param in the input stream to read the XML document from. |
| /// |
| /// @param env the environment to use. Note that the life time of |
| /// this environment must be greater than the lifetime of the |
| /// resulting corpus as the corpus uses resources that are allocated |
| /// in the environment. |
| /// |
| /// @return the resulting corpus group de-serialized from the parsing. |
| /// This is non-null iff the parsing resulted in a valid corpus group. |
| corpus_group_sptr |
| read_corpus_group_from_native_xml(std::istream* in, |
| environment* env) |
| { |
| read_context_sptr read_ctxt = create_native_xml_read_context(in, env); |
| return read_corpus_group_from_input(*read_ctxt); |
| } |
| |
| /// De-serialize an ABI corpus group from an XML document file which |
| /// root node is 'abi-corpus-group'. |
| /// |
| /// @param path the path to the input file to read the XML document |
| /// from. |
| /// |
| /// @param env the environment to use. Note that the life time of |
| /// this environment must be greater than the lifetime of the |
| /// resulting corpus as the corpus uses resources that are allocated |
| /// in the environment. |
| /// |
| /// @return the resulting corpus group de-serialized from the parsing. |
| /// This is non-null if the parsing successfully resulted in a corpus |
| /// group. |
| corpus_group_sptr |
| read_corpus_group_from_native_xml_file(const string& path, |
| environment* env) |
| { |
| read_context_sptr read_ctxt = create_native_xml_read_context(path, env); |
| corpus_group_sptr group = read_corpus_group_from_input(*read_ctxt); |
| return group; |
| } |
| |
| /// Parse an ABI instrumentation file (in XML format) at a given path. |
| /// |
| /// @param input_file a path to the file containing the xml document |
| /// to parse. |
| /// |
| /// @param env the environment to use. |
| /// |
| /// @return the translation unit resulting from the parsing upon |
| /// successful completion, or nil. |
| translation_unit_sptr |
| read_translation_unit_from_file(const string& input_file, |
| environment* env) |
| { |
| read_context ctxt(xml::new_reader_from_file(input_file), env); |
| translation_unit_sptr tu = read_translation_unit_from_input(ctxt); |
| ctxt.get_environment()->canonicalization_is_done(false); |
| ctxt.perform_late_type_canonicalizing(); |
| ctxt.get_environment()->canonicalization_is_done(true); |
| return tu; |
| } |
| |
| /// Parse an ABI instrumentation file (in XML format) from an |
| /// in-memory buffer. |
| /// |
| /// @param buffer the in-memory buffer containing the xml document to |
| /// parse. |
| /// |
| /// @param env the environment to use. |
| /// |
| /// @return the translation unit resulting from the parsing upon |
| /// successful completion, or nil. |
| translation_unit_sptr |
| read_translation_unit_from_buffer(const string& buffer, |
| environment* env) |
| { |
| read_context ctxt(xml::new_reader_from_buffer(buffer), env); |
| translation_unit_sptr tu = read_translation_unit_from_input(ctxt); |
| ctxt.get_environment()->canonicalization_is_done(false); |
| ctxt.perform_late_type_canonicalizing(); |
| ctxt.get_environment()->canonicalization_is_done(true); |
| return tu; |
| } |
| |
| /// This function is called by @ref read_translation_unit_from_input. |
| /// It handles the current xml element node of the reading context. |
| /// The result of the "handling" is to build the representation of the |
| /// xml node and tied it to the current translation unit. |
| /// |
| /// @param ctxt the current parsing context. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static type_or_decl_base_sptr |
| handle_element_node(read_context& ctxt, xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| type_or_decl_base_sptr decl; |
| if (!node) |
| return decl; |
| |
| ((decl = handle_namespace_decl(ctxt, node, add_to_current_scope)) |
| ||(decl = handle_type_decl(ctxt, node, add_to_current_scope)) |
| ||(decl = handle_qualified_type_decl(ctxt, node, |
| add_to_current_scope)) |
| ||(decl = handle_pointer_type_def(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_reference_type_def(ctxt, node, add_to_current_scope)) |
| || (decl = handle_function_type(ctxt, node, add_to_current_scope)) |
| || (decl = handle_array_type_def(ctxt, node, add_to_current_scope)) |
| || (decl = handle_enum_type_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_typedef_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_var_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_function_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_class_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_union_decl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_function_tdecl(ctxt, node, |
| add_to_current_scope)) |
| || (decl = handle_class_tdecl(ctxt, node, |
| add_to_current_scope))); |
| |
| // If the user wants us to track non-reachable types, then read the |
| // 'is-non-reachable-type' attribute on type elements and record |
| // reachable types accordingly. |
| if (ctxt.tracking_non_reachable_types()) |
| { |
| if (type_base_sptr t = is_type(decl)) |
| { |
| corpus_sptr abi = ctxt.get_corpus(); |
| ABG_ASSERT(abi); |
| bool is_non_reachable_type = false; |
| read_is_non_reachable_type(node, is_non_reachable_type); |
| if (!is_non_reachable_type) |
| abi->record_type_as_reachable_from_public_interfaces(*t); |
| } |
| } |
| |
| return decl; |
| } |
| |
| /// Parses location attributes on an xmlNodePtr. |
| /// |
| ///@param ctxt the current parsing context |
| /// |
| ///@param loc the resulting location. |
| /// |
| /// @return true upon sucessful parsing, false otherwise. |
| static bool |
| read_location(const read_context& ctxt, |
| xmlNodePtr node, |
| location& loc) |
| { |
| string file_path; |
| size_t line = 0, column = 0; |
| |
| if (xml_char_sptr f = xml::build_sptr(xmlGetProp(node, BAD_CAST("filepath")))) |
| file_path = CHAR_STR(f); |
| |
| if (file_path.empty()) |
| return read_artificial_location(ctxt, node, loc); |
| |
| if (xml_char_sptr l = xml::build_sptr(xmlGetProp(node, BAD_CAST("line")))) |
| line = atoi(CHAR_STR(l)); |
| else |
| return read_artificial_location(ctxt, node, loc); |
| |
| if (xml_char_sptr c = xml::build_sptr(xmlGetProp(node, BAD_CAST("column")))) |
| column = atoi(CHAR_STR(c)); |
| |
| read_context& c = const_cast<read_context&>(ctxt); |
| loc = c.get_translation_unit()->get_loc_mgr().create_new_location(file_path, |
| line, |
| column); |
| return true; |
| } |
| |
| /// Parses the artificial location attributes on an xmlNodePtr. |
| /// |
| /// The artificial location is the line number of the xmlNode as well |
| /// as the URI of the node. |
| /// |
| ///@param ctxt the current parsing context |
| /// |
| ///@param loc the resulting location. |
| /// |
| /// @return true upon sucessful parsing, false otherwise. |
| static bool |
| read_artificial_location(const read_context& ctxt, |
| xmlNodePtr node, |
| location& loc) |
| { |
| if (!node) |
| return false; |
| |
| string file_path; |
| size_t line = 0, column = 0; |
| |
| line = node->line; |
| |
| if (node->doc) |
| file_path = reinterpret_cast<const char*>(node->doc->URL); |
| |
| read_context& c = const_cast<read_context&>(ctxt); |
| loc = |
| c.get_translation_unit()->get_loc_mgr().create_new_location(file_path, |
| line, column); |
| loc.set_is_artificial(true); |
| return true; |
| } |
| |
| /// Set the artificial location of a xmlNode to an artifact. |
| /// |
| /// The artificial location is the line number of the xmlNode as well |
| /// as the URI of the node. |
| /// |
| /// The function sets the artificial location only if the artifact |
| /// doesn"t already have one. |
| /// |
| ///@param ctxt the current parsing context |
| /// |
| ///@param node the XML node to consider. |
| /// |
| ///@param artifact the ABI artifact. |
| /// |
| /// @return true iff the location was set on the artifact. |
| static bool |
| maybe_set_artificial_location(const read_context& ctxt, |
| xmlNodePtr node, |
| type_or_decl_base_sptr artefact) |
| { |
| if (artefact && !artefact->has_artificial_location()) |
| { |
| location l; |
| if (read_artificial_location(ctxt, node, l)) |
| { |
| artefact->set_artificial_location(l); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Parse the visibility attribute. |
| /// |
| /// @param node the xml node to read from. |
| /// |
| /// @param vis the resulting visibility. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| read_visibility(xmlNodePtr node, decl_base::visibility& vis) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "visibility")) |
| { |
| string v = CHAR_STR(s); |
| |
| if (v == "default") |
| vis = decl_base::VISIBILITY_DEFAULT; |
| else if (v == "hidden") |
| vis = decl_base::VISIBILITY_HIDDEN; |
| else if (v == "internal") |
| vis = decl_base::VISIBILITY_INTERNAL; |
| else if (v == "protected") |
| vis = decl_base::VISIBILITY_PROTECTED; |
| else |
| vis = decl_base::VISIBILITY_DEFAULT; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Parse the "binding" attribute on the current element. |
| /// |
| /// @param node the xml node to build parse the bind from. |
| /// |
| /// @param bind the resulting binding attribute. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| read_binding(xmlNodePtr node, decl_base::binding& bind) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "binding")) |
| { |
| string b = CHAR_STR(s); |
| |
| if (b == "global") |
| bind = decl_base::BINDING_GLOBAL; |
| else if (b == "local") |
| bind = decl_base::BINDING_LOCAL; |
| else if (b == "weak") |
| bind = decl_base::BINDING_WEAK; |
| else |
| bind = decl_base::BINDING_GLOBAL; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Read the 'access' attribute on the current xml node. |
| /// |
| /// @param node the xml node to consider. |
| /// |
| /// @param access the access attribute. Set iff the function returns true. |
| /// |
| /// @return true upon sucessful completion, false otherwise. |
| static bool |
| read_access(xmlNodePtr node, access_specifier& access) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "access")) |
| { |
| string a = CHAR_STR(s); |
| |
| if (a == "private") |
| access = private_access; |
| else if (a == "protected") |
| access = protected_access; |
| else if (a == "public") |
| access = public_access; |
| else |
| /// If there is an access specifier of an unsupported value, |
| /// we should not assume anything and abort. |
| abort(); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| /// Parse 'size-in-bits' and 'alignment-in-bits' attributes of a given |
| /// xmlNodePtr reprensting an xml element. |
| /// |
| /// @param node the xml element node to consider. |
| /// |
| /// @param size_in_bits the resulting value for the 'size-in-bits' |
| /// attribute. This set only if this function returns true and the if |
| /// the attribute was present on the xml element node. |
| /// |
| /// @param align_in_bits the resulting value for the |
| /// 'alignment-in-bits' attribute. This set only if this function |
| /// returns true and the if the attribute was present on the xml |
| /// element node. |
| /// |
| /// @return true if either one of the two attributes above were set, |
| /// false otherwise. |
| static bool |
| read_size_and_alignment(xmlNodePtr node, |
| size_t& size_in_bits, |
| size_t& align_in_bits) |
| { |
| |
| bool got_something = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits")) |
| { |
| size_in_bits = atoi(CHAR_STR(s)); |
| got_something = true; |
| } |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits")) |
| { |
| align_in_bits = atoi(CHAR_STR(s)); |
| got_something = true; |
| } |
| return got_something; |
| } |
| |
| /// Parse the 'static' attribute of a given xml element node. |
| /// |
| /// @param node the xml element node to consider. |
| /// |
| /// @param is_static the resulting the parsing. Is set if the |
| /// function returns true. |
| /// |
| /// @return true if the xml element node has the 'static' attribute |
| /// set, false otherwise. |
| static bool |
| read_static(xmlNodePtr node, bool& is_static) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "static")) |
| { |
| string b = CHAR_STR(s); |
| is_static = b == "yes"; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Parse the 'layout-offset-in-bits' attribute of a given xml element node. |
| /// |
| /// @param offset_in_bits set to true if the element node contains the |
| /// attribute. |
| /// |
| /// @return true iff the xml element node contains the attribute. |
| static bool |
| read_offset_in_bits(xmlNodePtr node, |
| size_t& offset_in_bits) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "layout-offset-in-bits")) |
| { |
| offset_in_bits = strtoull(CHAR_STR(s), 0, 0); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Parse the 'constructor', 'destructor' and 'const' attribute of a |
| /// given xml node. |
| /// |
| /// @param is_constructor the resulting value of the parsing of the |
| /// 'constructor' attribute. Is set if the xml node contains the |
| /// attribute and if the function returns true. |
| /// |
| /// @param is_destructor the resulting value of the parsing of the |
| /// 'destructor' attribute. Is set if the xml node contains the |
| /// attribute and if the function returns true. |
| /// |
| /// @param is_const the resulting value of the parsing of the 'const' |
| /// attribute. Is set if the xml node contains the attribute and if |
| /// the function returns true. |
| /// |
| /// @return true if at least of the attributes above is set, false |
| /// otherwise. |
| /// |
| /// Note that callers of this function should initialize |
| /// is_constructor, is_destructor and is_const prior to passing them |
| /// to this function. |
| static bool |
| read_cdtor_const(xmlNodePtr node, |
| bool& is_constructor, |
| bool& is_destructor, |
| bool& is_const) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "constructor")) |
| { |
| string b = CHAR_STR(s); |
| if (b == "yes") |
| is_constructor = true; |
| else |
| is_constructor = false; |
| |
| return true; |
| } |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "destructor")) |
| { |
| string b = CHAR_STR(s); |
| if (b == "yes") |
| is_destructor = true; |
| else |
| is_destructor = false; |
| |
| return true; |
| } |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "const")) |
| { |
| string b = CHAR_STR(s); |
| if (b == "yes") |
| is_const = true; |
| else |
| is_const = false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Read the "is-declaration-only" attribute of the current xml node. |
| /// |
| /// @param node the xml node to consider. |
| /// |
| /// @param is_decl_only is set to true iff the "is-declaration-only" attribute |
| /// is present and set to "yes". |
| /// |
| /// @return true iff the is_decl_only attribute was set. |
| static bool |
| read_is_declaration_only(xmlNodePtr node, bool& is_decl_only) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only")) |
| { |
| string str = CHAR_STR(s); |
| if (str == "yes") |
| is_decl_only = true; |
| else |
| is_decl_only = false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the "is-artificial" attribute of the current XML node. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @param is_artificial this output parameter is set to true iff the |
| /// "is-artificial" parameter is present and set to 'yes'. |
| /// |
| /// @return true iff the "is-artificial" parameter was present on the |
| /// XML node. |
| static bool |
| read_is_artificial(xmlNodePtr node, bool& is_artificial) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-artificial")) |
| { |
| string is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : ""; |
| is_artificial = is_artificial_str == "yes"; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'tracking-non-reachable-types' attribute on the current |
| /// XML element. |
| /// |
| /// @param node the current XML element. |
| /// |
| /// @param tracking_non_reachable_types output parameter. This is set |
| /// to true iff the 'tracking-non-reachable-types' attribute is |
| /// present on the current XML node and set to 'yes'. In that case, |
| /// the function returns true. |
| /// |
| /// @return true iff the 'tracking-non-reachable-types' attribute is |
| /// present on the current XML node and set to 'yes'. |
| static bool |
| read_tracking_non_reachable_types(xmlNodePtr node, |
| bool& tracking_non_reachable_types) |
| { |
| if (xml_char_sptr s = |
| XML_NODE_GET_ATTRIBUTE(node, "tracking-non-reachable-types")) |
| { |
| string tracking_non_reachable_types_str = CHAR_STR(s) ? CHAR_STR(s) : ""; |
| tracking_non_reachable_types = |
| (tracking_non_reachable_types_str == "yes") |
| ? true |
| : false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'is-non-reachable' attribute on the current XML element. |
| /// |
| /// @param node the current XML element. |
| /// |
| /// @param is_non_reachable_type output parameter. This is set to true |
| /// iff the 'is-non-reachable' attribute is present on the current XML |
| /// element with a value se to 'yes'. |
| /// |
| /// @return true iff the 'is-non-reachable' attribute is present on |
| /// the current XML element with a value se to 'yes'. |
| static bool |
| read_is_non_reachable_type(xmlNodePtr node, bool& is_non_reachable_type) |
| { |
| if (xml_char_sptr s = |
| XML_NODE_GET_ATTRIBUTE(node, "is-non-reachable")) |
| { |
| string is_non_reachable_type_str = CHAR_STR(s) ? CHAR_STR(s) : ""; |
| is_non_reachable_type = |
| (is_non_reachable_type_str == "yes") |
| ? true |
| : false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the "is-virtual" attribute of the current xml node. |
| /// |
| /// @param node the xml node to read the attribute from |
| /// |
| /// @param is_virtual is set to true iff the "is-virtual" attribute is |
| /// present and set to "yes". |
| /// |
| /// @return true iff the is-virtual attribute is present. |
| static bool |
| read_is_virtual(xmlNodePtr node, bool& is_virtual) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-virtual")) |
| { |
| string str = CHAR_STR(s); |
| if (str == "yes") |
| is_virtual = true; |
| else |
| is_virtual = false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'is-struct' attribute. |
| /// |
| /// @param node the xml node to read the attribute from. |
| /// |
| /// @param is_struct is set to true iff the "is-struct" attribute is |
| /// present and set to "yes". |
| /// |
| /// @return true iff the "is-struct" attribute is present. |
| static bool |
| read_is_struct(xmlNodePtr node, bool& is_struct) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-struct")) |
| { |
| string str = CHAR_STR(s); |
| if (str == "yes") |
| is_struct = true; |
| else |
| is_struct = false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'is-anonymous' attribute. |
| /// |
| /// @param node the xml node to read the attribute from. |
| /// |
| /// @param is_anonymous is set to true iff the "is-anonymous" is present |
| /// and set to "yes". |
| /// |
| /// @return true iff the "is-anonymous" attribute is present. |
| static bool |
| read_is_anonymous(xmlNodePtr node, bool& is_anonymous) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-anonymous")) |
| { |
| string str = CHAR_STR(s); |
| is_anonymous = (str == "yes"); |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'type' attribute of the 'elf-symbol' element. |
| /// |
| /// @param node the XML node to read the attribute from. |
| /// |
| /// @param t the resulting elf_symbol::type. |
| /// |
| /// @return true iff the function completed successfully. |
| static bool |
| read_elf_symbol_type(xmlNodePtr node, elf_symbol::type& t) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type")) |
| { |
| string str; |
| xml::xml_char_sptr_to_string(s, str); |
| if (!string_to_elf_symbol_type(str, t)) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'binding' attribute of the of the 'elf-symbol' element. |
| /// |
| /// @param node the XML node to read the attribute from. |
| /// |
| /// @param b the XML the resulting elf_symbol::binding. |
| /// |
| /// @return true iff the function completed successfully. |
| static bool |
| read_elf_symbol_binding(xmlNodePtr node, elf_symbol::binding& b) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "binding")) |
| { |
| string str; |
| xml::xml_char_sptr_to_string(s, str); |
| if (!string_to_elf_symbol_binding(str, b)) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Read the 'visibility' attribute of the of the 'elf-symbol' |
| /// element. |
| /// |
| /// @param node the XML node to read the attribute from. |
| /// |
| /// @param b the XML the resulting elf_symbol::visibility. |
| /// |
| /// @return true iff the function completed successfully. |
| static bool |
| read_elf_symbol_visibility(xmlNodePtr node, elf_symbol::visibility& v) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "visibility")) |
| { |
| string str; |
| xml::xml_char_sptr_to_string(s, str); |
| if (!string_to_elf_symbol_visibility(str, v)) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| /// Read the value of the 'id' attribute from a given XML node. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @param type_id the string to set the 'id' to. |
| /// |
| /// @return true iff @p type_id was successfully set. |
| static bool |
| read_type_id_string(xmlNodePtr node, string& type_id) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| { |
| type_id = CHAR_STR(s); |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| /// Build a @ref namespace_decl from an XML element node which name is |
| /// "namespace-decl". Note that this function recursively reads the |
| /// content of the namespace and builds the proper IR nodes |
| /// accordingly. |
| /// |
| /// @param ctxt the read context to use. |
| /// |
| /// @param node the XML node to consider. It must constain the |
| /// content of the namespace, that is, children XML nodes representing |
| /// what is inside the namespace, unless the namespace is empty. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting |
| /// namespace_decl is added to the IR being currently built. |
| /// |
| /// @return a pointer to the the resulting @ref namespace_decl. |
| static namespace_decl_sptr |
| build_namespace_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| namespace_decl_sptr nil; |
| if (!node || !xmlStrEqual(node->name, BAD_CAST("namespace-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| namespace_decl_sptr result = dynamic_pointer_cast<namespace_decl>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| const environment* env = ctxt.get_environment(); |
| namespace_decl_sptr decl(new namespace_decl(env, name, loc)); |
| maybe_set_artificial_location(ctxt, node, decl); |
| ctxt.push_decl_to_current_scope(decl, add_to_current_scope); |
| ctxt.map_xml_node_to_decl(node, decl); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| handle_element_node(ctxt, n, /*add_to_current_scope=*/true); |
| |
| ctxt.pop_scope_or_abort(decl); |
| |
| return decl; |
| } |
| |
| /// Build an instance of @ref elf_symbol from an XML element node |
| /// which name is 'elf-symbol'. |
| /// |
| /// @param ctxt the context used for reading the XML input. |
| /// |
| /// @param node the XML node to read. |
| /// |
| /// @param drop_if_suppressed if the elf symbol was suppressed by a |
| /// suppression specification then do not build it. |
| /// |
| /// @return the @ref elf_symbol built, or nil if it couldn't be built. |
| static elf_symbol_sptr |
| build_elf_symbol(read_context& ctxt, const xmlNodePtr node, |
| bool drop_if_suppressed) |
| { |
| elf_symbol_sptr nil; |
| |
| if (!node |
| || node->type != XML_ELEMENT_NODE |
| || !xmlStrEqual(node->name, BAD_CAST("elf-symbol"))) |
| return nil; |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| xml::xml_char_sptr_to_string(s, name); |
| |
| size_t size = 0; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size")) |
| size = strtol(CHAR_STR(s), NULL, 0); |
| |
| bool is_defined = true; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-defined")) |
| { |
| string value; |
| xml::xml_char_sptr_to_string(s, value); |
| if (value == "true" || value == "yes") |
| is_defined = true; |
| else |
| is_defined = false; |
| } |
| |
| bool is_common = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-common")) |
| { |
| string value; |
| xml::xml_char_sptr_to_string(s, value); |
| if (value == "true" || value == "yes") |
| is_common = true; |
| else |
| is_common = false; |
| } |
| |
| string version_string; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "version")) |
| xml::xml_char_sptr_to_string(s, version_string); |
| |
| bool is_default_version = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-default-version")) |
| { |
| string value; |
| xml::xml_char_sptr_to_string(s, value); |
| if (value == "true" || value == "yes") |
| is_default_version = true; |
| } |
| |
| uint64_t crc = 0; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "crc")) |
| crc = strtoull(CHAR_STR(s), NULL, 0); |
| |
| elf_symbol::type type = elf_symbol::NOTYPE_TYPE; |
| read_elf_symbol_type(node, type); |
| |
| elf_symbol::binding binding = elf_symbol::GLOBAL_BINDING; |
| read_elf_symbol_binding(node, binding); |
| |
| elf_symbol::visibility visibility = elf_symbol::DEFAULT_VISIBILITY; |
| read_elf_symbol_visibility(node, visibility); |
| |
| elf_symbol::version version(version_string, is_default_version); |
| |
| const bool is_suppressed = suppr::is_elf_symbol_suppressed(ctxt, name, type); |
| if (drop_if_suppressed && is_suppressed) |
| return elf_symbol_sptr(); |
| |
| const environment* env = ctxt.get_environment(); |
| elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0, |
| size, name, type, binding, |
| is_defined, is_common, |
| version, visibility); |
| |
| e->set_is_suppressed(is_suppressed); |
| |
| if (crc != 0) |
| e->set_crc(crc); |
| |
| return e; |
| } |
| |
| /// Build and instance of elf_symbol from an XML attribute named |
| /// 'elf-symbol-id' which value is the ID of a symbol that should |
| /// present in the symbol db of the corpus associated to the current |
| /// context. |
| /// |
| /// @param ctxt the current context to consider. |
| /// |
| /// @param node the xml element node to consider. |
| /// |
| /// @param function_symbol is true if we should look for a function |
| /// symbol, is false if we should look for a variable symbol. |
| /// |
| /// @return a shared pointer the resutling elf_symbol. |
| static elf_symbol_sptr |
| build_elf_symbol_from_reference(read_context& ctxt, const xmlNodePtr node) |
| { |
| elf_symbol_sptr nil; |
| |
| if (!node) |
| return nil; |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "elf-symbol-id")) |
| { |
| string sym_id; |
| xml::xml_char_sptr_to_string(s, sym_id); |
| if (sym_id.empty()) |
| return nil; |
| |
| string name, ver; |
| elf_symbol::get_name_and_version_from_id(sym_id, name, ver); |
| if (name.empty()) |
| return nil; |
| |
| const elf_symbols& symbols = |
| ctxt.get_corpus()->get_symtab()->lookup_symbol(name); |
| |
| for (const auto& symbol : symbols) |
| if (symbol->get_id_string() == sym_id) |
| return symbol; |
| } |
| |
| return nil; |
| } |
| |
| /// Build an instance of string_elf_symbols_map_type from an XML |
| /// element representing either a function symbols data base, or a |
| /// variable symbols database. |
| /// |
| /// @param ctxt the context to take in account. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @param function_syms true if we should look for a function symbols |
| /// data base, false if we should look for a variable symbols data |
| /// base. |
| static string_elf_symbols_map_sptr |
| build_elf_symbol_db(read_context& ctxt, |
| const xmlNodePtr node, |
| bool function_syms) |
| { |
| string_elf_symbols_map_sptr map, nil; |
| string_elf_symbol_sptr_map_type id_sym_map; |
| |
| if (!node) |
| return nil; |
| |
| if (function_syms |
| && !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols"))) |
| return nil; |
| |
| if (!function_syms |
| && !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols"))) |
| return nil; |
| |
| ctxt.set_corpus_node(node); |
| |
| typedef std::unordered_map<xmlNodePtr, elf_symbol_sptr> |
| xml_node_ptr_elf_symbol_sptr_map_type; |
| xml_node_ptr_elf_symbol_sptr_map_type xml_node_ptr_elf_symbol_map; |
| |
| elf_symbol_sptr sym; |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| if ((sym = build_elf_symbol(ctxt, n, /*drop_if_suppress=*/false))) |
| { |
| id_sym_map[sym->get_id_string()] = sym; |
| xml_node_ptr_elf_symbol_map[n] = sym; |
| } |
| |
| if (id_sym_map.empty()) |
| return nil; |
| |
| map.reset(new string_elf_symbols_map_type); |
| string_elf_symbols_map_type::iterator it; |
| for (string_elf_symbol_sptr_map_type::const_iterator i = id_sym_map.begin(); |
| i != id_sym_map.end(); |
| ++i) |
| (*map)[i->second->get_name()].push_back(i->second); |
| |
| // Now build the alias relations |
| for (xml_node_ptr_elf_symbol_sptr_map_type::const_iterator x = |
| xml_node_ptr_elf_symbol_map.begin(); |
| x != xml_node_ptr_elf_symbol_map.end(); |
| ++x) |
| { |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(x->first, "alias")) |
| { |
| string alias_id = CHAR_STR(s); |
| |
| // Symbol aliases can be multiple separated by comma(,), split them |
| std::vector<std::string> elems; |
| std::stringstream aliases(alias_id); |
| std::string item; |
| while (std::getline(aliases, item, ',')) |
| elems.push_back(item); |
| for (std::vector<string>::iterator alias = elems.begin(); |
| alias != elems.end(); ++alias) |
| { |
| string_elf_symbol_sptr_map_type::const_iterator i = |
| id_sym_map.find(*alias); |
| ABG_ASSERT(i != id_sym_map.end()); |
| ABG_ASSERT(i->second->is_main_symbol()); |
| |
| x->second->get_main_symbol()->add_alias(i->second); |
| } |
| } |
| } |
| |
| return map; |
| } |
| |
| /// Build a function parameter from a 'parameter' xml element node. |
| /// |
| /// @param ctxt the contexte of the xml parsing. |
| /// |
| /// @param node the xml 'parameter' element node to de-serialize from. |
| static shared_ptr<function_decl::parameter> |
| build_function_parameter(read_context& ctxt, const xmlNodePtr node) |
| { |
| shared_ptr<function_decl::parameter> nil; |
| |
| if (!node || !xmlStrEqual(node->name, BAD_CAST("parameter"))) |
| return nil; |
| |
| ABG_ASSERT(ctxt.get_environment()); |
| |
| bool is_variadic = false; |
| string is_variadic_str; |
| if (xml_char_sptr s = |
| xml::build_sptr(xmlGetProp(node, BAD_CAST("is-variadic")))) |
| { |
| is_variadic_str = CHAR_STR(s) ? CHAR_STR(s) : ""; |
| is_variadic = is_variadic_str == "yes"; |
| } |
| |
| bool is_artificial = false; |
| read_is_artificial(node, is_artificial); |
| |
| string type_id; |
| if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("type-id")))) |
| type_id = CHAR_STR(a); |
| |
| type_base_sptr type; |
| if (is_variadic) |
| type = ctxt.get_environment()->get_variadic_parameter_type(); |
| else |
| { |
| ABG_ASSERT(!type_id.empty()); |
| type = ctxt.build_or_get_type_decl(type_id, true); |
| } |
| ABG_ASSERT(type); |
| ABG_ASSERT(type->get_environment() == ctxt.get_environment()); |
| |
| string name; |
| if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("name")))) |
| name = CHAR_STR(a); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| function_decl::parameter_sptr p |
| (new function_decl::parameter(type, name, loc, |
| is_variadic, is_artificial)); |
| |
| return p; |
| } |
| |
| /// Build a function_decl from a 'function-decl' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the function_decl from. |
| /// |
| /// @param as_method_decl if this is set to a class_decl pointer, it |
| /// means that the 'function-decl' xml node should be parsed as a |
| /// method_decl. The class_decl pointer is the class decl to which |
| /// the resulting method_decl is a member function of. The resulting |
| /// shared_ptr<function_decl> that is returned is then really a |
| /// shared_ptr<method_decl>. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly created function_decl upon successful |
| /// completion, a null pointer otherwise. |
| static function_decl_sptr |
| build_function_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| class_or_union_sptr as_method_decl, |
| bool add_to_current_scope) |
| { |
| function_decl_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("function-decl"))) |
| return nil; |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string mangled_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name")) |
| mangled_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string inline_prop; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "declared-inline")) |
| inline_prop = CHAR_STR(s); |
| bool declared_inline = inline_prop == "yes"; |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| decl_base::binding bind = decl_base::BINDING_NONE; |
| read_binding(node, bind); |
| |
| size_t size = ctxt.get_translation_unit()->get_address_size(), align = 0; |
| read_size_and_alignment(node, size, align); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| std::vector<function_decl::parameter_sptr> parms; |
| type_base_sptr return_type = env->get_void_type(); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n ; |
| n = xmlNextElementSibling(n)) |
| { |
| if (xmlStrEqual(n->name, BAD_CAST("parameter"))) |
| { |
| if (function_decl::parameter_sptr p = |
| build_function_parameter(ctxt, n)) |
| parms.push_back(p); |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("return"))) |
| { |
| string type_id; |
| if (xml_char_sptr s = |
| xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id")))) |
| type_id = CHAR_STR(s); |
| if (!type_id.empty()) |
| return_type = ctxt.build_or_get_type_decl(type_id, true); |
| } |
| } |
| |
| function_type_sptr fn_type(as_method_decl |
| ? new method_type(return_type, as_method_decl, |
| parms, /*is_const=*/false, |
| size, align) |
| : new function_type(return_type, |
| parms, size, align)); |
| |
| ABG_ASSERT(fn_type); |
| |
| fn_type->set_is_artificial(true); |
| |
| function_decl_sptr fn_decl(as_method_decl |
| ? new method_decl (name, fn_type, |
| declared_inline, loc, |
| mangled_name, vis, bind) |
| : new function_decl(name, fn_type, |
| declared_inline, loc, |
| mangled_name, vis, |
| bind)); |
| |
| maybe_set_artificial_location(ctxt, node, fn_decl); |
| ctxt.push_decl_to_current_scope(fn_decl, add_to_current_scope); |
| |
| elf_symbol_sptr sym = build_elf_symbol_from_reference(ctxt, node); |
| if (sym) |
| fn_decl->set_symbol(sym); |
| |
| if (fn_decl->get_symbol() && fn_decl->get_symbol()->is_public()) |
| fn_decl->set_is_in_public_symbol_table(true); |
| |
| ctxt.get_translation_unit()->bind_function_type_life_time(fn_type); |
| |
| ctxt.maybe_canonicalize_type(fn_type, !add_to_current_scope); |
| |
| ctxt.maybe_add_fn_to_exported_decls(fn_decl.get()); |
| |
| return fn_decl; |
| } |
| |
| /// Build a function_decl from a 'function-decl' xml node if it's not |
| /// been suppressed by a suppression specification that is in the |
| /// context. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the function_decl from. |
| /// |
| /// @param as_method_decl if this is set to a class_or_union pointer, |
| /// it means that the 'function-decl' xml node should be parsed as a |
| /// method_decl. The class_or_union pointer is the class or union the |
| /// resulting method_decl is a member function of. The resulting @ref |
| /// function_decl_sptr that is returned is then really a @ref |
| /// method_decl_sptr. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly created function_decl upon successful |
| /// completion. If the function was suppressed by a suppression |
| /// specification then returns nil. |
| static function_decl_sptr |
| build_function_decl_if_not_suppressed(read_context& ctxt, |
| const xmlNodePtr node, |
| class_or_union_sptr as_method_decl, |
| bool add_to_current_scope) |
| { |
| function_decl_sptr fn; |
| |
| if (function_is_suppressed(ctxt, node)) |
| // The function was suppressed by at least one suppression |
| // specification associated to the current read context. So |
| // don't build any IR for it. |
| ; |
| else |
| fn = build_function_decl(ctxt, node, as_method_decl, |
| add_to_current_scope); |
| return fn; |
| } |
| |
| /// Test if a given function denoted by its name and linkage name is |
| /// suppressed by any of the suppression specifications associated to |
| /// a given context of native xml reading. |
| /// |
| /// @param ctxt the native xml reading context of interest. |
| /// |
| /// @param note the XML node that represents the fucntion. |
| /// match. |
| /// |
| /// @return true iff at least one function specification matches the |
| /// function denoted by the node @p node. |
| static bool |
| function_is_suppressed(const read_context& ctxt, xmlNodePtr node) |
| { |
| string fname; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| fname = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string flinkage_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name")) |
| flinkage_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| scope_decl* scope = ctxt.get_cur_scope(); |
| |
| string qualified_name = build_qualified_name(scope, fname); |
| |
| return suppr::function_is_suppressed(ctxt, qualified_name, flinkage_name); |
| } |
| |
| /// Test if a type denoted by its name, context and location is |
| /// suppressed by the suppression specifications that are associated |
| /// to a given read context. |
| /// |
| /// @param ctxt the read context to consider. |
| /// |
| /// @param note the XML node that represents the type. |
| /// |
| /// @return true iff the type designated by @p node is suppressed by |
| /// at least of suppression specifications associated to the current |
| /// read context. |
| static bool |
| type_is_suppressed(const read_context& ctxt, xmlNodePtr node) |
| { |
| string type_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| type_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location type_location; |
| read_location(ctxt, node, type_location); |
| |
| scope_decl* scope = ctxt.get_cur_scope(); |
| |
| string qualified_name = build_qualified_name(scope, type_name); |
| |
| bool type_is_private = false; |
| return suppr::type_is_suppressed(ctxt, qualified_name, type_location, |
| type_is_private, |
| /*require_drop_property=*/true); |
| } |
| |
| /// Build a @ref var_decl out of a an XML node that describes it iff |
| /// the variable denoted by the XML node is not suppressed by a |
| /// suppression specification associated to the current read context. |
| /// |
| /// @param ctxt the read context to use. |
| /// |
| /// @param node the XML node for the variable to consider. |
| /// |
| /// @parm add_to_current_scope whether to add the built @ref var_decl |
| /// to the current scope or not. |
| /// |
| /// @return true iff the @ref var_decl was built. |
| static var_decl_sptr |
| build_var_decl_if_not_suppressed(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| var_decl_sptr var; |
| if (!variable_is_suppressed(ctxt, node)) |
| var = build_var_decl(ctxt, node, add_to_current_scope); |
| return var; |
| } |
| |
| /// Test if a variable denoted by its XML node is suppressed by a |
| /// suppression specification that is present in a given read context. |
| /// |
| /// @param ctxt the read context to consider. |
| /// |
| /// @param node the XML node of the variable to consider. |
| /// |
| /// @return true iff the variable denoted by @p node is suppressed. |
| static bool |
| variable_is_suppressed(const read_context& ctxt, xmlNodePtr node) |
| { |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string linkage_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name")) |
| linkage_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| scope_decl* scope = ctxt.get_cur_scope(); |
| |
| string qualified_name = build_qualified_name(scope, name); |
| |
| return suppr::variable_is_suppressed(ctxt, qualified_name, linkage_name); |
| } |
| |
| /// Test if a variable in a particular scope is suppressed by a |
| /// suppression specification that is present in a given read context. |
| /// |
| /// @parm ctxt the read context to consider. |
| /// |
| /// @param scope the scope of the variable to consider. |
| /// |
| /// @param v the variable to consider. |
| /// |
| /// @return true iff the variable @p v is suppressed. |
| static bool |
| variable_is_suppressed(const read_context& ctxt, |
| const scope_decl* scope, |
| const var_decl& v) |
| { |
| string qualified_name = build_qualified_name(scope, v.get_name()); |
| return suppr::variable_is_suppressed(ctxt, qualified_name, |
| v.get_linkage_name()); |
| } |
| |
| /// Build pointer to var_decl from a 'var-decl' xml Node |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the var_decl from. |
| /// |
| /// @return a pointer to a newly built var_decl upon successful |
| /// completion, a null pointer otherwise. |
| static shared_ptr<var_decl> |
| build_var_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<var_decl> nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("var-decl"))) |
| return nil; |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| type_base_sptr underlying_type = ctxt.build_or_get_type_decl(type_id, |
| true); |
| ABG_ASSERT(underlying_type); |
| |
| string mangled_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name")) |
| mangled_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| decl_base::binding bind = decl_base::BINDING_NONE; |
| read_binding(node, bind); |
| |
| location locus; |
| read_location(ctxt, node, locus); |
| |
| var_decl_sptr decl(new var_decl(name, underlying_type, |
| locus, mangled_name, |
| vis, bind)); |
| maybe_set_artificial_location(ctxt, node, decl); |
| |
| elf_symbol_sptr sym = build_elf_symbol_from_reference(ctxt, node); |
| if (sym) |
| decl->set_symbol(sym); |
| |
| ctxt.push_decl_to_current_scope(decl, add_to_current_scope); |
| |
| if (decl->get_symbol() && decl->get_symbol()->is_public()) |
| decl->set_is_in_public_symbol_table(true); |
| |
| return decl; |
| } |
| |
| /// Build a type_decl from a "type-decl" XML Node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the XML node to build the type_decl from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to type_decl upon successful completion, a null |
| /// pointer otherwise. |
| static shared_ptr<type_decl> |
| build_type_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<type_decl> nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("type-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| type_decl_sptr result = dynamic_pointer_cast<type_decl>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| size_t size_in_bits= 0; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits")) |
| size_in_bits = atoi(CHAR_STR(s)); |
| |
| size_t alignment_in_bits = 0; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits")) |
| alignment_in_bits = atoi(CHAR_STR(s)); |
| |
| bool is_decl_only = false; |
| read_is_declaration_only(node, is_decl_only); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| bool is_anonymous = false; |
| read_is_anonymous(node, is_anonymous); |
| |
| if (type_base_sptr d = ctxt.get_type_decl(id)) |
| { |
| // I've seen instances of DSOs where a type_decl would appear |
| // several times. Hugh. |
| type_decl_sptr ty = dynamic_pointer_cast<type_decl>(d); |
| ABG_ASSERT(ty); |
| ABG_ASSERT(name == ty->get_name()); |
| ABG_ASSERT(ty->get_size_in_bits() == size_in_bits); |
| ABG_ASSERT(ty->get_alignment_in_bits() == alignment_in_bits); |
| return ty; |
| } |
| |
| const environment* env = ctxt.get_environment(); |
| type_decl_sptr decl(new type_decl(env, name, size_in_bits, |
| alignment_in_bits, loc)); |
| maybe_set_artificial_location(ctxt, node, decl); |
| decl->set_is_anonymous(is_anonymous); |
| decl->set_is_declaration_only(is_decl_only); |
| if (ctxt.push_and_key_type_decl(decl, id, add_to_current_scope)) |
| { |
| ctxt.map_xml_node_to_decl(node, decl); |
| return decl; |
| } |
| |
| return nil; |
| } |
| |
| /// Build a qualified_type_def from a 'qualified-type-def' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the qualified_type_def from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of this |
| /// function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built qualified_type_def upon |
| /// successful completion, a null pointer otherwise. |
| static qualified_type_def_sptr |
| build_qualified_type_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| qualified_type_def_sptr nil; |
| if (!xmlStrEqual(node->name, BAD_CAST("qualified-type-def"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| qualified_type_def_sptr result = |
| dynamic_pointer_cast<qualified_type_def>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE (node, "id")) |
| id = CHAR_STR(s); |
| |
| ABG_ASSERT(!id.empty()); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| qualified_type_def::CV cv = qualified_type_def::CV_NONE; |
| string const_str; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "const")) |
| const_str = CHAR_STR(s); |
| bool const_cv = const_str == "yes"; |
| |
| string volatile_str; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "volatile")) |
| volatile_str = CHAR_STR(s); |
| bool volatile_cv = volatile_str == "yes"; |
| |
| string restrict_str; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "restrict")) |
| restrict_str = CHAR_STR(s); |
| bool restrict_cv = restrict_str == "yes"; |
| |
| if (const_cv) |
| cv = cv | qualified_type_def::CV_CONST; |
| if (volatile_cv) |
| cv = cv | qualified_type_def::CV_VOLATILE; |
| if (restrict_cv) |
| cv = cv | qualified_type_def::CV_RESTRICT; |
| |
| // Create the qualified type /before/ the underlying type. After |
| // the creation, the type is 'keyed' using |
| // ctxt.push_and_key_type_decl. This means that the type can be |
| // retrieved from its type ID. This is so that if the underlying |
| // type indirectly uses this qualified type (via recursion) then |
| // that is made possible. |
| // |
| // The underlying type will later be set after it's created. |
| qualified_type_def_sptr decl; |
| decl.reset(new qualified_type_def(ctxt.get_environment(), cv, loc)); |
| maybe_set_artificial_location(ctxt, node, decl); |
| ctxt.push_and_key_type_decl(decl, id, add_to_current_scope); |
| ctxt.map_xml_node_to_decl(node, decl); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| ABG_ASSERT(!type_id.empty()); |
| |
| shared_ptr<type_base> underlying_type = |
| ctxt.build_or_get_type_decl(type_id, true); |
| ABG_ASSERT(underlying_type); |
| decl->set_underlying_type(underlying_type); |
| |
| return decl; |
| } |
| |
| /// Build a pointer_type_def from a 'pointer-type-def' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the pointer_type_def from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built pointer_type_def upon |
| /// successful completion, a null pointer otherwise. |
| static pointer_type_def_sptr |
| build_pointer_type_def(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| |
| shared_ptr<pointer_type_def> nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("pointer-type-def"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| pointer_type_def_sptr result = |
| dynamic_pointer_cast<pointer_type_def>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| if (type_base_sptr t = ctxt.get_type_decl(id)) |
| { |
| pointer_type_def_sptr result = is_pointer_type(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| |
| size_t size_in_bits = ctxt.get_translation_unit()->get_address_size(); |
| size_t alignment_in_bits = 0; |
| read_size_and_alignment(node, size_in_bits, alignment_in_bits); |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| // Create the pointer type /before/ the pointed-to type. After the |
| // creation, the type is 'keyed' using ctxt.push_and_key_type_decl. |
| // This means that the type can be retrieved from its type ID. This |
| // is so that if the pointed-to type indirectly uses this pointer |
| // type (via recursion) then that is made possible. |
| pointer_type_def_sptr t(new pointer_type_def(ctxt.get_environment(), |
| size_in_bits, |
| alignment_in_bits, |
| loc)); |
| maybe_set_artificial_location(ctxt, node, t); |
| |
| if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope)) |
| ctxt.map_xml_node_to_decl(node, t); |
| |
| type_base_sptr pointed_to_type = |
| ctxt.build_or_get_type_decl(type_id, true); |
| ABG_ASSERT(pointed_to_type); |
| |
| t->set_pointed_to_type(pointed_to_type); |
| |
| return t; |
| } |
| |
| /// Build a reference_type_def from a pointer to 'reference-type-def' |
| /// xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the reference_type_def from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built reference_type_def upon |
| /// successful completio, a null pointer otherwise. |
| static shared_ptr<reference_type_def> |
| build_reference_type_def(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<reference_type_def> nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("reference-type-def"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| reference_type_def_sptr result = |
| dynamic_pointer_cast<reference_type_def>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| if (type_base_sptr d = ctxt.get_type_decl(id)) |
| { |
| reference_type_def_sptr ty = is_reference_type(d); |
| ABG_ASSERT(ty); |
| return ty; |
| } |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| string kind; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "kind")) |
| kind = CHAR_STR(s); // this should be either "lvalue" or "rvalue". |
| bool is_lvalue = kind == "lvalue"; |
| |
| size_t size_in_bits = ctxt.get_translation_unit()->get_address_size(); |
| size_t alignment_in_bits = 0; |
| read_size_and_alignment(node, size_in_bits, alignment_in_bits); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| ABG_ASSERT(!type_id.empty()); |
| |
| // Create the reference type /before/ the pointed-to type. After |
| // the creation, the type is 'keyed' using |
| // ctxt.push_and_key_type_decl. This means that the type can be |
| // retrieved from its type ID. This is so that if the pointed-to |
| // type indirectly uses this reference type (via recursion) then |
| // that is made possible. |
| reference_type_def_sptr t(new reference_type_def(ctxt.get_environment(), |
| is_lvalue, size_in_bits, |
| alignment_in_bits, loc)); |
| maybe_set_artificial_location(ctxt, node, t); |
| if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope)) |
| ctxt.map_xml_node_to_decl(node, t); |
| |
| type_base_sptr pointed_to_type = |
| ctxt.build_or_get_type_decl(type_id,/*add_to_current_scope=*/ true); |
| ABG_ASSERT(pointed_to_type); |
| t->set_pointed_to_type(pointed_to_type); |
| |
| return t; |
| } |
| |
| /// Build a function_type from a pointer to 'function-type' |
| /// xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the function_type from. |
| /// |
| /// @param add_to_current_scope if set to yes, the result of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built function_type upon |
| /// successful completion, a null pointer otherwise. |
| static function_type_sptr |
| build_function_type(read_context& ctxt, |
| const xmlNodePtr node, |
| bool /*add_to_current_scope*/) |
| { |
| function_type_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("function-type"))) |
| return nil; |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| string method_class_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "method-class-id")) |
| method_class_id = CHAR_STR(s); |
| |
| bool is_method_t = !method_class_id.empty(); |
| |
| size_t size = ctxt.get_translation_unit()->get_address_size(), align = 0; |
| read_size_and_alignment(node, size, align); |
| |
| environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| std::vector<shared_ptr<function_decl::parameter> > parms; |
| type_base_sptr return_type = env->get_void_type(); |
| |
| class_decl_sptr method_class_type; |
| if (is_method_t) |
| { |
| method_class_type = |
| is_class_type(ctxt.build_or_get_type_decl(method_class_id, |
| /*add_decl_to_scope=*/true)); |
| ABG_ASSERT(method_class_type); |
| } |
| |
| |
| function_type_sptr fn_type(is_method_t |
| ? new method_type(method_class_type, |
| ctxt.get_environment(), |
| size, align) |
| : new function_type(return_type, |
| parms, size, align)); |
| |
| ctxt.get_translation_unit()->bind_function_type_life_time(fn_type); |
| ctxt.key_type_decl(fn_type, id); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (xmlStrEqual(n->name, BAD_CAST("parameter"))) |
| { |
| if (function_decl::parameter_sptr p = |
| build_function_parameter(ctxt, n)) |
| parms.push_back(p); |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("return"))) |
| { |
| string type_id; |
| if (xml_char_sptr s = |
| xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id")))) |
| type_id = CHAR_STR(s); |
| if (!type_id.empty()) |
| fn_type->set_return_type(ctxt.build_or_get_type_decl |
| (type_id, true)); |
| } |
| } |
| |
| fn_type->set_parameters(parms); |
| |
| return fn_type; |
| } |
| |
| /// Build a array_type_def::subrange_type from a 'subrange' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the |
| /// array_type_def::subrange_type from. |
| /// |
| /// |
| /// @return a pointer to a newly built array_type_def::subrange_type |
| /// upon successful completion, a null pointer otherwise. |
| static array_type_def::subrange_sptr |
| build_subrange_type(read_context& ctxt, |
| const xmlNodePtr node) |
| { |
| array_type_def::subrange_sptr nil; |
| |
| if (!node || !xmlStrEqual(node->name, BAD_CAST("subrange"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| array_type_def::subrange_sptr result = |
| dynamic_pointer_cast<array_type_def::subrange_type>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| // Note that in early implementations, the subrange didn't carry its |
| // own ID as the subrange was just a detail of an array. So we |
| // still need to support the abixml emitted by those early |
| // implementations. |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| |
| if (!id.empty()) |
| if (type_base_sptr d = ctxt.get_type_decl(id)) |
| { |
| array_type_def::subrange_sptr ty = is_subrange_type(d); |
| ABG_ASSERT(ty); |
| return ty; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = CHAR_STR(s); |
| |
| uint64_t length = 0; |
| string length_str; |
| bool is_infinite = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "length")) |
| { |
| if (string(CHAR_STR(s)) == "infinite") |
| is_infinite = true; |
| else |
| length = strtoull(CHAR_STR(s), NULL, 0); |
| } |
| |
| int64_t lower_bound = 0, upper_bound = 0; |
| bool bounds_present = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "lower-bound")) |
| { |
| lower_bound = strtoll(CHAR_STR(s), NULL, 0); |
| s = XML_NODE_GET_ATTRIBUTE(node, "upper-bound"); |
| if (!string(CHAR_STR(s)).empty()) |
| upper_bound = strtoll(CHAR_STR(s), NULL, 0); |
| bounds_present = true; |
| ABG_ASSERT(is_infinite |
| || (length == (uint64_t) upper_bound - lower_bound + 1)); |
| } |
| |
| string underlying_type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| underlying_type_id = CHAR_STR(s); |
| |
| type_base_sptr underlying_type; |
| if (!underlying_type_id.empty()) |
| { |
| underlying_type = ctxt.build_or_get_type_decl(underlying_type_id, true); |
| ABG_ASSERT(underlying_type); |
| } |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| // Note that DWARF would actually have a lower_bound of -1 for an |
| // array of length 0. |
| array_type_def::subrange_type::bound_value max_bound; |
| array_type_def::subrange_type::bound_value min_bound; |
| if (!is_infinite) |
| if (length > 0) |
| // By default, if no 'lower-bound/upper-bound' attributes are |
| // set, we assume that the lower bound is 0 and the upper bound |
| // is length - 1. |
| max_bound.set_signed(length - 1); |
| |
| if (bounds_present) |
| { |
| // So lower_bound/upper_bound are set. Let's set them rather |
| // than assume that mind_bound is zero. |
| min_bound.set_signed(lower_bound); |
| max_bound.set_signed(upper_bound); |
| } |
| |
| array_type_def::subrange_sptr p |
| (new array_type_def::subrange_type(ctxt.get_environment(), |
| name, min_bound, max_bound, |
| underlying_type, loc)); |
| maybe_set_artificial_location(ctxt, node, p); |
| p->is_infinite(is_infinite); |
| |
| return p; |
| } |
| |
| /// Build a array_type_def from a 'array-type-def' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the array_type_def from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built array_type_def upon |
| /// successful completion, a null pointer otherwise. |
| static array_type_def_sptr |
| build_array_type_def(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| |
| array_type_def_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("array-type-def"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| array_type_def_sptr result = |
| dynamic_pointer_cast<array_type_def>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| if (type_base_sptr d = ctxt.get_type_decl(id)) |
| { |
| array_type_def_sptr ty = is_array_type(d); |
| ABG_ASSERT(ty); |
| return ty; |
| } |
| |
| int dimensions = 0; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "dimensions")) |
| dimensions = atoi(CHAR_STR(s)); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| |
| // maybe building the type of array elements triggered building this |
| // one in the mean time ... |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| array_type_def_sptr result = |
| dynamic_pointer_cast<array_type_def>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| size_t size_in_bits = 0, alignment_in_bits = 0; |
| bool has_size_in_bits = false; |
| char *endptr; |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits")) |
| { |
| size_in_bits = strtoull(CHAR_STR(s), &endptr, 0); |
| if (*endptr != '\0') |
| { |
| if (!strcmp(CHAR_STR(s), "infinite")) |
| size_in_bits = (size_t) -1; |
| else |
| return nil; |
| } |
| has_size_in_bits = true; |
| } |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits")) |
| { |
| alignment_in_bits = strtoull(CHAR_STR(s), &endptr, 0); |
| if (*endptr != '\0') |
| return nil; |
| } |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| array_type_def::subranges_type subranges; |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| if (xmlStrEqual(n->name, BAD_CAST("subrange"))) |
| { |
| if (array_type_def::subrange_sptr s = |
| build_subrange_type(ctxt, n)) |
| { |
| if (add_to_current_scope) |
| { |
| add_decl_to_scope(s, ctxt.get_cur_scope()); |
| ctxt.maybe_canonicalize_type(s); |
| } |
| subranges.push_back(s); |
| } |
| } |
| |
| array_type_def_sptr ar_type(new array_type_def(ctxt.get_environment(), |
| subranges, loc)); |
| maybe_set_artificial_location(ctxt, node, ar_type); |
| if (ctxt.push_and_key_type_decl(ar_type, id, add_to_current_scope)) |
| ctxt.map_xml_node_to_decl(node, ar_type); |
| |
| // The type of array elements. |
| type_base_sptr type = |
| ctxt.build_or_get_type_decl(type_id, true); |
| ABG_ASSERT(type); |
| ar_type->set_element_type(type); |
| |
| if (dimensions != ar_type->get_dimension_count() |
| || (alignment_in_bits |
| != ar_type->get_element_type()->get_alignment_in_bits())) |
| return nil; |
| |
| if (has_size_in_bits && size_in_bits != (size_t) -1 |
| && size_in_bits != ar_type->get_size_in_bits()) |
| { |
| // We have a potential discrepancy between calculated and recorded sizes. |
| size_t element_size = ar_type->get_element_type()->get_size_in_bits(); |
| if (element_size && element_size != (size_t)-1) |
| { |
| // Older versions miscalculated multidimensional array sizes. |
| size_t bad_count = 0; |
| for (vector<array_type_def::subrange_sptr>::const_iterator i = |
| subranges.begin(); |
| i != subranges.end(); |
| ++i) |
| bad_count += (*i)->get_length(); |
| if (size_in_bits == bad_count * element_size) |
| { |
| static bool reported = false; |
| if (!reported) |
| { |
| std::cerr << "notice: Found incorrectly calculated array " |
| << "sizes in XML - this is benign.\nOlder versions " |
| << "of libabigail miscalculated multidimensional " |
| << "array sizes." << std::endl; |
| reported = true; |
| } |
| } |
| else |
| { |
| std::cerr << "error: Found incorrectly calculated array size in " |
| << "XML (id=\"" << id << "\")." << std::endl; |
| ABG_ASSERT_NOT_REACHED; |
| } |
| } |
| } |
| |
| return ar_type; |
| } |
| |
| /// Build an @ref enum_type_decl from the XML node that represents it, |
| /// if it was not suppressed by a supression specification present in |
| /// the current read_context. |
| /// |
| /// @param ctxt the read_context to take into account. |
| /// |
| /// @param node the XML node representing the @ref enum_type_decl to |
| /// build. |
| /// |
| /// @param add_to_current_scope whether to add the built @ref |
| /// enum_type_decl to the current scope. |
| /// |
| /// @return the newly built @ref enum_type_decl iff it was effectively |
| /// built. |
| static enum_type_decl_sptr |
| build_enum_type_decl_if_not_suppressed(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| enum_type_decl_sptr enum_type; |
| if (!type_is_suppressed(ctxt, node)) |
| enum_type = build_enum_type_decl(ctxt, node, add_to_current_scope); |
| return enum_type; |
| } |
| |
| /// Build an enum_type_decl from an 'enum-type-decl' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the enum_type_decl from. |
| /// |
| /// param add_to_current_scope if set to yes, the resulting of this |
| /// function is added to its current scope. |
| /// |
| /// @return a pointer to a newly built enum_type_decl upon successful |
| /// completion, a null pointer otherwise. |
| static enum_type_decl_sptr |
| build_enum_type_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| enum_type_decl_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("enum-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| enum_type_decl_sptr result = |
| dynamic_pointer_cast<enum_type_decl>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| string linkage_name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "linkage-name")) |
| linkage_name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| bool is_decl_only = false; |
| read_is_declaration_only(node, is_decl_only); |
| |
| bool is_anonymous = false; |
| read_is_anonymous(node, is_anonymous); |
| |
| bool is_artificial = false; |
| read_is_artificial(node, is_artificial); |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| |
| ABG_ASSERT(!id.empty()); |
| |
| const environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| |
| string base_type_id; |
| enum_type_decl::enumerators enums; |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (xmlStrEqual(n->name, BAD_CAST("underlying-type"))) |
| { |
| xml_char_sptr a = xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id"))); |
| if (a) |
| base_type_id = CHAR_STR(a); |
| continue; |
| } |
| |
| if (xmlStrEqual(n->name, BAD_CAST("enumerator"))) |
| { |
| string name; |
| int64_t value = 0; |
| |
| xml_char_sptr a = xml::build_sptr(xmlGetProp(n, BAD_CAST("name"))); |
| if (a) |
| name = xml::unescape_xml_string(CHAR_STR(a)); |
| |
| a = xml::build_sptr(xmlGetProp(n, BAD_CAST("value"))); |
| if (a) |
| { |
| value = strtoll(CHAR_STR(a), NULL, 0); |
| if (value == LLONG_MIN || value == LLONG_MAX) |
| return nil; |
| } |
| |
| enums.push_back(enum_type_decl::enumerator(env, name, value)); |
| } |
| } |
| |
| type_base_sptr underlying_type = |
| ctxt.build_or_get_type_decl(base_type_id, true); |
| ABG_ASSERT(underlying_type); |
| |
| enum_type_decl_sptr t(new enum_type_decl(name, loc, |
| underlying_type, |
| enums, linkage_name)); |
| maybe_set_artificial_location(ctxt, node, t); |
| t->set_is_anonymous(is_anonymous); |
| t->set_is_artificial(is_artificial); |
| t->set_is_declaration_only(is_decl_only); |
| if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope)) |
| { |
| ctxt.map_xml_node_to_decl(node, t); |
| return t; |
| } |
| |
| return nil; |
| } |
| |
| /// Build a typedef_decl from a 'typedef-decl' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the typedef_decl from. |
| /// |
| /// @return a pointer to a newly built typedef_decl upon successful |
| /// completion, a null pointer otherwise. |
| static shared_ptr<typedef_decl> |
| build_typedef_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<typedef_decl> nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("typedef-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| typedef_decl_sptr result = is_typedef(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| ABG_ASSERT(!id.empty()); |
| |
| if (type_base_sptr t = ctxt.get_type_decl(id)) |
| { |
| typedef_decl_sptr result = is_typedef(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| ABG_ASSERT(!type_id.empty()); |
| |
| // Create the typedef type /before/ the underlying type. After the |
| // creation, the type is 'keyed' using ctxt.push_and_key_type_decl. |
| // This means that the type can be retrieved from its type ID. This |
| // is so that if the underlying type indirectly (needs to) use(s) |
| // this very same typedef type (via recursion) then that is made |
| // possible. |
| typedef_decl_sptr t(new typedef_decl(name, ctxt.get_environment(), loc)); |
| maybe_set_artificial_location(ctxt, node, t); |
| ctxt.push_and_key_type_decl(t, id, add_to_current_scope); |
| ctxt.map_xml_node_to_decl(node, t); |
| type_base_sptr underlying_type(ctxt.build_or_get_type_decl(type_id, true)); |
| ABG_ASSERT(underlying_type); |
| t->set_underlying_type(underlying_type); |
| return t; |
| } |
| |
| /// Build a class from its XML node if it is not suppressed by a |
| /// suppression specification that is present in the read context. |
| /// |
| /// @param ctxt the read context to consider. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @param add_to_current_scope whether to add the built class to the |
| /// current context or not. |
| /// |
| /// @return true iff the class was built. |
| static class_decl_sptr |
| build_class_decl_if_not_suppressed(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| class_decl_sptr class_type; |
| if (!type_is_suppressed(ctxt, node)) |
| class_type = build_class_decl(ctxt, node, add_to_current_scope); |
| return class_type; |
| } |
| |
| /// Build a @ref union_decl from its XML node if it is not suppressed |
| /// by a suppression specification that is present in the read |
| /// context. |
| /// |
| /// @param ctxt the read context to consider. |
| /// |
| /// @param node the XML node to consider. |
| /// |
| /// @param add_to_current_scope whether to add the built @ref |
| /// union_decl to the current context or not. |
| /// |
| /// @return true iff the @ref union_decl was built. |
| static union_decl_sptr |
| build_union_decl_if_not_suppressed(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| union_decl_sptr union_type; |
| if (!type_is_suppressed(ctxt, node)) |
| union_type = build_union_decl(ctxt, node, add_to_current_scope); |
| return union_type; |
| } |
| |
| /// Build a class_decl from a 'class-decl' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the class_decl from. |
| /// |
| /// @param add_to_current_scope if yes, the resulting class node |
| /// hasn't triggered voluntarily the adding of the resulting |
| /// class_decl_sptr to the current scope. |
| /// |
| /// @return a pointer to class_decl upon successful completion, a null |
| /// pointer otherwise. |
| static class_decl_sptr |
| build_class_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| class_decl_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("class-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| class_decl_sptr result = dynamic_pointer_cast<class_decl>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| size_t size_in_bits = 0, alignment_in_bits = 0; |
| read_size_and_alignment(node, size_in_bits, alignment_in_bits); |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| bool is_artificial = false; |
| read_is_artificial(node, is_artificial); |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| class_decl::member_types mbrs; |
| class_decl::data_members data_mbrs; |
| class_decl::member_functions mbr_functions; |
| class_decl::base_specs bases; |
| |
| class_decl_sptr decl; |
| |
| bool is_decl_only = false; |
| read_is_declaration_only(node, is_decl_only); |
| |
| bool is_struct = false; |
| read_is_struct(node, is_struct); |
| |
| bool is_anonymous = false; |
| read_is_anonymous(node, is_anonymous); |
| |
| string naming_typedef_id; |
| |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "naming-typedef-id")) |
| naming_typedef_id = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| ABG_ASSERT(!id.empty()); |
| |
| class_decl_sptr previous_definition, previous_declaration; |
| if (!is_anonymous) |
| if (type_base_sptr t = ctxt.get_type_decl(id)) |
| { |
| previous_definition = is_class_type(t); |
| ABG_ASSERT(previous_definition); |
| } |
| |
| const vector<type_base_sptr> *types_ptr = 0; |
| if (!is_anonymous && !previous_definition) |
| types_ptr = ctxt.get_all_type_decls(id); |
| if (types_ptr) |
| { |
| // Lets look at the previous declarations and the first previous |
| // definition of this type that we've already seen while parsing |
| // this corpus. |
| for (vector<type_base_sptr>::const_iterator i = types_ptr->begin(); |
| i != types_ptr->end(); |
| ++i) |
| { |
| class_decl_sptr klass = is_class_type(*i); |
| ABG_ASSERT(klass); |
| if (klass->get_is_declaration_only() |
| && !klass->get_definition_of_declaration()) |
| previous_declaration = klass; |
| else if (!klass->get_is_declaration_only() |
| && !previous_definition) |
| previous_definition = klass; |
| if (previous_definition && previous_declaration) |
| break; |
| } |
| |
| if (previous_declaration) |
| ABG_ASSERT(previous_declaration->get_name() == name); |
| |
| if (previous_definition) |
| ABG_ASSERT(previous_definition->get_name() == name); |
| |
| if (is_decl_only && previous_declaration) |
| return previous_declaration; |
| } |
| |
| const environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| |
| if (!is_decl_only && previous_definition) |
| // We are in the case where we've read this class definition |
| // before, but we might need to update it to add some new stuff to |
| // it; we might thus find the new stuff to add in the current |
| // (new) incarnation of that definition that we are currently |
| // reading. |
| decl = previous_definition; |
| else |
| { |
| if (is_decl_only) |
| { |
| decl.reset(new class_decl(env, name, is_struct)); |
| if (size_in_bits) |
| decl->set_size_in_bits(size_in_bits); |
| if (is_anonymous) |
| decl->set_is_anonymous(is_anonymous); |
| decl->set_location(loc); |
| } |
| else |
| decl.reset(new class_decl(env, name, size_in_bits, alignment_in_bits, |
| is_struct, loc, vis, bases, mbrs, |
| data_mbrs, mbr_functions, is_anonymous)); |
| } |
| |
| maybe_set_artificial_location(ctxt, node, decl); |
| decl->set_is_artificial(is_artificial); |
| |
| string def_id; |
| bool is_def_of_decl = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id")) |
| def_id = CHAR_STR(s); |
| |
| if (!def_id.empty()) |
| { |
| decl_base_sptr d = is_decl(ctxt.get_type_decl(def_id)); |
| if (d && d->get_is_declaration_only()) |
| { |
| is_def_of_decl = true; |
| decl->set_earlier_declaration(d); |
| d->set_definition_of_declaration(decl); |
| } |
| } |
| |
| if (!is_decl_only |
| && decl |
| && !decl->get_is_declaration_only() |
| && previous_declaration) |
| { |
| // decl is the definition of the previous declaration |
| // previous_declaration. |
| // |
| // Let's link them. |
| decl->set_earlier_declaration(is_decl(previous_declaration)); |
| for (vector<type_base_sptr>::const_iterator i = types_ptr->begin(); |
| i != types_ptr->end(); |
| ++i) |
| { |
| class_decl_sptr d = is_class_type(*i); |
| ABG_ASSERT(d); |
| if (d->get_is_declaration_only() |
| && !d->get_definition_of_declaration()) |
| { |
| previous_declaration->set_definition_of_declaration(decl); |
| is_def_of_decl = true; |
| } |
| } |
| } |
| |
| if (is_decl_only && previous_definition) |
| { |
| // decl is a declaration of the previous definition |
| // previous_definition. Let's link them. |
| ABG_ASSERT(decl->get_is_declaration_only() |
| && !decl->get_definition_of_declaration()); |
| decl->set_definition_of_declaration(previous_definition); |
| } |
| |
| ABG_ASSERT(!is_decl_only || !is_def_of_decl); |
| |
| ctxt.push_decl_to_current_scope(decl, add_to_current_scope); |
| |
| ctxt.map_xml_node_to_decl(node, decl); |
| ctxt.key_type_decl(decl, id); |
| |
| // If this class has a naming typedef, get it and refer to it. |
| if (!naming_typedef_id.empty()) |
| { |
| typedef_decl_sptr naming_typedef = |
| is_typedef(ctxt.build_or_get_type_decl(naming_typedef_id, true)); |
| ABG_ASSERT(naming_typedef); |
| decl->set_naming_typedef(naming_typedef); |
| } |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| !is_decl_only && n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (xmlStrEqual(n->name, BAD_CAST("base-class"))) |
| { |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| read_access(n, access); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "type-id")) |
| type_id = CHAR_STR(s); |
| shared_ptr<class_decl> b = |
| dynamic_pointer_cast<class_decl> |
| (ctxt.build_or_get_type_decl(type_id, true)); |
| ABG_ASSERT(b); |
| |
| if (decl->find_base_class(b->get_qualified_name())) |
| // We are in updating mode for this class. The version of |
| // the class we have already has this base class, so we |
| // are not going to add it again. |
| continue; |
| |
| size_t offset_in_bits = 0; |
| bool offset_present = read_offset_in_bits (n, offset_in_bits); |
| |
| bool is_virtual = false; |
| read_is_virtual (n, is_virtual); |
| |
| shared_ptr<class_decl::base_spec> base (new class_decl::base_spec |
| (b, access, |
| offset_present |
| ? (long) offset_in_bits |
| : -1, |
| is_virtual)); |
| decl->add_base_specifier(base); |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("member-type"))) |
| { |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| read_access(n, access); |
| |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (type_base_sptr t = |
| build_type(ctxt, p, /*add_to_current_scope=*/true)) |
| { |
| decl_base_sptr td = get_type_declaration(t); |
| ABG_ASSERT(td); |
| set_member_access_specifier(td, access); |
| ctxt.maybe_canonicalize_type(t, !add_to_current_scope); |
| xml_char_sptr i= XML_NODE_GET_ATTRIBUTE(p, "id"); |
| string id = CHAR_STR(i); |
| ABG_ASSERT(!id.empty()); |
| ctxt.key_type_decl(t, id); |
| ctxt.map_xml_node_to_decl(p, td); |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("data-member"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| read_access(n, access); |
| |
| bool is_laid_out = false; |
| size_t offset_in_bits = 0; |
| if (read_offset_in_bits(n, offset_in_bits)) |
| is_laid_out = true; |
| |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (var_decl_sptr v = |
| build_var_decl(ctxt, p, /*add_to_cur_scope=*/false)) |
| { |
| if (decl->find_data_member(v)) |
| { |
| // We are in updating mode and the current |
| // version of this class already has this data |
| // member, so we are not going to add it again. |
| // So we need to discard the data member we have |
| // built (and that was pushed to the current |
| // stack of decls built) and move on. |
| decl_base_sptr d = ctxt.pop_decl(); |
| ABG_ASSERT(is_var_decl(d)); |
| continue; |
| } |
| |
| if (!variable_is_suppressed(ctxt, decl.get(), *v)) |
| { |
| decl->add_data_member(v, access, |
| is_laid_out, |
| is_static, |
| offset_in_bits); |
| if (is_static) |
| ctxt.maybe_add_var_to_exported_decls(v.get()); |
| } |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("member-function"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| read_access(n, access); |
| |
| bool is_virtual = false; |
| ssize_t vtable_offset = -1; |
| if (xml_char_sptr s = |
| XML_NODE_GET_ATTRIBUTE(n, "vtable-offset")) |
| { |
| is_virtual = true; |
| vtable_offset = atoi(CHAR_STR(s)); |
| } |
| |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| bool is_ctor = false, is_dtor = false, is_const = false; |
| read_cdtor_const(n, is_ctor, is_dtor, is_const); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (function_decl_sptr f = |
| build_function_decl_if_not_suppressed(ctxt, p, decl, |
| /*add_to_cur_sc=*/true)) |
| { |
| method_decl_sptr m = is_method_decl(f); |
| ABG_ASSERT(m); |
| set_member_access_specifier(m, access); |
| set_member_is_static(m, is_static); |
| if (vtable_offset != -1) |
| set_member_function_vtable_offset(m, vtable_offset); |
| set_member_function_is_virtual(m, is_virtual); |
| set_member_function_is_ctor(m, is_ctor); |
| set_member_function_is_dtor(m, is_dtor); |
| set_member_function_is_const(m, is_const); |
| break; |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("member-template"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| read_access(n, access); |
| |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| bool is_ctor = false, is_dtor = false, is_const = false; |
| read_cdtor_const(n, is_ctor, is_dtor, is_const); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (shared_ptr<function_tdecl> f = |
| build_function_tdecl(ctxt, p, |
| /*add_to_current_scope=*/true)) |
| { |
| shared_ptr<member_function_template> m |
| (new member_function_template(f, access, is_static, |
| is_ctor, is_const)); |
| ABG_ASSERT(f->get_scope()); |
| decl->add_member_function_template(m); |
| } |
| else if (shared_ptr<class_tdecl> c = |
| build_class_tdecl(ctxt, p, |
| /*add_to_current_scope=*/true)) |
| { |
| member_class_template_sptr m(new member_class_template(c, |
| access, |
| is_static)); |
| ABG_ASSERT(c->get_scope()); |
| decl->add_member_class_template(m); |
| } |
| } |
| } |
| } |
| |
| ctxt.pop_scope_or_abort(decl); |
| |
| return decl; |
| } |
| |
| /// Build a union_decl from a 'union-decl' xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the union_decl from. |
| /// |
| /// @param add_to_current_scope if yes, the resulting union node |
| /// hasn't triggered voluntarily the adding of the resulting |
| /// union_decl_sptr to the current scope. |
| /// |
| /// @return a pointer to union_decl upon successful completion, a null |
| /// pointer otherwise. |
| static union_decl_sptr |
| build_union_decl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| union_decl_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("union-decl"))) |
| return nil; |
| |
| if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) |
| { |
| union_decl_sptr result = dynamic_pointer_cast<union_decl>(d); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| size_t size_in_bits = 0, alignment_in_bits = 0; |
| read_size_and_alignment(node, size_in_bits, alignment_in_bits); |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| bool is_artificial = false; |
| read_is_artificial(node, is_artificial); |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| union_decl::member_types mbrs; |
| union_decl::data_members data_mbrs; |
| union_decl::member_functions mbr_functions; |
| |
| union_decl_sptr decl; |
| |
| bool is_decl_only = false; |
| read_is_declaration_only(node, is_decl_only); |
| |
| bool is_anonymous = false; |
| read_is_anonymous(node, is_anonymous); |
| |
| ABG_ASSERT(!id.empty()); |
| union_decl_sptr previous_definition, previous_declaration; |
| const vector<type_base_sptr> *types_ptr = 0; |
| if (!is_anonymous) |
| types_ptr = ctxt.get_all_type_decls(id); |
| if (types_ptr) |
| { |
| // Lets look at the previous declarations and the first previous |
| // definition of this type that we've already seen while parsing |
| // this corpus. |
| for (vector<type_base_sptr>::const_iterator i = types_ptr->begin(); |
| i != types_ptr->end(); |
| ++i) |
| { |
| union_decl_sptr onion = is_union_type(*i); |
| ABG_ASSERT(onion); |
| if (onion->get_is_declaration_only() |
| && !onion->get_definition_of_declaration()) |
| previous_declaration = onion; |
| else if (!onion->get_is_declaration_only() |
| && !previous_definition) |
| previous_definition = onion; |
| if (previous_definition && previous_declaration) |
| break; |
| } |
| |
| if (previous_declaration) |
| ABG_ASSERT(previous_declaration->get_name() == name); |
| |
| if (previous_definition) |
| ABG_ASSERT(previous_definition->get_name() == name); |
| |
| if (is_decl_only && previous_declaration) |
| return previous_declaration; |
| } |
| |
| const environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| |
| if (!is_decl_only && previous_definition) |
| // We are in the case where we've read this class definition |
| // before, but we might need to update it to add some new stuff to |
| // it; we might thus find the new stuff to add in the current |
| // (new) incarnation of that definition that we are currently |
| // reading. |
| decl = previous_definition; |
| else |
| { |
| if (is_decl_only) |
| decl.reset(new union_decl(env, name)); |
| else |
| decl.reset(new union_decl(env, name, |
| size_in_bits, |
| loc, vis, mbrs, |
| data_mbrs, |
| mbr_functions, |
| is_anonymous)); |
| } |
| |
| maybe_set_artificial_location(ctxt, node, decl); |
| decl->set_is_artificial(is_artificial); |
| |
| string def_id; |
| bool is_def_of_decl = false; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id")) |
| def_id = CHAR_STR(s); |
| |
| if (!def_id.empty()) |
| { |
| class_decl_sptr d = |
| dynamic_pointer_cast<class_decl>(ctxt.get_type_decl(def_id)); |
| if (d && d->get_is_declaration_only()) |
| { |
| is_def_of_decl = true; |
| decl->set_earlier_declaration(d); |
| d->set_definition_of_declaration(decl); |
| } |
| } |
| |
| if (!is_decl_only |
| && decl |
| && !decl->get_is_declaration_only() |
| && previous_declaration) |
| { |
| // decl is the definition of the previous declaration |
| // previous_declaration. |
| // |
| // Let's link them. |
| decl->set_earlier_declaration(previous_declaration); |
| for (vector<type_base_sptr>::const_iterator i = types_ptr->begin(); |
| i != types_ptr->end(); |
| ++i) |
| { |
| union_decl_sptr d = is_union_type(*i); |
| ABG_ASSERT(d); |
| if (d->get_is_declaration_only() |
| && !d->get_definition_of_declaration()) |
| { |
| previous_declaration->set_definition_of_declaration(decl); |
| is_def_of_decl = true; |
| } |
| } |
| } |
| |
| if (is_decl_only && previous_definition) |
| { |
| // decl is a declaration of the previous definition |
| // previous_definition. Let's link them. |
| ABG_ASSERT(decl->get_is_declaration_only() |
| && !decl->get_definition_of_declaration()); |
| decl->set_definition_of_declaration(previous_definition); |
| } |
| |
| ABG_ASSERT(!is_decl_only || !is_def_of_decl); |
| |
| ctxt.push_decl_to_current_scope(decl, add_to_current_scope); |
| |
| ctxt.map_xml_node_to_decl(node, decl); |
| ctxt.key_type_decl(decl, id); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| !is_decl_only && n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (xmlStrEqual(n->name, BAD_CAST("member-type"))) |
| { |
| access_specifier access = private_access; |
| read_access(n, access); |
| |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (type_base_sptr t = |
| build_type(ctxt, p, /*add_to_current_scope=*/true)) |
| { |
| decl_base_sptr td = get_type_declaration(t); |
| ABG_ASSERT(td); |
| set_member_access_specifier(td, access); |
| ctxt.maybe_canonicalize_type(t, !add_to_current_scope); |
| xml_char_sptr i= XML_NODE_GET_ATTRIBUTE(p, "id"); |
| string id = CHAR_STR(i); |
| ABG_ASSERT(!id.empty()); |
| ctxt.key_type_decl(t, id); |
| ctxt.map_xml_node_to_decl(p, td); |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("data-member"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = private_access; |
| read_access(n, access); |
| |
| bool is_laid_out = true; |
| size_t offset_in_bits = 0; |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (var_decl_sptr v = |
| build_var_decl(ctxt, p, /*add_to_cur_scope=*/false)) |
| { |
| if (decl->find_data_member(v)) |
| { |
| // We are in updating mode and the current |
| // version of this class already has this data |
| // member, so we are not going to add it again. |
| // So we need to discard the data member we have |
| // built (and that was pushed to the current |
| // stack of decls built) and move on. |
| decl_base_sptr d = ctxt.pop_decl(); |
| ABG_ASSERT(is_var_decl(d)); |
| continue; |
| } |
| if (!is_static |
| || !variable_is_suppressed(ctxt, decl.get(), *v)) |
| decl->add_data_member(v, access, |
| is_laid_out, |
| is_static, |
| offset_in_bits); |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("member-function"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = private_access; |
| read_access(n, access); |
| |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| bool is_ctor = false, is_dtor = false, is_const = false; |
| read_cdtor_const(n, is_ctor, is_dtor, is_const); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (function_decl_sptr f = |
| build_function_decl_if_not_suppressed(ctxt, p, decl, |
| /*add_to_cur_sc=*/true)) |
| { |
| method_decl_sptr m = is_method_decl(f); |
| ABG_ASSERT(m); |
| set_member_access_specifier(m, access); |
| set_member_is_static(m, is_static); |
| set_member_function_is_ctor(m, is_ctor); |
| set_member_function_is_dtor(m, is_dtor); |
| set_member_function_is_const(m, is_const); |
| break; |
| } |
| } |
| } |
| else if (xmlStrEqual(n->name, BAD_CAST("member-template"))) |
| { |
| ctxt.map_xml_node_to_decl(n, decl); |
| |
| access_specifier access = private_access; |
| read_access(n, access); |
| |
| bool is_static = false; |
| read_static(n, is_static); |
| |
| bool is_ctor = false, is_dtor = false, is_const = false; |
| read_cdtor_const(n, is_ctor, is_dtor, is_const); |
| |
| for (xmlNodePtr p = xmlFirstElementChild(n); |
| p; |
| p = xmlNextElementSibling(p)) |
| { |
| if (function_tdecl_sptr f = |
| build_function_tdecl(ctxt, p, |
| /*add_to_current_scope=*/true)) |
| { |
| member_function_template_sptr m |
| (new member_function_template(f, access, is_static, |
| is_ctor, is_const)); |
| ABG_ASSERT(f->get_scope()); |
| decl->add_member_function_template(m); |
| } |
| else if (class_tdecl_sptr c = |
| build_class_tdecl(ctxt, p, |
| /*add_to_current_scope=*/true)) |
| { |
| member_class_template_sptr m(new member_class_template(c, |
| access, |
| is_static)); |
| ABG_ASSERT(c->get_scope()); |
| decl->add_member_class_template(m); |
| } |
| } |
| } |
| } |
| |
| ctxt.pop_scope_or_abort(decl); |
| |
| return decl; |
| } |
| |
| /// Build an intance of function_tdecl, from an |
| /// 'function-template-decl' xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of |
| /// this function is added to its current scope. |
| /// |
| /// @return the newly built function_tdecl upon successful |
| /// completion, a null pointer otherwise. |
| static shared_ptr<function_tdecl> |
| build_function_tdecl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<function_tdecl> nil, result; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("function-template-decl"))) |
| return nil; |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| if (id.empty() || ctxt.get_fn_tmpl_decl(id)) |
| return nil; |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| decl_base::binding bind = decl_base::BINDING_NONE; |
| read_binding(node, bind); |
| |
| const environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| |
| function_tdecl_sptr fn_tmpl_decl(new function_tdecl(env, loc, vis, bind)); |
| maybe_set_artificial_location(ctxt, node, fn_tmpl_decl); |
| |
| ctxt.push_decl_to_current_scope(fn_tmpl_decl, add_to_current_scope); |
| |
| unsigned parm_index = 0; |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (template_parameter_sptr parm = |
| build_template_parameter(ctxt, n, parm_index, fn_tmpl_decl)) |
| { |
| fn_tmpl_decl->add_template_parameter(parm); |
| ++parm_index; |
| } |
| else if (function_decl_sptr f = |
| build_function_decl_if_not_suppressed(ctxt, n, class_decl_sptr(), |
| /*add_to_current_scope=*/true)) |
| fn_tmpl_decl->set_pattern(f); |
| } |
| |
| ctxt.key_fn_tmpl_decl(fn_tmpl_decl, id); |
| |
| return fn_tmpl_decl; |
| } |
| |
| /// Build an intance of class_tdecl, from a |
| /// 'class-template-decl' xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of this |
| /// function is added to its current scope. |
| /// |
| /// @return the newly built function_tdecl upon successful |
| /// completion, a null pointer otherwise. |
| static shared_ptr<class_tdecl> |
| build_class_tdecl(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| shared_ptr<class_tdecl> nil, result; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("class-template-decl"))) |
| return nil; |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| if (id.empty() || ctxt.get_class_tmpl_decl(id)) |
| return nil; |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| decl_base::visibility vis = decl_base::VISIBILITY_NONE; |
| read_visibility(node, vis); |
| |
| const environment* env = ctxt.get_environment(); |
| ABG_ASSERT(env); |
| |
| class_tdecl_sptr class_tmpl (new class_tdecl(env, loc, vis)); |
| maybe_set_artificial_location(ctxt, node, class_tmpl); |
| |
| ctxt.push_decl_to_current_scope(class_tmpl, add_to_current_scope); |
| |
| unsigned parm_index = 0; |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if (template_parameter_sptr parm= |
| build_template_parameter(ctxt, n, parm_index, class_tmpl)) |
| { |
| class_tmpl->add_template_parameter(parm); |
| ++parm_index; |
| } |
| else if (class_decl_sptr c = |
| build_class_decl_if_not_suppressed(ctxt, n, |
| add_to_current_scope)) |
| { |
| if (c->get_scope()) |
| ctxt.maybe_canonicalize_type(c, /*force_delay=*/false); |
| class_tmpl->set_pattern(c); |
| } |
| } |
| |
| ctxt.key_class_tmpl_decl(class_tmpl, id); |
| |
| return class_tmpl; |
| } |
| |
| /// Build a type_tparameter from a 'template-type-parameter' |
| /// xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param index the index (occurrence index, starting from 0) of the |
| /// template parameter. |
| /// |
| /// @param tdecl the enclosing template declaration that holds the |
| /// template type parameter. |
| /// |
| /// @return a pointer to a newly created instance of |
| /// type_tparameter, a null pointer otherwise. |
| static type_tparameter_sptr |
| build_type_tparameter(read_context& ctxt, |
| const xmlNodePtr node, |
| unsigned index, |
| template_decl_sptr tdecl) |
| { |
| type_tparameter_sptr nil, result; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("template-type-parameter"))) |
| return nil; |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| if (!id.empty()) |
| ABG_ASSERT(!ctxt.get_type_decl(id)); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| if (!type_id.empty() |
| && !(result = dynamic_pointer_cast<type_tparameter> |
| (ctxt.build_or_get_type_decl(type_id, true)))) |
| abort(); |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node,loc); |
| |
| result.reset(new type_tparameter(index, tdecl, name, loc)); |
| maybe_set_artificial_location(ctxt, node, result); |
| |
| if (id.empty()) |
| ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result), |
| /*add_to_current_scope=*/true); |
| else |
| ctxt.push_and_key_type_decl(result, id, /*add_to_current_scope=*/true); |
| |
| ABG_ASSERT(result->get_environment()); |
| |
| ctxt.maybe_canonicalize_type(result, /*force_delay=*/false); |
| |
| return result; |
| } |
| |
| |
| /// Build a tmpl_parm_type_composition from a |
| /// "template-parameter-type-composition" xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param index the index of the previous normal template parameter. |
| /// |
| /// @param tdecl the enclosing template declaration that holds this |
| /// template parameter type composition. |
| /// |
| /// @return a pointer to a new instance of tmpl_parm_type_composition |
| /// upon successful completion, a null pointer otherwise. |
| static type_composition_sptr |
| build_type_composition(read_context& ctxt, |
| const xmlNodePtr node, |
| unsigned index, |
| template_decl_sptr tdecl) |
| { |
| type_composition_sptr nil, result; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("template-parameter-type-composition"))) |
| return nil; |
| |
| type_base_sptr composed_type; |
| result.reset(new type_composition(index, tdecl, composed_type)); |
| ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result), |
| /*add_to_current_scope=*/true); |
| |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| { |
| if ((composed_type = |
| build_pointer_type_def(ctxt, n, |
| /*add_to_current_scope=*/true)) |
| ||(composed_type = |
| build_reference_type_def(ctxt, n, |
| /*add_to_current_scope=*/true)) |
| ||(composed_type = |
| build_array_type_def(ctxt, n, |
| /*add_to_current_scope=*/true)) |
| || (composed_type = |
| build_qualified_type_decl(ctxt, n, |
| /*add_to_current_scope=*/true))) |
| { |
| ctxt.maybe_canonicalize_type(composed_type, |
| /*force_delay=*/true); |
| result->set_composed_type(composed_type); |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| /// Build an instance of non_type_tparameter from a |
| /// 'template-non-type-parameter' xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param index the index of the parameter. |
| /// |
| /// @param tdecl the enclosing template declaration that holds this |
| /// non type template parameter. |
| /// |
| /// @return a pointer to a newly created instance of |
| /// non_type_tparameter upon successful completion, a null |
| /// pointer code otherwise. |
| static non_type_tparameter_sptr |
| build_non_type_tparameter(read_context& ctxt, |
| const xmlNodePtr node, |
| unsigned index, |
| template_decl_sptr tdecl) |
| { |
| non_type_tparameter_sptr r; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("template-non-type-parameter"))) |
| return r; |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| type_base_sptr type; |
| if (type_id.empty() |
| || !(type = ctxt.build_or_get_type_decl(type_id, true))) |
| abort(); |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node,loc); |
| |
| r.reset(new non_type_tparameter(index, tdecl, name, type, loc)); |
| maybe_set_artificial_location(ctxt, node, r); |
| ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(r), |
| /*add_to_current_scope=*/true); |
| |
| return r; |
| } |
| |
| /// Build an intance of template_tparameter from a |
| /// 'template-template-parameter' xml element node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to parse from. |
| /// |
| /// @param index the index of the template parameter. |
| /// |
| /// @param tdecl the enclosing template declaration that holds this |
| /// template template parameter. |
| /// |
| /// @return a pointer to a new instance of template_tparameter |
| /// upon successful completion, a null pointer otherwise. |
| static template_tparameter_sptr |
| build_template_tparameter(read_context& ctxt, |
| const xmlNodePtr node, |
| unsigned index, |
| template_decl_sptr tdecl) |
| { |
| template_tparameter_sptr nil; |
| |
| if (!xmlStrEqual(node->name, BAD_CAST("template-template-parameter"))) |
| return nil; |
| |
| string id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) |
| id = CHAR_STR(s); |
| // Bail out if a type with the same ID already exists. |
| ABG_ASSERT(!id.empty()); |
| |
| string type_id; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) |
| type_id = CHAR_STR(s); |
| // Bail out if no type with this ID exists. |
| if (!type_id.empty() |
| && !(dynamic_pointer_cast<template_tparameter> |
| (ctxt.build_or_get_type_decl(type_id, true)))) |
| abort(); |
| |
| string name; |
| if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name")) |
| name = xml::unescape_xml_string(CHAR_STR(s)); |
| |
| location loc; |
| read_location(ctxt, node, loc); |
| |
| template_tparameter_sptr result(new template_tparameter(index, tdecl, |
| name, loc)); |
| maybe_set_artificial_location(ctxt, node, result); |
| ctxt.push_decl_to_current_scope(result, /*add_to_current_scope=*/true); |
| |
| // Go parse template parameters that are children nodes |
| int parm_index = 0; |
| for (xmlNodePtr n = xmlFirstElementChild(node); |
| n; |
| n = xmlNextElementSibling(n)) |
| if (shared_ptr<template_parameter> p = |
| build_template_parameter(ctxt, n, parm_index, result)) |
| { |
| result->add_template_parameter(p); |
| ++parm_index; |
| } |
| |
| if (result) |
| { |
| ctxt.key_type_decl(result, id); |
| ctxt.maybe_canonicalize_type(result, /*force_delay=*/false); |
| } |
| |
| return result; |
| } |
| |
| /// Build a template parameter type from several possible xml elment |
| /// nodes representing a serialized form a template parameter. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml element node to parse from. |
| /// |
| /// @param index the index of the template parameter we are parsing. |
| /// |
| /// @param tdecl the enclosing template declaration that holds this |
| /// template parameter. |
| /// |
| /// @return a pointer to a newly created instance of |
| /// template_parameter upon successful completion, a null pointer |
| /// otherwise. |
| static template_parameter_sptr |
| build_template_parameter(read_context& ctxt, |
| const xmlNodePtr node, |
| unsigned index, |
| template_decl_sptr tdecl) |
| { |
| shared_ptr<template_parameter> r; |
| ((r = build_type_tparameter(ctxt, node, index, tdecl)) |
| || (r = build_non_type_tparameter(ctxt, node, index, tdecl)) |
| || (r = build_template_tparameter(ctxt, node, index, tdecl)) |
| || (r = build_type_composition(ctxt, node, index, tdecl))); |
| |
| return r; |
| } |
| |
| /// Build a type from an xml node. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the xml node to build the type_base from. |
| /// |
| /// @return a pointer to the newly built type_base upon successful |
| /// completion, a null pointer otherwise. |
| static type_base_sptr |
| build_type(read_context& ctxt, |
| const xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| type_base_sptr t; |
| |
| ((t = build_type_decl(ctxt, node, add_to_current_scope)) |
| || (t = build_qualified_type_decl(ctxt, node, add_to_current_scope)) |
| || (t = build_pointer_type_def(ctxt, node, add_to_current_scope)) |
| || (t = build_reference_type_def(ctxt, node , add_to_current_scope)) |
| || (t = build_function_type(ctxt, node, add_to_current_scope)) |
| || (t = build_array_type_def(ctxt, node, add_to_current_scope)) |
| || (t = build_enum_type_decl_if_not_suppressed(ctxt, node, |
| add_to_current_scope)) |
| || (t = build_typedef_decl(ctxt, node, add_to_current_scope)) |
| || (t = build_class_decl_if_not_suppressed(ctxt, node, |
| add_to_current_scope)) |
| || (t = build_union_decl_if_not_suppressed(ctxt, node, |
| add_to_current_scope))); |
| |
| if (ctxt.tracking_non_reachable_types() && t) |
| { |
| corpus_sptr abi = ctxt.get_corpus(); |
| ABG_ASSERT(abi); |
| bool is_non_reachable_type = false; |
| read_is_non_reachable_type(node, is_non_reachable_type); |
| if (!is_non_reachable_type) |
| abi->record_type_as_reachable_from_public_interfaces(*t); |
| } |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| environment *env = ctxt.get_environment(); |
| if (t && env->self_comparison_debug_is_on()) |
| { |
| string type_id; |
| if (read_type_id_string(node, type_id)) |
| // Let's store the type-id of this type pointer. |
| env->get_pointer_type_id_map()[reinterpret_cast<uintptr_t>(t.get())] = type_id; |
| } |
| #endif |
| |
| if (t) |
| ctxt.maybe_canonicalize_type(t,/*force_delay=*/false ); |
| return t; |
| } |
| |
| /// Parses 'type-decl' xml element. |
| /// |
| /// @param ctxt the parsing context. |
| /// |
| /// @return true upon successful parsing, false otherwise. |
| static decl_base_sptr |
| handle_type_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| type_decl_sptr decl = build_type_decl(ctxt, node, add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parses 'namespace-decl' xml element. |
| /// |
| /// @param ctxt the parsing context. |
| /// |
| /// @return true upon successful parsing, false otherwise. |
| static decl_base_sptr |
| handle_namespace_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| namespace_decl_sptr d = build_namespace_decl(ctxt, node, |
| add_to_current_scope); |
| return d; |
| } |
| |
| /// Parse a qualified-type-def xml element. |
| /// |
| /// @param ctxt the parsing context. |
| /// |
| /// @return true upon successful parsing, false otherwise. |
| static decl_base_sptr |
| handle_qualified_type_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| qualified_type_def_sptr decl = |
| build_qualified_type_decl(ctxt, node, |
| add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a pointer-type-decl element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static decl_base_sptr |
| handle_pointer_type_def(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| pointer_type_def_sptr decl = build_pointer_type_def(ctxt, node, |
| add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a reference-type-def element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// reference_type_def is added to. |
| static decl_base_sptr |
| handle_reference_type_def(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| reference_type_def_sptr decl = build_reference_type_def(ctxt, node, |
| add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a function-type element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// function_type is added to. |
| static type_base_sptr |
| handle_function_type(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| function_type_sptr type = build_function_type(ctxt, node, |
| add_to_current_scope); |
| ctxt.maybe_canonicalize_type(type, /*force_delay=*/true); |
| return type; |
| } |
| |
| /// Parse a array-type-def element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// array_type_def is added to. |
| static decl_base_sptr |
| handle_array_type_def(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| array_type_def_sptr decl = build_array_type_def(ctxt, node, |
| add_to_current_scope); |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse an enum-decl element. |
| /// |
| /// @param ctxt the context of the parsing. |
| static decl_base_sptr |
| handle_enum_type_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| enum_type_decl_sptr decl = |
| build_enum_type_decl_if_not_suppressed(ctxt, node, |
| add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a typedef-decl element. |
| /// |
| /// @param ctxt the context of the parsing. |
| static decl_base_sptr |
| handle_typedef_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| typedef_decl_sptr decl = build_typedef_decl(ctxt, node, |
| add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a var-decl element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param node the node to read & parse from. |
| /// |
| /// @param add_to_current_scope if set to yes, the resulting of this |
| /// function is added to its current scope. |
| static decl_base_sptr |
| handle_var_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| decl_base_sptr decl = build_var_decl_if_not_suppressed(ctxt, node, |
| add_to_current_scope); |
| ctxt.maybe_add_var_to_exported_decls(is_var_decl(decl)); |
| return decl; |
| } |
| |
| /// Parse a function-decl element. |
| /// |
| /// @param ctxt the context of the parsing |
| /// |
| /// @return true upon successful completion of the parsing, false |
| /// otherwise. |
| static decl_base_sptr |
| handle_function_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| return build_function_decl_if_not_suppressed(ctxt, node, class_decl_sptr(), |
| add_to_current_scope); |
| } |
| |
| /// Parse a 'class-decl' xml element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @return the resulting @ref class_decl built from the XML element |
| /// upon successful completion of the parsing, nil otherwise. |
| static decl_base_sptr |
| handle_class_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| class_decl_sptr decl = |
| build_class_decl_if_not_suppressed(ctxt, node, add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a 'union-decl' xml element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @return the resulting @ref union_decl built from the XML element |
| /// upon successful completion of the parsing, nil otherwise. |
| static decl_base_sptr |
| handle_union_decl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| union_decl_sptr decl = |
| build_union_decl_if_not_suppressed(ctxt, node, add_to_current_scope); |
| if (decl && decl->get_scope()) |
| ctxt.maybe_canonicalize_type(decl, /*force_delay=*/false); |
| return decl; |
| } |
| |
| /// Parse a 'function-template-decl' xml element. |
| /// |
| /// @param ctxt the parsing context. |
| /// |
| /// @return true upon successful completion of the parsing, false |
| /// otherwise. |
| static decl_base_sptr |
| handle_function_tdecl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| function_tdecl_sptr d = build_function_tdecl(ctxt, node, |
| add_to_current_scope); |
| return d; |
| } |
| |
| /// Parse a 'class-template-decl' xml element. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static decl_base_sptr |
| handle_class_tdecl(read_context& ctxt, |
| xmlNodePtr node, |
| bool add_to_current_scope) |
| { |
| class_tdecl_sptr decl = build_class_tdecl(ctxt, node, |
| add_to_current_scope); |
| return decl; |
| } |
| |
| /// De-serialize a translation unit from an ABI Instrumentation xml |
| /// file coming from an input stream. |
| /// |
| /// @param in a pointer to the input stream. |
| /// |
| /// @param env the environment to use. |
| /// |
| /// @return the translation unit resulting from the parsing upon |
| /// successful completion, or nil. |
| translation_unit_sptr |
| read_translation_unit_from_istream(istream* in, environment* env) |
| { |
| read_context read_ctxt(xml::new_reader_from_istream(in), env); |
| return read_translation_unit_from_input(read_ctxt); |
| } |
| template<typename T> |
| struct array_deleter |
| { |
| void |
| operator()(T* a) |
| { |
| delete [] a; |
| } |
| };//end array_deleter |
| |
| |
| /// Create an xml_reader::read_context to read a native XML ABI file. |
| /// |
| /// @param path the path to the native XML file to read. |
| /// |
| /// @param env the environment to use. |
| /// |
| /// @return the created context. |
| read_context_sptr |
| create_native_xml_read_context(const string& path, environment *env) |
| { |
| read_context_sptr result(new read_context(xml::new_reader_from_file(path), |
| env)); |
| corpus_sptr corp(new corpus(env)); |
| result->set_corpus(corp); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| if (env->self_comparison_debug_is_on()) |
| env->set_self_comparison_debug_input(result->get_corpus()); |
| #endif |
| result->set_path(path); |
| return result; |
| } |
| |
| /// Create an xml_reader::read_context to read a native XML ABI from |
| /// an input stream.. |
| /// |
| /// @param in the input stream that contains the native XML file to read. |
| /// |
| /// @param env the environment to use. |
| /// |
| /// @return the created context. |
| read_context_sptr |
| create_native_xml_read_context(std::istream* in, environment* env) |
| { |
| read_context_sptr result(new read_context(xml::new_reader_from_istream(in), |
| env)); |
| corpus_sptr corp(new corpus(env, "")); |
| result->set_corpus(corp); |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| if (env->self_comparison_debug_is_on()) |
| env->set_self_comparison_debug_input(result->get_corpus()); |
| #endif |
| return result; |
| } |
| |
| /// Getter for the path to the binary this @ref read_context is for. |
| /// |
| /// @return the path to the binary the @ref read_context is for. |
| const string& |
| read_context_get_path(const read_context& ctxt) |
| {return ctxt.get_path();} |
| |
| /// De-serialize an ABI corpus from an input XML document which root |
| /// node is 'abi-corpus'. |
| /// |
| /// @param in the input stream to read the XML document from. |
| /// |
| /// @param env the environment to use. Note that the life time of |
| /// this environment must be greater than the lifetime of the |
| /// resulting corpus as the corpus uses resources that are allocated |
| /// in the environment. |
| /// |
| /// @return the resulting corpus de-serialized from the parsing. This |
| /// is non-null iff the parsing resulted in a valid corpus. |
| corpus_sptr |
| read_corpus_from_native_xml(std::istream* in, |
| environment* env) |
| { |
| read_context_sptr read_ctxt = create_native_xml_read_context(in, env); |
| return read_corpus_from_input(*read_ctxt); |
| } |
| |
| /// De-serialize an ABI corpus from an XML document file which root |
| /// node is 'abi-corpus'. |
| /// |
| /// @param path the path to the input file to read the XML document |
| /// from. |
| /// |
| /// @param env the environment to use. Note that the life time of |
| /// this environment must be greater than the lifetime of the |
| /// resulting corpus as the corpus uses resources that are allocated |
| /// in the environment. |
| /// |
| /// @return the resulting corpus de-serialized from the parsing. This |
| /// is non-null if the parsing successfully resulted in a corpus. |
| corpus_sptr |
| read_corpus_from_native_xml_file(const string& path, |
| environment* env) |
| { |
| read_context_sptr read_ctxt = create_native_xml_read_context(path, env); |
| corpus_sptr corp = read_corpus_from_input(*read_ctxt); |
| return corp; |
| } |
| |
| }//end namespace xml_reader |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| /// Load the map that is stored at |
| /// environment::get_type_id_canonical_type_map(). |
| /// |
| /// That map associates type-ids to the pointer value of the canonical |
| /// types they correspond to. The map is loaded from a file that was |
| /// stored on disk by some debugging primitive that is activated when |
| /// the command "abidw --debug-abidiff <binary>' is used." |
| /// |
| /// The function that stored the map in that file is |
| /// write_canonical_type_ids. |
| /// |
| /// @param ctxt the read context to use. |
| /// |
| /// @param file_path the path to the file containing the type-ids <-> |
| /// canonical type mapping. |
| /// |
| /// @return true iff the loading was successful. |
| bool |
| load_canonical_type_ids(xml_reader::read_context& ctxt, const string &file_path) |
| { |
| xmlDocPtr doc = xmlReadFile(file_path.c_str(), NULL, XML_PARSE_NOERROR); |
| if (!doc) |
| return false; |
| |
| xmlNodePtr node = xmlDocGetRootElement(doc); |
| if (!node) |
| return false; |
| |
| // We expect a file which content looks like: |
| // |
| // <abixml-types-check> |
| // <type> |
| // <id>type-id-573</id> |
| // <c>0x262ee28</c> |
| // </type> |
| // <type> |
| // <id>type-id-569</id> |
| // <c>0x2628298</c> |
| // </type> |
| // <type> |
| // <id>type-id-575</id> |
| // <c>0x25f9ba8</c> |
| // </type> |
| // <abixml-types-check> |
| // |
| // So let's parse it! |
| |
| if (xmlStrcmp(node->name, (xmlChar*) "abixml-types-check")) |
| return false; |
| |
| for (node = xmlFirstElementChild(node); |
| node; |
| node = xmlNextElementSibling(node)) |
| { |
| if (xmlStrcmp(node->name, (xmlChar*) "type")) |
| continue; |
| |
| string id, canonical_address; |
| xmlNodePtr data = xmlFirstElementChild(node); |
| if (data && !xmlStrcmp(data->name, (xmlChar*) "id") |
| && data->children && xmlNodeIsText(data->children)) |
| id = (char*) XML_GET_CONTENT(data->children); |
| |
| data = xmlNextElementSibling(data); |
| if (data && !xmlStrcmp(data->name, (xmlChar*) "c") |
| && data->children && xmlNodeIsText(data->children)) |
| { |
| canonical_address = (char*) XML_GET_CONTENT(data->children); |
| std::stringstream s; |
| s << canonical_address; |
| uintptr_t v = 0; |
| s >> std::hex >> v; |
| if (!id.empty()) |
| ctxt.get_environment()->get_type_id_canonical_type_map()[id] = v; |
| } |
| } |
| return true; |
| } |
| #endif |
| |
| }//end namespace abigail |