forked from OSchip/llvm-project
580 lines
18 KiB
C++
580 lines
18 KiB
C++
//===--- DocumentXML.cpp - XML document for ASTs --------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the XML document class, which provides the means to
|
|
// dump out the AST in a XML form that exposes type details and other fields.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Frontend/DocumentXML.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
namespace clang {
|
|
|
|
//---------------------------------------------------------
|
|
struct DocumentXML::NodeXML
|
|
{
|
|
std::string Name;
|
|
NodeXML* Parent;
|
|
|
|
NodeXML(const std::string& name, NodeXML* parent) :
|
|
Name(name),
|
|
Parent(parent)
|
|
{}
|
|
};
|
|
|
|
//---------------------------------------------------------
|
|
DocumentXML::DocumentXML(const std::string& rootName, llvm::raw_ostream& out) :
|
|
Root(new NodeXML(rootName, 0)),
|
|
CurrentNode(Root),
|
|
Out(out),
|
|
Ctx(0),
|
|
CurrentIndent(0),
|
|
HasCurrentNodeSubNodes(false)
|
|
{
|
|
Out << "<?xml version=\"1.0\"?>\n<" << rootName;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
DocumentXML::~DocumentXML()
|
|
{
|
|
assert(CurrentNode == Root && "not completely backtracked");
|
|
delete Root;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
DocumentXML& DocumentXML::addSubNode(const std::string& name)
|
|
{
|
|
if (!HasCurrentNodeSubNodes)
|
|
{
|
|
Out << ">\n";
|
|
}
|
|
CurrentNode = new NodeXML(name, CurrentNode);
|
|
HasCurrentNodeSubNodes = false;
|
|
CurrentIndent += 2;
|
|
Indent();
|
|
Out << "<" << CurrentNode->Name;
|
|
return *this;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::Indent()
|
|
{
|
|
for (int i = 0; i < CurrentIndent; ++i)
|
|
Out << ' ';
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
DocumentXML& DocumentXML::toParent()
|
|
{
|
|
assert(CurrentNode != Root && "to much backtracking");
|
|
|
|
if (HasCurrentNodeSubNodes)
|
|
{
|
|
Indent();
|
|
Out << "</" << CurrentNode->Name << ">\n";
|
|
}
|
|
else
|
|
{
|
|
Out << "/>\n";
|
|
}
|
|
NodeXML* NodeToDelete = CurrentNode;
|
|
CurrentNode = CurrentNode->Parent;
|
|
delete NodeToDelete;
|
|
HasCurrentNodeSubNodes = true;
|
|
CurrentIndent -= 2;
|
|
return *this;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
namespace {
|
|
|
|
enum tIdType { ID_NORMAL, ID_FILE, ID_LAST };
|
|
|
|
unsigned getNewId(tIdType idType)
|
|
{
|
|
static unsigned int idCounts[ID_LAST] = { 0 };
|
|
return ++idCounts[idType];
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
inline std::string getPrefixedId(unsigned uId, tIdType idType)
|
|
{
|
|
static const char idPrefix[ID_LAST] = { '_', 'f' };
|
|
char buffer[20];
|
|
char* BufPtr = llvm::utohex_buffer(uId, buffer + 20);
|
|
*--BufPtr = idPrefix[idType];
|
|
return BufPtr;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
template<class T, class V>
|
|
bool addToMap(T& idMap, const V& value, tIdType idType = ID_NORMAL)
|
|
{
|
|
typename T::iterator i = idMap.find(value);
|
|
bool toAdd = i == idMap.end();
|
|
if (toAdd)
|
|
{
|
|
idMap.insert(typename T::value_type(value, getNewId(idType)));
|
|
}
|
|
return toAdd;
|
|
}
|
|
|
|
} // anon NS
|
|
|
|
//---------------------------------------------------------
|
|
std::string DocumentXML::escapeString(const char* pStr, std::string::size_type len)
|
|
{
|
|
std::string value;
|
|
value.reserve(len + 1);
|
|
char buffer[16];
|
|
for (unsigned i = 0; i < len; ++i) {
|
|
switch (char C = pStr[i]) {
|
|
default:
|
|
if (isprint(C))
|
|
value += C;
|
|
else
|
|
{
|
|
sprintf(buffer, "\\%03o", C);
|
|
value += buffer;
|
|
}
|
|
break;
|
|
|
|
case '\n': value += "\\n"; break;
|
|
case '\t': value += "\\t"; break;
|
|
case '\a': value += "\\a"; break;
|
|
case '\b': value += "\\b"; break;
|
|
case '\r': value += "\\r"; break;
|
|
|
|
case '&': value += "&"; break;
|
|
case '<': value += "<"; break;
|
|
case '>': value += ">"; break;
|
|
case '"': value += """; break;
|
|
case '\'': value += "'"; break;
|
|
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::finalize()
|
|
{
|
|
assert(CurrentNode == Root && "not completely backtracked");
|
|
|
|
addSubNode("ReferenceSection");
|
|
addSubNode("Types");
|
|
|
|
for (XML::IdMap<QualType>::iterator i = Types.begin(), e = Types.end(); i != e; ++i)
|
|
{
|
|
if (i->first.getCVRQualifiers() != 0)
|
|
{
|
|
addSubNode("CvQualifiedType");
|
|
addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
|
|
addAttribute("type", getPrefixedId(BasicTypes[i->first.getTypePtr()], ID_NORMAL));
|
|
if (i->first.isConstQualified()) addAttribute("const", "1");
|
|
if (i->first.isVolatileQualified()) addAttribute("volatile", "1");
|
|
if (i->first.isRestrictQualified()) addAttribute("restrict", "1");
|
|
toParent();
|
|
}
|
|
}
|
|
|
|
for (XML::IdMap<const Type*>::iterator i = BasicTypes.begin(), e = BasicTypes.end(); i != e; ++i)
|
|
{
|
|
// don't use the get methods as they strip of typedef infos
|
|
if (const BuiltinType *BT = dyn_cast<BuiltinType>(i->first)) {
|
|
addSubNode("FundamentalType");
|
|
addAttribute("name", BT->getName(Ctx->getLangOptions().CPlusPlus));
|
|
}
|
|
else if (const PointerType *PT = dyn_cast<PointerType>(i->first)) {
|
|
addSubNode("PointerType");
|
|
addTypeAttribute(PT->getPointeeType());
|
|
}
|
|
else if (dyn_cast<FunctionType>(i->first) != 0) {
|
|
addSubNode("FunctionType");
|
|
}
|
|
else if (const ReferenceType *RT = dyn_cast<ReferenceType>(i->first)) {
|
|
addSubNode("ReferenceType");
|
|
addTypeAttribute(RT->getPointeeType());
|
|
}
|
|
else if (const TypedefType * TT = dyn_cast<TypedefType>(i->first)) {
|
|
addSubNode("Typedef");
|
|
addAttribute("name", TT->getDecl()->getNameAsString());
|
|
addTypeAttribute(TT->getDecl()->getUnderlyingType());
|
|
addContextAttribute(TT->getDecl()->getDeclContext());
|
|
}
|
|
else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(i->first)) {
|
|
addSubNode("QualifiedNameType");
|
|
addTypeAttribute(QT->getNamedType());
|
|
}
|
|
else if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(i->first)) {
|
|
addSubNode("ArrayType");
|
|
addAttribute("min", 0);
|
|
addAttribute("max", (CAT->getSize() - 1).toString(10, false));
|
|
addTypeAttribute(CAT->getElementType());
|
|
}
|
|
else if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(i->first)) {
|
|
addSubNode("VariableArrayType");
|
|
addTypeAttribute(VAT->getElementType());
|
|
}
|
|
else if (const TagType *RET = dyn_cast<TagType>(i->first)) {
|
|
const TagDecl *tagDecl = RET->getDecl();
|
|
std::string tagKind = tagDecl->getKindName();
|
|
tagKind[0] = std::toupper(tagKind[0]);
|
|
addSubNode(tagKind);
|
|
addAttribute("name", tagDecl->getNameAsString());
|
|
addContextAttribute(tagDecl->getDeclContext());
|
|
}
|
|
else if (const VectorType* VT = dyn_cast<VectorType>(i->first)) {
|
|
addSubNode("VectorType");
|
|
addTypeAttribute(VT->getElementType());
|
|
addAttribute("num_elements", VT->getNumElements());
|
|
}
|
|
else
|
|
{
|
|
addSubNode("FIXMEType");
|
|
}
|
|
addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
|
|
toParent();
|
|
}
|
|
|
|
|
|
toParent().addSubNode("Contexts");
|
|
|
|
for (XML::IdMap<const DeclContext*>::iterator i = Contexts.begin(), e = Contexts.end(); i != e; ++i)
|
|
{
|
|
addSubNode(i->first->getDeclKindName());
|
|
addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
|
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(i->first)) {
|
|
addAttribute("name", ND->getNameAsString());
|
|
}
|
|
if (const TagDecl *TD = dyn_cast<TagDecl>(i->first)) {
|
|
addAttribute("type", getPrefixedId(BasicTypes[TD->getTypeForDecl()], ID_NORMAL));
|
|
}
|
|
else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(i->first)) {
|
|
addAttribute("type", getPrefixedId(BasicTypes[FD->getType()->getAsFunctionType()], ID_NORMAL));
|
|
}
|
|
|
|
if (const DeclContext* parent = i->first->getParent())
|
|
{
|
|
addContextAttribute(parent);
|
|
}
|
|
toParent();
|
|
}
|
|
|
|
toParent().addSubNode("Files");
|
|
|
|
for (XML::IdMap<std::string>::iterator i = SourceFiles.begin(), e = SourceFiles.end(); i != e; ++i)
|
|
{
|
|
addSubNode("File");
|
|
addAttribute("id", getPrefixedId(i->second, ID_FILE));
|
|
addAttribute("name", escapeString(i->first.c_str(), i->first.size()));
|
|
toParent();
|
|
}
|
|
|
|
toParent().toParent();
|
|
|
|
// write the root closing node (which has always subnodes)
|
|
Out << "</" << CurrentNode->Name << ">\n";
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addTypeAttribute(const QualType& pType)
|
|
{
|
|
addTypeRecursively(pType);
|
|
addAttribute("type", getPrefixedId(Types[pType], ID_NORMAL));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addTypeIdAttribute(const Type* pType)
|
|
{
|
|
addBasicTypeRecursively(pType);
|
|
addAttribute("id", getPrefixedId(BasicTypes[pType], ID_NORMAL));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addTypeRecursively(const QualType& pType)
|
|
{
|
|
if (addToMap(Types, pType))
|
|
{
|
|
addBasicTypeRecursively(pType.getTypePtr());
|
|
// beautifier: a non-qualified type shall be transparent
|
|
if (pType.getCVRQualifiers() == 0)
|
|
{
|
|
Types[pType] = BasicTypes[pType.getTypePtr()];
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addBasicTypeRecursively(const Type* pType)
|
|
{
|
|
if (addToMap(BasicTypes, pType))
|
|
{
|
|
if (const PointerType *PT = dyn_cast<PointerType>(pType)) {
|
|
addTypeRecursively(PT->getPointeeType());
|
|
}
|
|
else if (const ReferenceType *RT = dyn_cast<ReferenceType>(pType)) {
|
|
addTypeRecursively(RT->getPointeeType());
|
|
}
|
|
else if (const TypedefType *TT = dyn_cast<TypedefType>(pType)) {
|
|
addTypeRecursively(TT->getDecl()->getUnderlyingType());
|
|
addContextsRecursively(TT->getDecl()->getDeclContext());
|
|
}
|
|
else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(pType)) {
|
|
addTypeRecursively(QT->getNamedType());
|
|
// FIXME: what to do with NestedNameSpecifier or shall this type be transparent?
|
|
}
|
|
else if (const ArrayType *AT = dyn_cast<ArrayType>(pType)) {
|
|
addTypeRecursively(AT->getElementType());
|
|
// FIXME: doesn't work in the immediate streaming approach
|
|
/*if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(AT))
|
|
{
|
|
addSubNode("VariableArraySizeExpression");
|
|
PrintStmt(VAT->getSizeExpr());
|
|
toParent();
|
|
}*/
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addContextAttribute(const DeclContext *DC, tContextUsage usage)
|
|
{
|
|
addContextsRecursively(DC);
|
|
const char* pAttributeTags[2] = { "context", "id" };
|
|
addAttribute(pAttributeTags[usage], getPrefixedId(Contexts[DC], ID_NORMAL));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addContextsRecursively(const DeclContext *DC)
|
|
{
|
|
if (DC != 0 && addToMap(Contexts, DC))
|
|
{
|
|
addContextsRecursively(DC->getParent());
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addSourceFileAttribute(const std::string& fileName)
|
|
{
|
|
addToMap(SourceFiles, fileName, ID_FILE);
|
|
addAttribute("file", getPrefixedId(SourceFiles[fileName], ID_FILE));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
PresumedLoc DocumentXML::addLocation(const SourceLocation& Loc)
|
|
{
|
|
SourceManager& SM = Ctx->getSourceManager();
|
|
SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
|
|
PresumedLoc PLoc;
|
|
if (!SpellingLoc.isInvalid())
|
|
{
|
|
PLoc = SM.getPresumedLoc(SpellingLoc);
|
|
addSourceFileAttribute(PLoc.getFilename());
|
|
addAttribute("line", PLoc.getLine());
|
|
addAttribute("col", PLoc.getColumn());
|
|
}
|
|
// else there is no error in some cases (eg. CXXThisExpr)
|
|
return PLoc;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addLocationRange(const SourceRange& R)
|
|
{
|
|
PresumedLoc PStartLoc = addLocation(R.getBegin());
|
|
if (R.getBegin() != R.getEnd())
|
|
{
|
|
SourceManager& SM = Ctx->getSourceManager();
|
|
SourceLocation SpellingLoc = SM.getSpellingLoc(R.getEnd());
|
|
if (!SpellingLoc.isInvalid())
|
|
{
|
|
PresumedLoc PLoc = SM.getPresumedLoc(SpellingLoc);
|
|
if (PStartLoc.isInvalid() ||
|
|
strcmp(PLoc.getFilename(), PStartLoc.getFilename()) != 0) {
|
|
addToMap(SourceFiles, PLoc.getFilename(), ID_FILE);
|
|
addAttribute("endfile", PLoc.getFilename());
|
|
addAttribute("endline", PLoc.getLine());
|
|
addAttribute("endcol", PLoc.getColumn());
|
|
} else if (PLoc.getLine() != PStartLoc.getLine()) {
|
|
addAttribute("endline", PLoc.getLine());
|
|
addAttribute("endcol", PLoc.getColumn());
|
|
} else {
|
|
addAttribute("endcol", PLoc.getColumn());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::PrintFunctionDecl(FunctionDecl *FD)
|
|
{
|
|
switch (FD->getStorageClass()) {
|
|
default: assert(0 && "Unknown storage class");
|
|
case FunctionDecl::None: break;
|
|
case FunctionDecl::Extern: addAttribute("storage_class", "extern"); break;
|
|
case FunctionDecl::Static: addAttribute("storage_class", "static"); break;
|
|
case FunctionDecl::PrivateExtern: addAttribute("storage_class", "__private_extern__"); break;
|
|
}
|
|
|
|
if (FD->isInline())
|
|
addAttribute("inline", "1");
|
|
|
|
const FunctionType *AFT = FD->getType()->getAsFunctionType();
|
|
addTypeAttribute(AFT->getResultType());
|
|
addBasicTypeRecursively(AFT);
|
|
|
|
if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(AFT)) {
|
|
addAttribute("num_args", FD->getNumParams());
|
|
for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
|
|
addSubNode("Argument");
|
|
ParmVarDecl *argDecl = FD->getParamDecl(i);
|
|
addAttribute("name", argDecl->getNameAsString());
|
|
addTypeAttribute(FT->getArgType(i));
|
|
addDeclIdAttribute(argDecl);
|
|
if (argDecl->getDefaultArg())
|
|
{
|
|
addAttribute("default_arg", "1");
|
|
PrintStmt(argDecl->getDefaultArg());
|
|
}
|
|
toParent();
|
|
}
|
|
|
|
if (FT->isVariadic()) {
|
|
addSubNode("Ellipsis").toParent();
|
|
}
|
|
} else {
|
|
assert(isa<FunctionNoProtoType>(AFT));
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addRefAttribute(const NamedDecl* D)
|
|
{
|
|
// FIXME: in case of CXX inline member functions referring to a member defined
|
|
// after the function it needs to be tested, if the ids are already there
|
|
// (should work, but I couldn't test it)
|
|
if (const DeclContext* DC = dyn_cast<DeclContext>(D))
|
|
{
|
|
addAttribute("ref", getPrefixedId(Contexts[DC], ID_NORMAL));
|
|
}
|
|
else
|
|
{
|
|
addAttribute("ref", getPrefixedId(Decls[D], ID_NORMAL));
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::addDeclIdAttribute(const NamedDecl* D)
|
|
{
|
|
addToMap(Decls, D);
|
|
addAttribute("id", getPrefixedId(Decls[D], ID_NORMAL));
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
void DocumentXML::PrintDecl(Decl *D)
|
|
{
|
|
addSubNode(D->getDeclKindName());
|
|
addContextAttribute(D->getDeclContext());
|
|
addLocation(D->getLocation());
|
|
if (DeclContext* DC = dyn_cast<DeclContext>(D))
|
|
{
|
|
addContextAttribute(DC, CONTEXT_AS_ID);
|
|
}
|
|
|
|
if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
|
|
addAttribute("name", ND->getNameAsString());
|
|
|
|
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
PrintFunctionDecl(FD);
|
|
if (Stmt *Body = FD->getBody(*Ctx)) {
|
|
addSubNode("Body");
|
|
PrintStmt(Body);
|
|
toParent();
|
|
}
|
|
} else if (RecordDecl *RD = dyn_cast<RecordDecl>(D)) {
|
|
addBasicTypeRecursively(RD->getTypeForDecl());
|
|
addAttribute("type", getPrefixedId(BasicTypes[RD->getTypeForDecl()], ID_NORMAL));
|
|
if (!RD->isDefinition())
|
|
{
|
|
addAttribute("forward", "1");
|
|
}
|
|
|
|
for (RecordDecl::field_iterator i = RD->field_begin(*Ctx), e = RD->field_end(*Ctx); i != e; ++i)
|
|
{
|
|
PrintDecl(*i);
|
|
}
|
|
} else if (EnumDecl *ED = dyn_cast<EnumDecl>(D)) {
|
|
const QualType& enumType = ED->getIntegerType();
|
|
if (!enumType.isNull())
|
|
{
|
|
addTypeAttribute(enumType);
|
|
for (EnumDecl::enumerator_iterator i = ED->enumerator_begin(*Ctx), e = ED->enumerator_end(*Ctx); i != e; ++i)
|
|
{
|
|
PrintDecl(*i);
|
|
}
|
|
}
|
|
} else if (EnumConstantDecl* ECD = dyn_cast<EnumConstantDecl>(D)) {
|
|
addTypeAttribute(ECD->getType());
|
|
addAttribute("value", ECD->getInitVal().toString(10, true));
|
|
if (ECD->getInitExpr())
|
|
{
|
|
PrintStmt(ECD->getInitExpr());
|
|
}
|
|
} else if (FieldDecl *FdD = dyn_cast<FieldDecl>(D)) {
|
|
addTypeAttribute(FdD->getType());
|
|
addDeclIdAttribute(ND);
|
|
if (FdD->isMutable())
|
|
addAttribute("mutable", "1");
|
|
if (FdD->isBitField())
|
|
{
|
|
addAttribute("bitfield", "1");
|
|
PrintStmt(FdD->getBitWidth());
|
|
}
|
|
} else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
|
|
addTypeIdAttribute(Ctx->getTypedefType(TD).getTypePtr());
|
|
addTypeAttribute(TD->getUnderlyingType());
|
|
} else if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) {
|
|
addTypeAttribute(VD->getType());
|
|
addDeclIdAttribute(ND);
|
|
|
|
VarDecl *V = dyn_cast<VarDecl>(VD);
|
|
if (V && V->getStorageClass() != VarDecl::None)
|
|
{
|
|
addAttribute("storage_class", VarDecl::getStorageClassSpecifierString(V->getStorageClass()));
|
|
}
|
|
|
|
if (V && V->getInit())
|
|
{
|
|
PrintStmt(V->getInit());
|
|
}
|
|
}
|
|
} else if (LinkageSpecDecl* LSD = dyn_cast<LinkageSpecDecl>(D)) {
|
|
switch (LSD->getLanguage())
|
|
{
|
|
case LinkageSpecDecl::lang_c: addAttribute("lang", "C"); break;
|
|
case LinkageSpecDecl::lang_cxx: addAttribute("lang", "CXX"); break;
|
|
default: assert(0 && "Unexpected lang id");
|
|
}
|
|
} else if (isa<FileScopeAsmDecl>(D)) {
|
|
// FIXME: Implement this
|
|
} else {
|
|
assert(0 && "Unexpected decl");
|
|
}
|
|
toParent();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
} // NS clang
|
|
|