Files
third_party_libabigail/src/abg-reader.cc
T
Dodji Seketeli 5b09ea77e2 Handle the life time of the map of canonical types
While working on something else, it turned out that we need to cleanup
(de-allocate) the map of canonical types when all the translation
units that own types are de-allocated.  Otherwise, when new
translation units are created later, the types in the canonical types
map become unrelated to the types in these new translation units,
leading to memory management issues.

This patch introduces a "usage watchdog" which detects when no
translation unit uses the type system anymore.  That usage watchdog is
then used in the destructor of the translation_unit type to
de-allocate the global data that is logically owned by by the type
system.

The patch also changes the API to read translation units and corpora
in a way that forces users to get a handle on the resulting shared
pointer.

	* include/abg-ir.h (type_base::canonical_types_map_type): Move
	this typedef into abg-ir.cc and out of the type_base namespace.
	(type_base::get_canonical_types_map): Likewise.
	* src/abg-ir.cc (canonical_types_map_type): New typedef that got
	moved here from type_base::canonical_types_map_type.
	(get_canonical_types_map): Likewise got moved here from
	type_base::get_canonical_types_map.  Made static in the process.
	(class usage_watchdog): New type.
	(usage_watchdog_sptr, usage_watchdog_wptr): New typedefs.
	(get_usage_watchdog, get_usage_watchdog_wptr, ref_usage_watchdog)
	(maybe_cleanup_type_system_data): New static functions.
	(translation_unit::priv::usage_watchdog_): Add new data member.
	(translation_unit::priv::priv): Get a reference on the usage
	watchdog.
	(translation_unit::priv::~priv): If the usage watchdog says that
	the type system is not used, then cleanup the global data
	logically owned by the type system.

	* include/abg-dwarf-reader.h (read_corpus_from_elf): Make this
	return a corpus and set the status by reference using a parameter.
	* src/abg-dwarf-reader.cc (read_corpus_from_elf): Implement the
	above.
	* include/abg-reader.h (read_translation_unit_from_file)
	(read_translation_unit_from_buffer)
	(read_translation_unit_from_istream): Remove the overloads that do
	not return a translation_unit_sptr and that pass it as a
	parameter.  Only keep the overloads that return a
	translation_unit_sptr, forcing users of the API to own a proper
	reference on the resulting translation_unit pointer.  That is
	important to handle the life time of the global data of the type
	system that need to be cleared when the last translation unit is
	de-allocated.
	* src/abg-reader.cc (read_translation_unit_from_input): Make this
	return a translation_unit_sptr.
	(read_translation_unit_from_file)
	(read_translation_unit_from_buffer)
	(read_translation_unit_from_istream): Remove the overloads that do
	not return a translation_unit_sptr and that pass it as a
	parameter.  Only keep the overloads that return a
	translation_unit_sptr.
	(read_to_translation_unit): Make this return a
	translation_unit_sptr.
	* tests/print-diff-tree.cc (main): Adjust.
	* tests/test-diff-dwarf.cc (main): Likewise.
	* tests/test-ir-walker.cc (main): Likewise.
	* tests/test-read-dwarf.cc (main): Likewise.
	* tests/test-read-write.cc (main): Likewise.
	* tools/abicompat.cc (main): Likewise.
	* tools/abidiff.cc (main): Likewise.
	* tools/abidw.cc (main): Likewise.
	* tools/abilint.cc (main): Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2015-07-09 11:12:40 +02:00

4044 lines
112 KiB
C++

// -*- mode: C++ -*-
//
// Copyright (C) 2013-2015 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
/// @file
///
/// This file contains the definitions of the entry points to
/// de-serialize an instance of @ref abigail::translation_unit from an
/// ABI Instrumentation file in libabigail native XML format.
#include "config.h"
#include <cstring>
#include <cstdlib>
#include <tr1/unordered_map>
#include <deque>
#include <assert.h>
#include <sstream>
#include <libxml/xmlstring.h>
#include <libxml/xmlreader.h>
#include "abg-libxml-utils.h"
#include "abg-corpus.h"
#ifdef WITH_ZIP_ARCHIVE
#include "abg-libzip-utils.h"
#endif
namespace abigail
{
using xml::xml_char_sptr;
/// The namespace for the native XML file format reader.
namespace xml_reader
{
using std::string;
using std::deque;
using std::tr1::shared_ptr;
using std::tr1::unordered_map;
using std::tr1::dynamic_pointer_cast;
using std::vector;
using std::istream;
#ifdef WITH_ZIP_ARCHIVE
using zip_utils::zip_sptr;
using zip_utils::zip_file_sptr;
using zip_utils::open_archive;
using zip_utils::open_file_in_archive;
#endif //WITH_ZIP_ARCHIVE
class read_context;
/// This abstracts the context in which the current ABI
/// instrumentation dump is being de-serialized. It carries useful
/// information needed during the de-serialization, but that does not
/// make sense to be stored in the final resulting in-memory
/// representation of ABI Corpus.
class read_context
{
read_context();
public:
typedef unordered_map<string,
shared_ptr<type_base> >::const_iterator
const_types_map_it;
typedef unordered_map<string,
shared_ptr<function_tdecl> >::const_iterator
const_fn_tmpl_map_it;
typedef unordered_map<string,
shared_ptr<class_tdecl> >::const_iterator
const_class_tmpl_map_it;
typedef unordered_map<string, xmlNodePtr> string_xml_node_map;
typedef unordered_map<xmlNodePtr, decl_base_sptr> xml_node_decl_base_sptr_map;
private:
unordered_map<string, shared_ptr<type_base> > m_types_map;
unordered_map<string, shared_ptr<function_tdecl> > m_fn_tmpl_map;
unordered_map<string, shared_ptr<class_tdecl> > m_class_tmpl_map;
unordered_map<string, bool> m_wip_classes_map;
vector<type_base_sptr> m_types_to_canonicalize;
string_xml_node_map m_id_xml_node_map;
xml_node_decl_base_sptr_map m_xml_node_decl_map;
xml::reader_sptr m_reader;
deque<shared_ptr<decl_base> > m_decls_stack;
corpus_sptr m_corpus;
corpus::exported_decls_builder* m_exported_decls_builder_;
public:
read_context(xml::reader_sptr reader)
: m_reader(reader),
m_exported_decls_builder_()
{}
xml::reader_sptr
get_reader() const
{return m_reader;}
const string_xml_node_map&
get_id_xml_node_map() const
{return m_id_xml_node_map;}
string_xml_node_map&
get_id_xml_node_map()
{return m_id_xml_node_map;}
void
clear_id_xml_node_map()
{get_id_xml_node_map().clear();}
const xml_node_decl_base_sptr_map&
get_xml_node_decl_map() const
{return m_xml_node_decl_map;}
xml_node_decl_base_sptr_map&
get_xml_node_decl_map()
{return m_xml_node_decl_map;}
void
map_xml_node_to_decl(xmlNodePtr node,
decl_base_sptr decl)
{
if (node)
get_xml_node_decl_map()[node]= decl;
}
decl_base_sptr
get_decl_for_xml_node(xmlNodePtr node) const
{
xml_node_decl_base_sptr_map::const_iterator i =
get_xml_node_decl_map().find(node);
if (i != get_xml_node_decl_map().end())
return i->second;
return decl_base_sptr();
}
void
clear_xml_node_decl_map()
{get_xml_node_decl_map().clear();}
void
map_id_and_node (const string& id,
xmlNodePtr node)
{
if (!node)
return;
string_xml_node_map::const_iterator i = get_id_xml_node_map().find(id);
if (i != get_id_xml_node_map().end())
{
// So, there has already been an xml node that has been mapped
// to this ID. That means, there ware an another xml node
// with the same ID. There are just a few cases where we
// should allow this. Let's check that we are in one of these cases.
xml_char_sptr p0 = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only");
bool node_is_declaration_only =
(p0 && xmlStrEqual(p0.get(), BAD_CAST("yes")));
bool node_is_class =
(xmlStrEqual(node->name, BAD_CAST("class-decl")));
bool node_is_type_decl =
(xmlStrEqual(node->name, BAD_CAST("type-decl")));
bool node_is_pointer_or_reference =
(xmlStrEqual(node->name, BAD_CAST("pointer-type-def"))
|| xmlStrEqual(node->name, BAD_CAST("reference-type-def")));
bool node_is_array =
xmlStrEqual(node->name, BAD_CAST("array-type-def"));
bool node_is_typedef =
xmlStrEqual(node->name, BAD_CAST("typedef-decl"));
bool node_is_qualified_type =
(xmlStrEqual(node->name, BAD_CAST("qualified-type-def")));
xml_char_sptr name_val = XML_NODE_GET_ATTRIBUTE(node, "name");
bool node_is_unnamed_enum_ut =
xmlStrEqual(name_val.get(), BAD_CAST("unnamed-enum-underlying-type"));
xml_char_sptr p1 = XML_NODE_GET_ATTRIBUTE(i->second,
"is-declaration-only");
bool is_ok = (node_is_declaration_only
|| (p1 && xmlStrEqual(p1.get(), BAD_CAST("yes")))
|| node_is_class
|| node_is_type_decl
|| node_is_pointer_or_reference
|| node_is_array
|| node_is_typedef
|| node_is_qualified_type
|| node_is_unnamed_enum_ut);
assert(is_ok);
if (is_ok)
get_id_xml_node_map()[id] = node;
}
else
get_id_xml_node_map()[id] = node;
}
xmlNodePtr
get_xml_node_from_id(const string& id) const
{
string_xml_node_map::const_iterator i = get_id_xml_node_map().find(id);
if (i != get_id_xml_node_map().end())
return i->second;
return 0;
}
scope_decl_sptr
get_scope_for_node(xmlNodePtr node);
// This is defined later, after build_type() is declared, because it
// uses it.
type_base_sptr
build_or_get_type_decl(const string& id,
bool add_decl_to_scope);
/// Return the type that is identified by a unique ID. Note that
/// for a type to be "identified" by id, the function key_type_decl
/// must have been previously called with that type and with id.
///
/// @param id the unique id to consider.
///
/// @return the type identified by the unique id id, or a null
/// pointer if no type has ever been associated with id before.
shared_ptr<type_base>
get_type_decl(const string& id) const
{
const_types_map_it i = m_types_map.find(id);
if (i == m_types_map.end())
return shared_ptr<type_base>();
return shared_ptr<type_base>(i->second);
}
/// Return the function template that is identified by a unique ID.
///
/// Note that for a function template to be identified by id, the
/// function key_fn_tmpl_decl must have been previously called with
/// that function template and with id.
///
/// @param id the ID to consider.
///
/// @return the function template identified by id, or a null
/// pointer if no function template has ever been associated with
/// id before.
shared_ptr<function_tdecl>
get_fn_tmpl_decl(const string& id) const
{
const_fn_tmpl_map_it i = m_fn_tmpl_map.find(id);
if (i == m_fn_tmpl_map.end())
return shared_ptr<function_tdecl>();
return i->second;
}
/// Return the class template that is identified by a unique ID.
///
/// Note that for a class template to be identified by id, the
/// function key_class_tmpl_decl must have been previously called
/// with that class template and with id.
///
/// @param id the ID to consider.
///
/// @return the class template identified by id, or a null pointer
/// if no class template has ever been associated with id before.
shared_ptr<class_tdecl>
get_class_tmpl_decl(const string& id) const
{
const_class_tmpl_map_it i = m_class_tmpl_map.find(id);
if (i == m_class_tmpl_map.end())
return shared_ptr<class_tdecl>();
return i->second;
}
/// Return the current lexical scope.
scope_decl*
get_cur_scope()
{
shared_ptr<decl_base> cur_decl = get_cur_decl();
if (dynamic_cast<scope_decl*>(cur_decl.get()))
// The current decl is a scope_decl, so it's our lexical scope.
return dynamic_pointer_cast<scope_decl>(cur_decl).get();
else if (cur_decl)
// The current decl is not a scope_decl, so our lexical scope is
// the scope of this decl.
return cur_decl->get_scope();
else
// We have no scope set.
return 0;
}
decl_base_sptr
get_cur_decl() const
{
if (m_decls_stack.empty())
return shared_ptr<decl_base>(static_cast<decl_base*>(0));
return m_decls_stack.back();
}
translation_unit*
get_translation_unit()
{
const global_scope* global = 0;
if (shared_ptr<decl_base> d = m_decls_stack.front())
global = get_global_scope(d);
if (global)
return global->get_translation_unit();
return 0;
}
/// Test if a given type is from the current translation unit.
///
/// @param type the type to consider.
///
/// @return true iff the type is from the current translation unit.
bool
type_is_from_translation_unit(type_base_sptr type)
{
decl_base_sptr d = get_type_declaration(type);
if (d)
return (ir::get_translation_unit(d) == get_translation_unit());
else if (function_type_sptr fn_type = is_function_type(type))
return lookup_function_type_in_translation_unit(fn_type,
*get_translation_unit());
else
return false;
}
void
push_decl(decl_base_sptr d)
{
m_decls_stack.push_back(d);
}
decl_base_sptr
pop_decl()
{
if (m_decls_stack.empty())
return decl_base_sptr();
shared_ptr<decl_base> t = get_cur_decl();
m_decls_stack.pop_back();
return t;
}
/// Pop all decls until a give scope is popped.
///
/// @param scope the scope to pop.
///
/// @return true if the scope was popped, false otherwise. Note
/// that if the scope wasn't found, it might mean that many other
/// decls were popped.
bool
pop_scope(scope_decl_sptr scope)
{
decl_base_sptr d;
do
{
d = pop_decl();
scope_decl_sptr s = dynamic_pointer_cast<scope_decl>(d);
if (s == scope)
break;
}
while (d);
if (!d)
return false;
return dynamic_pointer_cast<scope_decl>(d) == scope;
}
/// like @ref pop_scope, but if the scope couldn't be popped, the
/// function aborts the execution of the process.
///
/// @param scope the scope to pop.
void
pop_scope_or_abort(scope_decl_sptr scope)
{assert(pop_scope(scope));}
void
clear_decls_stack()
{m_decls_stack.clear();}
void
clear_type_map()
{m_types_map.clear();}
/// Clean the vector of types to canonicalize after the translation
/// unit has been read.
void
clear_types_to_canonicalize()
{m_types_to_canonicalize.clear();}
/// Clean the map of classes that are "Work In Progress"; that is,
/// the map of the class that are currently being built, but at not
/// yet fully built.
void
clear_wip_classes_map()
{m_wip_classes_map.clear();}
/// Mark a given class as being "Work In Progress"; that is, mark it
/// as being currently built.
///
/// @param klass the class to mark as being "Work In Progress".
void
mark_class_as_wip(const class_decl_sptr klass)
{
if (!klass)
return;
string qname = klass->get_qualified_name();
m_wip_classes_map[qname] = true;
}
/// Mark a given class as being *NOT* "Work In Progress" anymore;
/// that is, mark it as being fully built.
///
/// @param klass the class to mark as being built.
void
unmark_class_as_wip(const class_decl_sptr klass)
{
if (!klass)
return;
string qname = klass->get_qualified_name();
m_wip_classes_map.erase(qname);
}
/// Test if a class a being currently built; that is, if it's "Work
/// In Progress".
///
/// @param klass the class to consider.
bool
is_wip_class(const class_decl_sptr klass)
{
if (!klass)
return false;
string qname = klass->get_qualified_name();
unordered_map<string, bool>::const_iterator i =
m_wip_classes_map.find(qname);
return i != m_wip_classes_map.end();
}
/// Associate an ID with a type.
///
/// @param type the type to associate witht he ID.
///
/// @param id the ID to associate to the type.
///
/// @return true upon successful completion, false otherwise. Note
/// that this returns false if the was already associate to an ID
/// before.
bool
key_type_decl(shared_ptr<type_base> type, const string& id,
bool force = false)
{
assert(type);
const_types_map_it i = m_types_map.find(id);
if (i != m_types_map.end() && !force)
return false;
m_types_map[id] = type;
return true;
}
/// Associate an ID with a type.
///
/// If ID is an id for an existing type, this function replaces the
/// exising type with the new DEFINITION type passe in argument.
///
/// @param definition the type to associate witht he ID.
///
/// @param id the ID to associate to the type.
///
/// @return true upon successful completion, false otherwise. Note
/// that this returns false if the was already associate to an ID
/// before.
bool
key_replacement_of_type_decl(shared_ptr<type_base> definition,
const string& id)
{
const_types_map_it i = m_types_map.find(id);
if (i != m_types_map.end())
m_types_map.erase(i);
key_type_decl(definition, id);
return true;
}
/// Associate an ID to a function template.
///
/// @param fn_tmpl_decl the function template to consider.
///
/// @param id the ID to associate to the function template.
///
/// @return true upon successful completion, false otherwise. Note
/// that the function returns false if an ID was previously
/// associated to the function template.
bool
key_fn_tmpl_decl(shared_ptr<function_tdecl> fn_tmpl_decl,
const string& id)
{
assert(fn_tmpl_decl);
const_fn_tmpl_map_it i = m_fn_tmpl_map.find(id);
if (i != m_fn_tmpl_map.end())
return false;
m_fn_tmpl_map[id] = fn_tmpl_decl;
return true;
}
/// Associate an ID to a class template.
///
/// @param class_tmpl_decl the class template to consider.
///
/// @param id the ID to associate to the class template.
///
/// @return true upon successful completion, false otherwise. Note
/// that the function returns false if an ID was previously
/// associated to the class template.
bool
key_class_tmpl_decl(shared_ptr<class_tdecl> class_tmpl_decl,
const string& id)
{
assert(class_tmpl_decl);
const_class_tmpl_map_it i = m_class_tmpl_map.find(id);
if (i != m_class_tmpl_map.end())
return false;
m_class_tmpl_map[id] = class_tmpl_decl;
return true;
}
/// This function must be called on each declaration that is created during
/// the parsing. It adds the declaration to the current scope, and updates
/// the state of the parsing context accordingly.
///
/// @param decl the newly created declaration.
void
push_decl_to_current_scope(shared_ptr<decl_base> decl,
bool add_to_current_scope)
{
assert(decl);
if (add_to_current_scope)
add_decl_to_scope(decl, get_cur_scope());
push_decl(decl);
}
/// This function must be called on each type decl that is created
/// during the parsing. It adds the type decl to the current scope
/// and associates a unique ID to it.
///
/// @param t type_decl
///
/// @param id the unique ID to be associated to t
///
/// @return true upon successful completion.
///
bool
push_and_key_type_decl(shared_ptr<type_base> t, const string& id,
bool add_to_current_scope)
{
shared_ptr<decl_base> decl = dynamic_pointer_cast<decl_base>(t);
assert(decl);
push_decl_to_current_scope(decl, add_to_current_scope);
key_type_decl(t, id);
return true;
}
const corpus_sptr
get_corpus() const
{return m_corpus;}
corpus_sptr
get_corpus()
{return m_corpus;}
void
set_corpus(corpus_sptr c)
{m_corpus = c;}
/// Getter for the object that determines if a given declaration
/// ought to be put in the set of exported decls of the current
/// corpus.
///
/// @return the exported decls builder.
corpus::exported_decls_builder*
get_exported_decls_builder()
{return m_exported_decls_builder_;}
/// Setter for the object that determines if a given declaration
/// ought to be put in the set of exported decls of the current
/// corpus.
///
/// @param d the new exported decls builder.
///
/// @return the exported decls builder.
void
set_exported_decls_builder(corpus::exported_decls_builder* d)
{m_exported_decls_builder_ = d;}
/// Add a given function to the set of exported functions of the
/// current corpus, if the function satisfies the different
/// constraints requirements.
///
/// @param fn the function to consider.
void
maybe_add_fn_to_exported_decls(function_decl* fn)
{
if (fn)
if (corpus::exported_decls_builder* b = get_exported_decls_builder())
b->maybe_add_fn_to_exported_fns(fn);
}
/// Add a given variable to the set of exported functions of the
/// current corpus, if the function satisfies the different
/// constraints requirements.
///
/// @param var the variable to consider.
void
maybe_add_var_to_exported_decls(var_decl* var)
{
if (var)
if (corpus::exported_decls_builder* b = get_exported_decls_builder())
b->maybe_add_var_to_exported_vars(var);
}
/// Clear all the data that must absolutely be cleared at the end of
/// the parsing of a translation unit.
void
clear_per_translation_unit_data()
{
clear_xml_node_decl_map();
clear_id_xml_node_map();
clear_decls_stack();
}
/// Clear all the data that must absolutely be cleared at the end of
/// the parsing of an ABI corpus.
void
clear_per_corpus_data()
{
clear_type_map();
clear_types_to_canonicalize();
}
/// Test if a type should be canonicalized early. If so,
/// canonicalize it right away. Otherwise, schedule it for late
/// canonicalizing; that is, schedule it so that it's going to be
/// canonicalized when the translation unit is fully read.
///
/// @param t the type to consider for canonicalizing.
void
maybe_canonicalize_type(type_base_sptr t)
{
if (!t)
return;
if (t->get_canonical_type())
return;
bool is_class_decl_only = false;
if (class_decl_sptr klass = is_class_type(t))
is_class_decl_only = klass->get_is_declaration_only();
// If this class has some non-canonicalized sub type, then wait
// for the when we've read all the translation unit to
// canonicalize all of its non-canonicalized sub types and then we
// can canonicalize this one.
//
// Also, if this is a declaration-only class, wait for the end of
// the translation unit reading so that we have its definition and
// then we'll use that for canonicalizing it.
if (!type_has_non_canonicalized_subtype(t)
&& !is_class_decl_only)
canonicalize(t);
else
schedule_type_for_late_canonicalizing(t);
}
/// Schedule a type for being canonicalized after the current
/// translation unit is read.
///
/// @param t the type to consider for canonicalization.
void
schedule_type_for_late_canonicalizing(type_base_sptr t)
{m_types_to_canonicalize.push_back(t);}
/// Perform the canonicalizing of types that ought to be done after
/// the current translation unit is read. This function is called
/// at the current translation unit is fully built.
void
perform_late_type_canonicalizing()
{
for (vector<type_base_sptr>::iterator i = m_types_to_canonicalize.begin();
i != m_types_to_canonicalize.end();
++i)
canonicalize(*i);
}
/// Test if a type ID is new in the current translation unit.
///
/// @param id the translation unit ID to test for.
///
/// @return true iff the type ID is new in the current translation
/// unit.
bool
type_id_new_in_translation_unit(const string& id)
{
type_base_sptr t = get_type_decl(id);
return !t || !type_is_from_translation_unit(t);
}
};// end class read_context
static int advance_cursor(read_context&);
static translation_unit_sptr read_translation_unit_from_input(read_context&);
static bool read_symbol_db_from_input(read_context&, bool,
string_elf_symbols_map_sptr&);
static bool read_location(read_context&, xmlNodePtr, location&);
static bool read_visibility(xmlNodePtr, decl_base::visibility&);
static bool read_binding(xmlNodePtr, decl_base::binding&);
static bool read_access(xmlNodePtr, access_specifier&);
static bool read_size_and_alignment(xmlNodePtr, size_t&, size_t&);
static bool read_static(xmlNodePtr, bool&);
static bool read_offset_in_bits(xmlNodePtr, size_t&);
static bool read_cdtor_const(xmlNodePtr, bool&, bool&, bool&);
static bool read_is_declaration_only(xmlNodePtr, bool&);
static bool read_is_virtual(xmlNodePtr, bool&);
static bool read_is_struct(xmlNodePtr, bool&);
static bool read_elf_symbol_type(xmlNodePtr, elf_symbol::type&);
static bool read_elf_symbol_binding(xmlNodePtr, elf_symbol::binding&);
static namespace_decl_sptr
build_namespace_decl(read_context&, const xmlNodePtr, bool);
// <build a c++ class from an instance of xmlNodePtr>
//
// Note that whenever a new function to build a type is added here,
// you should make sure to call it from the build_type function, which
// should be the last function of the list of declarated function
// below.
static elf_symbol_sptr
build_elf_symbol(read_context&, const xmlNodePtr);
static elf_symbol_sptr
build_elf_symbol_from_reference(read_context&, const xmlNodePtr,
bool);
static string_elf_symbols_map_sptr
build_elf_symbol_db(read_context&, const xmlNodePtr, bool);
static shared_ptr<function_decl::parameter>
build_function_parameter (read_context&, const xmlNodePtr);
static shared_ptr<function_decl>
build_function_decl(read_context&, const xmlNodePtr,
shared_ptr<class_decl>, bool);
static shared_ptr<var_decl>
build_var_decl(read_context&, const xmlNodePtr, bool);
static shared_ptr<type_decl>
build_type_decl(read_context&, const xmlNodePtr, bool);
static qualified_type_def_sptr
build_qualified_type_decl(read_context&, const xmlNodePtr, bool);
static shared_ptr<pointer_type_def>
build_pointer_type_def(read_context&, const xmlNodePtr, bool);
static shared_ptr<reference_type_def>
build_reference_type_def(read_context&, const xmlNodePtr, bool);
static array_type_def::subrange_sptr
build_subrange_type(read_context&, const xmlNodePtr);
static array_type_def_sptr
build_array_type_def(read_context&, const xmlNodePtr, bool);
static enum_type_decl_sptr
build_enum_type_decl(read_context&, const xmlNodePtr, bool);
static shared_ptr<typedef_decl>
build_typedef_decl(read_context&, const xmlNodePtr, bool);
static class_decl_sptr
build_class_decl(read_context&, const xmlNodePtr, bool);
static shared_ptr<function_tdecl>
build_function_tdecl(read_context&, const xmlNodePtr, bool);
static shared_ptr<class_tdecl>
build_class_tdecl(read_context&, const xmlNodePtr, bool);
static type_tparameter_sptr
build_type_tparameter(read_context&, const xmlNodePtr,
unsigned, template_decl_sptr);
static type_composition_sptr
build_type_composition(read_context&, const xmlNodePtr,
unsigned, template_decl_sptr);
static non_type_tparameter_sptr
build_non_type_tparameter(read_context&, const xmlNodePtr,
unsigned, template_decl_sptr);
static template_tparameter_sptr
build_template_tparameter(read_context&, const xmlNodePtr,
unsigned, template_decl_sptr);
static template_parameter_sptr
build_template_parameter(read_context&, const xmlNodePtr,
unsigned, template_decl_sptr);
// Please make this build_type function be the last one of the list.
// Note that it should call each type-building function above. So
// please make sure to update it accordingly, whenever a new
// type-building function is added here.
static shared_ptr<type_base>
build_type(read_context&, const xmlNodePtr, bool);
// </build a c++ class from an instance of xmlNodePtr>
static decl_base_sptr handle_element_node(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_type_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_namespace_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_qualified_type_decl(read_context&,
xmlNodePtr, bool);
static decl_base_sptr handle_pointer_type_def(read_context&,
xmlNodePtr, bool);
static decl_base_sptr handle_reference_type_def(read_context&,
xmlNodePtr, bool);
static decl_base_sptr handle_array_type_def(read_context&,
xmlNodePtr, bool);
static decl_base_sptr handle_enum_type_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_typedef_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_var_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_function_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_class_decl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_function_tdecl(read_context&, xmlNodePtr, bool);
static decl_base_sptr handle_class_tdecl(read_context&, xmlNodePtr, bool);
/// Get the IR node representing the scope for a given XML node.
///
/// This function might trigger the building of a full sub-tree of IR.
///
/// @param node the XML for which to return the scope decl. If its
/// parent XML node has no corresponding IR node, that IR node is constructed.
///
/// @return the IR node representing the scope of the IR node for the
/// XML node given in argument.
scope_decl_sptr
read_context::get_scope_for_node(xmlNodePtr node)
{
scope_decl_sptr nil, scope;
if (!node)
return nil;
xmlNodePtr parent = node->parent;
if (parent
&& (xmlStrEqual(parent->name, BAD_CAST("data-member"))
|| xmlStrEqual(parent->name, BAD_CAST("member-type"))
|| xmlStrEqual(parent->name, BAD_CAST("member-function"))
|| xmlStrEqual(parent->name, BAD_CAST("member-template"))))
parent = parent->parent;
xml_node_decl_base_sptr_map::const_iterator i =
get_xml_node_decl_map().find(parent);
if (i == get_xml_node_decl_map().end())
{
scope_decl_sptr parent_scope = get_scope_for_node(parent);
push_decl(parent_scope);
scope = dynamic_pointer_cast<scope_decl>
(handle_element_node(*this, parent, /*add_decl_to_scope=*/true));
assert(scope);
pop_scope_or_abort(parent_scope);
}
else
scope = dynamic_pointer_cast<scope_decl>(i->second);
return scope;
}
/// Get the type declaration IR node that matches a given XML type node ID.
///
/// If no IR node has been built for this ID, this function builds the
/// type declaration IR node and returns it. Subsequent invocation of
/// this function with this ID will just return that ID previously returned.
///
/// @param id the XML node ID to consider.
///
/// @return the type declaration for the ID given in parameter.
type_base_sptr
read_context::build_or_get_type_decl(const string& id,
bool add_decl_to_scope)
{
type_base_sptr t = get_type_decl(id);
if (!t)
{
xmlNodePtr n = get_xml_node_from_id(id);
assert(n);
scope_decl_sptr scope;
if (add_decl_to_scope)
{
scope = get_scope_for_node(n);
/// In some cases, if for instance the scope of 'n' is a
/// namespace, get_scope_for_node() can trigger the building
/// of what is underneath of the namespace, if that has not
/// already been done. So after that, the IR node for 'n'
/// might have been built; let's try to see if we are in
/// that case. Otherwise, we'll just build the IR node for
/// 'n' ourselves.
if ((t = get_type_decl(id)))
return t;
assert(scope);
push_decl(scope);
}
t = build_type(*this, n, add_decl_to_scope);
assert(t);
map_xml_node_to_decl(n, get_type_declaration(t));
if (add_decl_to_scope)
pop_scope_or_abort(scope);
}
return t;
}
/// Moves the xmlTextReader cursor to the next xml node in the input
/// document. Return 1 of the parsing was successful, 0 if no input
/// xml token is left, or -1 in case of error.
///
/// @param ctxt the read context
///
static int
advance_cursor(read_context& ctxt)
{
xml::reader_sptr reader = ctxt.get_reader();
return xmlTextReaderRead(reader.get());
}
/// Walk an entire XML sub-tree to build a map where the key is the
/// the value of the 'id' attribute (for type definitions) and the key
/// is the xml node containing the 'id' attribute.
///
/// @param ctxt the context of the reader.
///
/// @param node the XML sub-tree node to walk. It must be an element
/// node.
static void
walk_xml_node_to_map_type_ids(read_context& ctxt,
xmlNodePtr node)
{
xmlNodePtr n = node;
if (!n || n->type != XML_ELEMENT_NODE)
return;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "id"))
{
string id = CHAR_STR(s);
ctxt.map_id_and_node(id, n);
}
for (n = n->children; n; n = n->next)
walk_xml_node_to_map_type_ids(ctxt, n);
}
/// Parse the input XML document containing a translation_unit,
/// represented by an 'abi-instr' element node, associated to the current
/// context.
///
/// @param ctxt the current input context
///
/// @return the translation unit resulting from the parsing upon
/// successful completion, or nil.
static translation_unit_sptr
read_translation_unit_from_input(read_context& ctxt)
{
translation_unit_sptr tu, nil;
xml::reader_sptr reader = ctxt.get_reader();
if (!reader)
return nil;
// The document must start with the abi-instr node.
int status = 1;
while (status == 1
&& XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
status = advance_cursor (ctxt);
if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
BAD_CAST("abi-instr")))
return nil;
xmlNodePtr node = xmlTextReaderExpand(reader.get());
if (!node)
return nil;
tu.reset(new translation_unit(""));
xml::xml_char_sptr addrsize_str =
XML_NODE_GET_ATTRIBUTE(node, "address-size");
if (addrsize_str)
{
char address_size = atoi(reinterpret_cast<char*>(addrsize_str.get()));
tu->set_address_size(address_size);
}
xml::xml_char_sptr path_str = XML_NODE_GET_ATTRIBUTE(node, "path");
if (path_str)
tu->set_path(reinterpret_cast<char*>(path_str.get()));
// We are at global scope, as we've just seen the top-most
// "abi-instr" element.
ctxt.push_decl(tu->get_global_scope());
ctxt.map_xml_node_to_decl(node, tu->get_global_scope());
walk_xml_node_to_map_type_ids(ctxt, node);
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
assert(handle_element_node(ctxt, n,
/*add_decl_to_scope=*/true));
}
xmlTextReaderNext(reader.get());
ctxt.clear_per_translation_unit_data();
return tu;
}
/// Parse the input XML document containing a function symbols
/// or a variable symbol database.
///
/// A function symbols database is an XML element named
/// "elf-function-symbols" and a variable symbols database is an XML
/// element named "elf-variable-symbols." They contains "elf-symbol"
/// XML elements.
///
/// @param ctxt the read_context to use for the parsing.
///
/// @param function_symbols is true if this function should look for a
/// function symbols database, false if it should look for a variable
/// symbols database.
///
/// @param symdb the resulting symbol database object. This is set
/// iff the function return true.
///
/// @return true upon successful parsing, false otherwise.
static bool
read_symbol_db_from_input(read_context& ctxt,
bool function_symbols,
string_elf_symbols_map_sptr& symdb)
{
xml::reader_sptr reader = ctxt.get_reader();
if (!reader)
return false;
// The symbol db must start with the 'elf-function-symbols" or
// 'elf-variable-symbols' element node.
int status = 1;
while (status == 1
&& XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
status = advance_cursor (ctxt);
if (status != 1)
return false;
if (function_symbols
&& !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
BAD_CAST("elf-function-symbols")))
return false;
if (!function_symbols
&& !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
BAD_CAST("elf-variable-symbols")))
return false;
xmlNodePtr node = xmlTextReaderExpand(reader.get());
if (!node)
return false;
symdb = build_elf_symbol_db(ctxt, node, function_symbols);
xmlTextReaderNext(reader.get());
return symdb;
}
/// From an "elf-needed" XML_ELEMENT node, build a vector of strings
/// representing the vector of the dependencies needed by a given
/// corpus.
///
/// @param node the XML_ELEMENT node of name "elf-needed".
///
/// @param needed the output vector of string to populate with the
/// vector of dependency names found on the xml node @p node.
///
/// @return true upon successful completion, false otherwise.
static bool
build_needed(xmlNode* node, vector<string>& needed)
{
if (!node)
return false;
if (!node || !xmlStrEqual(node->name,BAD_CAST("elf-needed")))
return false;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE
|| !xmlStrEqual(n->name, BAD_CAST("dependency")))
continue;
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "name"))
xml::xml_char_sptr_to_string(s, name);
if (!name.empty())
needed.push_back(name);
}
return true;
}
/// Move to the next xml element node and expext it to be named
/// "elf-needed". Then read the sub-tree to made of that node and
/// extracts a vector of needed dependencies name from it.
///
/// @param ctxt the read context used to the xml reading.
///
/// @param needed the resulting vector of dependency names.
///
/// @return true upon successful completion, false otherwise.
static bool
read_elf_needed_from_input(read_context& ctxt,
vector<string>& needed)
{
xml::reader_sptr reader = ctxt.get_reader();
if (!reader)
return false;
int status = 1;
while (status == 1
&& XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
status = advance_cursor (ctxt);
if (status != 1)
return false;
if (!xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
BAD_CAST("elf-needed")))
return false;
xmlNodePtr node = xmlTextReaderExpand(reader.get());
if (!node)
return false;
bool result = build_needed(node, needed);
xmlTextReaderNext(reader.get());
return result;
}
/// Parse the input XML document containing an ABI corpus, represented
/// by an 'abi-corpus' element node, associated to the current
/// context.
///
/// @param ctxt the current input context.
///
/// @return the corpus resulting from the parsing
static corpus_sptr
read_corpus_from_input(read_context& ctxt)
{
corpus_sptr nil;
xml::reader_sptr reader = ctxt.get_reader();
if (!reader)
return nil;
// The document must start with the abi-corpus node.
int status = 1;
while (status == 1
&& XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT)
status = advance_cursor (ctxt);
if (status != 1 || !xmlStrEqual (XML_READER_GET_NODE_NAME(reader).get(),
BAD_CAST("abi-corpus")))
return nil;
if (!ctxt.get_corpus())
{
corpus_sptr c(new corpus(""));
ctxt.set_corpus(c);
}
ctxt.clear_per_corpus_data();
corpus& corp = *ctxt.get_corpus();
ctxt.set_exported_decls_builder(corp.get_exported_decls_builder().get());
xml::xml_char_sptr path_str = XML_READER_GET_ATTRIBUTE(reader, "path");
if (path_str)
corp.set_path(reinterpret_cast<char*>(path_str.get()));
xml::xml_char_sptr architecture_str =
XML_READER_GET_ATTRIBUTE(reader, "architecture");
if (architecture_str)
corp.set_architecture_name(reinterpret_cast<char*>(architecture_str.get()));
xml::xml_char_sptr soname_str = XML_READER_GET_ATTRIBUTE(reader, "soname");
if (soname_str)
corp.set_soname(reinterpret_cast<char*>(soname_str.get()));
// Advance the cursor until the next element.
do
status = advance_cursor (ctxt);
while (status == 1
&& XML_READER_GET_NODE_TYPE(reader) != XML_READER_TYPE_ELEMENT);
// Read the needed element
vector<string> needed;
read_elf_needed_from_input(ctxt, needed);
if (!needed.empty())
corp.set_needed(needed);
string_elf_symbols_map_sptr fn_sym_db, var_sym_db;
bool is_ok = false;
// Read the symbol databases.
do
{
is_ok = (read_symbol_db_from_input(ctxt, true, fn_sym_db)
|| read_symbol_db_from_input(ctxt, false, var_sym_db));
if (is_ok)
{
assert(fn_sym_db || var_sym_db);
if (fn_sym_db)
{
corp.set_fun_symbol_map(fn_sym_db);
fn_sym_db.reset();
}
else if (var_sym_db)
{
corp.set_var_symbol_map(var_sym_db);
var_sym_db.reset();
}
}
}
while (is_ok);
// Read the translation units.
do
{
translation_unit_sptr tu = read_translation_unit_from_input(ctxt);
is_ok = tu;
if (is_ok)
corp.add(tu);
}
while (is_ok);
ctxt.perform_late_type_canonicalizing();
corp.set_origin(corpus::NATIVE_XML_ORIGIN);
return ctxt.get_corpus();;
}
/// Parse an ABI instrumentation file (in XML format) at a given path.
///
/// @param input_file a path to the file containing the xml document
/// to parse.
///
/// @return the translation unit resulting from the parsing upon
/// successful completion, or nil.
translation_unit_sptr
read_translation_unit_from_file(const string& input_file)
{
read_context read_ctxt(xml::new_reader_from_file(input_file));
return read_translation_unit_from_input(read_ctxt);
}
/// Parse an ABI instrumentation file (in XML format) from an
/// in-memory buffer.
///
/// @param buffer the in-memory buffer containing the xml document to
/// parse.
///
/// @return the translation unit resulting from the parsing upon
/// successful completion, or nil.
translation_unit_sptr
read_translation_unit_from_buffer(const string& buffer)
{
read_context read_ctxt(xml::new_reader_from_buffer(buffer));
return read_translation_unit_from_input(read_ctxt);
}
/// This function is called by @ref read_translation_unit_from_input.
/// It handles the current xml element node of the reading context.
/// The result of the "handling" is to build the representation of the
/// xml node and tied it to the current translation unit.
///
/// @param ctxt the current parsing context.
///
/// @return true upon successful completion, false otherwise.
static decl_base_sptr
handle_element_node(read_context& ctxt, xmlNodePtr node,
bool add_to_current_scope)
{
decl_base_sptr decl;
if (!node)
return decl;
((decl = handle_namespace_decl(ctxt, node, add_to_current_scope))
||(decl = handle_type_decl(ctxt, node, add_to_current_scope))
||(decl = handle_qualified_type_decl(ctxt, node,
add_to_current_scope))
||(decl = handle_pointer_type_def(ctxt, node,
add_to_current_scope))
|| (decl = handle_reference_type_def(ctxt, node, add_to_current_scope))
|| (decl = handle_array_type_def(ctxt, node, add_to_current_scope))
|| (decl = handle_enum_type_decl(ctxt, node,
add_to_current_scope))
|| (decl = handle_typedef_decl(ctxt, node,
add_to_current_scope))
|| (decl = handle_var_decl(ctxt, node,
add_to_current_scope))
|| (decl = handle_function_decl(ctxt, node,
add_to_current_scope))
|| (decl = handle_class_decl(ctxt, node,
add_to_current_scope))
|| (decl = handle_function_tdecl(ctxt, node,
add_to_current_scope))
|| (decl = handle_class_tdecl(ctxt, node,
add_to_current_scope)));
return decl;
}
/// Parses location attributes on an xmlNodePtr.
///
///@param ctxt the current parsing context
///
///@param loc the resulting location.
///
/// @return true upon sucessful parsing, false otherwise.
static bool
read_location(read_context& ctxt,
xmlNodePtr node,
location& loc)
{
string file_path;
size_t line = 0, column = 0;
if (xml_char_sptr f = xml::build_sptr(xmlGetProp(node, BAD_CAST("filepath"))))
file_path = CHAR_STR(f);
if (file_path.empty())
return false;
if (xml_char_sptr l = xml::build_sptr(xmlGetProp(node, BAD_CAST("line"))))
line = atoi(CHAR_STR(l));
if (xml_char_sptr c = xml::build_sptr(xmlGetProp(node, BAD_CAST("column"))))
column = atoi(CHAR_STR(c));
loc =
ctxt.get_translation_unit()->get_loc_mgr().create_new_location(file_path,
line,
column);
return true;
}
/// Parse the visibility attribute.
///
/// @param node the xml node to read from.
///
/// @param vis the resulting visibility.
///
/// @return true upon successful completion, false otherwise.
static bool
read_visibility(xmlNodePtr node, decl_base::visibility& vis)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "visibility"))
{
string v = CHAR_STR(s);
if (v == "default")
vis = decl_base::VISIBILITY_DEFAULT;
else if (v == "hidden")
vis = decl_base::VISIBILITY_HIDDEN;
else if (v == "internal")
vis = decl_base::VISIBILITY_INTERNAL;
else if (v == "protected")
vis = decl_base::VISIBILITY_PROTECTED;
else
vis = decl_base::VISIBILITY_DEFAULT;
return true;
}
return false;
}
/// Parse the "binding" attribute on the current element.
///
/// @param node the xml node to build parse the bind from.
///
/// @param bind the resulting binding attribute.
///
/// @return true upon successful completion, false otherwise.
static bool
read_binding(xmlNodePtr node, decl_base::binding& bind)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "binding"))
{
string b = CHAR_STR(s);
if (b == "global")
bind = decl_base::BINDING_GLOBAL;
else if (b == "local")
bind = decl_base::BINDING_LOCAL;
else if (b == "weak")
bind = decl_base::BINDING_WEAK;
else
bind = decl_base::BINDING_GLOBAL;
return true;
}
return false;
}
/// Read the 'access' attribute on the current xml node.
///
/// @param node the xml node to consider.
///
/// @param access the access attribute. Set iff the function returns true.
///
/// @return true upon sucessful completion, false otherwise.
static bool
read_access(xmlNodePtr node, access_specifier& access)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "access"))
{
string a = CHAR_STR(s);
if (a == "private")
access = private_access;
else if (a == "protected")
access = protected_access;
else if (a == "public")
access = public_access;
else
access = private_access;
return true;
}
return false;
}
/// Parse 'size-in-bits' and 'alignment-in-bits' attributes of a given
/// xmlNodePtr reprensting an xml element.
///
/// @param node the xml element node to consider.
///
/// @param size_in_bits the resulting value for the 'size-in-bits'
/// attribute. This set only if this function returns true and the if
/// the attribute was present on the xml element node.
///
/// @param align_in_bits the resulting value for the
/// 'alignment-in-bits' attribute. This set only if this function
/// returns true and the if the attribute was present on the xml
/// element node.
///
/// @return true if either one of the two attributes above were set,
/// false otherwise.
static bool
read_size_and_alignment(xmlNodePtr node,
size_t& size_in_bits,
size_t& align_in_bits)
{
bool got_something = false;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits"))
{
size_in_bits = atoi(CHAR_STR(s));
got_something = true;
}
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits"))
{
align_in_bits = atoi(CHAR_STR(s));
got_something = true;
}
return got_something;
}
/// Parse the 'static' attribute of a given xml element node.
///
/// @param node the xml element node to consider.
///
/// @param is_static the resulting the parsing. Is set if the
/// function returns true.
///
/// @return true if the xml element node has the 'static' attribute
/// set, false otherwise.
static bool
read_static(xmlNodePtr node, bool& is_static)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "static"))
{
string b = CHAR_STR(s);
is_static = (b == "yes") ? true : false;
return true;
}
return false;
}
/// Parse the 'layout-offset-in-bits' attribute of a given xml element node.
///
/// @param offset_in_bits set to true if the element node contains the
/// attribute.
///
/// @return true iff the xml element node contain$s the attribute.
static bool
read_offset_in_bits(xmlNodePtr node,
size_t& offset_in_bits)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "layout-offset-in-bits"))
{
offset_in_bits = atoi(CHAR_STR(s));
return true;
}
return false;
}
/// Parse the 'constructor', 'destructor' and 'const' attribute of a
/// given xml node.
///
/// @param is_constructor the resulting value of the parsing of the
/// 'constructor' attribute. Is set if the xml node contains the
/// attribute and if the function returns true.
///
/// @param is_destructor the resulting value of the parsing of the
/// 'destructor' attribute. Is set if the xml node contains the
/// attribute and if the function returns true.
///
/// @param is_const the resulting value of the parsing of the 'const'
/// attribute. Is set if the xml node contains the attribute and if
/// the function returns true.
///
/// @return true if at least of the attributes above is set, false
/// otherwise.
///
/// Note that callers of this function should initialize
/// is_constructor, is_destructor and is_const prior to passing them
/// to this function.
static bool
read_cdtor_const(xmlNodePtr node,
bool& is_constructor,
bool& is_destructor,
bool& is_const)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "constructor"))
{
string b = CHAR_STR(s);
if (b == "yes")
is_constructor = true;
else
is_constructor = false;
return true;
}
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "destructor"))
{
string b = CHAR_STR(s);
if (b == "yes")
is_destructor = true;
else
is_destructor = false;
return true;
}
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "const"))
{
string b = CHAR_STR(s);
if (b == "yes")
is_const = true;
else
is_const = false;
return true;
}
return false;
}
/// Read the "is-declaration-only" attribute of the current xml node.
///
/// @param node the xml node to consider.
///
/// @param is_decl_only is set to true iff the "is-declaration-only" attribute
/// is present and set to "yes"
///
/// @return true iff the is_decl_only attribute was set.
static bool
read_is_declaration_only(xmlNodePtr node, bool& is_decl_only)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only"))
{
string str = CHAR_STR(s);
if (str == "yes")
is_decl_only = true;
else
is_decl_only = false;
return true;
}
return false;
}
/// Read the "is-virtual" attribute of the current xml node.
///
/// @param node the xml node to read the attribute from
///
/// @param is_virtual is set to true iff the "is-virtual" attribute is
/// present and set to "yes".
///
/// @return true iff the is-virtual attribute is present.
static bool
read_is_virtual(xmlNodePtr node, bool& is_virtual)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-virtual"))
{
string str = CHAR_STR(s);
if (str == "yes")
is_virtual = true;
else
is_virtual = false;
return true;
}
return false;
}
/// Read the 'is-struct' attribute.
///
/// @param node the xml node to read the attribute from.
///
/// @param is_virtual is set to true iff the "is-struct" is present
/// and set to "yes".
///
/// @return true iff the "is-struct" attribute is present.
static bool
read_is_struct(xmlNodePtr node, bool& is_struct)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-struct"))
{
string str = CHAR_STR(s);
if (str == "yes")
is_struct = true;
else
is_struct = false;
return true;
}
return false;
}
/// Read the 'type' attribute of the 'elf-symbol' element.
///
/// @param node the XML node to read the attribute from.
///
/// @param t the resulting elf_symbol::type.
///
/// @return true iff the function completed successfully.
static bool
read_elf_symbol_type(xmlNodePtr node, elf_symbol::type& t)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type"))
{
string str;
xml::xml_char_sptr_to_string(s, str);
if (!string_to_elf_symbol_type(str, t))
return false;
return true;
}
return false;
}
/// Read the 'binding' attribute of the of the 'elf-symbol' element.
///
/// @param node the XML node to read the attribute from.
///
/// @param b the XML the resulting elf_symbol::binding.
///
/// @return true iff the function completed successfully.
static bool
read_elf_symbol_binding(xmlNodePtr node, elf_symbol::binding& b)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "binding"))
{
string str;
xml::xml_char_sptr_to_string(s, str);
if (!string_to_elf_symbol_binding(str, b))
return false;
return true;
}
return false;
}
/// Build a @ref namespace_decl from an XML element node which name is
/// "namespace-decl". Note that this function recursively reads the
/// content of the namespace and builds the proper IR nodes
/// accordingly.
///
/// @param ctxt the read context to use.
///
/// @param node the XML node to consider. It must constain the
/// content of the namespace, that is, children XML nodes representing
/// what is inside the namespace, unless the namespace is empty.
///
/// @param add_to_current_scope if set to yes, the resulting
/// namespace_decl is added to the IR being currently built.
///
/// @return a pointer to the the resulting @ref namespace_decl.
static namespace_decl_sptr
build_namespace_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
namespace_decl_sptr nil;
if (!node || !xmlStrEqual(node->name, BAD_CAST("namespace-decl")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
namespace_decl_sptr result = dynamic_pointer_cast<namespace_decl>(d);
assert(result);
return result;
}
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
location loc;
read_location(ctxt, node, loc);
shared_ptr<namespace_decl> decl(new namespace_decl(name, loc));
ctxt.push_decl_to_current_scope(decl, add_to_current_scope);
ctxt.map_xml_node_to_decl(node, decl);
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
assert(handle_element_node(ctxt, n, /*add_to_current_scope=*/true));
}
ctxt.pop_scope_or_abort(decl);
return decl;
}
/// Build an instance of @ref elf_symbol from an XML element node
/// which name is 'elf-symbol'.
///
/// @param node the XML node to read.
///
/// @return the @ref elf_symbol built, or nil if it couldn't be built.
static elf_symbol_sptr
build_elf_symbol(read_context&, const xmlNodePtr node)
{
elf_symbol_sptr nil;
if (!node
|| node->type != XML_ELEMENT_NODE
|| !xmlStrEqual(node->name, BAD_CAST("elf-symbol")))
return nil;
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
xml::xml_char_sptr_to_string(s, name);
bool is_defined = true;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-defined"))
{
string value;
xml::xml_char_sptr_to_string(s, value);
if (value == "true" || value == "yes")
is_defined = true;
else
is_defined = false;
}
string version_string;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "version"))
xml::xml_char_sptr_to_string(s, version_string);
bool is_default_version = false;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-default-version"))
{
string value;
xml::xml_char_sptr_to_string(s, value);
if (value == "true" || value == "yes")
is_default_version = true;
}
elf_symbol::type type = elf_symbol::NOTYPE_TYPE;
read_elf_symbol_type(node, type);
elf_symbol::binding binding;
read_elf_symbol_binding(node, binding);
elf_symbol::version version(version_string, is_default_version);
elf_symbol_sptr e = elf_symbol::create(/*index=*/0, name,
type, binding,
is_defined, version);
return e;
}
/// Build and instance of elf_symbol from an XML attribute named
/// 'elf-symbol-id' which value is the ID of a symbol that should
/// present in the symbol db of the corpus associated to the current
/// context.
///
/// @param ctxt the current context to consider.
///
/// @param node the xml element node to consider.
///
/// @param function_symbol is true if we should look for a function
/// symbol, is false if we should look for a variable symbol.
///
/// @return a shared pointer the resutling elf_symbol.
static elf_symbol_sptr
build_elf_symbol_from_reference(read_context& ctxt, const xmlNodePtr node,
bool function_symbol)
{
elf_symbol_sptr nil;
if (!node)
return nil;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "elf-symbol-id"))
{
string sym_id;
xml::xml_char_sptr_to_string(s, sym_id);
if (sym_id.empty())
return nil;
string name, ver;
elf_symbol::get_name_and_version_from_id(sym_id, name, ver);
if (name.empty())
return nil;
string_elf_symbols_map_sptr sym_db =
(function_symbol)
? ctxt.get_corpus()->get_fun_symbol_map_sptr()
: ctxt.get_corpus()->get_var_symbol_map_sptr();
string_elf_symbols_map_type::const_iterator i = sym_db->find(name);
if (i != sym_db->end())
{
for (elf_symbols::const_iterator s = i->second.begin();
s != i->second.end();
++s)
if ((*s)->get_id_string() == sym_id)
return *s;
}
}
return nil;
}
/// Build an instance of string_elf_symbols_map_type from an XML
/// element representing either a function symbols data base, or a
/// variable symbols database.
///
/// @param ctxt the context to take in account.
///
/// @param node the XML node to consider.
///
/// @param function_syms true if we should look for a function symbols
/// data base, false if we should look for a variable symbols data
/// base.
static string_elf_symbols_map_sptr
build_elf_symbol_db(read_context& ctxt,
const xmlNodePtr node,
bool function_syms)
{
string_elf_symbols_map_sptr map, nil;
string_elf_symbol_sptr_map_type id_sym_map;
if (!node)
return nil;
if (function_syms
&& !xmlStrEqual(node->name, BAD_CAST("elf-function-symbols")))
return nil;
if (!function_syms
&& !xmlStrEqual(node->name, BAD_CAST("elf-variable-symbols")))
return nil;
typedef std::tr1::unordered_map<xmlNodePtr, elf_symbol_sptr>
xml_node_ptr_elf_symbol_sptr_map_type;
xml_node_ptr_elf_symbol_sptr_map_type xml_node_ptr_elf_symbol_map;
elf_symbol_sptr sym;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if ((sym = build_elf_symbol(ctxt, n)))
{
id_sym_map[sym->get_id_string()] = sym;
xml_node_ptr_elf_symbol_map[n] = sym;
}
}
if (id_sym_map.empty())
return nil;
map.reset(new string_elf_symbols_map_type);
string_elf_symbols_map_type::iterator it;
for (string_elf_symbol_sptr_map_type::const_iterator i = id_sym_map.begin();
i != id_sym_map.end();
++i)
{
it = map->find(i->second->get_name());
if (it == map->end())
{
(*map)[i->second->get_name()] = elf_symbols();
it = map->find(i->second->get_name());
}
it->second.push_back(i->second);
}
// Now build the alias relations
for (xml_node_ptr_elf_symbol_sptr_map_type::const_iterator x =
xml_node_ptr_elf_symbol_map.begin();
x != xml_node_ptr_elf_symbol_map.end();
++x)
{
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(x->first, "alias"))
{
string alias_id = CHAR_STR(s);
// Symbol aliases can be multiple separated by comma(,), split them
std::vector<std::string> elems;
std::stringstream aliases(alias_id);
std::string item;
while (std::getline(aliases, item, ','))
elems.push_back(item);
for (std::vector<string>::iterator alias = elems.begin();
alias != elems.end(); alias++)
{
string_elf_symbol_sptr_map_type::const_iterator i =
id_sym_map.find(*alias);
assert(i != id_sym_map.end());
assert(i->second->is_main_symbol());
x->second->get_main_symbol()->add_alias(i->second);
}
}
}
return map;
}
/// Build a function parameter from a 'parameter' xml element node.
///
/// @param ctxt the contexte of the xml parsing.
///
/// @param node the xml 'parameter' element node to de-serialize from.
static shared_ptr<function_decl::parameter>
build_function_parameter(read_context& ctxt, const xmlNodePtr node)
{
shared_ptr<function_decl::parameter> nil;
if (!node || !xmlStrEqual(node->name, BAD_CAST("parameter")))
return nil;
bool is_variadic = false;
string is_variadic_str;
if (xml_char_sptr s =
xml::build_sptr(xmlGetProp(node, BAD_CAST("is-variadic"))))
{
is_variadic_str = CHAR_STR(s) ? CHAR_STR(s) : "";
is_variadic = (is_variadic_str == "yes") ? true : false;
}
bool is_artificial = false;
string is_artificial_str;
if (xml_char_sptr s =
xml::build_sptr(xmlGetProp(node, BAD_CAST("is-artificial"))))
{
is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : "";
is_artificial = (is_artificial_str == "yes") ? true : false;
}
string type_id;
if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("type-id"))))
type_id = CHAR_STR(a);
shared_ptr<type_base> type;
if (!is_variadic)
{
assert(!type_id.empty());
type = ctxt.build_or_get_type_decl(type_id, true);
}
assert(type || is_variadic);
string name;
if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("name"))))
name = CHAR_STR(a);
location loc;
read_location(ctxt, node, loc);
shared_ptr<function_decl::parameter> p
(new function_decl::parameter(type, name, loc, is_variadic, is_artificial));
return p;
}
/// Build a function_decl from a 'function-decl' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the function_decl from.
///
/// @param as_method_decl if this is set to a class_decl pointer, it
/// means that the 'function-decl' xml node should be parsed as a
/// method_decl. The class_decl pointer is the class decl to which
/// the resulting method_decl is a member function of. The resulting
/// shared_ptr<function_decl> that is returned is then really a
/// shared_ptr<class_decl::method_decl>.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to a newly created function_decl upon successful
/// completion, a null pointer otherwise.
static function_decl_sptr
build_function_decl(read_context& ctxt,
const xmlNodePtr node,
shared_ptr<class_decl> as_method_decl,
bool add_to_current_scope)
{
shared_ptr<function_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("function-decl")))
return nil;
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
string mangled_name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name"))
mangled_name = xml::unescape_xml_string(CHAR_STR(s));
string inline_prop;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "declared-inline"))
inline_prop = CHAR_STR(s);
bool declared_inline = inline_prop == "yes" ? true : false;
decl_base::visibility vis = decl_base::VISIBILITY_NONE;
read_visibility(node, vis);
decl_base::binding bind = decl_base::BINDING_NONE;
read_binding(node, bind);
size_t size = 0, align = 0;
read_size_and_alignment(node, size, align);
location loc;
read_location(ctxt, node, loc);
std::vector<shared_ptr<function_decl::parameter> > parms;
shared_ptr<function_type> fn_type(as_method_decl
? new method_type(as_method_decl,
size, align)
: new function_type(size, align));
shared_ptr<function_decl> fn_decl(as_method_decl
? new class_decl::method_decl
(name, fn_type,
declared_inline, loc,
mangled_name, vis, bind)
: new function_decl(name, fn_type,
declared_inline, loc,
mangled_name, vis,
bind));
ctxt.push_decl_to_current_scope(fn_decl, add_to_current_scope);
elf_symbol_sptr sym = build_elf_symbol_from_reference(ctxt, node,
/*function_sym=*/true);
if (sym)
fn_decl->set_symbol(sym);
for (xmlNodePtr n = node->children; n ; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
else if (xmlStrEqual(n->name, BAD_CAST("parameter")))
{
if (shared_ptr<function_decl::parameter> p =
build_function_parameter(ctxt, n))
fn_type->append_parameter(p);
}
else if (xmlStrEqual(n->name, BAD_CAST("return")))
{
string type_id;
if (xml_char_sptr s =
xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id"))))
type_id = CHAR_STR(s);
if (!type_id.empty())
fn_type->set_return_type(ctxt.build_or_get_type_decl(type_id,
true));
}
}
if (fn_decl->get_symbol() && fn_decl->get_symbol()->is_public())
fn_decl->set_is_in_public_symbol_table(true);
ctxt.get_translation_unit()->bind_function_type_life_time(fn_type);
fn_decl->set_type(fn_type);
ctxt.maybe_canonicalize_type(fn_type);
ctxt.maybe_add_fn_to_exported_decls(fn_decl.get());
return fn_decl;
}
/// Build pointer to var_decl from a 'var-decl' xml Node
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the var_decl from.
///
/// @return a pointer to a newly built var_decl upon successful
/// completion, a null pointer otherwise.
static shared_ptr<var_decl>
build_var_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<var_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("var-decl")))
return nil;
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<type_base> underlying_type = ctxt.build_or_get_type_decl(type_id,
true);
assert(underlying_type);
string mangled_name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "mangled-name"))
mangled_name = xml::unescape_xml_string(CHAR_STR(s));
decl_base::visibility vis = decl_base::VISIBILITY_NONE;
read_visibility(node, vis);
decl_base::binding bind = decl_base::BINDING_NONE;
read_binding(node, bind);
location locus;
read_location(ctxt, node, locus);
shared_ptr<var_decl> decl(new var_decl(name, underlying_type,
locus, mangled_name,
vis, bind));
elf_symbol_sptr sym = build_elf_symbol_from_reference(ctxt, node,
/*function_sym=*/false);
if (sym)
decl->set_symbol(sym);
ctxt.push_decl_to_current_scope(decl, add_to_current_scope);
if (decl->get_symbol() && decl->get_symbol()->is_public())
decl->set_is_in_public_symbol_table(true);
ctxt.maybe_add_var_to_exported_decls(decl.get());
return decl;
}
/// Build a type_decl from a "type-decl" XML Node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the XML node to build the type_decl from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to type_decl upon successful completion, a null
/// pointer otherwise.
static shared_ptr<type_decl>
build_type_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<type_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("type-decl")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
type_decl_sptr result = dynamic_pointer_cast<type_decl>(d);
assert(result);
return result;
}
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
size_t size_in_bits= 0;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits"))
size_in_bits = atoi(CHAR_STR(s));
size_t alignment_in_bits = 0;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits"))
alignment_in_bits = atoi(CHAR_STR(s));
location loc;
read_location(ctxt, node, loc);
if (type_base_sptr d = ctxt.get_type_decl(id))
{
// I've seen instances of DSOs where a type_decl would appear
// several times. Hugh.
type_decl_sptr ty = dynamic_pointer_cast<type_decl>(d);
assert(ty);
assert(name == ty->get_name());
assert(ty->get_size_in_bits() == size_in_bits);
assert(ty->get_alignment_in_bits() == alignment_in_bits);
return ty;
}
shared_ptr<type_decl> decl(new type_decl(name, size_in_bits,
alignment_in_bits,
loc));
if (ctxt.push_and_key_type_decl(decl, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, decl);
canonicalize(decl);
return decl;
}
return nil;
}
/// Build a qualified_type_def from a 'qualified-type-def' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the qualified_type_def from.
///
/// @param add_to_current_scope if set to yes, the resulting of this
/// function is added to its current scope.
///
/// @return a pointer to a newly built qualified_type_def upon
/// successful completion, a null pointer otherwise.
static qualified_type_def_sptr
build_qualified_type_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
qualified_type_def_sptr nil;
if (!xmlStrEqual(node->name, BAD_CAST("qualified-type-def")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
qualified_type_def_sptr result =
dynamic_pointer_cast<qualified_type_def>(d);
assert(result);
return result;
}
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<type_base> underlying_type =
ctxt.build_or_get_type_decl(type_id, true);
assert(underlying_type);
// maybe building the underlying type triggered building this one in
// the mean time ...
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
qualified_type_def_sptr result =
dynamic_pointer_cast<qualified_type_def>(d);
assert(result);
return result;
}
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE (node, "id"))
id = CHAR_STR(s);
string const_str;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "const"))
const_str = CHAR_STR(s);
bool const_cv = const_str == "yes" ? true : false;
string volatile_str;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "volatile"))
volatile_str = CHAR_STR(s);
bool volatile_cv = volatile_str == "yes" ? true : false;
string restrict_str;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "restrict"))
restrict_str = CHAR_STR(s);
bool restrict_cv = restrict_str == "yes" ? true : false;
qualified_type_def::CV cv = qualified_type_def::CV_NONE;
if (const_cv)
cv = cv | qualified_type_def::CV_CONST;
if (volatile_cv)
cv = cv | qualified_type_def::CV_VOLATILE;
if (restrict_cv)
cv = cv | qualified_type_def::CV_RESTRICT;
location loc;
read_location(ctxt, node, loc);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
qualified_type_def_sptr decl;
if (type_base_sptr d = ctxt.get_type_decl(id))
{
qualified_type_def_sptr ty = is_qualified_type(d);
assert(ty);
assert(*ty->get_underlying_type() == *underlying_type);
assert(ty->get_cv_quals() == cv);
return ty;
}
decl.reset(new qualified_type_def(underlying_type, cv, loc));
if (ctxt.push_and_key_type_decl(decl, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, decl);
ctxt.maybe_canonicalize_type(decl);
return decl;
}
return shared_ptr<qualified_type_def>((qualified_type_def*)0);
}
/// Build a pointer_type_def from a 'pointer-type-def' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the pointer_type_def from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to a newly built pointer_type_def upon
/// successful completion, a null pointer otherwise.
static shared_ptr<pointer_type_def>
build_pointer_type_def(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<pointer_type_def> nil;
if (!xmlStrEqual(node->name, BAD_CAST("pointer-type-def")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
pointer_type_def_sptr result =
dynamic_pointer_cast<pointer_type_def>(d);
assert(result);
return result;
}
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<type_base> pointed_to_type =
ctxt.build_or_get_type_decl(type_id, true);
assert(pointed_to_type);
// maybe building the underlying type triggered building this one in
// the mean time ...
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
pointer_type_def_sptr result =
dynamic_pointer_cast<pointer_type_def>(d);
assert(result);
return result;
}
size_t size_in_bits = 0, alignment_in_bits = 0;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits"))
size_in_bits = atoi(CHAR_STR(s));
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits"))
alignment_in_bits = atoi(CHAR_STR(s));
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
if (type_base_sptr d = ctxt.get_type_decl(id))
{
pointer_type_def_sptr ty = is_pointer_type(d);
assert(ty);
assert(*pointed_to_type == *ty->get_pointed_to_type());
return ty;
}
location loc;
read_location(ctxt, node, loc);
shared_ptr<pointer_type_def> t(new pointer_type_def(pointed_to_type,
size_in_bits,
alignment_in_bits,
loc));
if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, t);
ctxt.maybe_canonicalize_type(t);
return t;
}
return nil;
}
/// Build a reference_type_def from a pointer to 'reference-type-def'
/// xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the reference_type_def from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to a newly built reference_type_def upon
/// successful completio, a null pointer otherwise.
static shared_ptr<reference_type_def>
build_reference_type_def(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<reference_type_def> nil;
if (!xmlStrEqual(node->name, BAD_CAST("reference-type-def")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
reference_type_def_sptr result =
dynamic_pointer_cast<reference_type_def>(d);
assert(result);
return result;
}
string kind;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "kind"))
kind = CHAR_STR(s); // this should be either "lvalue" or "rvalue".
bool is_lvalue = kind == "lvalue" ? true : false;
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<type_base> pointed_to_type = ctxt.build_or_get_type_decl(type_id,
true);
assert(pointed_to_type);
// maybe building the underlying type triggered building this one in
// the mean time ...
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
reference_type_def_sptr result =
dynamic_pointer_cast<reference_type_def>(d);
assert(result);
return result;
}
size_t size_in_bits = 0, alignment_in_bits = 0;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits"))
size_in_bits = atoi(CHAR_STR(s));
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits"))
alignment_in_bits = atoi(CHAR_STR(s));
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
if (type_base_sptr d = ctxt.get_type_decl(id))
{
reference_type_def_sptr ty = is_reference_type(d);
assert(ty);
assert(*pointed_to_type == *ty->get_pointed_to_type());
return ty;
}
location loc;
read_location(ctxt, node, loc);
shared_ptr<reference_type_def> t(new reference_type_def(pointed_to_type,
is_lvalue,
size_in_bits,
alignment_in_bits,
loc));
if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, t);
ctxt.maybe_canonicalize_type(t);
return t;
}
return nil;
}
/// Build a array_type_def from a 'array-type-def' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the array_type_def from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to a newly built array_type_def upon
/// successful completion, a null pointer otherwise.
static array_type_def::subrange_sptr
build_subrange_type(read_context& ctxt,
const xmlNodePtr node)
{
array_type_def::subrange_sptr nil;
if (!node || !xmlStrEqual(node->name, BAD_CAST("subrange")))
return nil;
size_t length = 0;
string length_str;
if (xml_char_sptr s =
xml::build_sptr(xmlGetProp(node, BAD_CAST("length"))))
length = atoi(CHAR_STR(s));
location loc;
read_location(ctxt, node, loc);
// Note that DWARF would actually have a lower_bound of -1 for an
// array of length 0
array_type_def::subrange_sptr p
(new array_type_def::subrange_type(0,
length - 1,
loc));
return p;
}
/// Build a array_type_def from a 'array-type-def' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the array_type_def from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return a pointer to a newly built array_type_def upon
/// successful completion, a null pointer otherwise.
static array_type_def_sptr
build_array_type_def(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
array_type_def_sptr nil;
if (!xmlStrEqual(node->name, BAD_CAST("array-type-def")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
array_type_def_sptr result =
dynamic_pointer_cast<array_type_def>(d);
assert(result);
return result;
}
int dimensions = 0;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "dimensions"))
dimensions = atoi(CHAR_STR(s));
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
// The type of array elements.
shared_ptr<type_base> type =
ctxt.build_or_get_type_decl(type_id, true);
assert(type);
// maybe building the type of array elements triggered building this
// one in the mean time ...
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
array_type_def_sptr result =
dynamic_pointer_cast<array_type_def>(d);
assert(result);
return result;
}
size_t size_in_bits = 0, alignment_in_bits = 0;
char *endptr;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "size-in-bits"))
{
size_in_bits = strtoull(CHAR_STR(s), &endptr, 0);
if (*endptr != '\0')
{
if (!strcmp(CHAR_STR(s), "infinite"))
size_in_bits = (size_t) -1;
else
return nil;
}
}
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "alignment-in-bits"))
{
alignment_in_bits = strtoull(CHAR_STR(s), &endptr, 0);
if (*endptr != '\0')
return nil;
}
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
if (type_base_sptr d = ctxt.get_type_decl(id))
{
array_type_def_sptr ty = is_array_type(d);
assert(ty);
assert(*type == *ty->get_element_type());
assert(type->get_alignment_in_bits() == alignment_in_bits);
return ty;
}
location loc;
read_location(ctxt, node, loc);
array_type_def::subranges_type subranges;
for (xmlNodePtr n = node->children; n ; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
else if (xmlStrEqual(n->name, BAD_CAST("subrange")))
{
if (array_type_def::subrange_sptr s =
build_subrange_type(ctxt, n))
subranges.push_back(s);
}
}
array_type_def_sptr ar_type(new array_type_def(type,
subranges,
loc));
if (dimensions != ar_type->get_dimension_count()
|| (alignment_in_bits
!= ar_type->get_element_type()->get_alignment_in_bits()))
return nil;
if (size_in_bits != ar_type->get_size_in_bits())
{
assert(size_in_bits == (size_t) -1
|| ar_type->get_element_type()->get_size_in_bits() == (size_t)-1
|| ar_type->get_element_type()->get_size_in_bits() == 0);
}
if (ctxt.push_and_key_type_decl(ar_type, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, ar_type);
ctxt.maybe_canonicalize_type(ar_type);
return ar_type;
}
return nil;
}
/// Build an enum_type_decl from an 'enum-type-decl' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the enum_type_decl from.
///
/// param add_to_current_scope if set to yes, the resulting of this
/// function is added to its current scope.
///
/// @return a pointer to a newly built enum_type_decl upon successful
/// completion, a null pointer otherwise.
static enum_type_decl_sptr
build_enum_type_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<enum_type_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("enum-decl")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
enum_type_decl_sptr result =
dynamic_pointer_cast<enum_type_decl>(d);
assert(result);
return result;
}
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
location loc;
read_location(ctxt, node, loc);
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
string base_type_id;
enum_type_decl::enumerators enums;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if (xmlStrEqual(n->name, BAD_CAST("underlying-type")))
{
xml_char_sptr a = xml::build_sptr(xmlGetProp(n, BAD_CAST("type-id")));
if (a)
base_type_id = CHAR_STR(a);
continue;
}
if (xmlStrEqual(n->name, BAD_CAST("enumerator")))
{
string name;
size_t value = 0;
xml_char_sptr a = xml::build_sptr(xmlGetProp(n, BAD_CAST("name")));
if (a)
name = xml::unescape_xml_string(CHAR_STR(a));
a = xml::build_sptr(xmlGetProp(n, BAD_CAST("value")));
if (a)
value = atoi(CHAR_STR(a));
enums.push_back(enum_type_decl::enumerator(name, value));
}
}
shared_ptr<type_base> underlying_type =
ctxt.build_or_get_type_decl(base_type_id, true);
assert(underlying_type);
shared_ptr<enum_type_decl> t(new enum_type_decl(name, loc,
underlying_type,
enums));
if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, t);
ctxt.maybe_canonicalize_type(t);
return t;
}
return nil;
}
/// Build a typedef_decl from a 'typedef-decl' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the typedef_decl from.
///
/// @return a pointer to a newly built typedef_decl upon successful
/// completion, a null pointer otherwise.
static shared_ptr<typedef_decl>
build_typedef_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<typedef_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("typedef-decl")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
typedef_decl_sptr result = is_typedef(d);
assert(result);
return result;
}
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
assert(!id.empty() && ctxt.type_id_new_in_translation_unit(id));
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<type_base> underlying_type(ctxt.build_or_get_type_decl(type_id,
true));
assert(underlying_type);
// maybe building the underlying type triggered building this one in
// the mean time ...
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
typedef_decl_sptr result = dynamic_pointer_cast<typedef_decl>(d);
assert(result);
return result;
}
location loc;
read_location(ctxt, node, loc);
if (type_base_sptr d = ctxt.get_type_decl(id))
{
typedef_decl_sptr ty = dynamic_pointer_cast<typedef_decl>(d);
assert(ty);
assert(name == ty->get_name());
assert(underlying_type == ty->get_underlying_type());
// it's possible to have the same typedef several times.
}
shared_ptr<typedef_decl> t(new typedef_decl(name, underlying_type, loc));
if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope))
{
ctxt.map_xml_node_to_decl(node, t);
// If this typedef is *NOT* meant to be a member type then try
// to canonicalize it. Otherwise, the code that is calling it
// from the building of a class type is going to handle the
// canonicalizing.
if (!add_to_current_scope)
ctxt.maybe_canonicalize_type(t);
return t;
}
return nil;
}
/// Build a class_decl from a 'class-decl' xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the class_decl from.
///
/// @param add_to_current_scope if yes, the resulting class node
/// hasn't triggered voluntarily the adding of the resulting
/// class_decl_sptr to the current scope.
///
/// @return a pointer to class_decl upon successful completion, a null
/// pointer otherwise.
static class_decl_sptr
build_class_decl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<class_decl> nil;
if (!xmlStrEqual(node->name, BAD_CAST("class-decl")))
return nil;
if (decl_base_sptr d = ctxt.get_decl_for_xml_node(node))
{
class_decl_sptr result = dynamic_pointer_cast<class_decl>(d);
assert(result);
return result;
}
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
size_t size_in_bits = 0, alignment_in_bits = 0;
read_size_and_alignment(node, size_in_bits, alignment_in_bits);
decl_base::visibility vis = decl_base::VISIBILITY_NONE;
read_visibility(node, vis);
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
location loc;
read_location(ctxt, node, loc);
class_decl::member_types mbrs;
class_decl::data_members data_mbrs;
class_decl::member_functions mbr_functions;
class_decl::base_specs bases;
shared_ptr<class_decl> decl;
bool is_decl_only = false;
read_is_declaration_only(node, is_decl_only);
bool is_struct = false;
read_is_struct(node, is_struct);
// Keep in mind that there can be instances of DSOs that define a
// class twice!! Hugh. So the test + assert() below doesn't work in
// these. Oh well.
#if 0
// If the id is not empty, then we should be seeing this type for
// the first time, unless it's a declaration-only type class.
if (!id.empty())
{
type_base_sptr t = ctxt.get_type_decl(id);
if (t)
{
class_decl_sptr c = as_non_member_class_decl(get_type_declaration(t));
assert((c && c->is_declaration_only())
|| is_decl_only);
}
}
#endif
if (!is_decl_only)
decl.reset(new class_decl(name, size_in_bits, alignment_in_bits,
is_struct, loc, vis, bases, mbrs, data_mbrs,
mbr_functions));
string def_id;
bool is_def_of_decl = false;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id"))
def_id = CHAR_STR(s);
if (!def_id.empty())
{
shared_ptr<class_decl> d =
dynamic_pointer_cast<class_decl>(ctxt.get_type_decl(def_id));
if (d && d->get_is_declaration_only())
{
is_def_of_decl = true;
decl->set_earlier_declaration(d);
d->set_definition_of_declaration(decl);
}
}
assert(!is_decl_only || !is_def_of_decl);
if (is_decl_only)
decl.reset(new class_decl(name, is_struct));
ctxt.push_decl_to_current_scope(decl, add_to_current_scope);
ctxt.map_xml_node_to_decl(node, decl);
ctxt.mark_class_as_wip(decl);
for (xmlNodePtr n = node->children; !is_decl_only && n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if (xmlStrEqual(n->name, BAD_CAST("base-class")))
{
access_specifier access = private_access;
read_access(n, access);
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(n, "type-id"))
type_id = CHAR_STR(s);
shared_ptr<class_decl> b =
dynamic_pointer_cast<class_decl>
(ctxt.build_or_get_type_decl(type_id, true));
assert(b);
size_t offset_in_bits = 0;
bool offset_present = read_offset_in_bits (n, offset_in_bits);
bool is_virtual = false;
read_is_virtual (n, is_virtual);
shared_ptr<class_decl::base_spec> base (new class_decl::base_spec
(b, access,
offset_present
? (long) offset_in_bits
: -1,
is_virtual));
decl->add_base_specifier(base);
}
else if (xmlStrEqual(n->name, BAD_CAST("member-type")))
{
access_specifier access = private_access;
read_access(n, access);
ctxt.map_xml_node_to_decl(n, decl);
for (xmlNodePtr p = n->children; p; p = p->next)
{
if (p->type != XML_ELEMENT_NODE)
continue;
if (type_base_sptr t =
build_type(ctxt, p, /*add_to_current_scope=*/false))
{
if (!get_type_declaration(t)->get_scope())
{
type_base_sptr m =
decl->add_member_type(t, access);
xml_char_sptr i= XML_NODE_GET_ATTRIBUTE(p, "id");
string id = CHAR_STR(i);
assert(!id.empty());
ctxt.key_type_decl(m, id, /*force=*/true);
ctxt.map_xml_node_to_decl(p, get_type_declaration(m));
if ((!is_class_type(t)
|| (!ctxt.is_wip_class(is_class_type(t))))
&& !type_has_non_canonicalized_subtype(t))
canonicalize(t);
else
ctxt.schedule_type_for_late_canonicalizing(t);
}
}
}
}
else if (xmlStrEqual(n->name, BAD_CAST("data-member")))
{
ctxt.map_xml_node_to_decl(n, decl);
access_specifier access = private_access;
read_access(n, access);
bool is_laid_out = false;
size_t offset_in_bits = 0;
if (read_offset_in_bits(n, offset_in_bits))
is_laid_out = true;
bool is_static = false;
read_static(n, is_static);
for (xmlNodePtr p = n->children; p; p = p->next)
{
if (p->type != XML_ELEMENT_NODE)
continue;
if (shared_ptr<var_decl> v =
build_var_decl(ctxt, p, /*add_to_current_scope=*/false))
decl->add_data_member(v, access, is_laid_out,
is_static, offset_in_bits);
}
}
else if (xmlStrEqual(n->name, BAD_CAST("member-function")))
{
ctxt.map_xml_node_to_decl(n, decl);
access_specifier access = private_access;
read_access(n, access);
bool is_virtual = false;
size_t vtable_offset = 0;
if (xml_char_sptr s =
XML_NODE_GET_ATTRIBUTE(n, "vtable-offset"))
{
is_virtual = true;
vtable_offset = atoi(CHAR_STR(s));
}
bool is_static = false;
read_static(n, is_static);
bool is_ctor = false, is_dtor = false, is_const = false;
read_cdtor_const(n, is_ctor, is_dtor, is_const);
for (xmlNodePtr p = n->children; p; p = p->next)
{
if (p->type != XML_ELEMENT_NODE)
continue;
if (function_decl_sptr f =
build_function_decl(ctxt, p, decl,
/*add_to_current_scope=*/false))
{
class_decl::method_decl_sptr m =
dynamic_pointer_cast<class_decl::method_decl>(f);
assert(m);
decl->add_member_function(m, access,
is_virtual,
vtable_offset,
is_static,
is_ctor, is_dtor,
is_const);
break;
}
}
}
else if (xmlStrEqual(n->name, BAD_CAST("member-template")))
{
ctxt.map_xml_node_to_decl(n, decl);
access_specifier access = private_access;
read_access(n, access);
bool is_static = false;
read_static(n, is_static);
bool is_ctor = false, is_dtor = false, is_const = false;
read_cdtor_const(n, is_ctor, is_dtor, is_const);
for (xmlNodePtr p = n->children; p; p = p->next)
{
if (p->type != XML_ELEMENT_NODE)
continue;
if (shared_ptr<function_tdecl> f =
build_function_tdecl(ctxt, p,
/*add_to_current_scope=*/false))
{
shared_ptr<class_decl::member_function_template> m
(new class_decl::member_function_template(f, access,
is_static,
is_ctor,
is_const));
assert(!f->get_scope());
decl->add_member_function_template(m);
}
else if (shared_ptr<class_tdecl> c =
build_class_tdecl(ctxt, p,
/*add_to_current_scope=*/false))
{
shared_ptr<class_decl::member_class_template> m
(new class_decl::member_class_template(c, access,
is_static));
assert(!c->get_scope());
decl->add_member_class_template(m);
}
}
}
}
ctxt.pop_scope_or_abort(decl);
if (decl)
ctxt.key_type_decl(decl, id);
ctxt.unmark_class_as_wip(decl);
ctxt.maybe_canonicalize_type(decl);
return decl;
}
/// Build an intance of function_tdecl, from an
/// 'function-template-decl' xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param add_to_current_scope if set to yes, the resulting of
/// this function is added to its current scope.
///
/// @return the newly built function_tdecl upon successful
/// completion, a null pointer otherwise.
static shared_ptr<function_tdecl>
build_function_tdecl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<function_tdecl> nil, result;
if (!xmlStrEqual(node->name, BAD_CAST("function-template-decl")))
return nil;
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
if (id.empty() || ctxt.get_fn_tmpl_decl(id))
return nil;
location loc;
read_location(ctxt, node, loc);
decl_base::visibility vis = decl_base::VISIBILITY_NONE;
read_visibility(node, vis);
decl_base::binding bind = decl_base::BINDING_NONE;
read_binding(node, bind);
function_tdecl_sptr fn_tmpl_decl(new function_tdecl(loc, vis, bind));
ctxt.push_decl_to_current_scope(fn_tmpl_decl, add_to_current_scope);
unsigned parm_index = 0;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if (template_parameter_sptr parm =
build_template_parameter(ctxt, n, parm_index, fn_tmpl_decl))
{
fn_tmpl_decl->add_template_parameter(parm);
++parm_index;
}
else if (shared_ptr<function_decl> f =
build_function_decl(ctxt, n, shared_ptr<class_decl>(),
/*add_to_current_scope=*/true))
fn_tmpl_decl->set_pattern(f);
}
ctxt.key_fn_tmpl_decl(fn_tmpl_decl, id);
return fn_tmpl_decl;
}
/// Build an intance of class_tdecl, from a
/// 'class-template-decl' xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param add_to_current_scope if set to yes, the resulting of this
/// function is added to its current scope.
///
/// @return the newly built function_tdecl upon successful
/// completion, a null pointer otherwise.
static shared_ptr<class_tdecl>
build_class_tdecl(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<class_tdecl> nil, result;
if (!xmlStrEqual(node->name, BAD_CAST("class-template-decl")))
return nil;
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
if (id.empty() || ctxt.get_class_tmpl_decl(id))
return nil;
location loc;
read_location(ctxt, node, loc);
decl_base::visibility vis = decl_base::VISIBILITY_NONE;
read_visibility(node, vis);
class_tdecl_sptr class_tmpl (new class_tdecl(loc, vis));
ctxt.push_decl_to_current_scope(class_tmpl, add_to_current_scope);
unsigned parm_index = 0;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if (template_parameter_sptr parm=
build_template_parameter(ctxt, n, parm_index, class_tmpl))
{
class_tmpl->add_template_parameter(parm);
++parm_index;
}
else if (shared_ptr<class_decl> c =
build_class_decl(ctxt, n, add_to_current_scope))
class_tmpl->set_pattern(c);
}
ctxt.key_class_tmpl_decl(class_tmpl, id);
return class_tmpl;
}
/// Build a type_tparameter from a 'template-type-parameter'
/// xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param index the index (occurrence index, starting from 0) of the
/// template parameter.
///
/// @param tdecl the enclosing template declaration that holds the
/// template type parameter.
///
/// @return a pointer to a newly created instance of
/// type_tparameter, a null pointer otherwise.
static type_tparameter_sptr
build_type_tparameter(read_context& ctxt,
const xmlNodePtr node,
unsigned index,
template_decl_sptr tdecl)
{
type_tparameter_sptr nil, result;
if (!xmlStrEqual(node->name, BAD_CAST("template-type-parameter")))
return nil;
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
if (!id.empty())
assert(!ctxt.get_type_decl(id));
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
if (!type_id.empty()
&& !(result = dynamic_pointer_cast<type_tparameter>
(ctxt.build_or_get_type_decl(type_id, true))))
abort();
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
location loc;
read_location(ctxt, node,loc);
result.reset(new type_tparameter(index, tdecl, name, loc));
if (id.empty())
ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result),
/*add_to_current_scope=*/true);
else
ctxt.push_and_key_type_decl(result, id, /*add_to_current_scope=*/true);
return result;
}
/// Build a tmpl_parm_type_composition from a
/// "template-parameter-type-composition" xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param index the index of the previous normal template parameter.
///
/// @param tdecl the enclosing template declaration that holds this
/// template parameter type composition.
///
/// @return a pointer to a new instance of tmpl_parm_type_composition
/// upon successful completion, a null pointer otherwise.
static type_composition_sptr
build_type_composition(read_context& ctxt,
const xmlNodePtr node,
unsigned index,
template_decl_sptr tdecl)
{
type_composition_sptr nil, result;
if (!xmlStrEqual(node->name, BAD_CAST("template-parameter-type-composition")))
return nil;
type_base_sptr composed_type;
result.reset(new type_composition(index, tdecl, composed_type));
ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(result),
/*add_to_current_scope=*/true);
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if ((composed_type =
build_pointer_type_def(ctxt, n,
/*add_to_current_scope=*/true))
||(composed_type =
build_reference_type_def(ctxt, n,
/*add_to_current_scope=*/true))
||(composed_type =
build_array_type_def(ctxt, n,
/*add_to_current_scope=*/true))
|| (composed_type =
build_qualified_type_decl(ctxt, n,
/*add_to_current_scope=*/true)))
{
result->set_composed_type(composed_type);
break;
}
}
return result;
}
/// Build an instance of non_type_tparameter from a
/// 'template-non-type-parameter' xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param index the index of the parameter.
///
/// @param tdecl the enclosing template declaration that holds this
/// non type template parameter.
///
/// @return a pointer to a newly created instance of
/// non_type_tparameter upon successful completion, a null
/// pointer code otherwise.
static non_type_tparameter_sptr
build_non_type_tparameter(read_context& ctxt,
const xmlNodePtr node,
unsigned index,
template_decl_sptr tdecl)
{
non_type_tparameter_sptr r;
if (!xmlStrEqual(node->name, BAD_CAST("template-non-type-parameter")))
return r;
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
type_base_sptr type;
if (type_id.empty()
|| !(type = ctxt.build_or_get_type_decl(type_id, true)))
abort();
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
location loc;
read_location(ctxt, node,loc);
r.reset(new non_type_tparameter(index, tdecl, name, type, loc));
ctxt.push_decl_to_current_scope(dynamic_pointer_cast<decl_base>(r),
/*add_to_current_scope=*/true);
return r;
}
/// Build an intance of template_tparameter from a
/// 'template-template-parameter' xml element node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to parse from.
///
/// @param index the index of the template parameter.
///
/// @param tdecl the enclosing template declaration that holds this
/// template template parameter.
///
/// @return a pointer to a new instance of template_tparameter
/// upon successful completion, a null pointer otherwise.
static template_tparameter_sptr
build_template_tparameter(read_context& ctxt,
const xmlNodePtr node,
unsigned index,
template_decl_sptr tdecl)
{
template_tparameter_sptr nil;
if (!xmlStrEqual(node->name, BAD_CAST("template-template-parameter")))
return nil;
string id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id"))
id = CHAR_STR(s);
// Bail out if a type with the same ID already exists.
assert(!id.empty() && !ctxt.type_id_new_in_translation_unit(id));
string type_id;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "type-id"))
type_id = CHAR_STR(s);
// Bail out if no type with this ID exists.
if (!type_id.empty()
&& !(dynamic_pointer_cast<template_tparameter>
(ctxt.build_or_get_type_decl(type_id, true))))
abort();
string name;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "name"))
name = xml::unescape_xml_string(CHAR_STR(s));
location loc;
read_location(ctxt, node, loc);
template_tparameter_sptr result(new template_tparameter(index, tdecl,
name, loc));
ctxt.push_decl_to_current_scope(result, /*add_to_current_scope=*/true);
// Go parse template parameters that are children nodes
int parm_index = 0;
for (xmlNodePtr n = node->children; n; n = n->next)
{
if (n->type != XML_ELEMENT_NODE)
continue;
if (shared_ptr<template_parameter> p =
build_template_parameter(ctxt, n, parm_index, result))
{
result->add_template_parameter(p);
++parm_index;
}
}
if (result)
ctxt.key_type_decl(result, id);
return result;
}
/// Build a template parameter type from several possible xml elment
/// nodes representing a serialized form a template parameter.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml element node to parse from.
///
/// @param index the index of the template parameter we are parsing.
///
/// @param tdecl the enclosing template declaration that holds this
/// template parameter.
///
/// @return a pointer to a newly created instance of
/// template_parameter upon successful completion, a null pointer
/// otherwise.
static template_parameter_sptr
build_template_parameter(read_context& ctxt,
const xmlNodePtr node,
unsigned index,
template_decl_sptr tdecl)
{
shared_ptr<template_parameter> r;
((r = build_type_tparameter(ctxt, node, index, tdecl))
|| (r = build_non_type_tparameter(ctxt, node, index, tdecl))
|| (r = build_template_tparameter(ctxt, node, index, tdecl))
|| (r = build_type_composition(ctxt, node, index, tdecl)));
return r;
}
/// Build a type from an xml node.
///
/// @param ctxt the context of the parsing.
///
/// @param node the xml node to build the type_base from.
///
/// @return a pointer to the newly built type_base upon successful
/// completion, a null pointer otherwise.
static shared_ptr<type_base>
build_type(read_context& ctxt,
const xmlNodePtr node,
bool add_to_current_scope)
{
shared_ptr<type_base> t;
((t = build_type_decl(ctxt, node, add_to_current_scope))
|| (t = build_qualified_type_decl(ctxt, node, add_to_current_scope))
|| (t = build_pointer_type_def(ctxt, node, add_to_current_scope))
|| (t = build_reference_type_def(ctxt, node , add_to_current_scope))
|| (t = build_array_type_def(ctxt, node, add_to_current_scope))
|| (t = build_enum_type_decl(ctxt, node, add_to_current_scope))
|| (t = build_typedef_decl(ctxt, node, add_to_current_scope))
|| (t = build_class_decl(ctxt, node, add_to_current_scope)));
return t;
}
/// Parses 'type-decl' xml element.
///
/// @param ctxt the parsing context.
///
/// @return true upon successful parsing, false otherwise.
static decl_base_sptr
handle_type_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
type_decl_sptr decl = build_type_decl(ctxt, node, add_to_current_scope);
return decl;
}
/// Parses 'namespace-decl' xml element.
///
/// @param ctxt the parsing context.
///
/// @return true upon successful parsing, false otherwise.
static decl_base_sptr
handle_namespace_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
namespace_decl_sptr d = build_namespace_decl(ctxt, node,
add_to_current_scope);
return d;
}
/// Parse a qualified-type-def xml element.
///
/// @param ctxt the parsing context.
///
/// @return true upon successful parsing, false otherwise.
static decl_base_sptr
handle_qualified_type_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
qualified_type_def_sptr decl =
build_qualified_type_decl(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a pointer-type-decl element.
///
/// @param ctxt the context of the parsing.
///
/// @return true upon successful completion, false otherwise.
static decl_base_sptr
handle_pointer_type_def(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
pointer_type_def_sptr decl = build_pointer_type_def(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a reference-type-def element.
///
/// @param ctxt the context of the parsing.
///
/// reference_type_def is added to.
static decl_base_sptr
handle_reference_type_def(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
reference_type_def_sptr decl = build_reference_type_def(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a array-type-def element.
///
/// @param ctxt the context of the parsing.
///
/// array_type_def is added to.
static decl_base_sptr
handle_array_type_def(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
array_type_def_sptr decl = build_array_type_def(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse an enum-decl element.
///
/// @param ctxt the context of the parsing.
static decl_base_sptr
handle_enum_type_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
enum_type_decl_sptr decl = build_enum_type_decl(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a typedef-decl element.
///
/// @param ctxt the context of the parsing.
static decl_base_sptr
handle_typedef_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
typedef_decl_sptr decl = build_typedef_decl(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a var-decl element.
///
/// @param ctxt the context of the parsing.
///
/// @param node the node to read & parse from.
///
/// @param add_to_current_scope if set to yes, the resulting of this
/// function is added to its current scope.
static decl_base_sptr
handle_var_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
decl_base_sptr decl = build_var_decl(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a function-decl element.
///
/// @param ctxt the context of the parsing
///
/// @return true upon successful completion of the parsing, false
/// otherwise.
static decl_base_sptr
handle_function_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
function_decl_sptr fn = build_function_decl(ctxt, node,
class_decl_sptr(),
add_to_current_scope);
return fn;
}
/// Parse a 'class-decl' xml element.
///
/// @param ctxt the context of the parsing.
///
/// @return true upon successful completion of the parsing, false
/// otherwise.
static decl_base_sptr
handle_class_decl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
class_decl_sptr decl = build_class_decl(ctxt, node,
add_to_current_scope);
return decl;
}
/// Parse a 'function-template-decl' xml element.
///
/// @param ctxt the parsing context.
///
/// @return true upon successful completion of the parsing, false
/// otherwise.
static decl_base_sptr
handle_function_tdecl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
function_tdecl_sptr d = build_function_tdecl(ctxt, node,
add_to_current_scope);
return d;
}
/// Parse a 'class-template-decl' xml element.
///
/// @param ctxt the context of the parsing.
///
/// @return true upon successful completion, false otherwise.
static decl_base_sptr
handle_class_tdecl(read_context& ctxt,
xmlNodePtr node,
bool add_to_current_scope)
{
class_tdecl_sptr decl = build_class_tdecl(ctxt, node,
add_to_current_scope);
return decl;
}
/// De-serialize a translation unit from an ABI Instrumentation xml
/// file coming from an input stream.
///
/// @param in a pointer to the input stream.
///
/// @return the translation unit resulting from the parsing upon
/// successful completion, or nil.
translation_unit_sptr
read_translation_unit_from_istream(istream* in)
{
read_context read_ctxt(xml::new_reader_from_istream(in));
return read_translation_unit_from_input(read_ctxt);
}
template<typename T>
struct array_deleter
{
void
operator()(T* a)
{
delete [] a;
}
};//end array_deleter
#ifdef WITH_ZIP_ARCHIVE
/// Deserialize an ABI Instrumentation XML file at a given index in a
/// zip archive, and populate a given @ref translation_unit object
/// with the result of that de-serialization.
///
/// @param the @ref translation_unit to populate with the result of
/// the de-serialization.
///
/// @param ar the zip archive to read from.
///
/// @param file_index the index of the ABI Instrumentation XML file to
/// read from the zip archive.
///
/// @return true upon successful completion, false otherwise.
static translation_unit_sptr
read_to_translation_unit(zip_sptr ar,
int file_index)
{
translation_unit_sptr nil;
if (!ar)
return nil;
zip_file_sptr f = open_file_in_archive(ar, file_index);
if (!f)
return nil;
string input;
{
// Allocate a 64K byte buffer to read the archive.
int buf_size = 64 * 1024;
shared_ptr<char> buf(new char[buf_size + 1], array_deleter<char>());
memset(buf.get(), 0, buf_size + 1);
input.reserve(buf_size);
while (zip_fread(f.get(), buf.get(), buf_size))
{
input.append(buf.get());
memset(buf.get(), 0, buf_size + 1);
}
}
return read_translation_unit_from_buffer(input);
}
/// Read an ABI corpus from an archive file which is a ZIP archive of
/// several ABI Instrumentation XML files.
///
/// @param ar an object representing the archive file.
///
/// @param corp the ABI Corpus object to populate with the content of
/// the archive @ref ar.
///
/// @return the number of ABI Instrumentation file read from the
/// archive.
static int
read_corpus_from_archive(zip_sptr ar,
corpus_sptr& corp)
{
if (!ar)
return -1;
int nb_of_tu_read = 0;
int nb_entries = zip_get_num_entries(ar.get(), 0);
if (nb_entries < 0)
return -1;
// TODO: ensure abi-info descriptor is present in the archive. Read
// it and ensure that version numbers match.
for (int i = 0; i < nb_entries; ++i)
{
shared_ptr<translation_unit>
tu(new translation_unit(zip_get_name(ar.get(), i, 0)));
if (read_to_translation_unit(*tu, ar, i))
{
if (!corp)
corp.reset(new corpus(""));
corp->add(tu);
++nb_of_tu_read;
}
}
if (nb_of_tu_read)
corp->set_origin(corpus::NATIVE_XML_ORIGIN);
return nb_of_tu_read;
}
/// Read an ABI corpus from an archive file which is a ZIP archive of
/// several ABI Instrumentation XML files.
///
/// @param corp the corpus to populate with the result of reading the
/// archive.
///
/// @param path the path to the archive file.
///
/// @return the number of ABI Instrument XML file read from the
/// archive, or -1 if the file could not read.
int
read_corpus_from_file(corpus_sptr& corp,
const string& path)
{
if (path.empty())
return -1;
int error_code = 0;
zip_sptr archive = open_archive(path, ZIP_CREATE|ZIP_CHECKCONS, &error_code);
if (error_code)
return -1;
assert(archive);
return read_corpus_from_archive(archive, corp);
}
/// Read an ABI corpus from an archive file which is a ZIP archive of
/// several ABI Instrumentation XML files.
///
/// @param corp the corpus to populate with the result of reading the
/// archive. The archive file to consider is corp.get_path().
///
/// @return the number of ABI Instrument XML file read from the
/// archive.
int
read_corpus_from_file(corpus_sptr& corp)
{return read_corpus_from_file(corp, corp->get_path());}
/// Read an ABI corpus from an archive file which is a ZIP archive of
/// several ABI Instrumentation XML files.
///
/// @param path the path to the archive file.
///
/// @return the resulting corpus object, or NULL if the file could not
/// be read.
corpus_sptr
read_corpus_from_file(const string& path)
{
if (path.empty())
return corpus_sptr();
corpus_sptr corp(new corpus(path));
if (read_corpus_from_file(corp, path) < 0)
return corpus_sptr();
return corp;
}
#endif //WITH_ZIP_ARCHIVE
/// De-serialize an ABI corpus from an input XML document which root
/// node is 'abi-corpus'.
///
/// @param in the input stream to read the XML document from.
///
/// @param corp the corpus de-serialized from the parsing. This is
/// set iff the function returns true.
///
/// @return the resulting corpus de-serialized from the parsing. This
/// is non-null iff the parsing resulted in a valid corpus.
corpus_sptr
read_corpus_from_native_xml(std::istream* in)
{
read_context read_ctxt(xml::new_reader_from_istream(in));
corpus_sptr corp(new corpus(""));
read_ctxt.set_corpus(corp);
return read_corpus_from_input(read_ctxt);
}
/// De-serialize an ABI corpus from an XML document file which root
/// node is 'abi-corpus'.
///
/// @param path the path to the input file to read the XML document
/// from.
///
/// @param corp the corpus de-serialized from the parsing. This is
/// set iff the function returns true.
///
/// @return the resulting corpus de-serialized from the parsing. This
/// is non-null if the parsing successfully resulted in a corpus.
corpus_sptr
read_corpus_from_native_xml_file(const string& path)
{
read_context read_ctxt(xml::new_reader_from_file(path));
corpus_sptr corp = read_corpus_from_input(read_ctxt);
if (corp)
{
if (corp->get_path().empty())
corp->set_path(path);
}
return corp;
}
}//end namespace xml_reader
}//end namespace abigail