| // 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 to an |
| /// ABI Instrumentation file in libabigail native XML format. This |
| /// native XML format is named "abixml". |
| |
| #include "config.h" |
| #include <assert.h> |
| #include <algorithm> |
| #include <fstream> |
| #include <iomanip> |
| #include <ios> |
| #include <iostream> |
| #include <memory> |
| #include <sstream> |
| #include <stack> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "abg-tools-utils.h" |
| |
| #include "abg-internal.h" |
| // <headers defining libabigail's API go under here> |
| ABG_BEGIN_EXPORT_DECLARATIONS |
| |
| #include "abg-config.h" |
| #include "abg-corpus.h" |
| #include "abg-hash.h" |
| #include "abg-sptr-utils.h" |
| |
| #include "abg-writer.h" |
| #include "abg-libxml-utils.h" |
| #include "abg-fwd.h" |
| |
| ABG_END_EXPORT_DECLARATIONS |
| // </headers defining libabigail's API> |
| |
| namespace abigail |
| { |
| using std::cerr; |
| using std::shared_ptr; |
| using std::dynamic_pointer_cast; |
| using std::static_pointer_cast; |
| using std::ofstream; |
| using std::ostream; |
| using std::ostringstream; |
| using std::list; |
| using std::vector; |
| using std::stack; |
| using std::unordered_map; |
| using abigail::sptr_utils::noop_deleter; |
| |
| /// The namespace for the native XML file format writer. |
| /// |
| /// It contains utilities to serialize ABI artifacts from the @ref ir |
| /// namespace into the native XML format. |
| namespace xml_writer |
| { |
| |
| class id_manager |
| { |
| const environment* m_env; |
| mutable unsigned long long m_cur_id; |
| |
| unsigned long long |
| get_new_id() const |
| { return ++m_cur_id; } |
| |
| public: |
| id_manager(const environment* env) |
| : m_env(env), |
| m_cur_id(0) {} |
| |
| const environment* |
| get_environment() const |
| {return m_env;} |
| |
| /// Return a unique string representing a numerical id. |
| interned_string |
| get_id() const |
| { |
| ostringstream o; |
| o << get_new_id(); |
| const environment* env = get_environment(); |
| ABG_ASSERT(env); |
| return env->intern(o.str()); |
| } |
| |
| /// Return a unique string representing a numerical ID, prefixed by |
| /// prefix. |
| /// |
| /// @param prefix the prefix of the returned unique id. |
| interned_string |
| get_id_with_prefix(const string& prefix) const |
| { |
| ostringstream o; |
| o << prefix << get_new_id(); |
| const environment* env = get_environment(); |
| ABG_ASSERT(env); |
| return env->intern(o.str()); |
| } |
| }; |
| |
| /// A convenience typedef for a map that associates a pointer to type |
| /// to a string. |
| typedef unordered_map<type_base*, interned_string> type_ptr_map; |
| |
| // A convenience typedef for a set of type_base*. |
| typedef std::unordered_set<const type_base*> type_ptr_set_type; |
| |
| /// A convenience typedef for a set of function type*. |
| typedef std::unordered_set<function_type*> fn_type_ptr_set_type; |
| |
| typedef unordered_map<shared_ptr<function_tdecl>, |
| string, |
| function_tdecl::shared_ptr_hash> fn_tmpl_shared_ptr_map; |
| |
| typedef unordered_map<shared_ptr<class_tdecl>, |
| string, |
| class_tdecl::shared_ptr_hash> class_tmpl_shared_ptr_map; |
| |
| class write_context |
| { |
| const environment* m_env; |
| id_manager m_id_manager; |
| ostream* m_ostream; |
| bool m_annotate; |
| bool m_show_locs; |
| bool m_write_architecture; |
| bool m_write_corpus_path; |
| bool m_write_comp_dir; |
| bool m_write_elf_needed; |
| bool m_write_parameter_names; |
| bool m_short_locs; |
| bool m_write_default_sizes; |
| type_id_style_kind m_type_id_style; |
| mutable type_ptr_map m_type_id_map; |
| mutable unordered_set<uint32_t> m_used_type_id_hashes; |
| mutable type_ptr_set_type m_emitted_type_set; |
| // A map of types that are referenced by emitted pointers, |
| // references or typedefs |
| type_ptr_set_type m_referenced_types_set; |
| fn_type_ptr_set_type m_referenced_fn_types_set; |
| type_ptr_set_type m_referenced_non_canonical_types_set; |
| fn_tmpl_shared_ptr_map m_fn_tmpl_id_map; |
| class_tmpl_shared_ptr_map m_class_tmpl_id_map; |
| string_elf_symbol_sptr_map_type m_fun_symbol_map; |
| string_elf_symbol_sptr_map_type m_var_symbol_map; |
| unordered_set<interned_string, hash_interned_string> m_emitted_decls_set; |
| |
| write_context(); |
| |
| public: |
| |
| /// Constructor. |
| /// |
| /// @param env the enviroment we are operating from. |
| /// |
| /// @param os the output stream to write to. |
| write_context(const environment* env, ostream& os) |
| : m_env(env), |
| m_id_manager(env), |
| m_ostream(&os), |
| m_annotate(false), |
| m_show_locs(true), |
| m_write_architecture(true), |
| m_write_corpus_path(true), |
| m_write_comp_dir(true), |
| m_write_elf_needed(true), |
| m_write_parameter_names(true), |
| m_short_locs(false), |
| m_write_default_sizes(true), |
| m_type_id_style(SEQUENCE_TYPE_ID_STYLE) |
| {} |
| |
| /// Getter of the environment we are operating from. |
| /// |
| /// @return the environment we are operating from. |
| const environment* |
| get_environment() const |
| {return m_env;} |
| |
| const config& |
| get_config() const |
| { |
| ABG_ASSERT(get_environment()); |
| return get_environment()->get_config(); |
| } |
| |
| /// Getter for the current ostream |
| /// |
| /// @return a reference to the current ostream |
| ostream& |
| get_ostream() |
| {return *m_ostream;} |
| |
| /// Setter for the current ostream |
| /// |
| /// @param os the new ostream |
| void |
| set_ostream(ostream& os) |
| {m_ostream = &os;} |
| |
| /// Getter of the annotation option. |
| /// |
| /// @return true iff ABIXML annotations are turned on |
| bool |
| get_annotate() |
| {return m_annotate;} |
| |
| /// Setter of the annotation option. |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_annotate(bool f) |
| {m_annotate = f;} |
| |
| /// Getter of the write-architecture option. |
| /// |
| /// @return true iff architecture information shall be emitted |
| bool |
| get_write_architecture() |
| {return m_write_architecture;} |
| |
| /// Setter of the write-architecture option |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_architecture(bool f) |
| {m_write_architecture = f;} |
| |
| /// Getter of the elf-needed option. |
| /// |
| /// @return true iff elf needed information shall be emitted |
| bool |
| get_write_elf_needed() |
| {return m_write_elf_needed;} |
| |
| /// Setter of the elf-needed option. |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_elf_needed(bool f) |
| {m_write_elf_needed = f;} |
| |
| /// Getter of the default-sizes option. |
| /// |
| /// @return true iff default size-in-bits needs to be emitted |
| bool |
| get_write_default_sizes() |
| {return m_write_default_sizes;} |
| |
| /// Setter of the default-sizes option. |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_default_sizes(bool f) |
| {m_write_default_sizes = f;} |
| |
| /// Getter of the write-corpus-path option. |
| /// |
| /// @return true iff corpus-path information shall be emitted |
| bool |
| get_write_corpus_path() |
| {return m_write_corpus_path;} |
| |
| /// Setter of the write-corpus-path option |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_corpus_path(bool f) |
| {m_write_corpus_path = f;} |
| |
| /// Getter of the comp-dir-path option. |
| /// |
| /// @return true iff compilation dir information shall be emitted |
| bool |
| get_write_comp_dir() |
| {return m_write_comp_dir;} |
| |
| /// Setter of the comp-dir-path option |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_comp_dir(bool f) |
| {m_write_comp_dir = f;} |
| |
| /// Getter of the short-locs option. |
| /// |
| /// @return true iff short locations shall be emitted |
| bool |
| get_short_locs() |
| {return m_short_locs;} |
| |
| /// Setter of the short-locs option |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_short_locs(bool f) |
| {m_short_locs = f;} |
| |
| /// Getter of the parameter-names option. |
| /// |
| /// @return true iff parameter names shall be emitted |
| bool |
| get_write_parameter_names() const |
| {return m_write_parameter_names;} |
| |
| /// Setter of the parameter-names option |
| /// |
| /// @param f the new value of the flag. |
| void |
| set_write_parameter_names(bool f) |
| {m_write_parameter_names = f;} |
| |
| /// Getter of the "show-locs" option. |
| /// |
| /// When this option is true then the XML writer emits location |
| /// information for emitted ABI artifacts. |
| /// |
| /// @return the value of the "show-locs" option. |
| bool |
| get_show_locs() const |
| {return m_show_locs;} |
| |
| /// Setter of the "show-locs" option. |
| /// |
| /// When this option is true then the XML writer emits location |
| /// information for emitted ABI artifacts. |
| /// |
| /// @param f the new value of the "show-locs" option. |
| void |
| set_show_locs(bool f) |
| {m_show_locs = f;} |
| |
| /// Getter of the "type-id-style" option. |
| /// |
| /// This option controls the kind of type ids used in XML output. |
| /// |
| /// @return the value of the "type-id-style" option. |
| type_id_style_kind |
| get_type_id_style() const |
| {return m_type_id_style;} |
| |
| /// Setter of the "type-id-style" option. |
| /// |
| /// This option controls the kind of type ids used in XML output. |
| /// |
| /// @param style the new value of the "type-id-style" option. |
| void |
| set_type_id_style(type_id_style_kind style) |
| {m_type_id_style = style;} |
| |
| /// Getter of the @ref id_manager. |
| /// |
| /// @return the @ref id_manager used by the current instance of @ref |
| /// write_context. |
| const id_manager& |
| get_id_manager() const |
| {return m_id_manager;} |
| |
| id_manager& |
| get_id_manager() |
| {return m_id_manager;} |
| |
| /// @return true iff type has already been assigned an ID. |
| bool |
| type_has_existing_id(type_base_sptr type) const |
| {return type_has_existing_id(type.get());} |
| |
| /// @return true iff type has already been assigned an ID. |
| bool |
| type_has_existing_id(type_base* type) const |
| { |
| type = get_exemplar_type(type); |
| return m_type_id_map.find(type) != m_type_id_map.end(); |
| } |
| |
| /// Associate a unique id to a given type. For that, put the type |
| /// in a hash table, hashing the type. So if the type has no id |
| /// associated to it, create a new one and return it. Otherwise, |
| /// return the existing id for that type. |
| interned_string |
| get_id_for_type(const type_base_sptr& t) |
| {return get_id_for_type(t.get());} |
| |
| /// Associate a unique id to a given type. For that, put the type |
| /// in a hash table, hashing the type. So if the type has no id |
| /// associated to it, create a new one and return it. Otherwise, |
| /// return the existing id for that type. |
| interned_string |
| get_id_for_type(type_base* type) const |
| { |
| type_base* c = get_exemplar_type(type); |
| |
| type_ptr_map::const_iterator it = m_type_id_map.find(c); |
| if (it != m_type_id_map.end()) |
| return it->second; |
| |
| switch (m_type_id_style) |
| { |
| case SEQUENCE_TYPE_ID_STYLE: |
| { |
| interned_string id = get_id_manager().get_id_with_prefix("type-id-"); |
| return m_type_id_map[c] = id; |
| } |
| case HASH_TYPE_ID_STYLE: |
| { |
| interned_string pretty = c->get_cached_pretty_representation(true); |
| size_t hash = hashing::fnv_hash(pretty); |
| while (!m_used_type_id_hashes.insert(hash).second) |
| ++hash; |
| std::ostringstream os; |
| os << std::hex << std::setfill('0') << std::setw(8) << hash; |
| return m_type_id_map[c] = c->get_environment()->intern(os.str()); |
| } |
| } |
| ABG_ASSERT_NOT_REACHED; |
| return interned_string(); |
| } |
| |
| string |
| get_id_for_fn_tmpl(const function_tdecl_sptr& f) |
| { |
| fn_tmpl_shared_ptr_map::const_iterator it = m_fn_tmpl_id_map.find(f); |
| if (it == m_fn_tmpl_id_map.end()) |
| { |
| string id = get_id_manager().get_id_with_prefix("fn-tmpl-id-"); |
| m_fn_tmpl_id_map[f] = id; |
| return id; |
| } |
| return m_fn_tmpl_id_map[f]; |
| } |
| |
| string |
| get_id_for_class_tmpl(const class_tdecl_sptr& c) |
| { |
| class_tmpl_shared_ptr_map::const_iterator it = m_class_tmpl_id_map.find(c); |
| if (it == m_class_tmpl_id_map.end()) |
| { |
| string id = get_id_manager().get_id_with_prefix("class-tmpl-id-"); |
| m_class_tmpl_id_map[c] = id; |
| return id; |
| } |
| return m_class_tmpl_id_map[c]; |
| } |
| |
| void |
| clear_type_id_map() |
| {m_type_id_map.clear();} |
| |
| |
| /// Getter of the set of types that were referenced by a pointer, |
| /// reference or typedef. |
| /// |
| /// This set contains only types that do have canonical types and |
| /// which are not function types. |
| /// |
| /// @return the set of types that were referenced. |
| const type_ptr_set_type& |
| get_referenced_types() const |
| {return m_referenced_types_set;} |
| |
| /// Getter of the set of function types that were referenced by a |
| /// pointer, reference or typedef. |
| /// |
| /// @return the set of function types that were referenced. |
| const fn_type_ptr_set_type& |
| get_referenced_function_types() const |
| {return m_referenced_fn_types_set;} |
| |
| /// Getter of the set of types which have no canonical types and |
| /// which were referenced by a pointer, reference or typedef. |
| /// |
| /// @return the of referenced type that have no canonical types. |
| const type_ptr_set_type& |
| get_referenced_non_canonical_types() const |
| {return m_referenced_non_canonical_types_set;} |
| |
| /// Test if there are non emitted referenced types. |
| /// |
| /// @return true iff there are non emitted referenced types. |
| bool |
| has_non_emitted_referenced_types() const |
| { |
| for (const auto t : get_referenced_types()) |
| if (!type_is_emitted(t)) |
| return false; |
| |
| for (const auto t : get_referenced_function_types()) |
| if (!type_is_emitted(t)) |
| return false; |
| |
| for (const auto t : get_referenced_non_canonical_types()) |
| if (!type_is_emitted(t)) |
| return false; |
| |
| return true; |
| } |
| |
| /// Record a given type as being referenced by a pointer, a |
| /// reference or a typedef type that is being emitted to the XML |
| /// output. |
| /// |
| /// @param t a shared pointer to a type |
| void |
| record_type_as_referenced(const type_base_sptr& type) |
| { |
| type_base* t = get_exemplar_type(type.get()); |
| // If the type is a function type, record it in a dedicated data |
| // structure. |
| if (function_type* f = is_function_type(t)) |
| m_referenced_fn_types_set.insert(f); |
| else if (!t->get_naked_canonical_type()) |
| // If the type doesn't have a canonical type, record it in a |
| // dedicated data structure. |
| m_referenced_non_canonical_types_set.insert(t); |
| else |
| m_referenced_types_set.insert(t); |
| } |
| |
| /// Test if a given type has been referenced by a pointer, a |
| /// reference or a typedef type that was emitted to the XML output. |
| /// |
| /// @param f a shared pointer to a type |
| /// |
| /// @return true if the type has been referenced, false |
| /// otherwise. |
| bool |
| type_is_referenced(const type_base_sptr& type) |
| { |
| type_base* t = get_exemplar_type(type.get()); |
| if (function_type* f = is_function_type(t)) |
| return (m_referenced_fn_types_set.find(f) |
| != m_referenced_fn_types_set.end()); |
| else if (!t->get_naked_canonical_type()) |
| return (m_referenced_non_canonical_types_set.find(t) |
| != m_referenced_non_canonical_types_set.end()); |
| else |
| return m_referenced_types_set.find(t) != m_referenced_types_set.end(); |
| } |
| |
| /// A comparison functor to compare pointers to @ref type_base. |
| /// |
| /// What is compared is the string representation of the pointed-to |
| /// type. |
| struct type_ptr_cmp |
| { |
| type_ptr_map *map; |
| type_ptr_cmp(type_ptr_map *m) |
| : map(m) |
| {} |
| |
| /// The comparison operator of the functor. |
| /// |
| /// @param l the first type to consider. |
| /// |
| /// @param r the second type to consider. |
| /// |
| /// @return true if the string representation of type @p l is |
| /// considered to be "less than" the string representation of the |
| /// type @p r. |
| /// |
| /// But when the two string representations are equal (for |
| /// instance, for typedefs that have the same string |
| /// representation), this function compares the type-ids of the |
| /// types. This allows for a stable result. |
| bool |
| operator()(const type_base* l, const type_base* r) const |
| { |
| if (!l && r) |
| return true; |
| if (l && !r) |
| return false; |
| if (!l && !r) |
| return false; |
| |
| string r1 = ir::get_pretty_representation(l, true), |
| r2 = ir::get_pretty_representation(r, true); |
| |
| if (r1 == r2) |
| { |
| type_ptr_map::const_iterator i = |
| map->find(const_cast<type_base*>(l)); |
| if (i != map->end()) |
| r1 = i->second; |
| i = map->find(const_cast<type_base*>(r)); |
| if (i != map->end()) |
| r2 = i->second; |
| } |
| |
| return r1 < r2; |
| } |
| |
| /// The comparison operator of the functor. |
| /// |
| /// @param l the first type to consider. |
| /// |
| /// @param r the second type to consider. |
| /// |
| /// @return true if the string representation of type @p l is |
| /// considered to be "less than" the string representation of the |
| /// type @p r. |
| /// |
| /// But when the two string representations are equal (for |
| /// instance, for typedefs that have the same string |
| /// representation), this function compares the type-ids of the |
| /// types. This allows for a stable result. |
| bool |
| operator()(const type_base_sptr& l, const type_base_sptr& r) const |
| {return operator()(l.get(), r.get());} |
| }; // end struct type_ptr_cmp |
| |
| /// Sort the content of a map of type pointers into a vector. |
| /// |
| /// The pointers are sorted by using their string representation as |
| /// the key to sort, lexicographically. |
| /// |
| /// @param types the map to sort. |
| /// |
| /// @return the resulting sorted vector. |
| std::vector<type_base*> |
| sort_types(type_ptr_set_type& types) |
| { |
| std::vector<type_base*> sorted; |
| sorted.reserve(types.size()); |
| for (const type_base* t : types) |
| sorted.push_back(const_cast<type_base*>(t)); |
| type_ptr_cmp comp(&m_type_id_map); |
| std::sort(sorted.begin(), sorted.end(), comp); |
| return sorted; |
| } |
| |
| /// Sort the content of a vector of function types into a vector of |
| /// types. |
| /// |
| /// The pointers are sorted by using their string representation as |
| /// the key to sort, lexicographically. |
| /// |
| /// @param types the vector of function types to sort. |
| /// |
| /// @return the resulting sorted vector. |
| std::vector<type_base_sptr> |
| sort_types(const vector<function_type_sptr>& types) |
| { |
| std::vector<type_base_sptr> sorted; |
| sorted.reserve(types.size()); |
| for (const auto& t : types) |
| sorted.push_back(t); |
| type_ptr_cmp comp(&m_type_id_map); |
| std::sort(sorted.begin(), sorted.end(), comp); |
| return sorted; |
| } |
| |
| /// Flag a type as having been written out to the XML output. |
| /// |
| /// @param t the type to flag. |
| void |
| record_type_as_emitted(const type_base_sptr &t) |
| {record_type_as_emitted(t.get());} |
| |
| /// Flag a type as having been written out to the XML output. |
| /// |
| /// @param t the type to flag. |
| void |
| record_type_as_emitted(const type_base* t) |
| { |
| type_base* c = get_exemplar_type(t); |
| m_emitted_type_set.insert(c); |
| } |
| |
| /// Test if a given type has been written out to the XML output. |
| /// |
| /// @param the type to test for. |
| /// |
| /// @return true if the type has already been emitted, false |
| /// otherwise. |
| bool |
| type_is_emitted(const type_base* t) const |
| { |
| type_base* c = get_exemplar_type(t); |
| return m_emitted_type_set.find(c) != m_emitted_type_set.end(); |
| } |
| |
| /// Test if a given type has been written out to the XML output. |
| /// |
| /// @param the type to test for. |
| /// |
| /// @return true if the type has already been emitted, false |
| /// otherwise. |
| bool |
| type_is_emitted(const type_base_sptr& t) const |
| {return type_is_emitted(t.get());} |
| |
| /// Test if a given decl has been written out to the XML output. |
| /// |
| /// @param the decl to consider. |
| /// |
| /// @return true if the decl has already been emitted, false |
| /// otherwise. |
| bool |
| decl_is_emitted(const decl_base_sptr& decl) const |
| { |
| ABG_ASSERT(!is_type(decl)); |
| string repr = get_pretty_representation(decl, true); |
| interned_string irepr = decl->get_environment()->intern(repr); |
| return m_emitted_decls_set.find(irepr) != m_emitted_decls_set.end(); |
| } |
| |
| /// Record a declaration as emitted in the abixml output. |
| /// |
| /// @param decl the decl to consider. |
| void |
| record_decl_as_emitted(const decl_base_sptr& decl) |
| { |
| string repr = get_pretty_representation(decl, true); |
| interned_string irepr = decl->get_environment()->intern(repr); |
| m_emitted_decls_set.insert(irepr); |
| } |
| |
| /// Get the set of types that have been emitted. |
| /// |
| /// @return the set of types that have been emitted. |
| const type_ptr_set_type& |
| get_emitted_types_set() const |
| {return m_emitted_type_set;} |
| |
| /// Clear the map that contains the IDs of the types that has been |
| /// recorded as having been written out to the XML output. |
| void |
| clear_referenced_types() |
| { |
| m_referenced_types_set.clear(); |
| m_referenced_non_canonical_types_set.clear(); |
| m_referenced_fn_types_set.clear(); |
| } |
| |
| const string_elf_symbol_sptr_map_type& |
| get_fun_symbol_map() const |
| {return m_fun_symbol_map;} |
| |
| string_elf_symbol_sptr_map_type& |
| get_fun_symbol_map() |
| {return m_fun_symbol_map;} |
| |
| };//end write_context |
| |
| static void write_location(const location&, write_context&); |
| static void write_location(const decl_base_sptr&, write_context&); |
| static bool write_visibility(const decl_base_sptr&, ostream&); |
| static bool write_binding(const decl_base_sptr&, ostream&); |
| static bool write_is_artificial(const decl_base_sptr&, ostream&); |
| static bool write_is_non_reachable(const type_base_sptr&, ostream&); |
| static bool write_tracking_non_reachable_types(const corpus_sptr&, ostream&); |
| static void write_array_size_and_alignment(const array_type_def_sptr, |
| ostream&); |
| static void write_size_and_alignment(const type_base_sptr, ostream&, |
| size_t default_size = 0, |
| size_t default_alignment = 0); |
| static void write_access(access_specifier, ostream&); |
| static void write_layout_offset(var_decl_sptr, ostream&); |
| static void write_layout_offset(class_decl::base_spec_sptr, ostream&); |
| static void write_cdtor_const_static(bool, bool, bool, bool, ostream&); |
| static void write_voffset(function_decl_sptr, ostream&); |
| static void write_elf_symbol_type(elf_symbol::type, ostream&); |
| static void write_elf_symbol_binding(elf_symbol::binding, ostream&); |
| static bool write_elf_symbol_aliases(const elf_symbol&, ostream&); |
| static bool write_elf_symbol_reference(const elf_symbol&, ostream&); |
| static bool write_elf_symbol_reference(const elf_symbol_sptr, ostream&); |
| static void write_is_declaration_only(const decl_base_sptr&, ostream&); |
| static void write_is_struct(const class_decl_sptr&, ostream&); |
| static void write_is_anonymous(const decl_base_sptr&, ostream&); |
| static void write_naming_typedef(const decl_base_sptr&, write_context&); |
| static bool write_decl(const decl_base_sptr&, write_context&, unsigned); |
| static void write_decl_in_scope(const decl_base_sptr&, |
| write_context&, unsigned); |
| static bool write_type_decl(const type_decl_sptr&, write_context&, unsigned); |
| static bool write_namespace_decl(const namespace_decl_sptr&, |
| write_context&, unsigned); |
| static bool write_qualified_type_def(const qualified_type_def_sptr&, |
| write_context&, unsigned); |
| static bool write_pointer_type_def(const pointer_type_def_sptr&, |
| write_context&, unsigned); |
| static bool write_reference_type_def(const reference_type_def_sptr&, |
| write_context&, unsigned); |
| static bool write_array_type_def(const array_type_def_sptr&, |
| write_context&, unsigned); |
| static bool write_enum_type_decl(const enum_type_decl_sptr&, |
| write_context&, unsigned); |
| static bool write_typedef_decl(const typedef_decl_sptr&, |
| write_context&, unsigned); |
| static bool write_elf_symbol(const elf_symbol_sptr&, |
| write_context&, unsigned); |
| static bool write_elf_symbols_table(const elf_symbols&, |
| write_context&, unsigned); |
| static bool write_var_decl(const var_decl_sptr&, |
| write_context&, bool, unsigned); |
| static bool write_function_decl(const function_decl_sptr&, |
| write_context&, bool, unsigned); |
| static bool write_function_type(const function_type_sptr&, |
| write_context&, unsigned); |
| static bool write_member_type_opening_tag(const type_base_sptr&, |
| write_context&, unsigned); |
| static bool write_member_type(const type_base_sptr&, |
| write_context&, unsigned); |
| static bool write_class_decl_opening_tag(const class_decl_sptr&, |
| write_context&, unsigned, bool); |
| static bool write_class_decl(const class_decl_sptr&, |
| write_context&, unsigned); |
| static bool write_union_decl_opening_tag(const union_decl_sptr&, |
| write_context&, unsigned, bool); |
| static bool write_union_decl(const union_decl_sptr&, write_context&, unsigned); |
| static bool write_type_tparameter |
| (const shared_ptr<type_tparameter>, write_context&, unsigned); |
| static bool write_non_type_tparameter |
| (const shared_ptr<non_type_tparameter>, write_context&, unsigned); |
| static bool write_template_tparameter |
| (const shared_ptr<template_tparameter>, write_context&, unsigned); |
| static bool write_type_composition |
| (const shared_ptr<type_composition>, write_context&, unsigned); |
| static bool write_template_parameter(const shared_ptr<template_parameter>, |
| write_context&, unsigned); |
| static void write_template_parameters(const shared_ptr<template_decl>, |
| write_context&, unsigned); |
| static bool write_function_tdecl |
| (const shared_ptr<function_tdecl>, |
| write_context&, unsigned); |
| static bool write_class_tdecl |
| (const shared_ptr<class_tdecl>, |
| write_context&, unsigned); |
| static void do_indent(ostream&, unsigned); |
| static void do_indent_to_level(write_context&, unsigned, unsigned); |
| static unsigned get_indent_to_level(write_context&, unsigned, unsigned); |
| |
| /// Emit nb_whitespaces white spaces into the output stream. |
| void |
| do_indent(ostream& o, unsigned nb_whitespaces) |
| { |
| for (unsigned i = 0; i < nb_whitespaces; ++i) |
| o << ' '; |
| } |
| |
| /// Indent initial_indent + level number of xml element indentation. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param initial_indent the initial number of white space to indent to. |
| /// |
| /// @param level the number of indentation level to indent to. |
| static void |
| do_indent_to_level(write_context& ctxt, |
| unsigned initial_indent, |
| unsigned level) |
| { |
| do_indent(ctxt.get_ostream(), |
| get_indent_to_level(ctxt, initial_indent, level)); |
| } |
| |
| /// Return the number of white space of indentation that |
| /// #do_indent_to_level would have used. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param initial_indent the initial number of white space to indent to. |
| /// |
| /// @param level the number of indentation level to indent to. |
| static unsigned |
| get_indent_to_level(write_context& ctxt, unsigned initial_indent, |
| unsigned level) |
| { |
| int nb_ws = initial_indent + |
| level * ctxt.get_config().get_xml_element_indent(); |
| return nb_ws; |
| } |
| |
| /// Annotate a declaration in form of an ABIXML comment. |
| /// |
| /// This function is further specialized for declarations and types |
| /// with special requirements. |
| /// |
| /// @tparam T shall be of type decl_base_sptr or a shared pointer to a |
| /// type derived from it, for the instantiation to be syntactically |
| /// correct. |
| /// |
| /// @param decl_sptr the shared pointer to the declaration of type T. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @return true iff decl is valid. |
| template <typename T> |
| static bool |
| annotate(const T& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<!-- " |
| << xml::escape_xml_comment(decl->get_pretty_representation(/*internal=*/false)) |
| << " -->\n"; |
| |
| return true; |
| } |
| |
| /// Annotate an elf symbol in form of an ABIXML comment, effectively |
| /// writing out its demangled form. |
| /// |
| /// @param sym the symbol, whose name should be demangled. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @return true iff decl is valid |
| template<> |
| bool |
| annotate(const elf_symbol_sptr& sym, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!sym) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| o << "<!-- " |
| << xml::escape_xml_comment(abigail::ir::demangle_cplus_mangled_name(sym->get_name())) |
| << " -->\n"; |
| |
| return true; |
| } |
| |
| /// Annotate a typedef declaration in form of an ABIXML comment. |
| /// |
| /// @param typedef_decl the typedef to annotate. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @return true iff decl is valid |
| template<> |
| bool |
| annotate(const typedef_decl_sptr& typedef_decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!typedef_decl) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<!-- typedef " |
| << get_type_name(typedef_decl->get_underlying_type()) |
| << " " |
| << get_type_name(typedef_decl) |
| << " -->\n"; |
| |
| return true; |
| } |
| |
| /// Annotate a function type in form of an ABIXML comment. |
| /// |
| /// @param function_type the function type to annotate. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @param skip_first_parm if true, do not serialize the first |
| /// parameter of the function decl. |
| // |
| /// @return true iff decl is valid |
| bool |
| annotate(const function_type_sptr& function_type, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!function_type) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| o << "<!-- " |
| << xml::escape_xml_comment(get_type_name(function_type->get_return_type())) |
| << " ("; |
| |
| vector<shared_ptr<function_decl::parameter> >::const_iterator pi = |
| function_type->get_first_non_implicit_parm(); |
| |
| for (; pi != function_type->get_parameters().end(); ++pi) |
| { |
| o << xml::escape_xml_comment((*pi)->get_type_name()); |
| // emit a comma after a param type, unless it's the last one |
| if (distance(pi, function_type->get_parameters().end()) > 1) |
| o << ", "; |
| } |
| o << ") -->\n"; |
| |
| return true; |
| } |
| |
| /// Annotate a function declaration in form of an ABIXML comment. |
| /// |
| /// @param fn the function decl to annotate. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @param skip_first_parm if true, do not serialize the first |
| /// parameter of the function decl. |
| // |
| /// @return true iff decl is valid |
| static bool |
| annotate(const function_decl_sptr& fn, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!fn) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| o << "<!-- "; |
| |
| if (is_member_function(fn) |
| && (get_member_function_is_ctor(fn) || get_member_function_is_dtor(fn))) |
| ; // we don't emit return types for ctor or dtors |
| else |
| o << xml::escape_xml_comment(get_type_name(fn->get_return_type())) |
| << " "; |
| |
| o << xml::escape_xml_comment(fn->get_qualified_name()) << "("; |
| |
| vector<function_decl::parameter_sptr>::const_iterator pi = |
| fn->get_first_non_implicit_parm(); |
| |
| for (; pi != fn->get_parameters().end(); ++pi) |
| { |
| o << xml::escape_xml_comment((*pi)->get_type_name()); |
| // emit a comma after a param type, unless it's the last one |
| if (distance(pi, fn->get_parameters().end()) > 1) |
| o << ", "; |
| } |
| o << ") -->\n"; |
| |
| return true; |
| } |
| |
| /// Annotate a function parameter in form of an ABIXML comment. |
| /// |
| /// @param parm the function parameter to annotate. |
| /// |
| /// @param ctxt the context of the parsing. |
| /// |
| /// @param indent the amount of white space to indent to. |
| /// |
| /// @return true iff decl is valid |
| template<> |
| bool |
| annotate(const function_decl::parameter_sptr& parm, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!parm) |
| return false; |
| |
| if (!ctxt.get_annotate()) |
| return true; |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<!-- "; |
| |
| if (parm->get_variadic_marker()) |
| o << "variadic parameter"; |
| else |
| { |
| if (parm->get_is_artificial()) |
| { |
| if (parm->get_index() == 0) |
| o << "implicit "; |
| else |
| o << "artificial "; |
| } |
| o << "parameter of type '" |
| << xml::escape_xml_comment(get_pretty_representation(parm->get_type())); |
| } |
| |
| o << "' -->\n"; |
| |
| return true; |
| } |
| |
| /// Write a location to the output stream. |
| /// |
| /// If the location is empty, nothing is written. |
| /// |
| /// @param loc the location to consider. |
| /// |
| /// @param tu the translation unit the location belongs to. |
| /// |
| /// @param ctxt the writer context to use. |
| static void |
| write_location(const location& loc, write_context& ctxt) |
| { |
| if (!loc || loc.get_is_artificial()) |
| return; |
| |
| if (!ctxt.get_show_locs()) |
| return; |
| |
| string filepath; |
| unsigned line = 0, column = 0; |
| |
| loc.expand(filepath, line, column); |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| if (ctxt.get_short_locs()) |
| tools_utils::base_name(filepath, filepath); |
| |
| o << " filepath='" << xml::escape_xml_string(filepath) << "'" |
| << " line='" << line << "'" |
| << " column='" << column << "'"; |
| } |
| |
| /// Write the location of a decl to the output stream. |
| /// |
| /// If the location is empty, nothing is written. |
| /// |
| /// @param decl the decl to consider. |
| /// |
| /// @param ctxt the @ref writer_context to use. |
| static void |
| write_location(const decl_base_sptr& decl, |
| write_context& ctxt) |
| { |
| if (!decl) |
| return; |
| |
| location loc = decl->get_location(); |
| if (!loc) |
| return; |
| |
| write_location(loc, ctxt); |
| } |
| |
| /// Serialize the visibility property of the current decl as the |
| /// 'visibility' attribute for the current xml element. |
| /// |
| /// @param decl the instance of decl_base to consider. |
| /// |
| /// @param o the output stream to serialize the property to. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_visibility(const shared_ptr<decl_base>& decl, ostream& o) |
| { |
| if (!decl) |
| return false; |
| |
| decl_base::visibility v = decl->get_visibility(); |
| string str; |
| |
| switch (v) |
| { |
| case decl_base::VISIBILITY_NONE: |
| return true; |
| case decl_base::VISIBILITY_DEFAULT: |
| str = "default"; |
| break; |
| case decl_base::VISIBILITY_PROTECTED: |
| str = "protected"; |
| break; |
| case decl_base::VISIBILITY_HIDDEN: |
| str = "hidden"; |
| break; |
| case decl_base::VISIBILITY_INTERNAL: |
| str = "internal"; |
| break; |
| } |
| |
| if (str.empty()) |
| return false; |
| |
| o << " visibility='" << str << "'"; |
| |
| return true; |
| } |
| |
| /// Serialize the 'binding' property of the current decl. |
| /// |
| /// @param decl the decl to consider. |
| /// |
| /// @param o the output stream to serialize the property to. |
| static bool |
| write_binding(const shared_ptr<decl_base>& decl, ostream& o) |
| { |
| if (!decl) |
| return false; |
| |
| decl_base::binding bind = decl_base::BINDING_NONE; |
| |
| shared_ptr<var_decl> var = |
| dynamic_pointer_cast<var_decl>(decl); |
| if (var) |
| bind = var->get_binding(); |
| else |
| { |
| shared_ptr<function_decl> fun = |
| dynamic_pointer_cast<function_decl>(decl); |
| if (fun) |
| bind = fun->get_binding(); |
| } |
| |
| string str; |
| switch (bind) |
| { |
| case decl_base::BINDING_NONE: |
| break; |
| case decl_base::BINDING_LOCAL: |
| str = "local"; |
| break; |
| case decl_base::BINDING_GLOBAL: |
| str = "global"; |
| break; |
| case decl_base::BINDING_WEAK: |
| str = "weak"; |
| break; |
| } |
| |
| if (!str.empty()) |
| o << " binding='" << str << "'"; |
| |
| return true; |
| } |
| |
| /// Write the "is-artificial" attribute of the @ref decl. |
| /// |
| /// @param decl the declaration to consider. |
| /// |
| /// @param o the output stream to emit the "is-artificial" attribute |
| /// to. |
| /// |
| /// @return true iff the "is-artificial" attribute was emitted. |
| static bool |
| write_is_artificial(const decl_base_sptr& decl, ostream& o) |
| { |
| if (!decl) |
| return false; |
| |
| if (decl->get_is_artificial()) |
| o << " is-artificial='yes'"; |
| |
| return true; |
| } |
| |
| /// Write the 'is-non-reachable' attribute if a given type we are |
| /// looking at is not reachable from global functions and variables |
| /// and if the user asked us to track that information. |
| /// |
| /// @param t the type to consider. |
| /// |
| /// @param o the output stream to write the 'is-non-reachable' |
| /// attribute to. |
| static bool |
| write_is_non_reachable(const type_base_sptr& t, ostream& o) |
| { |
| if (!t) |
| return false; |
| |
| corpus* c = t->get_corpus(); |
| if (!c) |
| return false; |
| |
| if (!c->recording_types_reachable_from_public_interface_supported() |
| || c->type_is_reachable_from_public_interfaces(*t)) |
| return false; |
| |
| o << " is-non-reachable='yes'"; |
| |
| return true; |
| } |
| |
| /// Write the 'tracking-non-reachable-types' attribute if for a given |
| /// corpus, the user wants us to track non-reachable types. |
| /// |
| /// @param corpus the ABI corpus to consider. |
| /// |
| /// @param o the output parameter to write the |
| /// 'tracking-non-reachable-types' attribute to. |
| static bool |
| write_tracking_non_reachable_types(const corpus_sptr& corpus, |
| ostream& o) |
| { |
| corpus_group* group = corpus->get_group(); |
| if (!group) |
| if (corpus->recording_types_reachable_from_public_interface_supported()) |
| { |
| o << " tracking-non-reachable-types='yes'"; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Serialize the size and alignment attributes of a given type. |
| /// |
| /// @param decl the type to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| /// |
| /// @param default_size size in bits that is the default for the type. |
| /// No size-in-bits attribute is written if it |
| /// would be the default value. |
| /// |
| /// @param default_alignment alignment in bits that is the default for |
| /// the type. No alignment-in-bits attribute is |
| /// written if it would be the default value. |
| static void |
| write_size_and_alignment(const shared_ptr<type_base> decl, ostream& o, |
| size_t default_size, size_t default_alignment) |
| { |
| size_t size_in_bits = decl->get_size_in_bits(); |
| if (size_in_bits != default_size) |
| o << " size-in-bits='" << size_in_bits << "'"; |
| |
| size_t alignment_in_bits = decl->get_alignment_in_bits(); |
| if (alignment_in_bits != default_alignment) |
| o << " alignment-in-bits='" << alignment_in_bits << "'"; |
| } |
| |
| /// Serialize the size and alignment attributes of a given type. |
| /// @param decl the type to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| static void |
| write_array_size_and_alignment(const shared_ptr<array_type_def> decl, ostream& o) |
| { |
| if (decl->is_infinite()) |
| o << " size-in-bits='" << "infinite" << "'"; |
| else { |
| size_t size_in_bits = decl->get_size_in_bits(); |
| if (size_in_bits) |
| o << " size-in-bits='" << size_in_bits << "'"; |
| } |
| |
| size_t alignment_in_bits = decl->get_alignment_in_bits(); |
| if (alignment_in_bits) |
| o << " alignment-in-bits='" << alignment_in_bits << "'"; |
| } |
| /// Serialize the access specifier. |
| /// |
| /// @param a the access specifier to serialize. |
| /// |
| /// @param o the output stream to serialize it to. |
| static void |
| write_access(access_specifier a, ostream& o) |
| { |
| string access_str = "private"; |
| |
| switch (a) |
| { |
| case private_access: |
| access_str = "private"; |
| break; |
| |
| case protected_access: |
| access_str = "protected"; |
| break; |
| |
| case public_access: |
| access_str = "public"; |
| break; |
| |
| default: |
| break; |
| } |
| |
| o << " access='" << access_str << "'"; |
| } |
| |
| /// Serialize the layout offset of a data member. |
| static void |
| write_layout_offset(var_decl_sptr member, ostream& o) |
| { |
| if (!is_data_member(member)) |
| return; |
| |
| if (get_data_member_is_laid_out(member)) |
| o << " layout-offset-in-bits='" |
| << get_data_member_offset(member) |
| << "'"; |
| } |
| |
| /// Serialize the layout offset of a base class |
| static void |
| write_layout_offset(shared_ptr<class_decl::base_spec> base, ostream& o) |
| { |
| if (!base) |
| return; |
| |
| if (base->get_offset_in_bits() >= 0) |
| o << " layout-offset-in-bits='" << base->get_offset_in_bits() << "'"; |
| } |
| |
| /// Serialize the access specifier of a class member. |
| /// |
| /// @param member a pointer to the class member to consider. |
| /// |
| /// @param o the ostream to serialize the member to. |
| static void |
| write_access(decl_base_sptr member, ostream& o) |
| {write_access(get_member_access_specifier(member), o);} |
| |
| /// Write the voffset of a member function if it's non-zero |
| /// |
| /// @param fn the member function to consider |
| /// |
| /// @param o the output stream to write to |
| static void |
| write_voffset(function_decl_sptr fn, ostream&o) |
| { |
| if (!fn) |
| return; |
| |
| if (get_member_function_is_virtual(fn)) |
| { |
| ssize_t voffset = get_member_function_vtable_offset(fn); |
| o << " vtable-offset='" << voffset << "'"; |
| } |
| } |
| |
| /// Serialize an elf_symbol::type into an XML node attribute named |
| /// 'type'. |
| /// |
| /// @param t the elf_symbol::type to serialize. |
| /// |
| /// @param o the output stream to serialize it to. |
| static void |
| write_elf_symbol_type(elf_symbol::type t, ostream& o) |
| { |
| string repr; |
| |
| switch (t) |
| { |
| case elf_symbol::NOTYPE_TYPE: |
| repr = "no-type"; |
| break; |
| case elf_symbol::OBJECT_TYPE: |
| repr = "object-type"; |
| break; |
| case elf_symbol::FUNC_TYPE: |
| repr = "func-type"; |
| break; |
| case elf_symbol::SECTION_TYPE: |
| repr = "section-type"; |
| break; |
| case elf_symbol::FILE_TYPE: |
| repr = "file-type"; |
| break; |
| case elf_symbol::COMMON_TYPE: |
| repr = "common-type"; |
| break; |
| case elf_symbol::TLS_TYPE: |
| repr = "tls-type"; |
| break; |
| case elf_symbol::GNU_IFUNC_TYPE: |
| repr = "gnu-ifunc-type"; |
| break; |
| default: |
| repr = "no-type"; |
| break; |
| } |
| |
| o << " type='" << repr << "'"; |
| } |
| |
| /// Serialize an elf_symbol::binding into an XML element attribute of |
| /// name 'binding'. |
| /// |
| /// @param b the elf_symbol::binding to serialize. |
| /// |
| /// @param o the output stream to serialize the binding to. |
| static void |
| write_elf_symbol_binding(elf_symbol::binding b, ostream& o) |
| { |
| string repr; |
| |
| switch (b) |
| { |
| case elf_symbol::LOCAL_BINDING: |
| repr = "local-binding"; |
| break; |
| case elf_symbol::GLOBAL_BINDING: |
| repr = "global-binding"; |
| break; |
| case elf_symbol::WEAK_BINDING: |
| repr = "weak-binding"; |
| break; |
| case elf_symbol::GNU_UNIQUE_BINDING: |
| repr = "gnu-unique-binding"; |
| break; |
| default: |
| repr = "no-binding"; |
| break; |
| } |
| |
| o << " binding='" << repr << "'"; |
| } |
| |
| /// Serialize an elf_symbol::binding into an XML element attribute of |
| /// name 'binding'. |
| /// |
| /// @param b the elf_symbol::binding to serialize. |
| /// |
| /// @param o the output stream to serialize the binding to. |
| static void |
| write_elf_symbol_visibility(elf_symbol::visibility v, ostream& o) |
| { |
| string repr; |
| |
| switch (v) |
| { |
| case elf_symbol::DEFAULT_VISIBILITY: |
| repr = "default-visibility"; |
| break; |
| case elf_symbol::PROTECTED_VISIBILITY: |
| repr = "protected-visibility"; |
| break; |
| case elf_symbol::HIDDEN_VISIBILITY: |
| repr = "hidden-visibility"; |
| break; |
| case elf_symbol::INTERNAL_VISIBILITY: |
| repr = "internal-visibility"; |
| break; |
| default: |
| repr = "default-visibility"; |
| break; |
| } |
| |
| o << " visibility='" << repr << "'"; |
| } |
| |
| /// Write alias attributes for the aliases of a given symbol. |
| /// |
| /// @param sym the symbol to write the attributes for. |
| /// |
| /// @param o the output stream to write the attributes to. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_elf_symbol_aliases(const elf_symbol& sym, ostream& out) |
| { |
| if (!sym.is_main_symbol() || !sym.has_aliases()) |
| return false; |
| |
| |
| std::vector<std::string> aliases; |
| for (elf_symbol_sptr s = sym.get_next_alias(); s && !s->is_main_symbol(); |
| s = s->get_next_alias()) |
| { |
| if (!s->is_public()) |
| continue; |
| |
| if (s->is_suppressed()) |
| continue; |
| |
| if (sym.is_in_ksymtab() != s->is_in_ksymtab()) |
| continue; |
| |
| aliases.push_back(s->get_id_string()); |
| } |
| |
| if (!aliases.empty()) |
| { |
| out << " alias='"; |
| std::string separator; |
| for (const auto& alias : aliases) |
| { |
| out << separator << alias; |
| separator = ","; |
| } |
| |
| out << "'"; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Write an XML attribute for the reference to a symbol for the |
| /// current decl. |
| /// |
| /// @param sym the symbol to consider. |
| /// |
| /// @param o the output stream to write the attribute to. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_elf_symbol_reference(const elf_symbol& sym, ostream& o) |
| { |
| const elf_symbol* main = sym.get_main_symbol().get(); |
| const elf_symbol* alias = &sym; |
| bool found = !alias->is_suppressed(); |
| // If the symbol itself is suppressed, check the alias chain. |
| if (!found) |
| { |
| alias = main; |
| found = !alias->is_suppressed(); |
| } |
| // If the main symbol is suppressed, search the remainder of the chain. |
| while (!found) |
| { |
| alias = alias->get_next_alias().get(); |
| // Two separate termination conditions at present. |
| if (!alias || alias == main) |
| break; |
| found = !alias->is_suppressed(); |
| } |
| // If all aliases are suppressed, just stick with the main symbol. |
| if (!found) |
| alias = main; |
| o << " elf-symbol-id='" << alias->get_id_string() << "'"; |
| return true; |
| } |
| |
| /// Write an XML attribute for the reference to a symbol for the |
| /// current decl. |
| /// |
| /// @param sym the symbol to consider. |
| /// |
| /// @param o the output stream to write the attribute to. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_elf_symbol_reference(const elf_symbol_sptr sym, ostream& o) |
| { |
| if (!sym) |
| return false; |
| |
| return write_elf_symbol_reference(*sym, o); |
| } |
| |
| /// Serialize the attributes "constructor", "destructor" or "static" |
| /// if they have true value. |
| /// |
| /// @param is_ctor if set to true, the "constructor='true'" string is |
| /// emitted. |
| /// |
| /// @param is_dtor if set to true the "destructor='true' string is |
| /// emitted. |
| /// |
| /// @param is_static if set to true the "static='true'" string is |
| /// emitted. |
| /// |
| /// @param o the output stream to use for the serialization. |
| static void |
| write_cdtor_const_static(bool is_ctor, |
| bool is_dtor, |
| bool is_const, |
| bool is_static, |
| ostream& o) |
| { |
| if (is_static) |
| o << " static='yes'"; |
| if (is_ctor) |
| o << " constructor='yes'"; |
| else if (is_dtor) |
| o << " destructor='yes'"; |
| if (is_const) |
| o << " const='yes'"; |
| } |
| |
| /// Serialize the attribute "is-declaration-only", if the |
| /// decl_base_sptr has its 'is_declaration_only property set. |
| /// |
| /// @param t the pointer to instance of @ref decl_base to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| static void |
| write_is_declaration_only(const decl_base_sptr& d, ostream& o) |
| { |
| if (d->get_is_declaration_only()) |
| o << " is-declaration-only='yes'"; |
| } |
| |
| /// Serialize the attribute "is-struct", if the current instance of |
| /// class_decl is a struct. |
| /// |
| /// @param klass a pointer to the instance of class_decl to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| static void |
| write_is_struct(const class_decl_sptr& klass, ostream& o) |
| { |
| if (klass->is_struct()) |
| o << " is-struct='yes'"; |
| } |
| |
| /// Serialize the attribute "is-anonymous", if the current instance of |
| /// decl is anonymous |
| /// |
| /// @param dcl a pointer to the instance of @ref decl_base to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| static void |
| write_is_anonymous(const decl_base_sptr& decl, ostream& o) |
| { |
| if (decl->get_is_anonymous()) |
| o << " is-anonymous='yes'"; |
| } |
| |
| /// Serialize the "naming-typedef-id" attribute, if the current |
| /// instance of @ref class_decl has a naming typedef. |
| /// |
| /// @param klass the @ref class_decl to consider. |
| /// |
| /// @param ctxt the write context to use. |
| static void |
| write_naming_typedef(const decl_base_sptr& decl, write_context& ctxt) |
| { |
| if (!decl) |
| return; |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| if (typedef_decl_sptr typedef_type = decl->get_naming_typedef()) |
| { |
| o << " naming-typedef-id='" << ctxt.get_id_for_type(typedef_type) << "'"; |
| ctxt.record_type_as_referenced(typedef_type); |
| } |
| } |
| |
| /// Helper to serialize a type artifact. |
| /// |
| /// @param type the type to serialize. |
| /// |
| /// @param ctxt the @ref write_context to use. |
| /// |
| /// @param indent the number of white space to use for indentation. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_type(const type_base_sptr& type, write_context& ctxt, unsigned indent) |
| { |
| if (write_type_decl(dynamic_pointer_cast<type_decl> (type), |
| ctxt, indent) |
| || write_qualified_type_def (dynamic_pointer_cast<qualified_type_def> |
| (type), |
| ctxt, indent) |
| || write_pointer_type_def(dynamic_pointer_cast<pointer_type_def>(type), |
| ctxt, indent) |
| || write_reference_type_def(dynamic_pointer_cast |
| <reference_type_def>(type), ctxt, indent) |
| || write_array_type_def(dynamic_pointer_cast |
| <array_type_def>(type), ctxt, indent) |
| || write_enum_type_decl(dynamic_pointer_cast<enum_type_decl>(type), |
| ctxt, indent) |
| || write_typedef_decl(dynamic_pointer_cast<typedef_decl>(type), |
| ctxt, indent) |
| || write_class_decl(is_class_type(type), ctxt, indent) |
| || write_union_decl(is_union_type(type), ctxt, indent) |
| || (write_function_tdecl |
| (dynamic_pointer_cast<function_tdecl>(type), ctxt, indent)) |
| || (write_class_tdecl |
| (dynamic_pointer_cast<class_tdecl>(type), ctxt, indent))) |
| return true; |
| |
| return false; |
| } |
| |
| /// Serialize a pointer to an of decl_base into an output stream. |
| /// |
| /// @param decl the pointer to decl_base to serialize |
| /// |
| /// @param ctxt the context of the serialization. It contains e.g, the |
| /// output stream to serialize to. |
| /// |
| /// @param indent how many indentation spaces to use during the |
| /// serialization. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_decl(const decl_base_sptr& decl, write_context& ctxt, unsigned indent) |
| { |
| if (write_type_decl(dynamic_pointer_cast<type_decl> (decl), |
| ctxt, indent) |
| || write_namespace_decl(dynamic_pointer_cast<namespace_decl>(decl), |
| ctxt, indent) |
| || write_qualified_type_def (dynamic_pointer_cast<qualified_type_def> |
| (decl), |
| ctxt, indent) |
| || write_pointer_type_def(dynamic_pointer_cast<pointer_type_def>(decl), |
| ctxt, indent) |
| || write_reference_type_def(dynamic_pointer_cast |
| <reference_type_def>(decl), ctxt, indent) |
| || write_array_type_def(dynamic_pointer_cast |
| <array_type_def>(decl), ctxt, indent) |
| || write_enum_type_decl(dynamic_pointer_cast<enum_type_decl>(decl), |
| ctxt, indent) |
| || write_typedef_decl(dynamic_pointer_cast<typedef_decl>(decl), |
| ctxt, indent) |
| || write_var_decl(dynamic_pointer_cast<var_decl>(decl), ctxt, |
| /*write_linkage_name=*/true, indent) |
| || write_function_decl(dynamic_pointer_cast<method_decl> |
| (decl), ctxt, /*skip_first_parameter=*/true, |
| indent) |
| || write_function_decl(dynamic_pointer_cast<function_decl>(decl), |
| ctxt, /*skip_first_parameter=*/false, indent) |
| || write_class_decl(is_class_type(decl), ctxt, indent) |
| || write_union_decl(is_union_type(decl), ctxt, indent) |
| || (write_function_tdecl |
| (dynamic_pointer_cast<function_tdecl>(decl), ctxt, indent)) |
| || (write_class_tdecl |
| (dynamic_pointer_cast<class_tdecl>(decl), ctxt, indent))) |
| return true; |
| |
| return false; |
| } |
| |
| /// Emit a declaration, along with its scope. |
| /// |
| /// This function is called at the end of emitting a translation unit, |
| /// to emit type declarations that were referenced by types that were |
| /// emitted in the TU already, but that were not emitted themselves. |
| /// |
| /// @param decl the decl to emit. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param initial_indent the number of indentation spaces to use. |
| static void |
| write_decl_in_scope(const decl_base_sptr& decl, |
| write_context& ctxt, |
| unsigned initial_indent) |
| { |
| type_base_sptr type = is_type(decl); |
| ABG_ASSERT(type); |
| |
| if (ctxt.type_is_emitted(type)) |
| return; |
| |
| list<scope_decl*> scopes; |
| for (scope_decl* s = decl->get_scope(); |
| s && !is_global_scope(s); |
| s = s->get_scope()) |
| scopes.push_front(s); |
| |
| ostream& o = ctxt.get_ostream(); |
| const config& c = ctxt.get_config(); |
| stack<string> closing_tags; |
| stack<unsigned> closing_indents; |
| unsigned indent = initial_indent; |
| for (list<scope_decl*>::const_iterator i = scopes.begin(); |
| i != scopes.end(); |
| ++i) |
| { |
| ABG_ASSERT(!is_global_scope(*i)); |
| |
| // A type scope is either a namespace ... |
| if (namespace_decl* n = is_namespace(*i)) |
| { |
| do_indent(o, indent); |
| o << "<namespace-decl name='" |
| << xml::escape_xml_string(n->get_name()) |
| << "'>\n"; |
| closing_tags.push("</namespace-decl>"); |
| closing_indents.push(indent); |
| } |
| // ... or a class. |
| else if (class_decl* c = is_class_type(*i)) |
| { |
| class_decl_sptr class_type(c, noop_deleter()); |
| write_class_decl_opening_tag(class_type, ctxt, indent, |
| /*prepare_to_handle_members=*/false); |
| closing_tags.push("</class-decl>"); |
| closing_indents.push(indent); |
| |
| unsigned nb_ws = get_indent_to_level(ctxt, indent, 1); |
| write_member_type_opening_tag(type, ctxt, nb_ws); |
| indent = nb_ws; |
| closing_tags.push("</member-type>"); |
| closing_indents.push(nb_ws); |
| } |
| else if (union_decl *u = is_union_type(*i)) |
| { |
| union_decl_sptr union_type(u, noop_deleter()); |
| write_union_decl_opening_tag(union_type, ctxt, indent, |
| /*prepare_to_handle_members=*/false); |
| closing_tags.push("</union-decl>"); |
| closing_indents.push(indent); |
| |
| unsigned nb_ws = get_indent_to_level(ctxt, indent, 1); |
| write_member_type_opening_tag(type, ctxt, nb_ws); |
| indent = nb_ws; |
| closing_tags.push("</member-type>"); |
| closing_indents.push(nb_ws); |
| } |
| else |
| // We should never reach this point. |
| abort(); |
| indent += c.get_xml_element_indent(); |
| } |
| |
| write_decl(decl, ctxt, indent); |
| |
| while (!closing_tags.empty()) |
| { |
| do_indent(o, closing_indents.top()); |
| o << closing_tags.top() << "\n"; |
| closing_tags.pop(); |
| closing_indents.pop(); |
| } |
| } |
| |
| /// Create a @ref write_context object that can be used to emit abixml |
| /// files. |
| /// |
| /// @param env the environment for the @ref write_context object to use. |
| /// |
| /// @param default_output_stream the default output stream to use. |
| /// |
| /// @return the new @ref write_context object. |
| write_context_sptr |
| create_write_context(const environment *env, |
| ostream& default_output_stream) |
| { |
| write_context_sptr ctxt(new write_context(env, default_output_stream)); |
| return ctxt; |
| } |
| |
| /// Set the "show-locs" flag. |
| /// |
| /// When this flag is set then the XML writer emits location (/// |
| /// information (file name, line and column) for the ABI artifacts |
| /// that it emits. |
| /// |
| /// @param ctxt the @ref write_context to set the option for. |
| /// |
| /// @param flag the new value of the option. |
| void |
| set_show_locs(write_context& ctxt, bool flag) |
| {ctxt.set_show_locs(flag);} |
| |
| /// Set the 'annotate' flag. |
| /// |
| /// When this flag is set then the XML writer annotates ABI artifacts |
| /// with a human readable description. |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'annotate' flag. |
| void |
| set_annotate(write_context& ctxt, bool flag) |
| {ctxt.set_annotate(flag);} |
| |
| /// Set the new ostream. |
| /// |
| /// The ostream refers to the object, writers should stream new output to. |
| /// |
| /// @param ctxt the context to set this to. |
| /// |
| /// @param os the new ostream |
| void |
| set_ostream(write_context& ctxt, ostream& os) |
| {ctxt.set_ostream(os);} |
| |
| /// Set the 'write-architecture' flag. |
| /// |
| /// When this flag is set then the XML writer will emit architecture |
| /// information |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'write-architecture' flag. |
| void |
| set_write_architecture(write_context& ctxt, bool flag) |
| {ctxt.set_write_architecture(flag);} |
| |
| /// Set the 'write-corpus-path' flag. |
| /// |
| /// When this flag is set then the XML writer will emit corpus-path |
| /// information |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'write-corpus-path' flag. |
| void |
| set_write_corpus_path(write_context& ctxt, bool flag) |
| {ctxt.set_write_corpus_path(flag);} |
| |
| /// Set the 'write-comp-dir' flag. |
| /// |
| /// When this flag is set then the XML writer will emit compilation dir |
| /// information |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'write-comp-dir' flag. |
| void |
| set_write_comp_dir(write_context& ctxt, bool flag) |
| {ctxt.set_write_comp_dir(flag);} |
| |
| /// Set the 'short-locs' flag. |
| /// |
| /// When this flag is set then the XML writer will emit only file names |
| /// rather than full paths. |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'short-locs' flag. |
| void |
| set_short_locs(write_context& ctxt, bool flag) |
| {ctxt.set_short_locs(flag);} |
| |
| /// Set the 'parameter-names' flag. |
| /// |
| /// When this flag is set then the XML writer will emit the names of |
| /// function parameters. |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'parameter-names' flag. |
| void |
| set_write_parameter_names(write_context& ctxt, bool flag) |
| {ctxt.set_write_parameter_names(flag);} |
| |
| /// Set the 'elf-needed' flag. |
| /// |
| /// When this flag is set then the XML writer will emit corpus |
| /// get_needed() (DT_NEEDED) information. |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'elf-needed' flag. |
| void |
| set_write_elf_needed(write_context& ctxt, bool flag) |
| {ctxt.set_write_elf_needed(flag);} |
| |
| /// Set the 'default-sizes' flag. |
| /// |
| /// When this flag is set then the XML writer will emit default |
| /// size-in-bits attributes for pointer type definitions, reference |
| /// type definitions, function declarations and function types even |
| /// when they are equal to the default address size of the translation |
| /// unit. |
| /// |
| /// @param ctxt the context to set this flag on to. |
| /// |
| /// @param flag the new value of the 'default-sizes' flag. |
| void |
| set_write_default_sizes(write_context& ctxt, bool flag) |
| {ctxt.set_write_default_sizes(flag);} |
| |
| /// Set the 'type-id-style' property. |
| /// |
| /// This property controls the kind of type ids used in XML output. |
| /// |
| /// @param ctxt the context to set this property on. |
| /// |
| /// @param style the new value of the 'type-id-style' property. |
| void |
| set_type_id_style(write_context& ctxt, type_id_style_kind style) |
| {ctxt.set_type_id_style(style);} |
| |
| /// Serialize the canonical types of a given scope. |
| /// |
| /// @param scope the scope to consider. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param indent the number of white space indentation to use. |
| // |
| // @param is_member_type if true, the canonical types are emitted as |
| // member types (of a class). |
| // |
| // return true upon successful completion. |
| static bool |
| write_canonical_types_of_scope(const scope_decl &scope, |
| write_context &ctxt, |
| const unsigned indent, |
| bool is_member_type = false) |
| { |
| const type_base_sptrs_type &canonical_types = |
| scope.get_sorted_canonical_types(); |
| |
| for (type_base_sptrs_type::const_iterator i = canonical_types.begin(); |
| i != canonical_types.end(); |
| ++i) |
| { |
| if (is_member_type) |
| write_member_type(*i, ctxt, indent); |
| else |
| write_type(*i, ctxt, indent); |
| } |
| |
| return true; |
| } |
| |
| /// Test if a type referenced in a given translation unit should be |
| /// emitted or not. |
| /// |
| /// This is a subroutine of @ref write_translation_unit. |
| /// |
| /// @param t the type to consider. |
| /// |
| /// @param ctxt the write context to consider. |
| /// |
| /// @param tu the translation unit to consider. |
| /// |
| /// @param tu_is_last true if @p tu is the last translation unit being |
| /// emitted. |
| /// |
| /// @return true iff @p t is to be emitted. |
| static bool |
| referenced_type_should_be_emitted(const type_base *t, |
| const write_context& ctxt, |
| const translation_unit& tu, |
| bool tu_is_last) |
| { |
| if ((tu_is_last || (t->get_translation_unit() |
| && (t->get_translation_unit()->get_absolute_path() |
| == tu.get_absolute_path()))) |
| && !ctxt.type_is_emitted(t)) |
| return true; |
| return false; |
| } |
| |
| /// Emit the types that were referenced by other emitted types. |
| /// |
| /// This is a sub-routine of write_translation_unit. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param tu the current translation unit that is being emitted. |
| /// |
| /// @param indent the indentation string. |
| /// |
| /// @param is_last whether @p tu is the last translation unit or not. |
| static void |
| write_referenced_types(write_context & ctxt, |
| const translation_unit& tu, |
| const unsigned indent, |
| bool is_last) |
| { |
| const config& c = ctxt.get_config(); |
| // Now let's handle types that were referenced, but not yet |
| // emitted because they are either: |
| // 1/ Types without canonical type |
| // 2/ or function types (these might have no scope). |
| |
| while (true) |
| { |
| // This set will contain the referenced types we need to emit. |
| type_ptr_set_type referenced_types_to_emit; |
| |
| // For each referenced type, ensure that it is either emitted in the |
| // translation unit to which it belongs or in the last translation |
| // unit as a last resort. |
| for (type_ptr_set_type::const_iterator i = |
| ctxt.get_referenced_types().begin(); |
| i != ctxt.get_referenced_types().end(); |
| ++i) |
| if (referenced_type_should_be_emitted(*i, ctxt, tu, is_last)) |
| referenced_types_to_emit.insert(*i); |
| |
| for (fn_type_ptr_set_type::const_iterator i = |
| ctxt.get_referenced_function_types().begin(); |
| i != ctxt.get_referenced_function_types().end(); |
| ++i) |
| if (referenced_type_should_be_emitted(*i, ctxt, tu, is_last)) |
| referenced_types_to_emit.insert(*i); |
| |
| for (type_ptr_set_type::const_iterator i = |
| ctxt.get_referenced_non_canonical_types().begin(); |
| i != ctxt.get_referenced_non_canonical_types().end(); |
| ++i) |
| if (referenced_type_should_be_emitted(*i, ctxt, tu, is_last)) |
| referenced_types_to_emit.insert(*i); |
| |
| if (referenced_types_to_emit.empty()) |
| break; |
| // Ok, now let's emit the referenced type for good. |
| |
| // But first, we need to sort them, otherwise, emitting the ABI |
| // (in xml) of the same binary twice will yield different |
| // results, because we'd be walking an *unordered* hash table. |
| std::vector<type_base*> sorted_types = |
| ctxt.sort_types(referenced_types_to_emit); |
| |
| // Now, emit the referenced decls in a sorted order. |
| for (type_base* t : sorted_types) |
| // We handle types which have declarations *and* function |
| // types here. |
| if (ctxt.type_is_emitted(t)) |
| continue; |
| else if (decl_base* d = get_type_declaration(t)) |
| write_decl_in_scope(decl_base_sptr(d, noop_deleter()), ctxt, |
| indent + c.get_xml_element_indent()); |
| else if (function_type* f = is_function_type(t)) |
| write_function_type(function_type_sptr(f, noop_deleter()), ctxt, |
| indent + c.get_xml_element_indent()); |
| else |
| ABG_ASSERT_NOT_REACHED; |
| |
| // While emitting those referenced type, other types |
| // might have been referenced by those referenced types |
| // themselves! So let's go around again. |
| } |
| } |
| |
| /// Serialize a translation unit to an output stream. |
| /// |
| /// @param ctxt the context of the serialization. It contains e.g, |
| /// the output stream to serialize to. |
| /// |
| /// @param tu the translation unit to serialize. |
| /// |
| /// @param indent how many indentation spaces to use during the |
| /// serialization. |
| /// |
| /// @param is_last If true, it means the TU to emit is the last one of |
| /// the corpus. If this is the case, all the remaining referenced |
| /// types that were not emitted are going to be emitted here, |
| /// irrespective of if they belong to this TU or not. This is quite a |
| /// hack. Ideally, we should have a pass that walks all the TUs, |
| /// detect their non-emitted referenced types, before hand. Then, |
| /// when we start emitting the TUs, we know for each TU which |
| /// non-emitted referenced type should be emitted. As we don't yet |
| /// have such a pass, we do our best for now. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| bool |
| write_translation_unit(write_context& ctxt, |
| const translation_unit& tu, |
| const unsigned indent, |
| bool is_last) |
| { |
| if (tu.is_empty() && !is_last) |
| return false; |
| |
| if (is_last |
| && tu.is_empty() |
| && ctxt.has_non_emitted_referenced_types()) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| const config& c = ctxt.get_config(); |
| |
| do_indent(o, indent); |
| |
| o << "<abi-instr"; |
| |
| if (tu.get_address_size() != 0) |
| o << " address-size='" << static_cast<int>(tu.get_address_size()) << "'"; |
| |
| std::string tu_path = tu.get_path(); |
| if (ctxt.get_short_locs()) |
| tools_utils::base_name(tu_path, tu_path); |
| if (!tu_path.empty()) |
| o << " path='" << xml::escape_xml_string(tu_path) << "'"; |
| |
| if (!tu.get_compilation_dir_path().empty() && ctxt.get_write_comp_dir()) |
| o << " comp-dir-path='" |
| << xml::escape_xml_string(tu.get_compilation_dir_path()) << "'"; |
| |
| if (tu.get_language() != translation_unit::LANG_UNKNOWN) |
| o << " language='" |
| << translation_unit_language_to_string(tu.get_language()) |
| <<"'"; |
| |
| if (tu.is_empty() && !is_last) |
| { |
| o << "/>\n"; |
| return true; |
| } |
| |
| o << ">\n"; |
| |
| write_canonical_types_of_scope(*tu.get_global_scope(), |
| ctxt, indent + c.get_xml_element_indent()); |
| |
| typedef scope_decl::declarations declarations; |
| const declarations& decls = tu.get_global_scope()->get_sorted_member_decls(); |
| |
| for (const decl_base_sptr& decl : decls) |
| { |
| if (type_base_sptr t = is_type(decl)) |
| { |
| // Emit declaration-only classes that are needed. Some of |
| // these classes can be empty. Those beasts can be classes |
| // that only contain member types. They can also be classes |
| // considered "opaque". |
| if (class_decl_sptr class_type = is_class_type(t)) |
| if (class_type->get_is_declaration_only() |
| && !ctxt.type_is_emitted(class_type)) |
| write_type(class_type, ctxt, |
| indent + c.get_xml_element_indent()); |
| } |
| else |
| { |
| if (!ctxt.decl_is_emitted(decl)) |
| write_decl(decl, ctxt, indent + c.get_xml_element_indent()); |
| } |
| } |
| |
| write_referenced_types(ctxt, tu, indent, is_last); |
| |
| // Now handle all function types that were not only referenced by |
| // emitted types. |
| std::vector<type_base_sptr> sorted_types = |
| ctxt.sort_types(tu.get_live_fn_types()); |
| |
| for (const type_base_sptr& t : sorted_types) |
| { |
| function_type_sptr fn_type = is_function_type(t); |
| |
| if (fn_type->get_is_artificial() || ctxt.type_is_emitted(fn_type)) |
| // This function type is either already emitted or it's |
| // artificial (i.e, artificially created just to represent the |
| // conceptual type of a function), so skip it. |
| continue; |
| |
| ABG_ASSERT(fn_type); |
| write_function_type(fn_type, ctxt, indent + c.get_xml_element_indent()); |
| } |
| |
| // After we've written out the live function types, we need to write |
| // the types they referenced. |
| write_referenced_types(ctxt, tu, indent, is_last); |
| |
| do_indent(o, indent); |
| o << "</abi-instr>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of basic type declaration, into |
| /// an output stream. |
| /// |
| /// @param d the basic type declaration to serialize. |
| /// |
| /// @param ctxt the context of the serialization. It contains e.g, the |
| /// output stream to serialize to. |
| /// |
| /// @param indent how many indentation spaces to use during the |
| /// serialization. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_type_decl(const type_decl_sptr& d, write_context& ctxt, unsigned indent) |
| { |
| if (!d) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| annotate(d, ctxt, indent); |
| |
| do_indent(o, indent); |
| |
| o << "<type-decl name='" << xml::escape_xml_string(d->get_name()) << "'"; |
| |
| write_is_anonymous(d, o); |
| |
| write_size_and_alignment(d, o); |
| |
| write_is_declaration_only(d, o); |
| |
| write_location(d, ctxt); |
| |
| o << " id='" << ctxt.get_id_for_type(d) << "'" << "/>\n"; |
| |
| ctxt.record_type_as_emitted(d); |
| |
| return true; |
| } |
| |
| /// Serialize a namespace declaration int an output stream. |
| /// |
| /// @param decl the namespace declaration to serialize. |
| /// |
| /// @param ctxt the context of the serialization. It contains e.g, the |
| /// output stream to serialize to. |
| /// |
| /// @param indent how many indentation spaces to use during the |
| /// serialization. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_namespace_decl(const namespace_decl_sptr& decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!decl || decl->is_empty_or_has_empty_sub_namespaces()) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| const config &c = ctxt.get_config(); |
| |
| annotate(decl, ctxt, indent); |
| |
| do_indent(o, indent); |
| |
| o << "<namespace-decl name='" |
| << xml::escape_xml_string(decl->get_name()) |
| << "'>\n"; |
| |
| typedef scope_decl::declarations declarations; |
| typedef declarations::const_iterator const_iterator; |
| const declarations& d = decl->get_sorted_member_decls(); |
| |
| write_canonical_types_of_scope(*decl, ctxt, |
| indent + c.get_xml_element_indent()); |
| |
| for (const_iterator i = d.begin(); i != d.end(); ++i) |
| { |
| if (type_base_sptr t = is_type(*i)) |
| if (ctxt.type_is_emitted(t)) |
| // This type has already been emitted to the current |
| // translation unit so do not emit it again. |
| continue; |
| write_decl(*i, ctxt, indent + c.get_xml_element_indent()); |
| } |
| |
| do_indent(o, indent); |
| o << "</namespace-decl>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize a qualified type declaration to an output stream. |
| /// |
| /// @param decl the qualfied type declaration to write. |
| /// |
| /// @param ctxt the write context. |
| /// |
| /// @param indent the number of space to indent to during the |
| /// serialization. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_qualified_type_def(const qualified_type_def_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| |
| type_base_sptr underlying_type = decl->get_underlying_type(); |
| |
| annotate(decl, ctxt, indent); |
| |
| do_indent(o, indent); |
| o << "<qualified-type-def type-id='" |
| << ctxt.get_id_for_type(underlying_type) |
| << "'"; |
| |
| ctxt.record_type_as_referenced(underlying_type); |
| |
| if (decl->get_cv_quals() & qualified_type_def::CV_CONST) |
| o << " const='yes'"; |
| if (decl->get_cv_quals() & qualified_type_def::CV_VOLATILE) |
| o << " volatile='yes'"; |
| if (decl->get_cv_quals() & qualified_type_def::CV_RESTRICT) |
| o << " restrict='yes'"; |
| |
| write_location(static_pointer_cast<decl_base>(decl), ctxt); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of pointer_type_def. |
| /// |
| /// @param decl the pointer_type_def to serialize. |
| /// |
| /// @param id the type id identitifier to use in the serialized |
| /// output. If this is empty, the function will compute an |
| /// appropriate one. This is useful when this function is called to |
| /// serialize the underlying type of a member type; in that case, the |
| /// caller has already computed the id of the *member type*, and that |
| /// id is the one to be written as the value of the 'id' attribute of |
| /// the XML element of the underlying type. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_pointer_type_def(const pointer_type_def_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| |
| type_base_sptr pointed_to_type = decl->get_pointed_to_type(); |
| |
| annotate(decl->get_canonical_type(), ctxt, indent); |
| |
| do_indent(o, indent); |
| o << "<pointer-type-def type-id='" |
| << ctxt.get_id_for_type(pointed_to_type) |
| << "'"; |
| |
| ctxt.record_type_as_referenced(pointed_to_type); |
| |
| write_size_and_alignment(decl, o, |
| (ctxt.get_write_default_sizes() |
| ? 0 |
| : decl->get_translation_unit()->get_address_size()), |
| 0); |
| |
| string id = ctxt.get_id_for_type(decl); |
| o << " id='" << id << "'"; |
| |
| write_location(static_pointer_cast<decl_base>(decl), ctxt); |
| o << "/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of reference_type_def. |
| /// |
| /// @param decl the reference_type_def to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_reference_type_def(const reference_type_def_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| annotate(decl->get_canonical_type(), ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<reference-type-def kind='"; |
| if (decl->is_lvalue()) |
| o << "lvalue"; |
| else |
| o << "rvalue"; |
| o << "'"; |
| |
| type_base_sptr pointed_to_type = decl->get_pointed_to_type(); |
| o << " type-id='" << ctxt.get_id_for_type(pointed_to_type) << "'"; |
| |
| ctxt.record_type_as_referenced(pointed_to_type); |
| |
| if (function_type_sptr f = is_function_type(decl->get_pointed_to_type())) |
| ctxt.record_type_as_referenced(f); |
| |
| write_size_and_alignment(decl, o, |
| (ctxt.get_write_default_sizes() |
| ? 0 |
| : decl->get_translation_unit()->get_address_size()), |
| 0); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| write_location(static_pointer_cast<decl_base>(decl), ctxt); |
| |
| o << "/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize an instance of @ref array_type_def::subrange_type. |
| /// |
| /// @param decl the array_type_def::subrange_type to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// return true upon successful completion, false otherwise. |
| static bool |
| write_array_subrange_type(const array_type_def::subrange_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<subrange"; |
| |
| if (!decl->get_name().empty()) |
| o << " name='" << decl->get_name() << "'"; |
| |
| o << " length='"; |
| if (decl->is_infinite()) |
| o << "infinite"; |
| else |
| o << decl->get_length(); |
| |
| o << "'"; |
| |
| if (decl->get_lower_bound()) |
| { |
| ABG_ASSERT(decl->is_infinite() |
| || (decl->get_length() == |
| (uint64_t) (decl->get_upper_bound() |
| - decl->get_lower_bound() + 1))); |
| o << " lower-bound='" << decl->get_lower_bound() << "' upper-bound='" |
| << decl->get_upper_bound() << "'"; |
| } |
| |
| type_base_sptr underlying_type = decl->get_underlying_type(); |
| if (underlying_type) |
| { |
| o << " type-id='" |
| << ctxt.get_id_for_type(underlying_type) |
| << "'"; |
| ctxt.record_type_as_referenced(underlying_type); |
| } |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| write_location(decl->get_location(), ctxt); |
| |
| o << "/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of array_type_def. |
| /// |
| /// @param decl the array_type_def to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_array_type_def(const array_type_def_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| o << "<array-type-def"; |
| |
| o << " dimensions='" << decl->get_dimension_count() << "'"; |
| |
| type_base_sptr element_type = decl->get_element_type(); |
| o << " type-id='" << ctxt.get_id_for_type(element_type) << "'"; |
| |
| ctxt.record_type_as_referenced(element_type); |
| |
| write_array_size_and_alignment(decl, o); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| write_location(static_pointer_cast<decl_base>(decl), ctxt); |
| |
| if (!decl->get_dimension_count()) |
| o << "/>\n"; |
| else |
| { |
| o << ">\n"; |
| |
| vector<array_type_def::subrange_sptr>::const_iterator si; |
| |
| for (si = decl->get_subranges().begin(); |
| si != decl->get_subranges().end(); ++si) |
| { |
| unsigned local_indent = |
| indent + ctxt.get_config().get_xml_element_indent(); |
| write_array_subrange_type(*si, ctxt, local_indent); |
| } |
| |
| do_indent(o, indent); |
| o << "</array-type-def>\n"; |
| } |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of enum_type_decl. |
| /// |
| /// @param decl the enum_type_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_enum_type_decl(const enum_type_decl_sptr& d, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!d) |
| return false; |
| |
| enum_type_decl_sptr decl = is_enum_type(look_through_decl_only_enum(d)); |
| |
| annotate(decl->get_canonical_type(), ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| o << "<enum-decl name='" << xml::escape_xml_string(decl->get_name()) << "'"; |
| |
| write_is_anonymous(decl, o); |
| write_naming_typedef(decl, ctxt); |
| write_is_artificial(decl, o); |
| write_is_non_reachable(is_type(decl), o); |
| |
| if (!decl->get_linkage_name().empty()) |
| o << " linkage-name='" |
| << xml::escape_xml_string(decl->get_linkage_name()) |
| << "'"; |
| |
| write_location(decl, ctxt); |
| write_is_declaration_only(decl, o); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'>\n"; |
| |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<underlying-type type-id='" |
| << ctxt.get_id_for_type(decl->get_underlying_type()) |
| << "'/>\n"; |
| |
| for (enum_type_decl::enumerators::const_iterator i = |
| decl->get_enumerators().begin(); |
| i != decl->get_enumerators().end(); |
| ++i) |
| { |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<enumerator name='" |
| << i->get_name() |
| << "' value='" |
| << i->get_value() |
| << "'/>\n"; |
| } |
| |
| do_indent(o, indent); |
| o << "</enum-decl>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize an @ref elf_symbol to an XML element of name |
| /// 'elf-symbol'. |
| /// |
| /// @param sym the elf symbol to serialize. |
| /// |
| /// @param ctxt the read context to use. |
| /// |
| /// @param indent the number of white spaces to use as indentation. |
| /// |
| /// @return true iff the function completed successfully. |
| static bool |
| write_elf_symbol(const elf_symbol_sptr& sym, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!sym) |
| return false; |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| annotate(sym, ctxt, indent); |
| do_indent(o, indent); |
| o << "<elf-symbol name='" << sym->get_name() << "'"; |
| if (sym->is_variable() && sym->get_size()) |
| o << " size='" << sym->get_size() << "'"; |
| |
| if (!sym->get_version().is_empty()) |
| { |
| o << " version='" << sym->get_version().str() << "'"; |
| o << " is-default-version='"; |
| if (sym->get_version().is_default()) |
| o << "yes"; |
| else |
| o << "no"; |
| o << "'"; |
| } |
| |
| write_elf_symbol_type(sym->get_type(), o); |
| |
| write_elf_symbol_binding(sym->get_binding(), o); |
| |
| write_elf_symbol_visibility(sym->get_visibility(), o); |
| |
| write_elf_symbol_aliases(*sym, o); |
| |
| o << " is-defined='"; |
| if (sym->is_defined()) |
| o << "yes"; |
| else |
| o << "no"; |
| o << "'"; |
| |
| if (sym->is_common_symbol()) |
| o << " is-common='yes'"; |
| |
| if (sym->get_crc().has_value()) |
| o << " crc='" |
| << std::hex << std::showbase << sym->get_crc().value() |
| << std::dec << std::noshowbase << "'"; |
| |
| if (sym->get_namespace().has_value()) |
| o << " namespace='" << sym->get_namespace().value() << "'"; |
| |
| o << "/>\n"; |
| |
| return true; |
| } |
| |
| /// Write the elf symbol database to the output associated to the |
| /// current context. |
| /// |
| /// @param syms the sorted elf symbol data to write out. |
| /// |
| /// @param ctxt the context to consider. |
| /// |
| /// @param indent the number of white spaces to use as indentation. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_elf_symbols_table(const elf_symbols& syms, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (syms.empty()) |
| return false; |
| |
| for (elf_symbols::const_iterator it = syms.begin(); it != syms.end(); ++it) |
| write_elf_symbol(*it, ctxt, indent); |
| |
| return true; |
| } |
| |
| /// Write a vector of dependency names for the current corpus we are |
| /// writting. |
| /// |
| /// @param needed the vector of dependency names to write. |
| /// |
| /// @param ctxt the write context to use for the writting. |
| /// |
| /// @param indent the number of indendation spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_elf_needed(const vector<string>& needed, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (needed.empty()) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| for (vector<string>::const_iterator i = needed.begin(); |
| i != needed.end(); |
| ++i) |
| { |
| do_indent(o, indent); |
| o << "<dependency name='" << *i << "'/>\n"; |
| } |
| return true; |
| } |
| |
| /// Serialize a pointer to an instance of typedef_decl. |
| /// |
| /// @param decl the typedef_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_typedef_decl(const typedef_decl_sptr& decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| annotate(decl, ctxt, indent); |
| |
| do_indent(o, indent); |
| |
| o << "<typedef-decl name='" |
| << xml::escape_xml_string(decl->get_name()) |
| << "'"; |
| |
| type_base_sptr underlying_type = decl->get_underlying_type(); |
| o << " type-id='" << ctxt.get_id_for_type(underlying_type) << "'"; |
| ctxt.record_type_as_referenced(underlying_type); |
| |
| write_location(decl, ctxt); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to an instances of var_decl. |
| /// |
| /// @param decl the var_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param write_linkage_name if true, serialize the mangled name of |
| /// this variable. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_var_decl(const var_decl_sptr& decl, write_context& ctxt, |
| bool write_linkage_name, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<var-decl name='" << xml::escape_xml_string(decl->get_name()) << "'"; |
| type_base_sptr var_type = decl->get_type(); |
| o << " type-id='" << ctxt.get_id_for_type(var_type) << "'"; |
| ctxt.record_type_as_referenced(var_type); |
| |
| if (write_linkage_name) |
| { |
| const string& linkage_name = decl->get_linkage_name(); |
| if (!linkage_name.empty()) |
| o << " mangled-name='" << linkage_name << "'"; |
| } |
| |
| write_visibility(decl, o); |
| |
| write_binding(decl, o); |
| |
| write_location(decl, ctxt); |
| |
| write_elf_symbol_reference(decl->get_symbol(), o); |
| |
| o << "/>\n"; |
| |
| ctxt.record_decl_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a pointer to a function_decl. |
| /// |
| /// @param decl the pointer to function_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param skip_first_parm if true, do not serialize the first |
| /// parameter of the function decl. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_function_decl(const function_decl_sptr& decl, write_context& ctxt, |
| bool skip_first_parm, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| do_indent(o, indent); |
| |
| o << "<function-decl name='" |
| << xml::escape_xml_string(decl->get_name()) |
| << "'"; |
| |
| if (!decl->get_linkage_name().empty()) |
| o << " mangled-name='" |
| << xml::escape_xml_string(decl->get_linkage_name()) << "'"; |
| |
| write_location(decl, ctxt); |
| |
| if (decl->is_declared_inline()) |
| o << " declared-inline='yes'"; |
| |
| write_visibility(decl, o); |
| |
| write_binding(decl, o); |
| |
| write_size_and_alignment(decl->get_type(), o, |
| (ctxt.get_write_default_sizes() |
| ? 0 |
| : decl->get_translation_unit()->get_address_size()), |
| 0); |
| write_elf_symbol_reference(decl->get_symbol(), o); |
| |
| o << ">\n"; |
| |
| type_base_sptr parm_type; |
| vector<shared_ptr<function_decl::parameter> >::const_iterator pi = |
| decl->get_parameters().begin(); |
| for ((skip_first_parm && pi != decl->get_parameters().end()) ? ++pi: pi; |
| pi != decl->get_parameters().end(); |
| ++pi) |
| { |
| if ((*pi)->get_variadic_marker()) |
| { |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<parameter is-variadic='yes'"; |
| } |
| else |
| { |
| parm_type = (*pi)->get_type(); |
| |
| annotate(*pi, ctxt, |
| indent + ctxt.get_config().get_xml_element_indent()); |
| |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| |
| o << "<parameter type-id='" |
| << ctxt.get_id_for_type(parm_type) |
| << "'"; |
| ctxt.record_type_as_referenced(parm_type); |
| |
| if (ctxt.get_write_parameter_names() && !(*pi)->get_name().empty()) |
| o << " name='" << (*pi)->get_name() << "'"; |
| } |
| write_is_artificial(*pi, o); |
| write_location((*pi)->get_location(), ctxt); |
| o << "/>\n"; |
| } |
| |
| if (shared_ptr<type_base> return_type = decl->get_return_type()) |
| { |
| annotate(return_type , ctxt, |
| indent + ctxt.get_config().get_xml_element_indent()); |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<return type-id='" << ctxt.get_id_for_type(return_type) << "'/>\n"; |
| ctxt.record_type_as_referenced(return_type); |
| } |
| |
| do_indent(o, indent); |
| o << "</function-decl>\n"; |
| |
| ctxt.record_decl_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a function_type. |
| /// |
| /// @param decl the pointer to function_type to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the number of indentation white spaces to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_function_type(const function_type_sptr& fn_type, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!fn_type) |
| return false; |
| |
| ostream &o = ctxt.get_ostream(); |
| |
| annotate(fn_type, ctxt, indent); |
| |
| do_indent(o, indent); |
| |
| o << "<function-type"; |
| |
| write_size_and_alignment(fn_type, o, |
| (ctxt.get_write_default_sizes() |
| ? 0 |
| : fn_type->get_translation_unit()->get_address_size()), |
| 0); |
| |
| if (method_type_sptr method_type = is_method_type(fn_type)) |
| { |
| o << " method-class-id='" |
| << ctxt.get_id_for_type(method_type->get_class_type()) |
| << "'"; |
| |
| write_cdtor_const_static(/*is_ctor=*/false, /*is_dtor=*/false, |
| /*is_const=*/method_type->get_is_const(), |
| /*is_static=*/false, o); |
| } |
| |
| o << " id='" << ctxt.get_id_for_type(fn_type) << "'" << ">\n"; |
| |
| type_base_sptr parm_type; |
| for (vector<function_decl::parameter_sptr>::const_iterator pi = |
| fn_type->get_parameters().begin(); |
| pi != fn_type->get_parameters().end(); |
| ++pi) |
| { |
| |
| if ((*pi)->get_variadic_marker()) |
| { |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<parameter is-variadic='yes'"; |
| } |
| else |
| { |
| parm_type = (*pi)->get_type(); |
| |
| annotate(*pi, ctxt, indent + ctxt.get_config().get_xml_element_indent()); |
| |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<parameter type-id='" |
| << ctxt.get_id_for_type(parm_type) |
| << "'"; |
| ctxt.record_type_as_referenced(parm_type); |
| |
| if (!(*pi)->get_name().empty()) |
| { |
| string name = xml::escape_xml_string((*pi)->get_name()); |
| o << " name='" << name << "'"; |
| } |
| } |
| write_is_artificial(*pi, o); |
| o << "/>\n"; |
| } |
| |
| if (type_base_sptr return_type = fn_type->get_return_type()) |
| { |
| annotate(return_type, ctxt, indent + ctxt.get_config().get_xml_element_indent()); |
| do_indent(o, indent + ctxt.get_config().get_xml_element_indent()); |
| o << "<return type-id='" << ctxt.get_id_for_type(return_type) << "'/>\n"; |
| ctxt.record_type_as_referenced(return_type); |
| } |
| |
| do_indent(o, indent); |
| o << "</function-type>\n"; |
| |
| ctxt.record_type_as_emitted(fn_type); |
| return true; |
| } |
| |
| /// Write the opening tag of a 'class-decl' element. |
| /// |
| /// @param decl the class declaration to serialize. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param indent the number of white space to use for indentation. |
| /// |
| /// @param prepare_to_handle_members if set to true, then this function |
| /// figures out if the opening tag should be for an empty element or |
| /// not. If set to false, then the opening tag is unconditionnaly for |
| /// a non-empty element. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_class_decl_opening_tag(const class_decl_sptr& decl, |
| write_context& ctxt, |
| unsigned indent, |
| bool prepare_to_handle_members) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<class-decl name='" << xml::escape_xml_string(decl->get_name()) << "'"; |
| |
| write_size_and_alignment(decl, o); |
| |
| write_is_struct(decl, o); |
| |
| write_is_anonymous(decl, o); |
| |
| write_is_artificial(decl, o); |
| |
| write_is_non_reachable(is_type(decl), o); |
| |
| write_naming_typedef(decl, ctxt); |
| |
| write_visibility(decl, o); |
| |
| write_location(decl, ctxt); |
| |
| write_is_declaration_only(decl, o); |
| |
| if (decl->get_earlier_declaration()) |
| { |
| // This instance is the definition of an earlier declaration. |
| o << " def-of-decl-id='" |
| << ctxt.get_id_for_type(is_type(decl->get_earlier_declaration())) |
| << "'"; |
| } |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| if (prepare_to_handle_members && decl->has_no_base_nor_member()) |
| o << "/>\n"; |
| else |
| o << ">\n"; |
| |
| return true; |
| } |
| |
| /// Write the opening tag of a 'union-decl' element. |
| /// |
| /// @param decl the union declaration to serialize. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param indent the number of white space to use for indentation. |
| /// |
| /// @param prepare_to_handle_members if set to true, then this function |
| /// figures out if the opening tag should be for an empty element or |
| /// not. If set to false, then the opening tag is unconditionnaly for |
| /// a non-empty element. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_union_decl_opening_tag(const union_decl_sptr& decl, |
| write_context& ctxt, |
| unsigned indent, |
| bool prepare_to_handle_members) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<union-decl name='" << xml::escape_xml_string(decl->get_name()) << "'"; |
| |
| if (!decl->get_is_declaration_only()) |
| write_size_and_alignment(decl, o); |
| |
| write_is_anonymous(decl, o); |
| |
| write_naming_typedef(decl, ctxt); |
| |
| write_visibility(decl, o); |
| |
| write_is_artificial(decl, o); |
| |
| write_is_non_reachable(is_type(decl), o); |
| |
| write_location(decl, ctxt); |
| |
| write_is_declaration_only(decl, o); |
| |
| o << " id='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| if (prepare_to_handle_members && decl->has_no_member()) |
| o << "/>\n"; |
| else |
| o << ">\n"; |
| |
| return true; |
| } |
| |
| /// Serialize a class_decl type. |
| /// |
| /// @param d the pointer to class_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| static bool |
| write_class_decl(const class_decl_sptr& d, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!d) |
| return false; |
| |
| class_decl_sptr decl = is_class_type(look_through_decl_only_class(d)); |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| write_class_decl_opening_tag(decl, ctxt, indent, |
| /*prepare_to_handle_members=*/true); |
| |
| if (!decl->has_no_base_nor_member()) |
| { |
| unsigned nb_ws = get_indent_to_level(ctxt, indent, 1); |
| type_base_sptr base_type; |
| for (class_decl::base_specs::const_iterator base = |
| decl->get_base_specifiers().begin(); |
| base != decl->get_base_specifiers().end(); |
| ++base) |
| { |
| annotate((*base)->get_base_class(), ctxt, nb_ws); |
| do_indent(o, nb_ws); |
| o << "<base-class"; |
| |
| write_access((*base)->get_access_specifier(), o); |
| |
| write_layout_offset (*base, o); |
| |
| if ((*base)->get_is_virtual ()) |
| o << " is-virtual='yes'"; |
| |
| base_type = (*base)->get_base_class(); |
| o << " type-id='" |
| << ctxt.get_id_for_type(base_type) |
| << "'/>\n"; |
| |
| ctxt.record_type_as_referenced(base_type); |
| } |
| |
| write_canonical_types_of_scope(*decl, ctxt, nb_ws, |
| /*is_member_type=*/true); |
| |
| for (class_decl::member_types::const_iterator ti = |
| decl->get_member_types().begin(); |
| ti != decl->get_member_types().end(); |
| ++ti) |
| if (!(*ti)->get_naked_canonical_type()) |
| write_member_type(*ti, ctxt, nb_ws); |
| |
| for (class_decl::data_members::const_iterator data = |
| decl->get_data_members().begin(); |
| data != decl->get_data_members().end(); |
| ++data) |
| { |
| do_indent(o, nb_ws); |
| o << "<data-member"; |
| write_access(get_member_access_specifier(*data), o); |
| |
| bool is_static = get_member_is_static(*data); |
| write_cdtor_const_static(/*is_ctor=*/false, |
| /*is_dtor=*/false, |
| /*is_const=*/false, |
| /*is_static=*/is_static, |
| o); |
| write_layout_offset(*data, o); |
| o << ">\n"; |
| |
| write_var_decl(*data, ctxt, is_static, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| o << "</data-member>\n"; |
| } |
| |
| for (class_decl::member_functions::const_iterator f = |
| decl->get_member_functions().begin(); |
| f != decl->get_member_functions().end(); |
| ++f) |
| { |
| function_decl_sptr fn = *f; |
| if (get_member_function_is_virtual(fn)) |
| // All virtual member functions are emitted together, |
| // later. |
| continue; |
| |
| ABG_ASSERT(!get_member_function_is_virtual(fn)); |
| |
| do_indent(o, nb_ws); |
| o << "<member-function"; |
| write_access(get_member_access_specifier(fn), o); |
| write_cdtor_const_static( get_member_function_is_ctor(fn), |
| get_member_function_is_dtor(fn), |
| get_member_function_is_const(fn), |
| get_member_is_static(fn), |
| o); |
| o << ">\n"; |
| |
| write_function_decl(fn, ctxt, |
| /*skip_first_parameter=*/false, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| o << "</member-function>\n"; |
| } |
| |
| for (class_decl::member_functions::const_iterator f = |
| decl->get_virtual_mem_fns().begin(); |
| f != decl->get_virtual_mem_fns().end(); |
| ++f) |
| { |
| function_decl_sptr fn = *f; |
| |
| ABG_ASSERT(get_member_function_is_virtual(fn)); |
| |
| do_indent(o, nb_ws); |
| o << "<member-function"; |
| write_access(get_member_access_specifier(fn), o); |
| write_cdtor_const_static( get_member_function_is_ctor(fn), |
| get_member_function_is_dtor(fn), |
| get_member_function_is_const(fn), |
| get_member_is_static(fn), |
| o); |
| write_voffset(fn, o); |
| o << ">\n"; |
| |
| write_function_decl(fn, ctxt, |
| /*skip_first_parameter=*/false, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| o << "</member-function>\n"; |
| } |
| |
| for (member_function_templates::const_iterator fn = |
| decl->get_member_function_templates().begin(); |
| fn != decl->get_member_function_templates().end(); |
| ++fn) |
| { |
| do_indent(o, nb_ws); |
| o << "<member-template"; |
| write_access((*fn)->get_access_specifier(), o); |
| write_cdtor_const_static((*fn)->is_constructor(), |
| /*is_dtor=*/false, |
| (*fn)->is_const(), |
| (*fn)->get_is_static(), o); |
| o << ">\n"; |
| write_function_tdecl((*fn)->as_function_tdecl(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| do_indent(o, nb_ws); |
| o << "</member-template>\n"; |
| } |
| |
| for (member_class_templates::const_iterator cl = |
| decl->get_member_class_templates().begin(); |
| cl != decl->get_member_class_templates().end(); |
| ++cl) |
| { |
| do_indent(o, nb_ws); |
| o << "<member-template"; |
| write_access((*cl)->get_access_specifier(), o); |
| write_cdtor_const_static(false, false, false, |
| (*cl)->get_is_static(), o); |
| o << ">\n"; |
| write_class_tdecl((*cl)->as_class_tdecl(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| do_indent(o, nb_ws); |
| o << "</member-template>\n"; |
| } |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "</class-decl>\n"; |
| } |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize a @ref union_decl type. |
| /// |
| /// @param d the pointer to @ref union_decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_union_decl(const union_decl_sptr& d, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!d) |
| return false; |
| |
| union_decl_sptr decl = is_union_type(look_through_decl_only_class(d)); |
| |
| annotate(decl, ctxt, indent); |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| write_union_decl_opening_tag(decl, ctxt, indent, |
| /*prepare_to_handle_members=*/true); |
| if (!decl->has_no_member()) |
| { |
| unsigned nb_ws = get_indent_to_level(ctxt, indent, 1); |
| for (class_decl::member_types::const_iterator ti = |
| decl->get_member_types().begin(); |
| ti != decl->get_member_types().end(); |
| ++ti) |
| if (!(*ti)->get_naked_canonical_type()) |
| write_member_type(*ti, ctxt, nb_ws); |
| |
| write_canonical_types_of_scope(*decl, ctxt, nb_ws, |
| /*is_member_type=*/true); |
| |
| for (union_decl::data_members::const_iterator data = |
| decl->get_data_members().begin(); |
| data != decl->get_data_members().end(); |
| ++data) |
| { |
| do_indent(o, nb_ws); |
| o << "<data-member"; |
| write_access(get_member_access_specifier(*data), o); |
| |
| bool is_static = get_member_is_static(*data); |
| write_cdtor_const_static(/*is_ctor=*/false, |
| /*is_dtor=*/false, |
| /*is_const=*/false, |
| /*is_static=*/is_static, |
| o); |
| o << ">\n"; |
| |
| write_var_decl(*data, ctxt, is_static, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| o << "</data-member>\n"; |
| } |
| |
| for (union_decl::member_functions::const_iterator f = |
| decl->get_member_functions().begin(); |
| f != decl->get_member_functions().end(); |
| ++f) |
| { |
| function_decl_sptr fn = *f; |
| if (get_member_function_is_virtual(fn)) |
| // All virtual member functions are emitted together, |
| // later. |
| continue; |
| |
| ABG_ASSERT(!get_member_function_is_virtual(fn)); |
| |
| do_indent(o, nb_ws); |
| o << "<member-function"; |
| write_access(get_member_access_specifier(fn), o); |
| write_cdtor_const_static( get_member_function_is_ctor(fn), |
| get_member_function_is_dtor(fn), |
| get_member_function_is_const(fn), |
| get_member_is_static(fn), |
| o); |
| o << ">\n"; |
| |
| write_function_decl(fn, ctxt, |
| /*skip_first_parameter=*/false, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| o << "</member-function>\n"; |
| } |
| |
| for (member_function_templates::const_iterator fn = |
| decl->get_member_function_templates().begin(); |
| fn != decl->get_member_function_templates().end(); |
| ++fn) |
| { |
| do_indent(o, nb_ws); |
| o << "<member-template"; |
| write_access((*fn)->get_access_specifier(), o); |
| write_cdtor_const_static((*fn)->is_constructor(), |
| /*is_dtor=*/false, |
| (*fn)->is_const(), |
| (*fn)->get_is_static(), o); |
| o << ">\n"; |
| write_function_tdecl((*fn)->as_function_tdecl(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| do_indent(o, nb_ws); |
| o << "</member-template>\n"; |
| } |
| |
| for (member_class_templates::const_iterator cl = |
| decl->get_member_class_templates().begin(); |
| cl != decl->get_member_class_templates().end(); |
| ++cl) |
| { |
| do_indent(o, nb_ws); |
| o << "<member-template"; |
| write_access((*cl)->get_access_specifier(), o); |
| write_cdtor_const_static(false, false, false, |
| (*cl)->get_is_static(), o); |
| o << ">\n"; |
| write_class_tdecl((*cl)->as_class_tdecl(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| do_indent(o, nb_ws); |
| o << "</member-template>\n"; |
| } |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "</union-decl>\n"; |
| } |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Write the opening tag for a 'member-type' element. |
| /// |
| /// @param t the member type to consider. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param indent the number of white spaces to use for indentation. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| write_member_type_opening_tag(const type_base_sptr& t, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| decl_base_sptr decl = get_type_declaration(t); |
| ABG_ASSERT(decl); |
| |
| o << "<member-type"; |
| write_access(decl, o); |
| o << ">\n"; |
| |
| return true; |
| } |
| |
| /// Serialize a member type. |
| /// |
| /// @param decl the declaration of the member type to serialize. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param indent the number of levels to use for indentation |
| static bool |
| write_member_type(const type_base_sptr& t, write_context& ctxt, unsigned indent) |
| { |
| if (!t) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| write_member_type_opening_tag(t, ctxt, indent); |
| |
| unsigned nb_ws = get_indent_to_level(ctxt, indent, 1); |
| ABG_ASSERT(write_qualified_type_def(dynamic_pointer_cast<qualified_type_def>(t), |
| ctxt, nb_ws) |
| || write_pointer_type_def(dynamic_pointer_cast<pointer_type_def>(t), |
| ctxt, nb_ws) |
| || write_reference_type_def(dynamic_pointer_cast<reference_type_def>(t), |
| ctxt, nb_ws) |
| || write_array_type_def(dynamic_pointer_cast<array_type_def>(t), |
| ctxt, nb_ws) |
| || write_enum_type_decl(dynamic_pointer_cast<enum_type_decl>(t), |
| ctxt, nb_ws) |
| || write_typedef_decl(dynamic_pointer_cast<typedef_decl>(t), |
| ctxt, nb_ws) |
| || write_union_decl(dynamic_pointer_cast<union_decl>(t), |
| ctxt, nb_ws) |
| || write_class_decl(dynamic_pointer_cast<class_decl>(t), |
| ctxt, nb_ws)); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| o << "</member-type>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize an instance of type_tparameter. |
| /// |
| /// @param decl the instance to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_type_tparameter(const type_tparameter_sptr decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream &o = ctxt.get_ostream(); |
| do_indent_to_level(ctxt, indent, 0); |
| |
| string id_attr_name; |
| if (ctxt.type_has_existing_id(decl)) |
| id_attr_name = "type-id"; |
| else |
| id_attr_name = "id"; |
| |
| o << "<template-type-parameter " |
| << id_attr_name << "='" << ctxt.get_id_for_type(decl) << "'"; |
| |
| std::string name = xml::escape_xml_string(decl->get_name ()); |
| if (!name.empty()) |
| o << " name='" << name << "'"; |
| |
| write_location(decl, ctxt); |
| |
| o << "/>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize an instance of non_type_tparameter. |
| /// |
| /// @param decl the instance to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the intial indentation to use. |
| /// |
| /// @return true open successful completion, false otherwise. |
| static bool |
| write_non_type_tparameter( |
| const shared_ptr<non_type_tparameter> decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream &o = ctxt.get_ostream(); |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<template-non-type-parameter type-id='" |
| << ctxt.get_id_for_type(decl->get_type()) |
| << "'"; |
| |
| string name = xml::escape_xml_string(decl->get_name()); |
| if (!name.empty()) |
| o << " name='" << name << "'"; |
| |
| write_location(decl, ctxt); |
| |
| o << "/>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize an instance of template template parameter. |
| /// |
| /// @param decl the instance to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| |
| static bool |
| write_template_tparameter (const template_tparameter_sptr decl, |
| write_context& ctxt, |
| unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| do_indent_to_level(ctxt, indent, 0); |
| |
| string id_attr_name = "id"; |
| if (ctxt.type_has_existing_id(decl)) |
| id_attr_name = "type-id"; |
| |
| o << "<template-template-parameter " << id_attr_name << "='" |
| << ctxt.get_id_for_type(decl) << "'"; |
| |
| string name = xml::escape_xml_string(decl->get_name()); |
| if (!name.empty()) |
| o << " name='" << name << "'"; |
| |
| o << ">\n"; |
| |
| unsigned nb_spaces = get_indent_to_level(ctxt, indent, 1); |
| for (list<shared_ptr<template_parameter> >::const_iterator p = |
| decl->get_template_parameters().begin(); |
| p != decl->get_template_parameters().end(); |
| ++p) |
| write_template_parameter(decl, ctxt, nb_spaces); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| o << "</template-template-parameter>\n"; |
| |
| ctxt.record_type_as_emitted(decl); |
| |
| return true; |
| } |
| |
| /// Serialize an instance of type_composition. |
| /// |
| /// @param decl the decl to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_type_composition |
| (const shared_ptr<type_composition> decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<template-parameter-type-composition>\n"; |
| |
| unsigned nb_spaces = get_indent_to_level(ctxt, indent, 1); |
| (write_pointer_type_def |
| (dynamic_pointer_cast<pointer_type_def>(decl->get_composed_type()), |
| ctxt, nb_spaces) |
| || write_reference_type_def |
| (dynamic_pointer_cast<reference_type_def>(decl->get_composed_type()), |
| ctxt, nb_spaces) |
| || write_array_type_def |
| (dynamic_pointer_cast<array_type_def>(decl->get_composed_type()), |
| ctxt, nb_spaces) |
| || write_qualified_type_def |
| (dynamic_pointer_cast<qualified_type_def>(decl->get_composed_type()), |
| ctxt, nb_spaces)); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| o << "</template-parameter-type-composition>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize an instance of template_parameter. |
| /// |
| /// @param decl the instance to serialize. |
| /// |
| /// @param ctxt the context of the serialization. |
| /// |
| /// @param indent the initial indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_template_parameter(const shared_ptr<template_parameter> decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if ((!write_type_tparameter |
| (dynamic_pointer_cast<type_tparameter>(decl), ctxt, indent)) |
| && (!write_non_type_tparameter |
| (dynamic_pointer_cast<non_type_tparameter>(decl), |
| ctxt, indent)) |
| && (!write_template_tparameter |
| (dynamic_pointer_cast<template_tparameter>(decl), |
| ctxt, indent)) |
| && (!write_type_composition |
| (dynamic_pointer_cast<type_composition>(decl), |
| ctxt, indent))) |
| return false; |
| |
| return true; |
| } |
| |
| /// Serialize the template parameters of the a given template. |
| /// |
| /// @param tmpl the template for which to emit the template parameters. |
| static void |
| write_template_parameters(const shared_ptr<template_decl> tmpl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!tmpl) |
| return; |
| |
| unsigned nb_spaces = get_indent_to_level(ctxt, indent, 1); |
| for (list<shared_ptr<template_parameter> >::const_iterator p = |
| tmpl->get_template_parameters().begin(); |
| p != tmpl->get_template_parameters().end(); |
| ++p) |
| write_template_parameter(*p, ctxt, nb_spaces); |
| } |
| |
| /// Serialize an instance of function_tdecl. |
| /// |
| /// @param decl the instance to serialize. |
| /// |
| /// @param ctxt the context of the serialization |
| /// |
| /// @param indent the initial indentation. |
| static bool |
| write_function_tdecl(const shared_ptr<function_tdecl> decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<function-template-decl id='" << ctxt.get_id_for_fn_tmpl(decl) << "'"; |
| |
| write_location(decl, ctxt); |
| |
| write_visibility(decl, o); |
| |
| write_binding(decl, o); |
| |
| o << ">\n"; |
| |
| write_template_parameters(decl, ctxt, indent); |
| |
| write_function_decl(decl->get_pattern(), ctxt, |
| /*skip_first_parameter=*/false, |
| get_indent_to_level(ctxt, indent, 1)); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "</function-template-decl>\n"; |
| |
| return true; |
| } |
| |
| |
| /// Serialize an instance of class_tdecl |
| /// |
| /// @param decl a pointer to the instance of class_tdecl to serialize. |
| /// |
| /// @param ctxt the context of the serializtion. |
| /// |
| /// @param indent the initial number of white space to use for |
| /// indentation. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| static bool |
| write_class_tdecl(const shared_ptr<class_tdecl> decl, |
| write_context& ctxt, unsigned indent) |
| { |
| if (!decl) |
| return false; |
| |
| ostream& o = ctxt.get_ostream(); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "<class-template-decl id='" << ctxt.get_id_for_class_tmpl(decl) << "'"; |
| |
| write_location(decl, ctxt); |
| |
| write_visibility(decl, o); |
| |
| o << ">\n"; |
| |
| write_template_parameters(decl, ctxt, indent); |
| |
| write_class_decl(decl->get_pattern(), ctxt, |
| get_indent_to_level(ctxt, indent, 1)); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| o << "</class-template-decl>\n"; |
| |
| return true; |
| } |
| |
| /// Serialize the current version number of the ABIXML format. |
| /// |
| /// @param ctxt the writing context to use. |
| static void |
| write_version_info(write_context& ctxt) |
| { |
| ostream& o = ctxt.get_ostream(); |
| const config& c = ctxt.get_config(); |
| |
| o << "version='" |
| << c.get_format_major_version_number() |
| << "." << c.get_format_minor_version_number() |
| << "'"; |
| } |
| |
| /// Serialize an ABI corpus to a single native xml document. The root |
| /// note of the resulting XML document is 'abi-corpus'. |
| /// |
| /// Note: If either corpus is null or corpus does not contain serializable |
| /// content (i.e. corpus.is_empty()), nothing is emitted to the ctxt's |
| /// output stream. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param corpus the corpus to serialize. |
| /// |
| /// @param indent the number of white space indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| bool |
| write_corpus(write_context& ctxt, |
| const corpus_sptr& corpus, |
| unsigned indent, |
| bool member_of_group) |
| { |
| if (!corpus) |
| return false; |
| |
| if (corpus->is_empty()) |
| return true; |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| std::ostream& out = ctxt.get_ostream(); |
| |
| out << "<abi-corpus "; |
| |
| write_version_info(ctxt); |
| |
| // For an abi-corpus as part of an abi-corpus group, only omit the path, but |
| // keep the filename. |
| std::string corpus_path = corpus->get_path(); |
| if (!ctxt.get_write_corpus_path()) |
| { |
| if (member_of_group) |
| tools_utils::base_name(corpus_path, corpus_path); |
| else |
| corpus_path.clear(); |
| } |
| else |
| { |
| if (ctxt.get_short_locs()) |
| tools_utils::base_name(corpus_path, corpus_path); |
| } |
| if (!corpus_path.empty()) |
| out << " path='" << xml::escape_xml_string(corpus_path) << "'"; |
| |
| if (!corpus->get_architecture_name().empty() |
| && ctxt.get_write_architecture()) |
| out << " architecture='" << corpus->get_architecture_name()<< "'"; |
| |
| if (!corpus->get_soname().empty()) |
| out << " soname='" << corpus->get_soname()<< "'"; |
| |
| write_tracking_non_reachable_types(corpus, out); |
| |
| out << ">\n"; |
| |
| // Write the list of needed corpora. |
| |
| if (ctxt.get_write_elf_needed () && !corpus->get_needed().empty()) |
| { |
| do_indent_to_level(ctxt, indent, 1); |
| out << "<elf-needed>\n"; |
| write_elf_needed(corpus->get_needed(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| do_indent_to_level(ctxt, indent, 1); |
| out << "</elf-needed>\n"; |
| } |
| |
| // Write the function symbols data base. |
| if (!corpus->get_fun_symbol_map().empty()) |
| { |
| do_indent_to_level(ctxt, indent, 1); |
| out << "<elf-function-symbols>\n"; |
| |
| write_elf_symbols_table(corpus->get_sorted_fun_symbols(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| out << "</elf-function-symbols>\n"; |
| } |
| |
| // Write the variable symbols data base. |
| if (!corpus->get_var_symbol_map().empty()) |
| { |
| do_indent_to_level(ctxt, indent, 1); |
| out << "<elf-variable-symbols>\n"; |
| |
| write_elf_symbols_table(corpus->get_sorted_var_symbols(), ctxt, |
| get_indent_to_level(ctxt, indent, 2)); |
| |
| do_indent_to_level(ctxt, indent, 1); |
| out << "</elf-variable-symbols>\n"; |
| } |
| |
| // Now write the translation units. |
| unsigned nb_tus = corpus->get_translation_units().size(), n = 0; |
| for (translation_units::const_iterator i = |
| corpus->get_translation_units().begin(); |
| i != corpus->get_translation_units().end(); |
| ++i, ++n) |
| { |
| translation_unit& tu = **i; |
| write_translation_unit(ctxt, tu, |
| get_indent_to_level(ctxt, indent, 1), |
| n == nb_tus - 1); |
| } |
| |
| do_indent_to_level(ctxt, indent, 0); |
| out << "</abi-corpus>\n"; |
| |
| ctxt.clear_referenced_types(); |
| |
| return true; |
| } |
| |
| /// Serialize an ABI corpus group to a single native xml document. |
| /// The root note of the resulting XML document is 'abi-corpus-group'. |
| /// |
| /// @param ctxt the write context to use. |
| /// |
| /// @param group the corpus group to serialize. |
| /// |
| /// @param indent the number of white space indentation to use. |
| /// |
| /// @return true upon successful completion, false otherwise. |
| bool |
| write_corpus_group(write_context& ctxt, |
| const corpus_group_sptr& group, |
| unsigned indent) |
| |
| { |
| if (!group) |
| return false; |
| |
| do_indent_to_level(ctxt, indent, 0); |
| |
| std::ostream& out = ctxt.get_ostream(); |
| |
| out << "<abi-corpus-group "; |
| write_version_info(ctxt); |
| |
| if (!group->get_path().empty() && ctxt.get_write_corpus_path()) |
| out << " path='" << xml::escape_xml_string(group->get_path()) << "'"; |
| |
| if (!group->get_architecture_name().empty() && ctxt.get_write_architecture()) |
| out << " architecture='" << group->get_architecture_name()<< "'"; |
| |
| write_tracking_non_reachable_types(group, out); |
| |
| if (group->is_empty()) |
| { |
| out << "/>\n"; |
| return true; |
| } |
| |
| out << ">\n"; |
| |
| // Write the list of corpora |
| for (corpus_group::corpora_type::const_iterator c = |
| group->get_corpora().begin(); |
| c != group->get_corpora().end(); |
| ++c) |
| write_corpus(ctxt, *c, get_indent_to_level(ctxt, indent, 1), true); |
| |
| do_indent_to_level(ctxt, indent, 0); |
| out << "</abi-corpus-group>\n"; |
| |
| return true; |
| } |
| |
| } //end namespace xml_writer |
| |
| // <Debugging routines> |
| |
| using namespace abigail::ir; |
| |
| /// Serialize a pointer to decl_base to an output stream. |
| /// |
| /// @param d the pointer to decl_base to serialize. |
| /// |
| /// @param o the output stream to consider. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const decl_base_sptr d, std::ostream& o, const bool annotate) |
| { |
| xml_writer::write_context ctxt(d->get_environment(), o); |
| xml_writer::set_annotate(ctxt, annotate); |
| write_decl(d, ctxt, /*indent=*/0); |
| } |
| |
| /// Serialize a pointer to decl_base to stderr. |
| /// |
| /// @param d the pointer to decl_base to serialize. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const decl_base_sptr d, const bool annotate) |
| {dump(d, cerr, annotate);} |
| |
| /// Serialize a pointer to type_base to an output stream. |
| /// |
| /// @param t the pointer to type_base to serialize. |
| /// |
| /// @param o the output stream to serialize the @ref type_base to. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const type_base_sptr t, std::ostream& o, const bool annotate) |
| {dump(get_type_declaration(t), o, annotate);} |
| |
| /// Serialize a pointer to type_base to stderr. |
| /// |
| /// @param t the pointer to type_base to serialize. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const type_base_sptr t, const bool annotate) |
| {dump(t, cerr, annotate);} |
| |
| /// Serialize a pointer to var_decl to an output stream. |
| /// |
| /// @param v the pointer to var_decl to serialize. |
| /// |
| /// @param o the output stream to serialize the @ref var_decl to. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const var_decl_sptr v, std::ostream& o, const bool annotate) |
| { |
| xml_writer::write_context ctxt(v->get_environment(), o); |
| xml_writer::set_annotate(ctxt, annotate); |
| write_var_decl(v, ctxt, /*linkage_name*/true, /*indent=*/0); |
| } |
| |
| /// Serialize a pointer to var_decl to stderr. |
| /// |
| /// @param v the pointer to var_decl to serialize. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const var_decl_sptr v, const bool annotate) |
| {dump(v, cerr, annotate);} |
| |
| /// Serialize a @ref translation_unit to an output stream. |
| /// |
| /// @param t the translation_unit to serialize. |
| /// |
| /// @param o the outpout stream to serialize the translation_unit to. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const translation_unit& t, std::ostream& o, const bool annotate) |
| { |
| xml_writer::write_context ctxt(t.get_environment(), o); |
| xml_writer::set_annotate(ctxt, annotate); |
| write_translation_unit(ctxt, t, /*indent=*/0); |
| } |
| |
| /// Serialize an instance of @ref translation_unit to stderr. |
| /// |
| /// @param t the translation_unit to serialize. |
| void |
| dump(const translation_unit& t, const bool annotate) |
| {dump(t, cerr, annotate);} |
| |
| /// Serialize a pointer to @ref translation_unit to an output stream. |
| /// |
| /// @param t the @ref translation_unit_sptr to serialize. |
| /// |
| /// @param o the output stream to serialize the translation unit to. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const translation_unit_sptr t, std::ostream& o, const bool annotate) |
| { |
| if (t) |
| dump(*t, o, annotate); |
| } |
| |
| /// Serialize a pointer to @ref translation_unit to stderr. |
| /// |
| /// @param t the translation_unit_sptr to serialize. |
| /// |
| /// @param annotate whether ABIXML output should be annotated. |
| void |
| dump(const translation_unit_sptr t, const bool annotate) |
| { |
| if (t) |
| dump(*t, annotate); |
| } |
| |
| /// Serialize a source location to an output stream. |
| /// |
| /// @param l the declaration to consider. |
| /// |
| /// @param o the output stream to serialize to. |
| void |
| dump_location(const location& l, ostream& o) |
| { |
| string path; |
| unsigned line = 0, col = 0; |
| |
| l.expand(path, line, col); |
| o << path << ":" << line << "," << col << "\n"; |
| } |
| |
| /// Serialize a source location for debugging purposes. |
| /// |
| /// The location is serialized to the standard error output stream. |
| /// |
| /// @param l the declaration to consider. |
| /// |
| void |
| dump_location(const location& l) |
| {dump_location(l, cerr);} |
| |
| /// Serialize the source location of a decl to an output stream for |
| /// debugging purposes. |
| /// |
| /// @param d the declaration to consider. |
| /// |
| /// @param o the output stream to serizalize the location to. |
| void |
| dump_decl_location(const decl_base& d, ostream& o) |
| {dump_location(d.get_location(), o);} |
| |
| /// Serialize the source location of a decl to stderr for debugging |
| /// purposes. |
| /// |
| /// @param d the declaration to consider. |
| void |
| dump_decl_location(const decl_base& d) |
| {dump_decl_location(d, cerr);} |
| |
| /// Serialize the source location of a dcl to stderr for debugging |
| /// purposes. |
| /// |
| /// @param d the declaration to consider. |
| void |
| dump_decl_location(const decl_base* d) |
| { |
| if (d) |
| dump_decl_location(*d); |
| } |
| |
| /// Serialize the source location of a decl to stderr for debugging |
| /// purposes. |
| /// |
| /// @param d the declaration to consider. |
| void |
| dump_decl_location(const decl_base_sptr d) |
| {dump_decl_location(d.get());} |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| /// Write one of the records of the "type-ids" debugging file. |
| /// |
| /// This is a sub-routine of write_canonical_type_ids. |
| /// |
| /// @param ctxt the context to use. |
| /// |
| /// @param type the type which canonical type pointer value to emit. |
| /// |
| /// @param o the output stream to write to. |
| static void |
| write_type_record(xml_writer::write_context& ctxt, |
| const type_base* type, |
| ostream& o) |
| { |
| // We want to serialize a type record which content looks like: |
| // |
| // <type> |
| // <id>type-id-573</id> |
| // <c>0x262ee28</c> |
| // </type> |
| // <type> |
| // <id>type-id-569</id> |
| // <c>0x2628298</c> |
| // </type> |
| // <type> |
| // <id>type-id-575</id> |
| // <c>0x25f9ba8</c> |
| // </type> |
| |
| string id = ctxt.get_id_for_type (const_cast<type_base*>(type)); |
| o << " <type>\n" |
| << " <id>" << id << "</id>\n" |
| << " <c>" |
| << std::hex |
| << (type->get_canonical_type() |
| ? reinterpret_cast<uintptr_t>(type->get_canonical_type().get()) |
| : 0xdeadbabe) |
| << "</c>\n" |
| << " </type>\n"; |
| } |
| |
| /// Serialize the map that is stored at |
| /// environment::get_type_id_canonical_type_map() to an output stream. |
| /// |
| /// This is for debugging purposes and is triggered ultimately by |
| /// invoking the command 'abidw --debug-abidiff <binary>'. |
| /// |
| /// @param ctxt the write context. |
| /// |
| /// @param o the output stream to serialize the map to. |
| void |
| write_canonical_type_ids(xml_writer::write_context& ctxt, ostream& o) |
| { |
| // We want to serialize a file which content looks like: |
| // |
| // <abixml-types-check> |
| // <type> |
| // <id>type-id-573</id> |
| // <c>0x262ee28</c> |
| // </type> |
| // <type> |
| // <id>type-id-569</id> |
| // <c>0x2628298</c> |
| // </type> |
| // <type> |
| // <id>type-id-575</id> |
| // <c>0x25f9ba8</c> |
| // </type> |
| // <abixml-types-check> |
| |
| o << "<abixml-types-check>\n"; |
| |
| for (const auto &type : ctxt.get_emitted_types_set()) |
| write_type_record(ctxt, type, o); |
| |
| o << "</abixml-types-check>\n"; |
| } |
| |
| /// Serialize the map that is stored at |
| /// environment::get_type_id_canonical_type_map() to a file. |
| /// |
| /// This is for debugging purposes and is triggered ultimately by |
| /// invoking the command 'abidw --debug-abidiff <binary>'. |
| /// |
| /// @param ctxt the write context. |
| /// |
| /// @param file_path the file to serialize the map to. |
| bool |
| write_canonical_type_ids(xml_writer::write_context& ctxt, |
| const string &file_path) |
| { |
| std:: ofstream o (file_path); |
| |
| if (!o.is_open()) |
| return true; |
| write_canonical_type_ids(ctxt, o); |
| o.close(); |
| return true; |
| } |
| #endif |
| // </Debugging routines> |
| } //end namespace abigail |