// 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 #include #include #include #include #include #include #include #include #include #include "abg-suppression-priv.h" #include "abg-internal.h" #include "abg-tools-utils.h" // 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 // 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&); 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 >::const_iterator const_types_map_it; typedef unordered_map >::iterator types_map_it; typedef unordered_map >::const_iterator const_fn_tmpl_map_it; typedef unordered_map >::const_iterator const_class_tmpl_map_it; typedef unordered_map string_xml_node_map; typedef unordered_map xml_node_decl_base_sptr_map; private: string m_path; environment* m_env; unordered_map > m_types_map; unordered_map > m_fn_tmpl_map; unordered_map > m_class_tmpl_map; vector 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 > 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; bool m_merge_translation_units; 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(), m_merge_translation_units() {} /// 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 for the flag that tells us if we are merging translation /// units. /// /// @return true iff we are merging translation units. bool merge_translation_units() const {return m_merge_translation_units;} /// Setter for the flag that tells us if we are merging translation /// units. /// /// @param f the new value of the flag. void merge_translation_units(bool f) {m_merge_translation_units = 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;} xmlNodePtr get_corpus_node() const {return m_corp_node;} 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* 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 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(); 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 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(); return i->second; } /// Return the current lexical scope. scope_decl* get_cur_scope() const { shared_ptr cur_decl = get_cur_decl(); if (dynamic_cast(cur_decl.get())) // The current decl is a scope_decl, so it's our lexical scope. return dynamic_pointer_cast(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(static_cast(0)); return m_decls_stack.back(); } translation_unit* get_translation_unit() { const global_scope* global = 0; for (deque >::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 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(d); if (s == scope) break; } while (d); if (!d) return false; return dynamic_pointer_cast(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, 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 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_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 t, const string& id, bool add_to_current_scope) { shared_ptr decl = dynamic_pointer_cast(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(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(); } /// 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_qualified_type(t) && !is_typedef(t) && !is_enum_type(t) && !is_function_type(t)) canonicalize(t); 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::iterator i = m_types_to_canonicalize.begin(); i != m_types_to_canonicalize.end(); ++i) canonicalize(*i); } /// 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_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); // // // 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 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 build_pointer_type_def(read_context&, const xmlNodePtr, bool); static shared_ptr build_reference_type_def(read_context&, const xmlNodePtr, bool); static shared_ptr 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 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 build_function_tdecl(read_context&, const xmlNodePtr, bool); static shared_ptr 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 build_type(read_context&, const xmlNodePtr, bool); // 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 (handle_element_node(*this, parent, /*add_decl_to_scope=*/true)); ABG_ASSERT(scope); pop_scope_or_abort(parent_scope); } else scope = dynamic_pointer_cast(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 = n->children; n; n = n->next) 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(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(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 (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(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 = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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(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 { node = 0; for (xmlNodePtr n = ctxt.get_corpus_node()->next; n; n = n->next) { if (!n || n->type != XML_ELEMENT_NODE) continue; if (!xmlStrEqual(n->name, BAD_CAST("abi-instr"))) return nil; node = n; break; } } if (node == 0) return nil; tu = get_or_read_and_add_translation_unit(ctxt, node); // So read_translation_unit() can trigger (under the hood) reading // from several translation units just because // read_context::get_scope_for_node() has been called. In that // case, after that unexpected call to read_translation_unit(), the // current corpus node of the context is going to point to that // translation unit that has been read under the hood. Let's set // the corpus node to the one we initially called // read_translation_unit() on here. ctxt.set_corpus_node(node); return tu; } /// Parse the input XML document containing a function symbols /// or a variable symbol database. /// /// 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 function_symbols is true if this function should look for a /// function symbols database, false if it should look for a variable /// symbols database. /// /// @param symdb the resulting symbol database object. This is set /// iff the function return true. /// /// @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; bool found = 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); if (fn_symdb) found = true; } else if (has_var_syms) { var_symdb = build_elf_symbol_db(ctxt, node, false); if (var_symdb) found = true; } xmlTextReaderNext(reader.get()); } else for (xmlNodePtr n = ctxt.get_corpus_node()->next; n; n = n->next) { if (!n || n->type != XML_ELEMENT_NODE) continue; 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 break; ctxt.set_corpus_node(n); if (has_fn_syms) { fn_symdb = build_elf_symbol_db(ctxt, n, true); found = true; } else if (has_var_syms) { var_symdb = build_elf_symbol_db(ctxt, n, false); found = true; } else break; } return found; } /// 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& needed) { if (!node) return false; if (!node || !xmlStrEqual(node->name,BAD_CAST("elf-needed"))) return false; for (xmlNodePtr n = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE || !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& needed) { xml::reader_sptr reader = ctxt.get_reader(); if (!reader) return false; xmlNodePtr node = 0; if (ctxt.get_corpus_node() == 0) { 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 { for (xmlNodePtr n = ctxt.get_corpus_node()->next; n; n = n->next) { if (!n || n->type != XML_ELEMENT_NODE) continue; if (!xmlStrEqual(n->name, BAD_CAST("elf-needed"))) return false; node = n; break; } } bool result = false; if (node) { result = build_needed(node, needed); ctxt.set_corpus_node(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 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; 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; if (!ctxt.get_corpus()) { corpus_sptr c(new corpus(ctxt.get_environment(), "")); ctxt.set_corpus(c); } 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(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(architecture_str.get())); xml::xml_char_sptr soname_str = XML_READER_GET_ATTRIBUTE(reader, "soname"); string soname; if (soname_str) { soname = reinterpret_cast(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) = 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 { if (!ctxt.get_corpus()) { corpus_sptr c(new corpus(ctxt.get_environment(), "")); ctxt.set_corpus(c); } 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(path_str.get())); xml::xml_char_sptr architecture_str = XML_NODE_GET_ATTRIBUTE(node, "architecture"); if (architecture_str) corp.set_architecture_name (reinterpret_cast(architecture_str.get())); xml::xml_char_sptr soname_str = XML_NODE_GET_ATTRIBUTE(node, "soname"); if (soname_str) corp.set_soname(reinterpret_cast(soname_str.get())); } if (!node->children) return nil; ctxt.set_corpus_node(node->children); corpus& corp = *ctxt.get_corpus(); walk_xml_node_to_map_type_ids(ctxt, node); // Read the needed element vector 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. bool is_ok = read_symbol_db_from_input(ctxt, fn_sym_db, var_sym_db); if (is_ok) { // 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. do { translation_unit_sptr tu = read_translation_unit_from_input(ctxt); is_ok = bool(tu); } while (is_ok); 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()); } else { node = ctxt.get_corpus_node(); node = xml::advance_to_next_sibling_element(node); if (!node) { node = ctxt.get_corpus_node(); node = xml::advance_to_next_sibling_element(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(path_str.get())); xmlNodePtr node = xmlTextReaderExpand(reader.get()); if (!node) return nil; //node = xml::get_first_element_sibling_if_text(node->children); node = xml::advance_to_next_sibling_element(node->children); 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 false; if (xml_char_sptr l = xml::build_sptr(xmlGetProp(node, BAD_CAST("line")))) line = atoi(CHAR_STR(l)); if (xml_char_sptr c = xml::build_sptr(xmlGetProp(node, BAD_CAST("column")))) column = atoi(CHAR_STR(c)); read_context& c = const_cast(ctxt); loc = c.get_translation_unit()->get_loc_mgr().create_new_location(file_path, line, column); return true; } /// 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; } /// 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(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)); ctxt.push_decl_to_current_scope(decl, add_to_current_scope); ctxt.map_xml_node_to_decl(node, decl); for (xmlNodePtr n = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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, /*is_linux_string_cst=*/false); 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 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 = node->children; n; n = n->next) { 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 elems; std::stringstream aliases(alias_id); std::string item; while (std::getline(aliases, item, ',')) elems.push_back(item); for (std::vector::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 build_function_parameter(read_context& ctxt, const xmlNodePtr node) { shared_ptr 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 that is returned is then really a /// shared_ptr. /// /// @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 parms; type_base_sptr return_type = env->get_void_type(); for (xmlNodePtr n = node->children; n ; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; else 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); 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)); 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 build_var_decl(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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)); 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 build_type_decl(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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(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(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)); 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(d); 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); shared_ptr underlying_type = ctxt.build_or_get_type_decl(type_id, true); ABG_ASSERT(underlying_type); // maybe building the underlying type triggered building this one in // the mean time ... if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) { qualified_type_def_sptr result = dynamic_pointer_cast(d); ABG_ASSERT(result); return result; } string id; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE (node, "id")) id = CHAR_STR(s); 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"; qualified_type_def::CV cv = qualified_type_def::CV_NONE; 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; location loc; read_location(ctxt, node, loc); ABG_ASSERT(!id.empty()); qualified_type_def_sptr decl; if (type_base_sptr d = ctxt.get_type_decl(id)) { qualified_type_def_sptr ty = is_qualified_type(d); ABG_ASSERT(ty); string pr1 = get_pretty_representation(ty->get_underlying_type()), pr2 = get_pretty_representation(underlying_type); return ty; } decl.reset(new qualified_type_def(underlying_type, cv, loc)); if (ctxt.push_and_key_type_decl(decl, id, add_to_current_scope)) { ctxt.map_xml_node_to_decl(node, decl); return decl; } return shared_ptr((qualified_type_def*)0); } /// 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 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(d); 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); shared_ptr pointed_to_type = ctxt.build_or_get_type_decl(type_id, true); ABG_ASSERT(pointed_to_type); // maybe building the underlying type triggered building this one in // the mean time ... if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) { pointer_type_def_sptr result = dynamic_pointer_cast(d); ABG_ASSERT(result); return result; } 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 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)) { pointer_type_def_sptr ty = is_pointer_type(d); ABG_ASSERT(ty); ABG_ASSERT(ctxt.types_equal(pointed_to_type, ty->get_pointed_to_type())); return ty; } location loc; read_location(ctxt, node, loc); shared_ptr t(new pointer_type_def(pointed_to_type, size_in_bits, alignment_in_bits, loc)); 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 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 build_reference_type_def(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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(d); ABG_ASSERT(result); return result; } 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"; string type_id; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id")) type_id = CHAR_STR(s); shared_ptr pointed_to_type = ctxt.build_or_get_type_decl(type_id, true); ABG_ASSERT(pointed_to_type); // maybe building the underlying type triggered building this one in // the mean time ... if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) { reference_type_def_sptr result = dynamic_pointer_cast(d); ABG_ASSERT(result); return result; } 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 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); ABG_ASSERT(ctxt.types_equal(pointed_to_type, ty->get_pointed_to_type())); return ty; } location loc; read_location(ctxt, node, loc); shared_ptr t(new reference_type_def(pointed_to_type, is_lvalue, size_in_bits, alignment_in_bits, loc)); 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 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 > 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 = node->children; n ; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; else 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(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)); 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(d); ABG_ASSERT(result); return result; } 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); // The type of array elements. type_base_sptr type = ctxt.build_or_get_type_decl(type_id, true); ABG_ASSERT(type); // 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(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; } 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); ABG_ASSERT(*type == *ty->get_element_type()); ABG_ASSERT(type->get_alignment_in_bits() == alignment_in_bits); return ty; } location loc; read_location(ctxt, node, loc); array_type_def::subranges_type subranges; for (xmlNodePtr n = node->children; n ; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; else 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(type, subranges, loc)); 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::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; } } } if (ctxt.push_and_key_type_decl(ar_type, id, add_to_current_scope)) { ctxt.map_xml_node_to_decl(node, ar_type); return ar_type; } return nil; } /// 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(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 = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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)); 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 build_typedef_decl(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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()); 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); shared_ptr underlying_type(ctxt.build_or_get_type_decl(type_id, true)); ABG_ASSERT(underlying_type); // maybe building the underlying type triggered building this one in // the mean time ... if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node)) { typedef_decl_sptr result = dynamic_pointer_cast(d); ABG_ASSERT(result); return result; } location loc; read_location(ctxt, node, loc); if (type_base_sptr d = ctxt.get_type_decl(id)) { typedef_decl_sptr ty = dynamic_pointer_cast(d); ABG_ASSERT(ty); ABG_ASSERT(name == ty->get_name()); ABG_ASSERT(get_type_name(underlying_type) == get_type_name(ty->get_underlying_type())); // it's possible to have the same typedef several times. } typedef_decl_sptr t(new typedef_decl(name, underlying_type, loc)); 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 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(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; const vector *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::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); } 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)); } 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::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 = node->children; !is_decl_only && n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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 b = dynamic_pointer_cast (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 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; if (shared_ptr f = build_function_tdecl(ctxt, p, /*add_to_current_scope=*/true)) { shared_ptr 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 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(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 *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::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)); } 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(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::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 = node->children; !is_decl_only && n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 = n->children; p; p = p->next) { if (p->type != XML_ELEMENT_NODE) continue; 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 build_function_tdecl(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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)); ctxt.push_decl_to_current_scope(fn_tmpl_decl, add_to_current_scope); unsigned parm_index = 0; for (xmlNodePtr n = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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 build_class_tdecl(read_context& ctxt, const xmlNodePtr node, bool add_to_current_scope) { shared_ptr 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)); ctxt.push_decl_to_current_scope(class_tmpl, add_to_current_scope); unsigned parm_index = 0; for (xmlNodePtr n = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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 (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)); if (id.empty()) ctxt.push_decl_to_current_scope(dynamic_pointer_cast(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(result), /*add_to_current_scope=*/true); for (xmlNodePtr n = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; 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)); ctxt.push_decl_to_current_scope(dynamic_pointer_cast(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 (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)); 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 = node->children; n; n = n->next) { if (n->type != XML_ELEMENT_NODE) continue; if (shared_ptr 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 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); } ctxt.maybe_canonicalize_type(t); 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 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); 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); 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 }//end namespace abigail