blob: 739e6aed3713ec950135e0bc362b71404e1d3bd3 [file] [log] [blame]
//===-- FormatManager.h -------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef lldb_FormatManager_h_
#define lldb_FormatManager_h_
// C Includes
#include <stdint.h>
#include <unistd.h>
// C++ Includes
#ifdef __GNUC__
#include <ext/hash_map>
namespace std
{
using namespace __gnu_cxx;
}
#else
#include <hash_map>
#endif
#include <list>
#include <map>
#include <stack>
// Other libraries and framework includes
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/AST/DeclObjC.h"
// Project includes
#include "lldb/lldb-public.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/Core/Communication.h"
#include "lldb/Core/FormatClasses.h"
#include "lldb/Core/InputReaderStack.h"
#include "lldb/Core/Listener.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/UserID.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Interpreter/ScriptInterpreterPython.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/TargetList.h"
namespace lldb_private {
class IFormatChangeListener
{
public:
virtual void
Changed() = 0;
virtual
~IFormatChangeListener() {}
};
template<typename KeyType, typename ValueType>
class FormatNavigator;
template<typename KeyType, typename ValueType>
class FormatMap
{
private:
typedef typename ValueType::SharedPointer ValueSP;
Mutex m_map_mutex;
IFormatChangeListener* listener;
friend class FormatNavigator<KeyType, ValueType>;
friend class FormatManager;
public:
typedef std::map<KeyType, ValueSP> MapType;
private:
MapType m_map;
MapType& map()
{
return m_map;
}
Mutex& mutex()
{
return m_map_mutex;
}
public:
typedef typename MapType::iterator MapIterator;
typedef bool(*CallbackType)(void*, KeyType, const ValueSP&);
FormatMap(IFormatChangeListener* lst = NULL) :
m_map(),
m_map_mutex(Mutex::eMutexTypeRecursive),
listener(lst)
{
}
void
Add(KeyType name,
const ValueSP& entry)
{
Mutex::Locker(m_map_mutex);
m_map[name] = entry;
if (listener)
listener->Changed();
}
bool
Delete(KeyType name)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(name);
if (iter == m_map.end())
return false;
m_map.erase(name);
if (listener)
listener->Changed();
return true;
}
void
Clear()
{
Mutex::Locker(m_map_mutex);
m_map.clear();
if (listener)
listener->Changed();
}
bool
Get(KeyType name,
ValueSP& entry)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(name);
if (iter == m_map.end())
return false;
entry = iter->second;
return true;
}
void
LoopThrough(CallbackType callback, void* param)
{
if (callback)
{
Mutex::Locker(m_map_mutex);
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
KeyType type = pos->first;
if (!callback(param, type, pos->second))
break;
}
}
}
uint32_t
GetCount()
{
return m_map.size();
}
};
template<typename KeyType, typename ValueType>
class FormatNavigator
{
private:
typedef FormatMap<KeyType,ValueType> BackEndType;
BackEndType m_format_map;
public:
typedef typename BackEndType::MapType MapType;
typedef typename MapType::iterator MapIterator;
typedef typename MapType::key_type MapKeyType;
typedef typename MapType::mapped_type MapValueType;
typedef typename BackEndType::CallbackType CallbackType;
typedef typename lldb::SharedPtr<FormatNavigator<KeyType, ValueType> >::Type SharedPointer;
friend class FormatCategory;
FormatNavigator(IFormatChangeListener* lst = NULL) :
m_format_map(lst)
{
}
void
Add(const MapKeyType &type, const MapValueType& entry)
{
m_format_map.Add(type,entry);
}
// using const char* instead of MapKeyType is necessary here
// to make the partial template specializations below work
bool
Delete(const char *type)
{
return m_format_map.Delete(type);
}
bool
Get(ValueObject& vobj,
MapValueType& entry,
uint32_t* why = NULL)
{
uint32_t value = lldb::eFormatterDirectChoice;
clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType());
bool ret = Get(vobj, type, entry, value);
if (ret)
entry = MapValueType(entry);
else
entry = MapValueType();
if (why)
*why = value;
return ret;
}
void
Clear()
{
m_format_map.Clear();
}
void
LoopThrough(CallbackType callback, void* param)
{
m_format_map.LoopThrough(callback,param);
}
uint32_t
GetCount()
{
return m_format_map.GetCount();
}
private:
DISALLOW_COPY_AND_ASSIGN(FormatNavigator);
// using const char* instead of MapKeyType is necessary here
// to make the partial template specializations below work
bool
Get(const char* type, MapValueType& entry)
{
return m_format_map.Get(type, entry);
}
bool Get(ValueObject& vobj,
clang::QualType type,
MapValueType& entry,
uint32_t& reason)
{
if (type.isNull())
return false;
// clang::QualType type = q_type.getUnqualifiedType();
type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict();
const clang::Type* typePtr = type.getTypePtrOrNull();
if (!typePtr)
return false;
ConstString name(ClangASTType::GetTypeNameForQualType(type).c_str());
if (vobj.GetBitfieldBitSize() > 0)
{
// for bitfields, append size to the typename so one can custom format them
StreamString sstring;
sstring.Printf("%s:%d",name.AsCString(),vobj.GetBitfieldBitSize());
name = ConstString(sstring.GetData());
}
//printf("trying to get format for VO name %s of type %s\n",vobj.GetName().AsCString(),name.AsCString());
if (Get(name.GetCString(), entry))
return true;
// look for a "base type", whatever that means
if (typePtr->isReferenceType())
{
if (Get(vobj,type.getNonReferenceType(),entry, reason) && !entry->m_skip_references)
{
reason |= lldb::eFormatterStrippedPointerReference;
return true;
}
}
if (typePtr->isPointerType())
{
if (Get(vobj, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers)
{
reason |= lldb::eFormatterStrippedPointerReference;
return true;
}
}
if (typePtr->isObjCObjectPointerType())
{
/*
for some reason, C++ can quite easily obtain the type hierarchy for a ValueObject
even if the VO represent a pointer-to-class, as long as the typePtr is right
Objective-C on the other hand cannot really complete an @interface when
the VO refers to a pointer-to-@interface
*/
Error error;
ValueObject* target = vobj.Dereference(error).get();
if (error.Fail() || !target)
return false;
if (Get(*target, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers)
{
reason |= lldb::eFormatterStrippedPointerReference;
return true;
}
}
const clang::ObjCObjectType *objc_class_type = typePtr->getAs<clang::ObjCObjectType>();
if (objc_class_type)
{
//printf("working with ObjC\n");
clang::ASTContext *ast = vobj.GetClangAST();
if (ClangASTContext::GetCompleteType(ast, vobj.GetClangType()) && !objc_class_type->isObjCId())
{
clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
if (class_interface_decl)
{
//printf("down here\n");
clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass();
//printf("one further step and we're there...\n");
if (superclass_interface_decl)
{
//printf("the end is here\n");
clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl));
if (Get(vobj, ivar_qual_type, entry, reason) && entry->m_cascades)
{
reason |= lldb::eFormatterNavigatedBaseClasses;
return true;
}
}
}
}
}
// for C++ classes, navigate up the hierarchy
if (typePtr->isRecordType())
{
clang::CXXRecordDecl* record = typePtr->getAsCXXRecordDecl();
if (record)
{
if (!record->hasDefinition())
ClangASTContext::GetCompleteType(vobj.GetClangAST(), vobj.GetClangType());
if (record->hasDefinition())
{
clang::CXXRecordDecl::base_class_iterator pos,end;
if (record->getNumBases() > 0)
{
end = record->bases_end();
for (pos = record->bases_begin(); pos != end; pos++)
{
if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterNavigatedBaseClasses;
return true;
}
}
}
if (record->getNumVBases() > 0)
{
end = record->vbases_end();
for (pos = record->vbases_begin(); pos != end; pos++)
{
if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterNavigatedBaseClasses;
return true;
}
}
}
}
}
}
// try to strip typedef chains
const clang::TypedefType* type_tdef = type->getAs<clang::TypedefType>();
if (type_tdef)
{
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterNavigatedTypedefs;
return true;
}
}
return false;
}
};
template<>
bool
FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Get(const char* key, SummaryFormat::SharedPointer& value);
template<>
bool
FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Delete(const char* type);
class CategoryMap;
class FormatCategory
{
private:
typedef FormatNavigator<const char*, SummaryFormat> SummaryNavigator;
typedef FormatNavigator<lldb::RegularExpressionSP, SummaryFormat> RegexSummaryNavigator;
typedef SummaryNavigator::MapType SummaryMap;
typedef RegexSummaryNavigator::MapType RegexSummaryMap;
SummaryNavigator::SharedPointer m_summary_nav;
RegexSummaryNavigator::SharedPointer m_regex_summary_nav;
bool m_enabled;
IFormatChangeListener* m_change_listener;
Mutex m_mutex;
void
Enable(bool value = true)
{
Mutex::Locker(m_mutex);
m_enabled = value;
if (m_change_listener)
m_change_listener->Changed();
}
void
Disable()
{
Enable(false);
}
friend class CategoryMap;
public:
typedef SummaryNavigator::SharedPointer SummaryNavigatorSP;
typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP;
FormatCategory(IFormatChangeListener* clist) :
m_summary_nav(new SummaryNavigator(clist)),
m_regex_summary_nav(new RegexSummaryNavigator(clist)),
m_enabled(false),
m_change_listener(clist),
m_mutex(Mutex::eMutexTypeRecursive)
{}
SummaryNavigatorSP
Summary()
{
return SummaryNavigatorSP(m_summary_nav);
}
RegexSummaryNavigatorSP
RegexSummary()
{
return RegexSummaryNavigatorSP(m_regex_summary_nav);
}
bool
IsEnabled() const
{
return m_enabled;
}
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry,
uint32_t* reason = NULL)
{
if (!IsEnabled())
return false;
if (Summary()->Get(vobj, entry, reason))
return true;
bool regex = RegexSummary()->Get(vobj, entry, reason);
if (regex && reason)
*reason |= lldb::eFormatterRegularExpressionSummary;
return regex;
}
void
Clear()
{
m_summary_nav->Clear();
m_regex_summary_nav->Clear();
}
bool
Delete(const char* name)
{
bool del_sum = m_summary_nav->Delete(name);
bool del_rex = m_regex_summary_nav->Delete(name);
return (del_sum || del_rex);
}
typedef lldb::SharedPtr<FormatCategory>::Type SharedPointer;
};
class CategoryMap
{
private:
typedef const char* KeyType;
typedef FormatCategory ValueType;
typedef ValueType::SharedPointer ValueSP;
typedef std::list<FormatCategory::SharedPointer> ActiveCategoriesList;
typedef ActiveCategoriesList::iterator ActiveCategoriesIterator;
Mutex m_map_mutex;
IFormatChangeListener* listener;
friend class FormatNavigator<KeyType, ValueType>;
friend class FormatManager;
public:
typedef std::map<KeyType, ValueSP> MapType;
private:
MapType m_map;
ActiveCategoriesList m_active_categories;
MapType& map()
{
return m_map;
}
ActiveCategoriesList& active_list()
{
return m_active_categories;
}
Mutex& mutex()
{
return m_map_mutex;
}
public:
typedef MapType::iterator MapIterator;
typedef bool(*CallbackType)(void*, KeyType, const ValueSP&);
CategoryMap(IFormatChangeListener* lst = NULL) :
m_map_mutex(Mutex::eMutexTypeRecursive),
listener(lst),
m_map(),
m_active_categories()
{
}
void
Add(KeyType name,
const ValueSP& entry)
{
Mutex::Locker(m_map_mutex);
m_map[name] = entry;
if (listener)
listener->Changed();
}
bool
Delete(KeyType name)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(name);
if (iter == m_map.end())
return false;
m_map.erase(name);
DisableCategory(name);
if (listener)
listener->Changed();
return true;
}
void
EnableCategory(KeyType category_name)
{
Mutex::Locker(m_map_mutex);
ValueSP category;
if (!Get(category_name,category))
return;
category->Enable();
m_active_categories.push_front(category);
}
class delete_matching_categories
{
FormatCategory::SharedPointer ptr;
public:
delete_matching_categories(FormatCategory::SharedPointer p) : ptr(p)
{}
bool operator()(const FormatCategory::SharedPointer& other)
{
return ptr.get() == other.get();
}
};
void
DisableCategory(KeyType category_name)
{
Mutex::Locker(m_map_mutex);
ValueSP category;
if (!Get(category_name,category))
return;
category->Disable();
m_active_categories.remove_if(delete_matching_categories(category));
}
void
Clear()
{
Mutex::Locker(m_map_mutex);
m_map.clear();
m_active_categories.clear();
if (listener)
listener->Changed();
}
bool
Get(KeyType name,
ValueSP& entry)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(name);
if (iter == m_map.end())
return false;
entry = iter->second;
return true;
}
void
LoopThrough(CallbackType callback, void* param)
{
if (callback)
{
Mutex::Locker(m_map_mutex);
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
KeyType type = pos->first;
if (!callback(param, type, pos->second))
break;
}
}
}
uint32_t
GetCount()
{
return m_map.size();
}
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry)
{
Mutex::Locker(m_map_mutex);
uint32_t reason_why;
bool first = true;
ActiveCategoriesIterator begin, end = m_active_categories.end();
for (begin = m_active_categories.begin(); begin != end; begin++)
{
FormatCategory::SharedPointer category = *begin;
lldb::SummaryFormatSP current_format;
if (!category->Get(vobj, current_format, &reason_why))
continue;
if (reason_why == lldb::eFormatterDirectChoice)
{
entry = current_format;
return true;
}
else if (first)
{
entry = current_format;
first = false;
}
}
return !first;
}
};
class FormatManager : public IFormatChangeListener
{
private:
typedef FormatNavigator<const char*, ValueFormat> ValueNavigator;
typedef ValueNavigator::MapType ValueMap;
typedef FormatMap<const char*, SummaryFormat> NamedSummariesMap;
ValueNavigator m_value_nav;
NamedSummariesMap m_named_summaries_map;
uint32_t m_last_revision;
CategoryMap m_categories_map;
const char* m_default_category_name;
const char* m_system_category_name;
typedef CategoryMap::MapType::iterator CategoryMapIterator;
public:
typedef bool (*CategoryCallback)(void*, const char*, const FormatCategory::SharedPointer&);
FormatManager() :
m_value_nav(this),
m_named_summaries_map(this),
m_last_revision(0),
m_categories_map(this)
{
// build default categories
m_default_category_name = ConstString("default").GetCString();
m_system_category_name = ConstString("system").GetCString();
// add some default stuff
// most formats, summaries, ... actually belong to the users' lldbinit file rather than here
SummaryFormat::SharedPointer string_format(new StringSummaryFormat(false,
true,
false,
true,
false,
false,
"${var%s}"));
SummaryFormat::SharedPointer string_array_format(new StringSummaryFormat(false,
true,
false,
false,
false,
false,
"${var%s}"));
lldb::RegularExpressionSP any_size_char_arr(new RegularExpression("char \\[[0-9]+\\]"));
Category(m_system_category_name)->Summary()->Add(ConstString("char *").GetCString(), string_format);
Category(m_system_category_name)->Summary()->Add(ConstString("const char *").GetCString(), string_format);
Category(m_system_category_name)->RegexSummary()->Add(any_size_char_arr, string_array_format);
Category(m_default_category_name); // this call is there to force LLDB into creating an empty "default" category
// the order of these two calls IS important, if you invert it "system" summaries will prevail over the user's
EnableCategory(m_system_category_name);
EnableCategory(m_default_category_name);
}
CategoryMap& Categories() { return m_categories_map; }
ValueNavigator& Value() { return m_value_nav; }
NamedSummariesMap& NamedSummary() { return m_named_summaries_map; }
void
EnableCategory(const char* category_name)
{
m_categories_map.EnableCategory(category_name);
}
void
DisableCategory(const char* category_name)
{
m_categories_map.DisableCategory(category_name);
}
void
LoopThroughCategories(CategoryCallback callback, void* param)
{
m_categories_map.LoopThrough(callback, param);
}
FormatCategory::SummaryNavigatorSP
Summary(const char* category_name = NULL)
{
return Category(category_name)->Summary();
}
FormatCategory::RegexSummaryNavigatorSP
RegexSummary(const char* category_name = NULL)
{
return Category(category_name)->RegexSummary();
}
lldb::FormatCategorySP
Category(const char* category_name = NULL)
{
if (!category_name)
return Category(m_default_category_name);
lldb::FormatCategorySP category;
if (m_categories_map.Get(category_name, category))
return category;
Categories().Add(category_name,lldb::FormatCategorySP(new FormatCategory(this)));
return Category(category_name);
}
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry)
{
return m_categories_map.Get(vobj, entry);
}
static bool
GetFormatFromCString (const char *format_cstr,
bool partial_match_ok,
lldb::Format &format);
static char
GetFormatAsFormatChar (lldb::Format format);
static const char *
GetFormatAsCString (lldb::Format format);
// when DataExtractor dumps a vectorOfT, it uses a predefined format for each item
// this method returns it, or eFormatInvalid if vector_format is not a vectorOf
static lldb::Format
GetSingleItemFormat(lldb::Format vector_format);
void
Changed()
{
__sync_add_and_fetch(&m_last_revision, +1);
}
uint32_t
GetCurrentRevision() const
{
return m_last_revision;
}
~FormatManager()
{
}
};
} // namespace lldb_private
#endif // lldb_FormatManager_h_