|  | //===--- 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/ASTContext.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  |  | 
|  | namespace clang { | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | DocumentXML::DocumentXML(const std::string& rootName, llvm::raw_ostream& out) : | 
|  | Out(out), | 
|  | Ctx(0), | 
|  | HasCurrentNodeSubNodes(false) { | 
|  | NodeStack.push(rootName); | 
|  | Out << "<?xml version=\"1.0\"?>\n<" << rootName; | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | DocumentXML& DocumentXML::addSubNode(const std::string& name) { | 
|  | if (!HasCurrentNodeSubNodes) | 
|  | Out << ">\n"; | 
|  | NodeStack.push(name); | 
|  | HasCurrentNodeSubNodes = false; | 
|  | Indent(); | 
|  | Out << "<" << NodeStack.top(); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::Indent() { | 
|  | for (size_t i = 0, e = (NodeStack.size() - 1) * 2; i < e; ++i) | 
|  | Out << ' '; | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | DocumentXML& DocumentXML::toParent() { | 
|  | assert(NodeStack.size() > 1 && "too much backtracking"); | 
|  |  | 
|  | if (HasCurrentNodeSubNodes) { | 
|  | Indent(); | 
|  | Out << "</" << NodeStack.top() << ">\n"; | 
|  | } else | 
|  | Out << "/>\n"; | 
|  | NodeStack.pop(); | 
|  | HasCurrentNodeSubNodes = true; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | namespace { | 
|  |  | 
|  | enum tIdType { ID_NORMAL, ID_FILE, ID_LABEL, 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', 'l' }; | 
|  | 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(NodeStack.size() == 1 && "not completely backtracked"); | 
|  |  | 
|  | addSubNode("ReferenceSection"); | 
|  | addSubNode("Types"); | 
|  |  | 
|  | for (XML::IdMap<QualType>::iterator i = Types.begin(), e = Types.end(); | 
|  | i != e; ++i) { | 
|  | if (i->first.hasLocalQualifiers()) { | 
|  | writeTypeToXML(i->first); | 
|  | addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); | 
|  | toParent(); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (XML::IdMap<const Type*>::iterator i = BasicTypes.begin(), | 
|  | e = BasicTypes.end(); i != e; ++i) { | 
|  | writeTypeToXML(i->first); | 
|  | 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()->getAs<FunctionType>()], ID_NORMAL)); | 
|  |  | 
|  | if (const DeclContext* parent = i->first->getParent()) | 
|  | addAttribute("context", 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 << "</" << NodeStack.top() << ">\n"; | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addAttribute(const char* pAttributeName, | 
|  | const QualType& pType) { | 
|  | addTypeRecursively(pType); | 
|  | addAttribute(pAttributeName, getPrefixedId(Types[pType], ID_NORMAL)); | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pAttributeName, | 
|  | const Type* pType) { | 
|  | addTypeRecursively(pType); | 
|  | addAttribute(pAttributeName, getPrefixedId(BasicTypes[pType], ID_NORMAL)); | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pAttributeName, | 
|  | const NestedNameSpecifier* pNNS) { | 
|  | switch (pNNS->getKind()) { | 
|  | case NestedNameSpecifier::Identifier: { | 
|  | IdentifierInfo *ii = pNNS->getAsIdentifier(); | 
|  | // FIXME how should we handle those ? | 
|  | addPtrAttribute(pAttributeName, ii->getName().data()); | 
|  | break; | 
|  | } | 
|  | case NestedNameSpecifier::Namespace: { | 
|  | addPtrAttribute(pAttributeName, pNNS->getAsNamespace()); | 
|  | break; | 
|  | } | 
|  | case NestedNameSpecifier::TypeSpec: { | 
|  | addPtrAttribute(pAttributeName, pNNS->getAsType()); | 
|  | break; | 
|  | } | 
|  | case NestedNameSpecifier::TypeSpecWithTemplate: { | 
|  | addPtrAttribute(pAttributeName, pNNS->getAsType()); | 
|  | break; | 
|  | } | 
|  | case NestedNameSpecifier::Global: { | 
|  | addPtrAttribute(pAttributeName, "::"); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addTypeRecursively(const QualType& pType) | 
|  | { | 
|  | if (addToMap(Types, pType)) | 
|  | { | 
|  | addTypeRecursively(pType.getTypePtr()); | 
|  | // beautifier: a non-qualified type shall be transparent | 
|  | if (!pType.hasLocalQualifiers()) | 
|  | { | 
|  | Types[pType] = BasicTypes[pType.getTypePtr()]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addTypeRecursively(const Type* pType) | 
|  | { | 
|  | if (addToMap(BasicTypes, pType)) | 
|  | { | 
|  | addParentTypes(pType); | 
|  | /* | 
|  | // FIXME: doesn't work in the immediate streaming approach | 
|  | if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(pType)) | 
|  | { | 
|  | addSubNode("VariableArraySizeExpression"); | 
|  | PrintStmt(VAT->getSizeExpr()); | 
|  | toParent(); | 
|  | } | 
|  | */ | 
|  | } | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pName, const DeclContext* DC) | 
|  | { | 
|  | addContextsRecursively(DC); | 
|  | addAttribute(pName, getPrefixedId(Contexts[DC], ID_NORMAL)); | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pAttributeName, const NamedDecl* D) | 
|  | { | 
|  | if (const DeclContext* DC = dyn_cast<DeclContext>(D)) | 
|  | { | 
|  | addContextsRecursively(DC); | 
|  | addAttribute(pAttributeName, getPrefixedId(Contexts[DC], ID_NORMAL)); | 
|  | } | 
|  | else | 
|  | { | 
|  | addToMap(Decls, D); | 
|  | addAttribute(pAttributeName, getPrefixedId(Decls[D], ID_NORMAL)); | 
|  | } | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pName, const NamespaceDecl* D) | 
|  | { | 
|  | addPtrAttribute(pName, static_cast<const DeclContext*>(D)); | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | 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)); | 
|  | } | 
|  |  | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | void DocumentXML::addPtrAttribute(const char* pName, const LabelStmt* L) | 
|  | { | 
|  | addToMap(Labels, L, ID_LABEL); | 
|  | addAttribute(pName, getPrefixedId(Labels[L], ID_LABEL)); | 
|  | } | 
|  |  | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | 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::PrintDecl(Decl *D) | 
|  | { | 
|  | writeDeclToXML(D); | 
|  | } | 
|  |  | 
|  | //--------------------------------------------------------- | 
|  | } // NS clang | 
|  |  |