blob: 7562d2ae8714b4b06a10523426b12ac18c1e97e7 [file] [log] [blame]
Douglas Gregoree75c052009-05-21 20:55:50 +00001//===--- DocumentXML.cpp - XML document for ASTs --------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the XML document class, which provides the means to
11// dump out the AST in a XML form that exposes type details and other fields.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Frontend/DocumentXML.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/Expr.h"
19#include "clang/Basic/SourceManager.h"
20#include "llvm/ADT/StringExtras.h"
21
22namespace clang {
23
24//---------------------------------------------------------
25struct DocumentXML::NodeXML
26{
27 std::string Name;
28 NodeXML* Parent;
29
30 NodeXML(const std::string& name, NodeXML* parent) :
31 Name(name),
32 Parent(parent)
33 {}
34};
35
36//---------------------------------------------------------
37DocumentXML::DocumentXML(const std::string& rootName, llvm::raw_ostream& out) :
38 Root(new NodeXML(rootName, 0)),
39 CurrentNode(Root),
40 Out(out),
41 Ctx(0),
42 CurrentIndent(0),
43 HasCurrentNodeSubNodes(false)
44{
45 Out << "<?xml version=\"1.0\"?>\n<" << rootName;
46}
47
48//---------------------------------------------------------
49DocumentXML::~DocumentXML()
50{
51 assert(CurrentNode == Root && "not completely backtracked");
52 delete Root;
53}
54
55//---------------------------------------------------------
56DocumentXML& DocumentXML::addSubNode(const std::string& name)
57{
58 if (!HasCurrentNodeSubNodes)
59 {
60 Out << ">\n";
61 }
62 CurrentNode = new NodeXML(name, CurrentNode);
63 HasCurrentNodeSubNodes = false;
64 CurrentIndent += 2;
65 Indent();
66 Out << "<" << CurrentNode->Name;
67 return *this;
68}
69
70//---------------------------------------------------------
71void DocumentXML::Indent()
72{
73 for (int i = 0; i < CurrentIndent; ++i)
74 Out << ' ';
75}
76
77//---------------------------------------------------------
78DocumentXML& DocumentXML::toParent()
79{
80 assert(CurrentNode != Root && "to much backtracking");
81
82 if (HasCurrentNodeSubNodes)
83 {
84 Indent();
85 Out << "</" << CurrentNode->Name << ">\n";
86 }
87 else
88 {
89 Out << "/>\n";
90 }
91 NodeXML* NodeToDelete = CurrentNode;
92 CurrentNode = CurrentNode->Parent;
93 delete NodeToDelete;
94 HasCurrentNodeSubNodes = true;
95 CurrentIndent -= 2;
96 return *this;
97}
98
99//---------------------------------------------------------
100namespace {
101
102enum tIdType { ID_NORMAL, ID_FILE, ID_LAST };
103
104unsigned getNewId(tIdType idType)
105{
106 static unsigned int idCounts[ID_LAST] = { 0 };
107 return ++idCounts[idType];
108}
109
110//---------------------------------------------------------
111inline std::string getPrefixedId(unsigned uId, tIdType idType)
112{
113 static const char idPrefix[ID_LAST] = { '_', 'f' };
114 char buffer[20];
115 char* BufPtr = llvm::utohex_buffer(uId, buffer + 20);
116 *--BufPtr = idPrefix[idType];
117 return BufPtr;
118}
119
120//---------------------------------------------------------
121template<class T, class V>
122bool addToMap(T& idMap, const V& value, tIdType idType = ID_NORMAL)
123{
124 typename T::iterator i = idMap.find(value);
125 bool toAdd = i == idMap.end();
126 if (toAdd)
127 {
128 idMap.insert(typename T::value_type(value, getNewId(idType)));
129 }
130 return toAdd;
131}
132
133} // anon NS
134
135//---------------------------------------------------------
136std::string DocumentXML::escapeString(const char* pStr, std::string::size_type len)
137{
138 std::string value;
139 value.reserve(len + 1);
140 char buffer[16];
141 for (unsigned i = 0; i < len; ++i) {
142 switch (char C = pStr[i]) {
143 default:
144 if (isprint(C))
145 value += C;
146 else
147 {
148 sprintf(buffer, "\\%03o", C);
149 value += buffer;
150 }
151 break;
152
153 case '\n': value += "\\n"; break;
154 case '\t': value += "\\t"; break;
155 case '\a': value += "\\a"; break;
156 case '\b': value += "\\b"; break;
157 case '\r': value += "\\r"; break;
158
159 case '&': value += "&amp;"; break;
160 case '<': value += "&lt;"; break;
161 case '>': value += "&gt;"; break;
162 case '"': value += "&quot;"; break;
163 case '\'': value += "&apos;"; break;
164
165 }
166 }
167 return value;
168}
169
170//---------------------------------------------------------
171void DocumentXML::finalize()
172{
173 assert(CurrentNode == Root && "not completely backtracked");
174
175 addSubNode("ReferenceSection");
176 addSubNode("Types");
177
178 for (XML::IdMap<QualType>::iterator i = Types.begin(), e = Types.end(); i != e; ++i)
179 {
180 if (i->first.getCVRQualifiers() != 0)
181 {
182 addSubNode("CvQualifiedType");
183 addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
184 addAttribute("type", getPrefixedId(BasicTypes[i->first.getTypePtr()], ID_NORMAL));
185 if (i->first.isConstQualified()) addAttribute("const", "1");
186 if (i->first.isVolatileQualified()) addAttribute("volatile", "1");
187 if (i->first.isRestrictQualified()) addAttribute("restrict", "1");
188 toParent();
189 }
190 }
191
192 for (XML::IdMap<const Type*>::iterator i = BasicTypes.begin(), e = BasicTypes.end(); i != e; ++i)
193 {
194 // don't use the get methods as they strip of typedef infos
195 if (const BuiltinType *BT = dyn_cast<BuiltinType>(i->first)) {
196 addSubNode("FundamentalType");
Douglas Gregord249e1d1f2009-05-29 20:38:28 +0000197 addAttribute("name", BT->getName(Ctx->getLangOptions().CPlusPlus));
Douglas Gregoree75c052009-05-21 20:55:50 +0000198 }
199 else if (const PointerType *PT = dyn_cast<PointerType>(i->first)) {
200 addSubNode("PointerType");
201 addTypeAttribute(PT->getPointeeType());
202 }
203 else if (dyn_cast<FunctionType>(i->first) != 0) {
204 addSubNode("FunctionType");
205 }
206 else if (const ReferenceType *RT = dyn_cast<ReferenceType>(i->first)) {
207 addSubNode("ReferenceType");
208 addTypeAttribute(RT->getPointeeType());
209 }
210 else if (const TypedefType * TT = dyn_cast<TypedefType>(i->first)) {
211 addSubNode("Typedef");
212 addAttribute("name", TT->getDecl()->getNameAsString());
213 addTypeAttribute(TT->getDecl()->getUnderlyingType());
214 addContextAttribute(TT->getDecl()->getDeclContext());
215 }
216 else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(i->first)) {
217 addSubNode("QualifiedNameType");
218 addTypeAttribute(QT->getNamedType());
219 }
220 else if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(i->first)) {
221 addSubNode("ArrayType");
222 addAttribute("min", 0);
223 addAttribute("max", (CAT->getSize() - 1).toString(10, false));
224 addTypeAttribute(CAT->getElementType());
225 }
226 else if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(i->first)) {
227 addSubNode("VariableArrayType");
228 addTypeAttribute(VAT->getElementType());
229 }
230 else if (const TagType *RET = dyn_cast<TagType>(i->first)) {
231 const TagDecl *tagDecl = RET->getDecl();
232 std::string tagKind = tagDecl->getKindName();
233 tagKind[0] = std::toupper(tagKind[0]);
234 addSubNode(tagKind);
235 addAttribute("name", tagDecl->getNameAsString());
236 addContextAttribute(tagDecl->getDeclContext());
237 }
238 else if (const VectorType* VT = dyn_cast<VectorType>(i->first)) {
239 addSubNode("VectorType");
240 addTypeAttribute(VT->getElementType());
241 addAttribute("num_elements", VT->getNumElements());
242 }
243 else
244 {
245 addSubNode("FIXMEType");
246 }
247 addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
248 toParent();
249 }
250
251
252 toParent().addSubNode("Contexts");
253
254 for (XML::IdMap<const DeclContext*>::iterator i = Contexts.begin(), e = Contexts.end(); i != e; ++i)
255 {
256 addSubNode(i->first->getDeclKindName());
257 addAttribute("id", getPrefixedId(i->second, ID_NORMAL));
258 if (const NamedDecl *ND = dyn_cast<NamedDecl>(i->first)) {
259 addAttribute("name", ND->getNameAsString());
260 }
261 if (const TagDecl *TD = dyn_cast<TagDecl>(i->first)) {
262 addAttribute("type", getPrefixedId(BasicTypes[TD->getTypeForDecl()], ID_NORMAL));
263 }
264 else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(i->first)) {
265 addAttribute("type", getPrefixedId(BasicTypes[FD->getType()->getAsFunctionType()], ID_NORMAL));
266 }
267
268 if (const DeclContext* parent = i->first->getParent())
269 {
270 addContextAttribute(parent);
271 }
272 toParent();
273 }
274
275 toParent().addSubNode("Files");
276
277 for (XML::IdMap<std::string>::iterator i = SourceFiles.begin(), e = SourceFiles.end(); i != e; ++i)
278 {
279 addSubNode("File");
280 addAttribute("id", getPrefixedId(i->second, ID_FILE));
281 addAttribute("name", escapeString(i->first.c_str(), i->first.size()));
282 toParent();
283 }
284
285 toParent().toParent();
286
287 // write the root closing node (which has always subnodes)
288 Out << "</" << CurrentNode->Name << ">\n";
289}
290
291//---------------------------------------------------------
292void DocumentXML::addTypeAttribute(const QualType& pType)
293{
294 addTypeRecursively(pType);
295 addAttribute("type", getPrefixedId(Types[pType], ID_NORMAL));
296}
297
298//---------------------------------------------------------
299void DocumentXML::addTypeIdAttribute(const Type* pType)
300{
301 addBasicTypeRecursively(pType);
302 addAttribute("id", getPrefixedId(BasicTypes[pType], ID_NORMAL));
303}
304
305//---------------------------------------------------------
306void DocumentXML::addTypeRecursively(const QualType& pType)
307{
308 if (addToMap(Types, pType))
309 {
310 addBasicTypeRecursively(pType.getTypePtr());
311 // beautifier: a non-qualified type shall be transparent
312 if (pType.getCVRQualifiers() == 0)
313 {
314 Types[pType] = BasicTypes[pType.getTypePtr()];
315 }
316 }
317}
318
319//---------------------------------------------------------
320void DocumentXML::addBasicTypeRecursively(const Type* pType)
321{
322 if (addToMap(BasicTypes, pType))
323 {
324 if (const PointerType *PT = dyn_cast<PointerType>(pType)) {
325 addTypeRecursively(PT->getPointeeType());
326 }
327 else if (const ReferenceType *RT = dyn_cast<ReferenceType>(pType)) {
328 addTypeRecursively(RT->getPointeeType());
329 }
330 else if (const TypedefType *TT = dyn_cast<TypedefType>(pType)) {
331 addTypeRecursively(TT->getDecl()->getUnderlyingType());
332 addContextsRecursively(TT->getDecl()->getDeclContext());
333 }
334 else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(pType)) {
335 addTypeRecursively(QT->getNamedType());
336 // FIXME: what to do with NestedNameSpecifier or shall this type be transparent?
337 }
338 else if (const ArrayType *AT = dyn_cast<ArrayType>(pType)) {
339 addTypeRecursively(AT->getElementType());
340 // FIXME: doesn't work in the immediate streaming approach
341 /*if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(AT))
342 {
343 addSubNode("VariableArraySizeExpression");
344 PrintStmt(VAT->getSizeExpr());
345 toParent();
346 }*/
347 }
348 }
349}
350
351//---------------------------------------------------------
352void DocumentXML::addContextAttribute(const DeclContext *DC, tContextUsage usage)
353{
354 addContextsRecursively(DC);
355 const char* pAttributeTags[2] = { "context", "id" };
356 addAttribute(pAttributeTags[usage], getPrefixedId(Contexts[DC], ID_NORMAL));
357}
358
359//---------------------------------------------------------
360void DocumentXML::addContextsRecursively(const DeclContext *DC)
361{
362 if (DC != 0 && addToMap(Contexts, DC))
363 {
364 addContextsRecursively(DC->getParent());
365 }
366}
367
368//---------------------------------------------------------
369void DocumentXML::addSourceFileAttribute(const std::string& fileName)
370{
371 addToMap(SourceFiles, fileName, ID_FILE);
372 addAttribute("file", getPrefixedId(SourceFiles[fileName], ID_FILE));
373}
374
375//---------------------------------------------------------
376PresumedLoc DocumentXML::addLocation(const SourceLocation& Loc)
377{
378 SourceManager& SM = Ctx->getSourceManager();
379 SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
380 PresumedLoc PLoc;
381 if (!SpellingLoc.isInvalid())
382 {
383 PLoc = SM.getPresumedLoc(SpellingLoc);
384 addSourceFileAttribute(PLoc.getFilename());
385 addAttribute("line", PLoc.getLine());
386 addAttribute("col", PLoc.getColumn());
387 }
388 // else there is no error in some cases (eg. CXXThisExpr)
389 return PLoc;
390}
391
392//---------------------------------------------------------
393void DocumentXML::addLocationRange(const SourceRange& R)
394{
395 PresumedLoc PStartLoc = addLocation(R.getBegin());
396 if (R.getBegin() != R.getEnd())
397 {
398 SourceManager& SM = Ctx->getSourceManager();
399 SourceLocation SpellingLoc = SM.getSpellingLoc(R.getEnd());
400 if (!SpellingLoc.isInvalid())
401 {
402 PresumedLoc PLoc = SM.getPresumedLoc(SpellingLoc);
403 if (PStartLoc.isInvalid() ||
404 strcmp(PLoc.getFilename(), PStartLoc.getFilename()) != 0) {
405 addToMap(SourceFiles, PLoc.getFilename(), ID_FILE);
406 addAttribute("endfile", PLoc.getFilename());
407 addAttribute("endline", PLoc.getLine());
408 addAttribute("endcol", PLoc.getColumn());
409 } else if (PLoc.getLine() != PStartLoc.getLine()) {
410 addAttribute("endline", PLoc.getLine());
411 addAttribute("endcol", PLoc.getColumn());
412 } else {
413 addAttribute("endcol", PLoc.getColumn());
414 }
415 }
416 }
417}
418
419//---------------------------------------------------------
420void DocumentXML::PrintFunctionDecl(FunctionDecl *FD)
421{
422 switch (FD->getStorageClass()) {
423 default: assert(0 && "Unknown storage class");
424 case FunctionDecl::None: break;
425 case FunctionDecl::Extern: addAttribute("storage_class", "extern"); break;
426 case FunctionDecl::Static: addAttribute("storage_class", "static"); break;
427 case FunctionDecl::PrivateExtern: addAttribute("storage_class", "__private_extern__"); break;
428 }
429
430 if (FD->isInline())
431 addAttribute("inline", "1");
432
433 const FunctionType *AFT = FD->getType()->getAsFunctionType();
434 addTypeAttribute(AFT->getResultType());
435 addBasicTypeRecursively(AFT);
436
437 if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(AFT)) {
438 addAttribute("num_args", FD->getNumParams());
439 for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) {
440 addSubNode("Argument");
441 ParmVarDecl *argDecl = FD->getParamDecl(i);
442 addAttribute("name", argDecl->getNameAsString());
443 addTypeAttribute(FT->getArgType(i));
444 addDeclIdAttribute(argDecl);
445 if (argDecl->getDefaultArg())
446 {
447 addAttribute("default_arg", "1");
448 PrintStmt(argDecl->getDefaultArg());
449 }
450 toParent();
451 }
452
453 if (FT->isVariadic()) {
454 addSubNode("Ellipsis").toParent();
455 }
456 } else {
457 assert(isa<FunctionNoProtoType>(AFT));
458 }
459}
460
461//---------------------------------------------------------
462void DocumentXML::addRefAttribute(const NamedDecl* D)
463{
464 // FIXME: in case of CXX inline member functions referring to a member defined
465 // after the function it needs to be tested, if the ids are already there
466 // (should work, but I couldn't test it)
467 if (const DeclContext* DC = dyn_cast<DeclContext>(D))
468 {
469 addAttribute("ref", getPrefixedId(Contexts[DC], ID_NORMAL));
470 }
471 else
472 {
473 addAttribute("ref", getPrefixedId(Decls[D], ID_NORMAL));
474 }
475}
476
477//---------------------------------------------------------
478void DocumentXML::addDeclIdAttribute(const NamedDecl* D)
479{
480 addToMap(Decls, D);
481 addAttribute("id", getPrefixedId(Decls[D], ID_NORMAL));
482}
483
484//---------------------------------------------------------
485void DocumentXML::PrintDecl(Decl *D)
486{
487 addSubNode(D->getDeclKindName());
488 addContextAttribute(D->getDeclContext());
489 addLocation(D->getLocation());
490 if (DeclContext* DC = dyn_cast<DeclContext>(D))
491 {
492 addContextAttribute(DC, CONTEXT_AS_ID);
493 }
494
495 if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
496 addAttribute("name", ND->getNameAsString());
497
498 if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
499 PrintFunctionDecl(FD);
500 if (Stmt *Body = FD->getBody(*Ctx)) {
501 addSubNode("Body");
502 PrintStmt(Body);
503 toParent();
504 }
505 } else if (RecordDecl *RD = dyn_cast<RecordDecl>(D)) {
506 addBasicTypeRecursively(RD->getTypeForDecl());
507 addAttribute("type", getPrefixedId(BasicTypes[RD->getTypeForDecl()], ID_NORMAL));
508 if (!RD->isDefinition())
509 {
510 addAttribute("forward", "1");
511 }
512
513 for (RecordDecl::field_iterator i = RD->field_begin(*Ctx), e = RD->field_end(*Ctx); i != e; ++i)
514 {
515 PrintDecl(*i);
516 }
517 } else if (EnumDecl *ED = dyn_cast<EnumDecl>(D)) {
518 const QualType& enumType = ED->getIntegerType();
519 if (!enumType.isNull())
520 {
521 addTypeAttribute(enumType);
522 for (EnumDecl::enumerator_iterator i = ED->enumerator_begin(*Ctx), e = ED->enumerator_end(*Ctx); i != e; ++i)
523 {
524 PrintDecl(*i);
525 }
526 }
527 } else if (EnumConstantDecl* ECD = dyn_cast<EnumConstantDecl>(D)) {
528 addTypeAttribute(ECD->getType());
529 addAttribute("value", ECD->getInitVal().toString(10, true));
530 if (ECD->getInitExpr())
531 {
532 PrintStmt(ECD->getInitExpr());
533 }
534 } else if (FieldDecl *FdD = dyn_cast<FieldDecl>(D)) {
535 addTypeAttribute(FdD->getType());
536 addDeclIdAttribute(ND);
537 if (FdD->isMutable())
538 addAttribute("mutable", "1");
539 if (FdD->isBitField())
540 {
541 addAttribute("bitfield", "1");
542 PrintStmt(FdD->getBitWidth());
543 }
544 } else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
545 addTypeIdAttribute(Ctx->getTypedefType(TD).getTypePtr());
546 addTypeAttribute(TD->getUnderlyingType());
547 } else if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) {
548 addTypeAttribute(VD->getType());
549 addDeclIdAttribute(ND);
550
551 VarDecl *V = dyn_cast<VarDecl>(VD);
552 if (V && V->getStorageClass() != VarDecl::None)
553 {
554 addAttribute("storage_class", VarDecl::getStorageClassSpecifierString(V->getStorageClass()));
555 }
556
557 if (V && V->getInit())
558 {
559 PrintStmt(V->getInit());
560 }
561 }
562 } else if (LinkageSpecDecl* LSD = dyn_cast<LinkageSpecDecl>(D)) {
563 switch (LSD->getLanguage())
564 {
565 case LinkageSpecDecl::lang_c: addAttribute("lang", "C"); break;
566 case LinkageSpecDecl::lang_cxx: addAttribute("lang", "CXX"); break;
567 default: assert(0 && "Unexpected lang id");
568 }
Douglas Gregor4fe0c8e2009-05-30 00:08:05 +0000569 } else if (isa<FileScopeAsmDecl>(D)) {
570 // FIXME: Implement this
Douglas Gregoree75c052009-05-21 20:55:50 +0000571 } else {
572 assert(0 && "Unexpected decl");
573 }
574 toParent();
575}
576
577//---------------------------------------------------------
578} // NS clang
579