├── LICENSE.txt ├── Makefile ├── README.txt ├── extract.cpp ├── extract.h ├── main.cpp └── test1.h /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | 1. Redistributions of source code must retain the above copyright notice, this 5 | list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, 8 | this list of conditions and the following disclaimer in the documentation 9 | and/or other materials provided with the distribution. 10 | 11 | 3. The name of the author may not be used to endorse or promote products 12 | derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 19 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 23 | OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CONFIG := Release 3 | CONFIG := Debug 4 | LLVM_DIR ?= /PATH/TO/LLVM/ 5 | 6 | EXENAME := ./clang-extract.$(CONFIG) 7 | 8 | CXXFLAGS := -I$(LLVM_DIR)/include -I$(LLVM_DIR)/tools/clang/include \ 9 | -DNDEBUG -D_GNU_SOURCE -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS \ 10 | -pedantic -fomit-frame-pointer -fno-exceptions -fno-rtti -fPIC -fno-strict-aliasing \ 11 | -W -Wall -Woverloaded-virtual -Wcast-qual -Wno-long-long -Wno-unused-parameter -Wwrite-strings \ 12 | -Wno-variadic-macros -Wno-reorder -Wno-trigraphs -Wno-unknown-pragmas -Wno-unused 13 | LDFLAGS := -L$(LLVM_DIR)/$(CONFIG)/lib 14 | LIBS := -lclangFrontend -lclangSerialization -lclangParse -lclangSema -lclangAnalysis -lclangAST \ 15 | -lclangLex -lclangBasic -lLLVMMC -lLLVMCore -lLLVMSupport -lpthread -ldl -lm 16 | 17 | ifeq ($(CONFIG),"Debug") 18 | CXXFLAGS += -DDEBUG -g 19 | endif 20 | ifeq ($(CONFIG),"Release") 21 | CXXFLAGS += -O3 22 | endif 23 | 24 | SRCS := extract.cpp main.cpp 25 | $(EXENAME) : $(SRCS) extract.h Makefile 26 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(SRCS) $(LIBS) 27 | 28 | test : test1.h #$(EXENAME) 29 | $(EXENAME) -I . test1.h -o test.out 30 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | UPDATE Oct-2013: 3 | ============= 4 | 5 | Originally we had chose python as the main language for this project because it was a natural fit 6 | for text processing. However, we eventually reached a point where the simplified output wasn't 7 | powerful enough to handle all the nuances of templates and specialization. Since then, we have 8 | ported our scripts to C++ so that they can access the Clang AST directly. 9 | 10 | The C++ version is noticably faster and allows full clang AST access. This python version allows 11 | rapid iteration and is more suitable for experimentation. 12 | 13 | 14 | 15 | Clang-extract 16 | ============= 17 | 18 | 19 | Clang-extract uses clang to parse your header files and then prints out a description of what it parsed. 20 | 21 | This project was described in a talk a GDC 2012 http://www.gdcvault.com/play/1015586/ (subscription required) 22 | It was funded by Havok and is used as the basis of its reflection system. 23 | 24 | 25 | Building 26 | -------- 27 | * Either in your environment or in the Makefile, set LLVM_DIR to the folder containing a prebuilt LLVM and Clang. 28 | * make 29 | 30 | 31 | Test 32 | -------- 33 | To run clang-extract on a small test: 34 | * make test 35 | 36 | You'll notice the output format is actually python code so you can parse it thus : 37 | def parse(text): 38 | output = # your output structure 39 | def Method(id, recordid, typeid, name, static): 40 | # code here for a method, modifids output 41 | # more local function definitions here 42 | exec text in locals() 43 | return output 44 | 45 | 46 | Invoking 47 | -------- 48 | Run clang-extract --help to see command line options. 49 | 50 | 51 | Notes 52 | -------- 53 | clang-extract internally creates a file which includes all the input files specified on the command line. 54 | You may need to add "-I ." to find the input files. 55 | The -A option is useful to pass through annotations which are stored in the output file. 56 | -------------------------------------------------------------------------------- /extract.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2012 Havok. All rights reserved. This file is distributed under the terms 3 | // and conditions defined in file 'LICENSE.txt', which is part of this source code package. 4 | 5 | #include "extract.h" 6 | #include 7 | 8 | #pragma warning(push,0) 9 | #include 10 | #include 11 | #pragma warning(pop) 12 | 13 | using namespace clang; 14 | 15 | #define printf poisoned 16 | 17 | // ----------------------- Static Utility Functions ------------------------- // 18 | 19 | static SourceLocation s_getExpansionLoc(const SourceLocation& loc, SourceManager& sm) 20 | { 21 | assert(loc.isValid() && "invalid location for declaration"); 22 | if (loc.isFileID()) { 23 | return loc; 24 | } 25 | 26 | SourceLocation expLoc = sm.getExpansionLoc(loc); 27 | return s_getExpansionLoc(expLoc, sm); 28 | } 29 | 30 | static const char* s_getFileName(std::string& buf, const SourceLocation& loc, SourceManager& sm) 31 | { 32 | assert(loc.isFileID() && "not a file location"); 33 | PresumedLoc pLoc = sm.getPresumedLoc(loc); 34 | assert(pLoc.isValid() && "invalid presumed location for declaration"); 35 | 36 | buf = pLoc.getFilename(); 37 | for( std::string::iterator it = buf.begin(), end = buf.end(); it != end; ++ it) 38 | { 39 | if(*it=='\\') *it = '/'; 40 | } 41 | return buf.c_str(); 42 | } 43 | 44 | static FileID s_getFileId(const SourceLocation& loc, SourceManager& sm) 45 | { 46 | assert(loc.isFileID() && "not a file location"); 47 | FileID ret = sm.getFileID(loc); 48 | assert(!ret.isInvalid() && "invalid file id for declaration"); 49 | 50 | return ret; 51 | } 52 | 53 | static void s_printName(llvm::raw_ostream& os, const NamedDecl* decl) 54 | { 55 | os << ", name='"; 56 | decl->printName(os); 57 | os << "'"; 58 | } 59 | 60 | static void s_printRecordFlags(llvm::raw_ostream& os, const CXXRecordDecl* decl) 61 | { 62 | bool isPolymorphic = false; 63 | bool isAbstract = false; 64 | if(decl->hasDefinition()) 65 | { 66 | // the class contains or inherits a virtual function 67 | isPolymorphic = decl->isPolymorphic(); 68 | // the class contains or inherits a pure virtual function 69 | isAbstract = decl->isAbstract(); 70 | } 71 | os << ", polymorphic=" << (isPolymorphic ? "True" : "False"); 72 | os << ", abstract=" << (isAbstract ? "True" : "False"); 73 | } 74 | 75 | inline static void s_printDefaultRecordFlags(llvm::raw_ostream& os) 76 | { 77 | os << ", polymorphic=False, abstract=False"; 78 | } 79 | 80 | static void s_printAnnotations(llvm::raw_ostream& os, const Decl* decl, int declId) 81 | { 82 | if(decl->hasAttr()) 83 | { 84 | const AttrVec& attrVec = decl->getAttrs(); 85 | for( specific_attr_iterator iterator = specific_attr_begin(attrVec), end_iterator = specific_attr_end(attrVec); 86 | iterator != end_iterator; 87 | ++iterator ) 88 | { 89 | os << "Annotation( refid=" << declId << ", text=\"\"\""; 90 | os << iterator->getAnnotation(); 91 | os << "\"\"\" )\n"; 92 | } 93 | } 94 | } 95 | 96 | static const Type* s_getTrueType(const Type* type) 97 | { 98 | if(const SubstTemplateTypeParmType* substType = type->getAs()) 99 | { 100 | type = substType->getReplacementType().getTypePtr(); 101 | } 102 | if(const ElaboratedType* elabType = type->getAs()) 103 | { 104 | return elabType->getNamedType().getTypePtr(); 105 | } 106 | 107 | return type; 108 | } 109 | 110 | static const ClassTemplateDecl* s_getClassTemplateDefinition(const ClassTemplateDecl* classTemplateDecl) 111 | { 112 | const ClassTemplateDecl* current = NULL; 113 | for( ClassTemplateDecl::redecl_iterator it = classTemplateDecl->redecls_begin(); 114 | it != classTemplateDecl->redecls_end(); 115 | ++it ) 116 | { 117 | current = dyn_cast(*it); 118 | assert(current && "invalid redeclaration of the class template declaration"); 119 | if(current->isThisDeclarationADefinition()) 120 | return current; 121 | } 122 | return NULL; 123 | } 124 | 125 | // ------------------- ExtractASTConsumer Implementation -------------------- // 126 | 127 | // Initialize the database object with its global state. Each consumer object is only expected to be used once 128 | Havok::ExtractASTConsumer::ExtractASTConsumer(llvm::raw_ostream& os) 129 | : m_context(0), m_sema(0), m_os(os), m_dumpBits( DUMP_DEFAULT /*DUMP_FUNCTIONS*/ ) 130 | { 131 | } 132 | 133 | Havok::ExtractASTConsumer::~ExtractASTConsumer() 134 | { 135 | } 136 | 137 | // Called by the parser at the start of the process 138 | void Havok::ExtractASTConsumer::Initialize(ASTContext &Context) 139 | { 140 | // Remember the context so we can use it to find file names and locations 141 | m_context = &Context; 142 | } 143 | 144 | void Havok::ExtractASTConsumer::InitializeSema(Sema& sema) 145 | { 146 | // Remember the sema instance so we can use it to perform semantic analysis 147 | m_sema = &sema; 148 | } 149 | 150 | void Havok::ExtractASTConsumer::declareImplicitMethods(Decl* declIn) 151 | { 152 | if( !isa(declIn) && 153 | !isa(declIn) ) 154 | return; 155 | 156 | if( NamespaceDecl* namespaceDecl = dyn_cast(declIn) ) 157 | { 158 | DeclContext::decl_iterator it; 159 | for(it = namespaceDecl->decls_begin(); it != namespaceDecl->decls_end(); ++it) 160 | { 161 | declareImplicitMethods(*it); 162 | } 163 | } 164 | else // it must be a CXXRecordDecl 165 | { 166 | CXXRecordDecl* recordDecl = dyn_cast(declIn); 167 | assert(recordDecl && "The declaration should be a record declaration."); 168 | 169 | if(!recordDecl->isCompleteDefinition()) 170 | return; 171 | 172 | m_sema->ForceDeclarationOfImplicitMembers(recordDecl); 173 | 174 | typedef CXXRecordDecl::specific_decl_iterator NestedRecordIterator; 175 | for( NestedRecordIterator fi(recordDecl->decls_begin()), fe(recordDecl->decls_end()); fi != fe; ++fi ) 176 | { 177 | declareImplicitMethods( *fi ); 178 | } 179 | } 180 | } 181 | 182 | // Called by the parser for each top-level declaration encountered. 183 | // This includes top-level classes, namespaces, typedefs, constants, enums, functions 184 | // in every included header file. Contained elements are children of a top-level element. 185 | // Every element that is parsed is in here somewhere 186 | void Havok::ExtractASTConsumer::HandleTopLevelDecl(DeclGroupRef declGroupIn) 187 | { 188 | // If there are multiple declarations with the same semantic type in the same scope they are 189 | // returned as a group. [ e.g. class A { ... } B; ] Usually each group only contains one declaration. 190 | for (DeclGroupRef::iterator iter = declGroupIn.begin(), iterEnd = declGroupIn.end(); iter != iterEnd; ++iter) 191 | { 192 | declareImplicitMethods(*iter); 193 | m_decls.push_back(*iter); 194 | } 195 | } 196 | 197 | void Havok::ExtractASTConsumer::dumpAllDeclarations() 198 | { 199 | DumpEntry::dumpDefaultEntries(m_os); 200 | 201 | for( std::list::const_iterator it = m_decls.begin(); 202 | it != m_decls.end(); 203 | ++it ) 204 | { 205 | dumpDecl_i(*it); 206 | } 207 | } 208 | 209 | 210 | static const char* entryNames[] = 211 | { 212 | "Method", 213 | "Constructor", 214 | "Destructor", 215 | "Field", 216 | NULL 217 | }; 218 | 219 | struct KeyValuePair 220 | { 221 | const char* m_key; 222 | const char* m_value; 223 | }; 224 | 225 | static const KeyValuePair methodDefaults[] = 226 | { 227 | {"static", "False"}, 228 | {"const", "False"}, 229 | {"isCopyAssignment", "False"}, 230 | {"isImplicit", "False"}, 231 | {"access", "\"public\""}, 232 | {"numParamDefaults", "0"}, 233 | {NULL, NULL} 234 | }; 235 | 236 | static const KeyValuePair constructorDefaults[] = 237 | { 238 | {"static", "False"}, 239 | {"const", "False"}, 240 | {"isCopyAssignment", "False"}, 241 | {"isImplicit", "False"}, 242 | {"isCopyConstructor", "False"}, 243 | {"isDefaultConstructor", "False"}, 244 | {"access", "\"public\""}, 245 | {"numParamDefaults", "0"}, 246 | {NULL, NULL} 247 | }; 248 | 249 | static const KeyValuePair* destructorDefaults = methodDefaults; 250 | 251 | static const KeyValuePair fieldDefaults[] = 252 | { 253 | {"access", "\"public\""}, 254 | {NULL, NULL} 255 | }; 256 | 257 | 258 | static const KeyValuePair* defaults[] = 259 | { 260 | methodDefaults, 261 | constructorDefaults, 262 | destructorDefaults, 263 | fieldDefaults, 264 | NULL, 265 | }; 266 | 267 | Havok::ExtractASTConsumer::DumpEntry::DumpEntry(llvm::raw_ostream& os, EntryType et) 268 | : m_entryType(et) 269 | , m_hasOutputKeyValuePair(false) 270 | , m_os(os) 271 | { 272 | assert(0 <= et); 273 | assert(et < NUM_ENTRIES); 274 | // Check the entry names array has the correct size. 275 | assert((sizeof(entryNames) / sizeof(const char*)) == NUM_ENTRIES + 1); 276 | assert((sizeof(defaults) / sizeof(const KeyValuePair*)) == NUM_ENTRIES + 1); 277 | m_os << entryNames[et] << "( "; 278 | } 279 | 280 | void Havok::ExtractASTConsumer::DumpEntry::checkOutputComma() 281 | { 282 | if (m_hasOutputKeyValuePair) 283 | { 284 | m_os << ", "; 285 | } 286 | m_hasOutputKeyValuePair = true; 287 | } 288 | 289 | void Havok::ExtractASTConsumer::DumpEntry::finishEntry() 290 | { 291 | m_os << " )\n"; 292 | } 293 | 294 | void Havok::ExtractASTConsumer::DumpEntry::dumpKeyValuePair(const char* key, const char* val) 295 | { 296 | const KeyValuePair* kvpairs = defaults[m_entryType]; 297 | if (kvpairs != NULL) 298 | { 299 | while (kvpairs->m_key != NULL) 300 | { 301 | if (strcmp(kvpairs->m_key, key) == 0) 302 | { 303 | if (strcmp(kvpairs->m_value, val) == 0) 304 | { 305 | return; 306 | } 307 | else 308 | { 309 | break; 310 | } 311 | } 312 | ++kvpairs; 313 | } 314 | } 315 | checkOutputComma(); 316 | m_os << key << "=" << val; 317 | } 318 | 319 | void Havok::ExtractASTConsumer::DumpEntry::dumpKeyValuePair(const char* key, int i) 320 | { 321 | char buf[10]; 322 | snprintf(buf, sizeof(buf), "%d", i); 323 | dumpKeyValuePair(key, buf); 324 | } 325 | 326 | void Havok::ExtractASTConsumer::DumpEntry::dumpKeyValuePair(const char* key, bool b) 327 | { 328 | dumpKeyValuePair(key, (b ? "True" : "False")); 329 | } 330 | 331 | void Havok::ExtractASTConsumer::DumpEntry::dumpKeyValuePair(const char* key, AccessSpecifier as) 332 | { 333 | switch(as) 334 | { 335 | case AS_private: 336 | dumpKeyValuePair(key, "\"private\""); 337 | break; 338 | case AS_protected: 339 | dumpKeyValuePair(key, "\"protected\""); 340 | break; 341 | case AS_public: 342 | dumpKeyValuePair(key, "\"public\""); 343 | break; 344 | default: 345 | assert(false); 346 | } 347 | } 348 | 349 | void Havok::ExtractASTConsumer::DumpEntry::dumpDefaultEntries(llvm::raw_ostream& os) 350 | { 351 | for (int i = 0; i < NUM_ENTRIES; ++i) 352 | { 353 | os << "DefaultsFor" << entryNames[i] << "( "; 354 | const KeyValuePair* kvpairs = defaults[i]; 355 | if (kvpairs->m_key != NULL) 356 | { 357 | os << kvpairs->m_key << "=" << kvpairs->m_value; 358 | ++kvpairs; 359 | } 360 | while (kvpairs->m_key != NULL) 361 | { 362 | os << ", " << kvpairs->m_key << "=" << kvpairs->m_value; 363 | ++kvpairs; 364 | } 365 | os << " )\n"; 366 | } 367 | } 368 | 369 | int Havok::ExtractASTConsumer::dumpDecl_i(const Decl* declIn) 370 | { 371 | if( dyn_cast(declIn) ) 372 | { 373 | // Skipped silently 374 | } 375 | else if ( const ClassTemplateSpecializationDecl* classTemplateSpecializationDecl = dyn_cast(declIn)) 376 | { 377 | dumpTemplateClassSpecialization_i(classTemplateSpecializationDecl); 378 | } 379 | else if( const ClassTemplateDecl* classTemplateDecl = dyn_cast(declIn) ) 380 | { 381 | // templates do not represent types, but we dump a representation for the template itself 382 | // (and not for its instances). 383 | dumpTemplateClass_i(classTemplateDecl); 384 | } 385 | else if( const TagDecl* tagDecl = dyn_cast(declIn) ) 386 | { 387 | const TagDecl* tagDef = tagDecl->getDefinition(); 388 | int scopeId = dumpScope_i(tagDef != NULL ? tagDef : tagDecl); 389 | int typeId = dumpType_i(m_context->getTagDeclType(tagDecl), scopeId); 390 | if( tagDecl->isCompleteDefinition() ) 391 | { 392 | s_printAnnotations(m_os, tagDecl, typeId); 393 | dumpTagDefinition_i(tagDecl, typeId); 394 | } 395 | } 396 | else if( const TypedefDecl* typedefDecl = dyn_cast(declIn) ) 397 | { 398 | int scopeId = dumpScope_i(typedefDecl); 399 | dumpType_i(m_context->getTypedefType(typedefDecl), scopeId); 400 | } 401 | else if( const FieldDecl* fieldDecl = dyn_cast(declIn) ) 402 | { 403 | int tid = dumpType_i( fieldDecl->getType() ); 404 | // the containing record type has already been dumped 405 | int recId = getTypeId_i( m_context->getRecordType(fieldDecl->getParent()).getTypePtr() ); 406 | int fieldId = m_uid.alloc(); 407 | DumpEntry e(m_os, DumpEntry::ENTRY_FIELD); 408 | e.dumpKeyValuePair("id", fieldId); 409 | e.dumpKeyValuePair("recordid", recId); 410 | e.dumpKeyValuePair("typeid", tid); 411 | e.dumpKeyValuePair("access", fieldDecl->getAccess()); 412 | s_printName(m_os, fieldDecl); 413 | e.finishEntry(); 414 | s_printAnnotations(m_os, fieldDecl, fieldId); 415 | } 416 | else if( const CXXMethodDecl* methodDecl = dyn_cast(declIn) ) 417 | { 418 | // only handling non-template functions 419 | FunctionDecl::TemplatedKind tk = methodDecl->getTemplatedKind(); 420 | const CXXRecordDecl* parentRecord = methodDecl->getParent(); 421 | const ClassTemplateDecl* templateRecord = parentRecord->getDescribedClassTemplate(); 422 | // only dumping information for non-template functions of non-template records or 423 | // non-template functions of template records (but not non-template functions 424 | // of template record specializations or instantiations). 425 | if( methodDecl == methodDecl->getFirstDeclaration() && 426 | tk == FunctionDecl::TK_NonTemplate && // non template function 427 | ( templateRecord != NULL || // function from a template class declaration 428 | !isa(parentRecord) ) ) // not a template instantiation or specialization 429 | { 430 | int tid = dumpType_i( methodDecl->getType() ); 431 | // the containing record type has already been dumped 432 | int recId = getTypeId_i( m_context->getRecordType(parentRecord).getTypePtr() ); 433 | int methodId = m_uid.alloc(); 434 | 435 | const CXXConstructorDecl* constructorDecl = dyn_cast(methodDecl); 436 | const CXXDestructorDecl* destructorDecl = dyn_cast(methodDecl); 437 | DumpEntry::EntryType et; 438 | 439 | if (constructorDecl) 440 | { 441 | et = DumpEntry::ENTRY_CONSTRUCTOR; 442 | } 443 | else if (destructorDecl) 444 | { 445 | et = DumpEntry::ENTRY_DESTRUCTOR; 446 | } 447 | else 448 | { 449 | et = DumpEntry::ENTRY_METHOD; 450 | } 451 | 452 | DumpEntry e(m_os, et); 453 | 454 | e.dumpKeyValuePair("id", methodId); 455 | e.dumpKeyValuePair("recordid", recId); 456 | e.dumpKeyValuePair("typeid", tid); 457 | e.dumpKeyValuePair("static", methodDecl->isStatic()); 458 | e.dumpKeyValuePair("const", (bool)((methodDecl->getTypeQualifiers() & Qualifiers::Const))); 459 | { 460 | // Determine if this was explicitly declared by user, or auto-generated. 461 | bool implicitlyDeclared = false; 462 | bool defaultConstructor = false; 463 | bool copyConstructor = false; 464 | bool copyAssignment = false; 465 | 466 | if ( constructorDecl ) 467 | { 468 | if (constructorDecl->isCopyConstructor()) 469 | { 470 | copyConstructor = true; 471 | if (!parentRecord->hasUserDeclaredCopyConstructor()) 472 | { 473 | implicitlyDeclared = true; 474 | } 475 | } 476 | // Any user declared constructor suppresses the auto-generation of a default constructor. 477 | else if (constructorDecl->isDefaultConstructor()) 478 | { 479 | defaultConstructor = true; 480 | if (!parentRecord->hasUserDeclaredConstructor()) 481 | { 482 | implicitlyDeclared = true; 483 | } 484 | } 485 | e.dumpKeyValuePair("isDefaultConstructor", defaultConstructor); 486 | e.dumpKeyValuePair("isCopyConstructor", copyConstructor); 487 | } 488 | else if ( destructorDecl ) 489 | { 490 | if (!parentRecord->hasUserDeclaredDestructor()) 491 | { 492 | implicitlyDeclared = true; 493 | } 494 | } 495 | else if (methodDecl->isCopyAssignmentOperator()) 496 | { 497 | copyAssignment = true; 498 | if(!parentRecord->hasUserDeclaredCopyAssignment()) 499 | { 500 | implicitlyDeclared = true; 501 | } 502 | } 503 | e.dumpKeyValuePair("isCopyAssignment", copyAssignment); 504 | e.dumpKeyValuePair("isImplicit", implicitlyDeclared); 505 | } 506 | e.dumpKeyValuePair("access", methodDecl->getAccess()); 507 | // We use this value as it has a more obvious default. 508 | e.dumpKeyValuePair("numParamDefaults", (int)(methodDecl->getNumParams() - methodDecl->getMinRequiredArguments())); 509 | s_printName(m_os, methodDecl); 510 | e.finishEntry(); 511 | s_printAnnotations(m_os, methodDecl, methodId); 512 | } 513 | } 514 | else if( const EnumConstantDecl* enumConstantDecl = dyn_cast(declIn) ) 515 | { 516 | // the enum has already been dumped 517 | int enumId = getTypeId_i( enumConstantDecl->getType().getTypePtr() ); 518 | m_os << "EnumConstant( enumId=" << enumId; 519 | s_printName(m_os, enumConstantDecl); 520 | m_os << ", value='" << enumConstantDecl->getInitVal() << "' )\n"; 521 | s_printAnnotations(m_os, enumConstantDecl, enumId); 522 | } 523 | else if( const NamespaceDecl* namespaceDecl = dyn_cast(declIn) ) 524 | { 525 | // namespaces are not typed declarations, they have to be treated differently 526 | dumpNamespace_i(namespaceDecl); 527 | } 528 | else if( const VarDecl* varDecl = dyn_cast(declIn) ) 529 | { 530 | if( varDecl->isStaticDataMember() ) 531 | { 532 | int typeId = dumpType_i(varDecl->getType()); 533 | const RecordDecl* recordDecl = dyn_cast(varDecl->getDeclContext()); 534 | assert(recordDecl && "static member is not in a record declaration"); 535 | int recordId = getTypeId_i(m_context->getRecordType(recordDecl).getTypePtr()); 536 | int fieldId = m_uid.alloc(); 537 | m_os << "StaticField( id=" << fieldId << ", recordid=" << recordId << ", typeid=" << typeId; 538 | s_printName(m_os, varDecl); 539 | m_os << " )\n"; 540 | s_printAnnotations(m_os, varDecl, fieldId); 541 | } 542 | } 543 | else if( m_dumpBits & DUMP_VERBOSE ) 544 | { 545 | m_os << "### Skipped " << declIn->getDeclKindName(); 546 | if( const NamedDecl* nd = dyn_cast(declIn) ) 547 | { 548 | s_printName(m_os, nd); 549 | } 550 | m_os << '\n'; 551 | } 552 | return -1; 553 | } 554 | 555 | int Havok::ExtractASTConsumer::dumpType_i(QualType qualTypeIn, int scopeId) 556 | { 557 | const Type *const typeIn = qualTypeIn.getTypePtr(); 558 | int id = dumpNonQualifiedType_i(typeIn, scopeId); 559 | 560 | if (qualTypeIn.getQualifiers() & Qualifiers::Const) 561 | { 562 | int retId = findConstTypeId_i(id); 563 | if (retId == -1) 564 | { 565 | retId = m_uid.alloc(); 566 | m_constTypeIdMap[id] = retId; 567 | m_os << "ConstType( id=" << retId << ", typeid=" << id << ")\n"; 568 | } 569 | id = retId; 570 | } 571 | return id; 572 | } 573 | 574 | int Havok::ExtractASTConsumer::dumpNonQualifiedType_i(const Type* typeIn, int scopeId) 575 | { 576 | assert(typeIn && "invalid type specified"); 577 | 578 | // Dump any name specifiers used to resolve this type (eg. A::B::Type) 579 | // Not all preceding types might have been dumped correctly, in case 580 | // of a template we might refer to an instance which has never been encountered 581 | // before, and now we are referring to a nested type: 582 | // 583 | // template class C 584 | // { 585 | // Struct S {}; 586 | // }; 587 | // class D 588 | // { 589 | // C::S m_s; 590 | // }; 591 | dumpTypeSpecifiers_i(typeIn); 592 | 593 | return dumpNonQualifiedSimpleType_i(typeIn, scopeId); 594 | } 595 | 596 | 597 | int Havok::ExtractASTConsumer::dumpSimpleType_i(QualType qualTypeIn, int scopeId) 598 | { 599 | const Type *const typeIn = qualTypeIn.getTypePtr(); 600 | int id = dumpNonQualifiedSimpleType_i(typeIn, scopeId); 601 | 602 | if (qualTypeIn.getQualifiers() & Qualifiers::Const) 603 | { 604 | int retId = findConstTypeId_i(id); 605 | if (retId == -1) 606 | { 607 | retId = m_uid.alloc(); 608 | m_constTypeIdMap[id] = retId; 609 | m_os << "ConstType( id=" << retId << ", typeid=" << id << ")\n"; 610 | } 611 | id = retId; 612 | } 613 | return id; 614 | } 615 | 616 | int Havok::ExtractASTConsumer::dumpNonQualifiedSimpleType_i(const Type* typeIn, int scopeId) 617 | { 618 | 619 | // clean the type from any sugar used to specify it in source code (elaborated types) 620 | typeIn = s_getTrueType(typeIn); 621 | 622 | { 623 | const TemplateSpecializationType* templateSpecializationType = typeIn->getAs(); 624 | if( templateSpecializationType && 625 | (typeIn->getAs() == NULL) ) // not a typedef to a template specialization type 626 | { 627 | // this type is dumped in case of an explicit instantiation when this function 628 | // is called from dumpTemplateClassSpecialization_i(), or in case of an 629 | // implicit instantiation when this function is called in a generic way to 630 | // dump a needed type. 631 | 632 | return dumpTemplateInstantiationType_i(templateSpecializationType); 633 | } 634 | } 635 | 636 | // seen this type before? 637 | int retId = findTypeId_i(typeIn); 638 | if(retId != -1) 639 | { 640 | return retId; 641 | } 642 | 643 | if( const TypedefType* bt = typeIn->getAs() ) 644 | { 645 | // sometimes TypedefTypes can also be casted to InjectedClassNameTypes, 646 | // for this reason we need to handle this first. 647 | int tid = dumpType_i(bt->getDecl()->getUnderlyingType()); 648 | retId = m_uid.alloc(); 649 | m_os << "TypedefType( id=" << retId << ", typeid=" << tid; 650 | s_printName(m_os, bt->getDecl()); 651 | } 652 | else if( const TemplateTypeParmType* bt = typeIn->getAs() ) 653 | { 654 | // skipped, this is treated specially after calling this function 655 | } 656 | else if( const BuiltinType* bt = typeIn->getAs() ) 657 | { 658 | retId = m_uid.alloc(); 659 | m_os << "BuiltinType( id=" << retId << ", name='" << bt->getName(m_context->getPrintingPolicy()) << "'"; 660 | } 661 | else if( const PointerType* bt = typeIn->getAs() ) 662 | { 663 | int pt = dumpType_i(bt->getPointeeType()); 664 | retId = m_uid.alloc(); 665 | m_os << "PointerType( id=" << retId << ", typeid=" << pt; 666 | } 667 | else if( const ReferenceType* bt = typeIn->getAs() ) 668 | { 669 | int pt = dumpType_i(bt->getPointeeType()); 670 | retId = m_uid.alloc(); 671 | m_os << "ReferenceType( id=" << retId << ", typeid=" << pt; 672 | } 673 | else if( const MemberPointerType* bt = typeIn->getAs()) 674 | { 675 | // C++ pointer to member (function or data) 676 | int pt = dumpType_i(bt->getPointeeType()); 677 | int rt = dumpNonQualifiedType_i(bt->getClass()); 678 | retId = m_uid.alloc(); 679 | m_os << "MemberPointerType( id=" << retId << ", recordid=" << rt << ", typeid=" << pt; 680 | } 681 | else if( const RecordType* bt = typeIn->getAs() ) 682 | { 683 | retId = m_uid.alloc(); 684 | const CXXRecordDecl* decl = dyn_cast(bt->getDecl()); 685 | assert(decl && "retrieved declaration is not a CXX record declaration"); 686 | m_os << "RecordType( id=" << retId; 687 | s_printName(m_os, decl); 688 | s_printRecordFlags(m_os, decl); 689 | } 690 | else if( const EnumType* bt = typeIn->getAs() ) 691 | { 692 | retId = m_uid.alloc(); 693 | m_os << "EnumType( id=" << retId; 694 | const NamedDecl* decl = bt->getDecl(); 695 | s_printName(m_os, decl); 696 | } 697 | else if( const ConstantArrayType* bt = dyn_cast(typeIn) ) 698 | { 699 | uint64_t sz = bt->getSize().getZExtValue(); 700 | int pt = dumpType_i(bt->getElementType()); 701 | retId = m_uid.alloc(); 702 | m_os << "ConstantArrayType( id=" << retId << ", typeid=" << pt << ", count=" << int(sz); 703 | } 704 | else if( const ParenType* bt = typeIn->getAs() ) 705 | { 706 | int pt = dumpType_i(bt->getInnerType()); 707 | retId = m_uid.alloc(); 708 | m_os << "ParenType( id=" << retId << ", typeid=" << pt; 709 | } 710 | else if( const FunctionProtoType* bt = typeIn->getAs() ) 711 | { 712 | int resType = dumpType_i(bt->getResultType()); 713 | const int numArgs = bt->getNumArgs(); 714 | std::vector paramTypes; 715 | for(int i = 0; i < numArgs; ++i) 716 | { 717 | paramTypes.push_back(dumpType_i(bt->getArgType(i))); 718 | } 719 | retId = m_uid.alloc(); 720 | m_os << "FunctionProtoType( id=" << retId << ", rettypeid=" << resType << ", paramtypeids=["; 721 | for(unsigned int i = 0; i < paramTypes.size(); ++i) 722 | { 723 | m_os << paramTypes[i]; 724 | if(i != paramTypes.size()-1) 725 | m_os << ','; 726 | } 727 | m_os << "]"; 728 | m_os << ", isVariadic=" << (bt->isVariadic() ? "True" : "False"); 729 | } 730 | else if( const ArrayType* bt = typeIn->getAsArrayTypeUnsafe() ) 731 | { 732 | retId = m_uid.alloc(); 733 | m_os << "BuiltinType( id=" << retId << ", name='" << "unsupported" << "'"; 734 | 735 | // todo 736 | // int eid = _dumpType( bt->getElementType().getTypePtr() ); 737 | // retId = m_uid.alloc(); 738 | // m_os << "ArrayType id='" << retId << "' elemId='" << eid << "' count='" << bt->getSizeExpr() ->getType().getTypePtr() << "'\n"; 739 | } 740 | else if( const DependentNameType* bt = typeIn->getAs() ) 741 | { 742 | retId = m_uid.alloc(); 743 | //int tid = _dumpType( bt->desugar().getSingleStepDesugaredType(*m_context).getTypePtr(), scopeId ); 744 | m_os << "BuiltinType( id=" << retId << ", name='" << "unsupported" << "'"; 745 | // todo 746 | } 747 | else if( const InjectedClassNameType* bt = typeIn->getAs() ) 748 | { 749 | // templates are handled using a different code path, this function should never 750 | // end up printing template class declaration information. 751 | assert(false && "this type is expected for template class declarations, which should not be handled by this function"); 752 | } 753 | else 754 | { 755 | const char* name = typeIn->getTypeClassName(); 756 | m_os << "###Type kind='" << name << "'\n"; 757 | assert(0 && "Type not supported"); 758 | } 759 | if(retId < 0) 760 | { 761 | // type was skipped (it is supported but we don't have to do anything) 762 | retId = m_uid.alloc(); 763 | } 764 | else 765 | { 766 | if(scopeId >= 0) 767 | m_os << ", scopeid=" << scopeId; 768 | m_os << " )\n"; 769 | } 770 | m_knownTypes[typeIn] = retId; 771 | return retId; 772 | } 773 | 774 | int Havok::ExtractASTConsumer::dumpTemplateInstantiationType_i( 775 | const TemplateSpecializationType* templateSpecializationType ) 776 | { 777 | // A template instantiation type associated with a certain template instantiation 778 | // class is not unique. We can have multiple Clang Types object that refer to the 779 | // same template instantiation. Even if they are not the same, we still have always 780 | // the same canonical type. We add the canonical type to the m_knownTypes map to 781 | // avoid the same template instantiation to be dumped again. The associated id is 782 | // the one associated with the template instantiation. When dumping members or 783 | // methods of a specific template instantiation, Clang will generate the address 784 | // of the canonical type as the parent type, thus the whole process works correctly. 785 | const Type* canonicalInstantiationType = templateSpecializationType->getCanonicalTypeInternal().getTypePtr(); 786 | 787 | int retId = findTypeId_i(canonicalInstantiationType); 788 | if(retId == -1) // not found 789 | { 790 | TemplateName templateName = templateSpecializationType->getTemplateName(); 791 | TemplateDecl* templateDecl = templateName.getAsTemplateDecl(); 792 | assert(templateDecl && "could not retrieve template declaration"); 793 | 794 | ClassTemplateDecl* classTemplateDecl = dyn_cast(templateDecl); 795 | const Decl* scopeDiscoveryDecl = NULL; 796 | const ClassTemplateSpecializationDecl* classTemplateInstantiationDecl = NULL; 797 | int templateId = -1; 798 | if(classTemplateDecl) 799 | { 800 | scopeDiscoveryDecl = classTemplateDecl; 801 | // retrieve corresponding declaration 802 | const ClassTemplateDecl::spec_iterator specBegin = const_cast(classTemplateDecl)->spec_begin(); 803 | const ClassTemplateDecl::spec_iterator specEnd = const_cast(classTemplateDecl)->spec_end(); 804 | for( ClassTemplateDecl::spec_iterator it = specBegin; 805 | it != specEnd; 806 | ++it ) 807 | { 808 | if( m_context->getRecordType(*it).getCanonicalType().getTypePtr() == 809 | canonicalInstantiationType ) 810 | { 811 | classTemplateInstantiationDecl = (*it); 812 | break; 813 | } 814 | } 815 | assert((classTemplateInstantiationDecl || templateSpecializationType->isDependentType()) && 816 | "could not retrieve template specialization declaration for instantiation"); 817 | // classTemplateInstantiationDecl will be NULL only if the template instance is dependent from a template parameter 818 | if(classTemplateInstantiationDecl != NULL) 819 | { 820 | scopeDiscoveryDecl = classTemplateInstantiationDecl; 821 | } 822 | 823 | const CXXRecordDecl* templatedDecl = dyn_cast(classTemplateDecl->getTemplatedDecl()); 824 | assert(templatedDecl && "could not retrieve templated declaration"); 825 | templateId = getTypeId_i(m_context->getRecordType(templatedDecl).getTypePtr()); 826 | } 827 | else 828 | { 829 | const TemplateTemplateParmDecl* templateTemplateParmDecl = 830 | dyn_cast(templateDecl); 831 | assert(templateTemplateParmDecl && "template declaration is not a class template or template parameter"); 832 | scopeDiscoveryDecl = templateTemplateParmDecl->getCanonicalDecl(); 833 | KnownTemplateTemplateParamMap::const_iterator it = m_knowTemplateTemplateParams.find(scopeDiscoveryDecl); 834 | assert((it != m_knowTemplateTemplateParams.end()) && "template template parameter not found in map"); 835 | templateId = it->second; 836 | } 837 | 838 | int scopeid = dumpScope_i(scopeDiscoveryDecl); 839 | 840 | retId = m_uid.alloc(); 841 | m_os << "TemplateRecordInstantiationType( id=" << retId << ", templateid=" 842 | << templateId; 843 | if(classTemplateInstantiationDecl != NULL) 844 | { 845 | s_printRecordFlags(m_os, classTemplateInstantiationDecl); 846 | } 847 | else 848 | { 849 | s_printDefaultRecordFlags(m_os); 850 | } 851 | m_os << ", scopeid=" << scopeid << " )\n"; 852 | m_knownTypes[templateSpecializationType] = retId; 853 | m_knownTypes[canonicalInstantiationType] = retId; 854 | 855 | const int argc = templateSpecializationType->getNumArgs(); 856 | const TemplateArgument* argv = templateSpecializationType->getArgs(); 857 | dumpTemplateArgumentList_i(argv, argc, retId); 858 | 859 | if(classTemplateInstantiationDecl != NULL) 860 | { 861 | m_knownTypes[m_context->getRecordType(classTemplateInstantiationDecl).getTypePtr()] = retId; 862 | 863 | const CXXRecordDecl* classTemplateInstantiationDefRecord = classTemplateInstantiationDecl->getDefinition(); 864 | if(classTemplateInstantiationDefRecord) 865 | { 866 | const ClassTemplateSpecializationDecl* classTemplateInstantiationDef = 867 | dyn_cast(classTemplateInstantiationDefRecord); 868 | 869 | if(classTemplateInstantiationDef != NULL) 870 | { 871 | dumpTagDefinition_i(classTemplateInstantiationDef, retId); 872 | } 873 | } 874 | // the definition for a template instantiation might not be found when it's only used for typedefs 875 | // or never referred explicitely when allocating storage. 876 | } 877 | } 878 | 879 | return retId; 880 | } 881 | 882 | int Havok::ExtractASTConsumer::dumpTemplateSpecializationType_i( 883 | const ClassTemplateSpecializationDecl* classTemplateSpecializationDecl, 884 | int scopeId ) 885 | { 886 | const TemplateArgumentList& argList = classTemplateSpecializationDecl->getTemplateArgs(); 887 | const QualType canonType = m_context->getRecordType(classTemplateSpecializationDecl); 888 | TemplateName tempName(classTemplateSpecializationDecl->getSpecializedTemplate()); 889 | const TemplateSpecializationType* templateSpecializationType = m_context-> 890 | getTemplateSpecializationType(tempName, argList.data(), argList.size(), canonType).getTypePtr()-> 891 | getAs(); 892 | assert(templateSpecializationType && "not a template specialization type"); 893 | 894 | const Type* recordType = m_context->getRecordType(classTemplateSpecializationDecl).getTypePtr(); 895 | 896 | // seen this type before? 897 | int retId = findTypeId_i(recordType); 898 | if(retId != -1) 899 | { 900 | return retId; 901 | } 902 | 903 | // While the type is always TemplateSpecializationType, we handle here just full 904 | // or partial template specializations, template instantiations are handled in the 905 | // usual _dumpType() function. 906 | 907 | const CXXRecordDecl* templatedDecl = dyn_cast(templateSpecializationType->getTemplateName().getAsTemplateDecl()->getTemplatedDecl()); 908 | assert(templatedDecl && "could not retrieve templated declaration"); 909 | int templateId = getTypeId_i(m_context->getRecordType(templatedDecl).getTypePtr()); 910 | 911 | retId = m_uid.alloc(); 912 | m_os << "TemplateRecordSpecialization( id=" << retId << ", templateid=" 913 | << templateId; 914 | s_printRecordFlags(m_os, classTemplateSpecializationDecl); 915 | m_os << ", scopeid=" << scopeId << " )\n"; 916 | 917 | if(const ClassTemplatePartialSpecializationDecl* classTemplatePartialSpecializationDecl = 918 | dyn_cast(classTemplateSpecializationDecl)) 919 | { 920 | // 2: dump the parameters 921 | dumpTemplateParameterList_i(classTemplatePartialSpecializationDecl->getTemplateParameters(), retId); 922 | 923 | // 3: add special entries in the type map for types referred by template arguments, consider the following specialization: 924 | // 925 | // template 926 | // class A 927 | // { 928 | // T1 m_a 929 | // }; 930 | // 931 | // We dump type T1 (type template parameter) when dumping the Parameter list above, but when dumping the argument 932 | // list we might refer to the types dumped in the parameter list (T1 in the above example). This cannot happen in 933 | // case of explicit instantiation of course. 934 | // The type returned by LLVM for T1 in the argument list is not the same we dumped in the previous step, 935 | // that type must be present in the map for everything to work properly, and that's why we do a special operation 936 | // adding (or replacing) all those types with the id obtained by looking up the Parameter list type. 937 | addOrReplaceSpecializationTypeParameterTypes_i(classTemplatePartialSpecializationDecl->getTemplateParameters()); 938 | } 939 | 940 | // 4: dump the argument list 941 | dumpTemplateArgumentList_i(templateSpecializationType->getArgs(), 942 | templateSpecializationType->getNumArgs(), 943 | retId); 944 | 945 | m_knownTypes[recordType] = retId; 946 | return retId; 947 | } 948 | 949 | int Havok::ExtractASTConsumer::dumpScope_i( const Decl* decl ) 950 | { 951 | int retScopeId = -1; 952 | 953 | const DeclContext* declContext = decl->getDeclContext(); 954 | if(const NamedDecl* namedDecl = dyn_cast(declContext)) 955 | { 956 | // it has already been dumped, just get the id 957 | if(const TypeDecl* typeDecl = dyn_cast(namedDecl)) 958 | { 959 | // return the id using the type information 960 | retScopeId = getTypeId_i( m_context->getTypeDeclType(typeDecl).getTypePtr() ); 961 | } 962 | else if(const NamespaceDecl* namespaceDecl = dyn_cast(namedDecl)) 963 | { 964 | // return the namespace id 965 | retScopeId = getNamespaceId_i(namespaceDecl); 966 | } else 967 | { 968 | assert(false && "Invalid declaration scope"); 969 | } 970 | } 971 | else 972 | { 973 | // we need to refer to the containing file, and dump if is not in the known file map 974 | SourceLocation loc = decl->getLocation(); 975 | loc = s_getExpansionLoc(loc, m_context->getSourceManager()); 976 | FileID fileId = s_getFileId(loc, m_context->getSourceManager()); 977 | KnownFilesMap::iterator it = m_knownFiles.find(fileId); 978 | if(it == m_knownFiles.end()) 979 | { 980 | retScopeId = m_uid.alloc(); 981 | m_knownFiles[fileId] = retScopeId; 982 | std::string buf; 983 | const char* fileName = s_getFileName(buf, loc, m_context->getSourceManager()); 984 | m_os << "File( id=" << retScopeId << ", location='" << fileName << "' )\n"; 985 | } 986 | else 987 | { 988 | retScopeId = it->second; 989 | } 990 | } 991 | 992 | return retScopeId; 993 | } 994 | 995 | void Havok::ExtractASTConsumer::dumpSpecifiersRecursive_i(const NestedNameSpecifier* nestedNameSpecifier) 996 | { 997 | const NestedNameSpecifier* nameSpecifierPrefix = nestedNameSpecifier->getPrefix(); 998 | if(nameSpecifierPrefix) 999 | { 1000 | dumpSpecifiersRecursive_i(nameSpecifierPrefix); 1001 | } 1002 | if(const Type* specifierType = nestedNameSpecifier->getAsType()) 1003 | { 1004 | if( dyn_cast(specifierType) ) 1005 | dumpNonQualifiedSimpleType_i(specifierType); 1006 | } 1007 | } 1008 | 1009 | void Havok::ExtractASTConsumer::dumpTypeSpecifiers_i( const Type* type ) 1010 | { 1011 | const ElaboratedType* elabType = type->getAs(); 1012 | if(elabType) 1013 | { 1014 | NestedNameSpecifier* nestedNameSpecifier = elabType->getQualifier(); 1015 | if(nestedNameSpecifier) 1016 | { 1017 | dumpSpecifiersRecursive_i(nestedNameSpecifier); 1018 | } 1019 | } 1020 | } 1021 | 1022 | void Havok::ExtractASTConsumer::dumpTagDefinition_i(const TagDecl* tagDecl, int recordId) 1023 | { 1024 | if( const RecordDecl* recordDecl = dyn_cast(tagDecl) ) 1025 | { 1026 | // class/struct/union 1027 | if( const CXXRecordDecl* cxxDecl = dyn_cast(recordDecl) ) 1028 | { 1029 | for( CXXRecordDecl::base_class_const_iterator bi = cxxDecl->bases_begin(), be = cxxDecl->bases_end(); bi != be; ++bi ) 1030 | { 1031 | int pid = dumpType_i( bi->getType() ); 1032 | m_os << "Inherit( id=" << recordId << ", parent=" << pid << " )\n"; 1033 | } 1034 | } 1035 | } 1036 | 1037 | // class/struct/union or enum 1038 | dumpDeclContext_i(tagDecl); 1039 | } 1040 | 1041 | void Havok::ExtractASTConsumer::dumpDeclContext_i(const DeclContext* context) 1042 | { 1043 | typedef RecordDecl::decl_iterator NestedIterator; 1044 | for( NestedIterator fi = context->decls_begin(), fe = context->decls_end(); fi != fe; ++fi ) 1045 | { 1046 | dumpDecl_i( *fi ); 1047 | } 1048 | } 1049 | 1050 | void Havok::ExtractASTConsumer::dumpNamespace_i(const NamespaceDecl* namespaceDecl) 1051 | { 1052 | // check if already seen (if not, dump the declaration) 1053 | const NamespaceDecl* originalNamespaceDecl = namespaceDecl->getOriginalNamespace(); 1054 | KnownNamespacesMap::iterator it = m_knownNamespaces.find(originalNamespaceDecl); 1055 | if( it == m_knownNamespaces.end() ) 1056 | { 1057 | int scopeId = dumpScope_i(namespaceDecl); 1058 | int newId = m_uid.alloc(); 1059 | m_os << "Namespace( id=" << newId; 1060 | s_printName(m_os, namespaceDecl); 1061 | m_os << ", scopeid=" << scopeId << " )\n"; 1062 | m_knownNamespaces[originalNamespaceDecl] = newId; 1063 | } 1064 | 1065 | // dump declarations in the namespace 1066 | dumpDeclContext_i(namespaceDecl); 1067 | } 1068 | 1069 | void Havok::ExtractASTConsumer::dumpTemplateClass_i(const ClassTemplateDecl* classTemplateDecl) 1070 | { 1071 | const CXXRecordDecl* templatedRecordDecl = classTemplateDecl->getTemplatedDecl(); 1072 | const Type* injectedClassnameType = m_context->getRecordType(templatedRecordDecl).getTypePtr(); 1073 | const ClassTemplateDecl* classTemplateDef = s_getClassTemplateDefinition(classTemplateDecl); 1074 | 1075 | int templateId; 1076 | templateId = findTypeId_i(injectedClassnameType); 1077 | if(templateId == -1) 1078 | { 1079 | int scopeId = dumpScope_i(classTemplateDef != NULL ? classTemplateDef : classTemplateDecl); 1080 | 1081 | templateId = m_uid.alloc(); 1082 | m_os << "TemplateRecord( id=" << templateId; 1083 | s_printName(m_os, templatedRecordDecl); 1084 | s_printRecordFlags(m_os, templatedRecordDecl); 1085 | 1086 | m_os << ", scopeid=" << scopeId << " )\n"; 1087 | s_printAnnotations(m_os, classTemplateDef != NULL ? classTemplateDef->getTemplatedDecl() : templatedRecordDecl, templateId); 1088 | m_knownTypes[injectedClassnameType] = templateId; 1089 | 1090 | // 1: dump template parameter list 1091 | const TemplateParameterList* paramList = 1092 | classTemplateDef != NULL ? 1093 | classTemplateDef->getTemplateParameters() : 1094 | classTemplateDecl->getTemplateParameters(); 1095 | dumpTemplateParameterList_i(paramList, templateId); 1096 | } 1097 | 1098 | if(classTemplateDecl->isThisDeclarationADefinition()) 1099 | { 1100 | // 2: add special entries in the type map for types referred by template parameters, consider the following template class declaration: 1101 | // 1102 | // template 1103 | // class A : public B 1104 | // { 1105 | // int m_a; 1106 | // }; 1107 | // 1108 | // We dump type T1 (type template parameter) when dumping the Parameter list above, but when dumping the parent 1109 | // type we might refer to the types dumped in the parameter list (T1 in the above example). 1110 | // The type returned by LLVM for T1 in the argument list is not the same we dumped in the previous step, 1111 | // that type must be present in the map for everything to work properly, and that's why we do a special operation 1112 | // adding (or replacing) all those types with the id obtained by looking up the Parameter list type. 1113 | addOrReplaceSpecializationTypeParameterTypes_i(classTemplateDecl->getTemplateParameters()); 1114 | 1115 | // dump underlying record (base classes and members) 1116 | dumpTagDefinition_i(templatedRecordDecl, templateId); 1117 | } 1118 | } 1119 | 1120 | void Havok::ExtractASTConsumer::dumpTemplateClassSpecialization_i(const ClassTemplateSpecializationDecl* classTemplateSpecializationDecl) 1121 | { 1122 | // it might be: 1123 | // - explicit instantiation (dump only a TemplateRecordExplicitInstantiation) 1124 | // - full specialization (dump a TemplateSpecializationRecord with arguments and members) 1125 | // - partial specialization (dump a TemplateSpecializationRecord with arguments and members) 1126 | 1127 | TemplateSpecializationKind kind = classTemplateSpecializationDecl->getSpecializationKind(); 1128 | 1129 | if(kind == TSK_ExplicitSpecialization) 1130 | { 1131 | const ClassTemplateSpecializationDecl* classTemplateSpecializationDef = NULL; 1132 | const CXXRecordDecl* classTemplateSpecializationDefRecord = classTemplateSpecializationDecl->getDefinition(); 1133 | if(classTemplateSpecializationDefRecord != NULL) 1134 | classTemplateSpecializationDef = dyn_cast(classTemplateSpecializationDefRecord); 1135 | 1136 | int scopeId = dumpScope_i(classTemplateSpecializationDefRecord != NULL ? classTemplateSpecializationDefRecord : classTemplateSpecializationDecl); 1137 | 1138 | // 1: dump the TemplateSpecializationRecord entry 1139 | // dump template specializations using specific function 1140 | int templateId = dumpTemplateSpecializationType_i(classTemplateSpecializationDef != NULL ? classTemplateSpecializationDef : classTemplateSpecializationDecl, scopeId); 1141 | 1142 | if(classTemplateSpecializationDecl->isThisDeclarationADefinition()) 1143 | { 1144 | if(const ClassTemplatePartialSpecializationDecl* classTemplatePartialSpecializationDecl = 1145 | dyn_cast(classTemplateSpecializationDecl)) 1146 | { 1147 | // 2: add special entries in the type map for types referred by template parameters, consider the following template class declaration: 1148 | // 1149 | // template 1150 | // class A : public B 1151 | // { 1152 | // int m_a; 1153 | // }; 1154 | // 1155 | // We dump type T1 (type template parameter) when dumping the Parameter list above, but when dumping the parent 1156 | // type we might refer to the types dumped in the parameter list (T1 in the above example). 1157 | // The type returned by LLVM for T1 in the argument list is not the same we dumped in the previous step, 1158 | // that type must be present in the map for everything to work properly, and that's why we do a special operation 1159 | // adding (or replacing) all those types with the id obtained by looking up the Parameter list type. 1160 | addOrReplaceSpecializationTypeParameterTypes_i(classTemplatePartialSpecializationDecl->getTemplateParameters()); 1161 | } 1162 | 1163 | // 3: dump underlying record (base classes and members) 1164 | dumpTagDefinition_i(classTemplateSpecializationDecl, templateId); 1165 | } 1166 | } 1167 | else if(kind == TSK_ExplicitInstantiationDefinition) 1168 | { 1169 | const TemplateArgumentList& argList = classTemplateSpecializationDecl->getTemplateArgs(); 1170 | const QualType canonType = m_context->getRecordType(classTemplateSpecializationDecl); 1171 | TemplateName tempName(classTemplateSpecializationDecl->getSpecializedTemplate()); 1172 | QualType templateSpecializationType = m_context->getTemplateSpecializationType(tempName, argList.data(), argList.size(), canonType); 1173 | assert(templateSpecializationType.getTypePtr()->getAs() && "not a template specialization type"); 1174 | 1175 | int scopeId = dumpScope_i(classTemplateSpecializationDecl); 1176 | // dump explicit instantiation using the standard dumpType function 1177 | dumpType_i(templateSpecializationType, scopeId); 1178 | } 1179 | else 1180 | { 1181 | assert(0 && "Template specialization declaration kind not supported"); 1182 | } 1183 | } 1184 | 1185 | void Havok::ExtractASTConsumer::dumpTemplateParameterList_i(const TemplateParameterList* paramList, int templateId) 1186 | { 1187 | for( TemplateParameterList::const_iterator it = paramList->begin(); 1188 | it != paramList->end(); 1189 | ++it ) 1190 | { 1191 | const NamedDecl* paramDecl = (*it); 1192 | if ( const NonTypeTemplateParmDecl* nonTypeTemplateParmDecl = dyn_cast(paramDecl) ) 1193 | { 1194 | int typeId = dumpType_i(nonTypeTemplateParmDecl->getType()); 1195 | m_os << "TemplateNonTypeParam( templateid=" << templateId << ", typeid=" << typeId; 1196 | s_printName(m_os, nonTypeTemplateParmDecl); 1197 | m_os << " )\n"; 1198 | } 1199 | else if ( const TemplateTypeParmDecl* templateTypeParmDecl = dyn_cast(paramDecl) ) 1200 | { 1201 | int typeId = dumpType_i(m_context->getTemplateTypeParmType( 1202 | templateTypeParmDecl->getDepth(), 1203 | templateTypeParmDecl->getIndex(), 1204 | templateTypeParmDecl->isParameterPack(), 1205 | const_cast(templateTypeParmDecl))); 1206 | m_os << "TemplateTypeParamType( templateid=" << templateId << ", id=" << typeId; 1207 | s_printName(m_os, templateTypeParmDecl); 1208 | m_os << " )\n"; 1209 | } 1210 | else if ( const TemplateTemplateParmDecl* templateTemplateParmDecl = dyn_cast(paramDecl) ) 1211 | { 1212 | int retId = m_uid.alloc(); 1213 | const Decl* canonical = templateTemplateParmDecl->getCanonicalDecl(); 1214 | m_knowTemplateTemplateParams[canonical] = retId; 1215 | m_os << "TemplateTemplateParam( templateid=" << templateId << ", id=" << retId; 1216 | s_printName(m_os, templateTemplateParmDecl); 1217 | m_os << " )\n"; 1218 | // dump template parameter list 1219 | const TemplateParameterList* paramList = templateTemplateParmDecl->getTemplateParameters(); 1220 | dumpTemplateParameterList_i(paramList, retId); 1221 | } 1222 | else 1223 | { 1224 | m_os << "### Template param declaration not supported"; 1225 | s_printName(m_os, paramDecl); 1226 | m_os << "\n"; 1227 | assert(0); 1228 | } 1229 | } 1230 | } 1231 | 1232 | void Havok::ExtractASTConsumer::dumpTemplateArgumentList_i(const TemplateArgument* argv, int argc, int templateId) 1233 | { 1234 | // Note that the argument list for a given template instantiation might not include an argument 1235 | // for every template parameters, this happens when we have default arguments associated with 1236 | // some template parameters. 1237 | for(int i = 0; i < argc; ++i) 1238 | { 1239 | if(argv[i].getKind() == TemplateArgument::Type) 1240 | { 1241 | QualType qualType = argv[i].getAsType(); 1242 | int typeId = dumpType_i(qualType); 1243 | m_os << "TemplateSpecializationTypeArg( recordid=" << templateId << ", typeid=" << typeId << " )\n"; 1244 | } 1245 | else if(argv[i].getKind() == TemplateArgument::Template) 1246 | { 1247 | TemplateName templateName = argv[i].getAsTemplate(); 1248 | TemplateDecl* templateDecl = templateName.getAsTemplateDecl(); 1249 | int argTemplateId = -1; 1250 | ClassTemplateDecl* classTemplateDecl = dyn_cast(templateDecl); 1251 | if(classTemplateDecl) 1252 | { 1253 | const CXXRecordDecl* templatedRecordDecl = classTemplateDecl->getTemplatedDecl(); 1254 | const Type* injectedClassnameType = m_context->getRecordType(templatedRecordDecl).getTypePtr(); 1255 | argTemplateId = findTypeId_i(injectedClassnameType); 1256 | } 1257 | else 1258 | { 1259 | const TemplateTemplateParmDecl* templateTemplateParmDecl = 1260 | dyn_cast(templateDecl); 1261 | assert(templateTemplateParmDecl && "template declaration is not a class template or template parameter"); 1262 | KnownTemplateTemplateParamMap::const_iterator it = m_knowTemplateTemplateParams.find(templateTemplateParmDecl->getCanonicalDecl()); 1263 | assert((it != m_knowTemplateTemplateParams.end()) && "template template parameter not found in map"); 1264 | argTemplateId = it->second; 1265 | } 1266 | m_os << "TemplateSpecializationTemplateArg( recordid=" << templateId << ", templateid=" << argTemplateId << " )\n"; 1267 | } 1268 | else if( (argv[i].getKind() == TemplateArgument::Integral) || 1269 | (argv[i].getKind() == TemplateArgument::Expression) ) 1270 | { 1271 | m_os << "TemplateSpecializationNonTypeArg( recordid=" << templateId << ", value='"; 1272 | argv[i].print(m_context->getPrintingPolicy(), m_os); 1273 | m_os << "' )\n"; 1274 | } 1275 | else 1276 | { 1277 | m_os << "### Template argument kind not supported\n"; 1278 | assert(0); 1279 | } 1280 | } 1281 | } 1282 | 1283 | int Havok::ExtractASTConsumer::findTypeId_i(const Type* typeIn) 1284 | { 1285 | KnownTypeMap::const_iterator it = m_knownTypes.find(typeIn); 1286 | if(it == m_knownTypes.end()) 1287 | { 1288 | return -1; 1289 | } 1290 | return it->second; 1291 | } 1292 | 1293 | int Havok::ExtractASTConsumer::findConstTypeId_i(int typeId) 1294 | { 1295 | ConstTypeIdMap::const_iterator it = m_constTypeIdMap.find(typeId); 1296 | if(it == m_constTypeIdMap.end()) 1297 | { 1298 | return -1; 1299 | } 1300 | return it->second; 1301 | } 1302 | 1303 | int Havok::ExtractASTConsumer::getTypeId_i(const Type* typeIn) 1304 | { 1305 | // clean the type from any sugar used to specify it in source code (elaborated types) 1306 | typeIn = s_getTrueType(typeIn); 1307 | 1308 | int id = findTypeId_i(typeIn); 1309 | assert(id != -1 && "type not found in map"); 1310 | return id; 1311 | } 1312 | 1313 | int Havok::ExtractASTConsumer::getNamespaceId_i(const NamespaceDecl* namespaceDecl) 1314 | { 1315 | const NamespaceDecl* originalNamespaceDecl = namespaceDecl->getOriginalNamespace(); 1316 | int id = m_knownNamespaces[originalNamespaceDecl]; 1317 | assert(id != 0 && "namespace Id not found"); 1318 | return id; 1319 | } 1320 | 1321 | void Havok::ExtractASTConsumer::addOrReplaceSpecializationTypeParameterTypes_i(const TemplateParameterList* paramList) 1322 | { 1323 | for( TemplateParameterList::const_iterator it = paramList->begin(); 1324 | it != paramList->end(); 1325 | ++it ) 1326 | { 1327 | const NamedDecl* paramDecl = (*it); 1328 | if ( const TemplateTypeParmDecl* templateTypeParmDecl = dyn_cast(paramDecl)) 1329 | { 1330 | int typeId = getTypeId_i(m_context->getTemplateTypeParmType( 1331 | templateTypeParmDecl->getDepth(), 1332 | templateTypeParmDecl->getIndex(), 1333 | templateTypeParmDecl->isParameterPack(), 1334 | const_cast(templateTypeParmDecl)).getTypePtr()); 1335 | const Type* newType = m_context->getTemplateTypeParmType( 1336 | templateTypeParmDecl->getDepth(), 1337 | templateTypeParmDecl->getIndex(), 1338 | templateTypeParmDecl->isParameterPack()).getTypePtr(); 1339 | m_knownTypes[newType] = typeId; 1340 | } 1341 | } 1342 | } 1343 | 1344 | // -------------------------------------------------------------------------- // 1345 | -------------------------------------------------------------------------------- /extract.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Havok. All rights reserved. This file is distributed under the terms 2 | // and conditions defined in file 'LICENSE.txt', which is part of this source code package. 3 | 4 | #ifndef RAW_DUMP_H 5 | #define RAW_DUMP_H 6 | 7 | #pragma warning(push,0) 8 | #include "clang/AST/AST.h" 9 | #include "clang/Sema/SemaConsumer.h" 10 | #include "clang/Frontend/CompilerInstance.h" 11 | #pragma warning(pop) 12 | 13 | namespace Havok 14 | { 15 | using namespace clang; 16 | 17 | /// Havok AST consumer class 18 | class ExtractASTConsumer : public SemaConsumer 19 | { 20 | public: 21 | 22 | ExtractASTConsumer(llvm::raw_ostream& os); 23 | virtual ~ExtractASTConsumer(); 24 | virtual void Initialize(ASTContext& context); 25 | virtual void InitializeSema(Sema& sema); 26 | // Base callback coming from LLVM (used to accumulate all declarations) 27 | virtual void HandleTopLevelDecl(DeclGroupRef DG); 28 | 29 | enum DumpBits 30 | { 31 | DUMP_DEFAULT = 0, 32 | DUMP_VERBOSE = 1, 33 | DUMP_FUNCTIONS = 2 34 | }; 35 | 36 | // delayed dumping of all declarations 37 | void dumpAllDeclarations(); 38 | 39 | protected: 40 | 41 | // Basic function to fix declarations of C++ special methods (e.g. copy constructor) in classes 42 | void declareImplicitMethods(Decl* declIn); 43 | // Basic function that dumps a generic declaration 44 | int dumpDecl_i(const Decl* declIn); 45 | // Functions used to dump the type referred by a declaration, the type is what we use to identify an entity 46 | int dumpType_i(QualType qualTypeIn, int scopeId = -1); 47 | int dumpSimpleType_i(QualType qualTypeIn, int scopeId = -1); 48 | int dumpNonQualifiedType_i(const Type* typeIn, int scopeId = -1); 49 | int dumpNonQualifiedSimpleType_i(const Type* typeIn, int scopeId = -1); 50 | int dumpTemplateInstantiationType_i(const TemplateSpecializationType* templateSpecializationType); 51 | int dumpTemplateSpecializationType_i(const ClassTemplateSpecializationDecl* classTemplateSpecializationDecl, int scopeId); 52 | // More dumping functions 53 | int dumpScope_i(const Decl* decl); 54 | void dumpSpecifiersRecursive_i(const NestedNameSpecifier* nestedNameSpecifier); 55 | void dumpTypeSpecifiers_i(const Type* type); 56 | void dumpTagDefinition_i(const TagDecl* tagDecl, int recordId); 57 | void dumpDeclContext_i(const DeclContext* context); 58 | void dumpNamespace_i(const NamespaceDecl* namespaceDecl); 59 | void dumpTemplateClass_i(const ClassTemplateDecl* classTemplateDecl); 60 | void dumpTemplateClassSpecialization_i(const ClassTemplateSpecializationDecl* classTemplateSpecializationDecl); 61 | void dumpTemplateParameterList_i(const TemplateParameterList* paramList, int templateId); 62 | void dumpTemplateArgumentList_i(const TemplateArgument* argv, int argc, int templateId); 63 | // Functions used for lookups in the internal structures 64 | int findTypeId_i(const Type* typeIn); 65 | int findConstTypeId_i(int typeId); 66 | int getTypeId_i(const Type* typeIn); 67 | int getNamespaceId_i(const NamespaceDecl* namespaceDecl); 68 | // More utility functions 69 | void addOrReplaceSpecializationTypeParameterTypes_i(const TemplateParameterList* paramList); 70 | 71 | // Some types of dump entry are routed through this class to unify default handling. 72 | class DumpEntry 73 | { 74 | public: 75 | enum EntryType 76 | { 77 | // Only these types are currently supported. 78 | ENTRY_METHOD, 79 | ENTRY_CONSTRUCTOR, 80 | ENTRY_DESTRUCTOR, 81 | ENTRY_FIELD, 82 | 83 | NUM_ENTRIES 84 | }; 85 | 86 | // Construct a new entry (outputs name to stream) 87 | DumpEntry(llvm::raw_ostream& os, EntryType t); 88 | 89 | // Output a key value pair when the value isn't the default value. 90 | void dumpKeyValuePair(const char* key, const char* val); 91 | void dumpKeyValuePair(const char* key, int i); 92 | void dumpKeyValuePair(const char* key, bool b); 93 | void dumpKeyValuePair(const char* key, AccessSpecifier as); 94 | 95 | // Output a closing parenthesis and newline. 96 | void finishEntry(); 97 | 98 | // Output a series of entries summarizing the defaults. 99 | static void dumpDefaultEntries(llvm::raw_ostream& os); 100 | 101 | protected: 102 | // Output a comma, if necessary. 103 | void checkOutputComma(); 104 | 105 | protected: 106 | EntryType m_entryType; 107 | /// Used to put commas in the correct position. 108 | bool m_hasOutputKeyValuePair; 109 | llvm::raw_ostream& m_os; 110 | 111 | private: 112 | // Private and unimplemented. 113 | DumpEntry(DumpEntry& other); 114 | DumpEntry& operator=(DumpEntry& other); 115 | }; 116 | 117 | 118 | // List of declarations, declarations are collected and then dumped in a second phase 119 | std::list m_decls; 120 | 121 | // Output stream 122 | llvm::raw_ostream& m_os; 123 | 124 | // AST context used during consumption of the AST 125 | ASTContext* m_context; 126 | 127 | // Map of know types (types are used to identify declarations of the same entity) 128 | typedef llvm::DenseMap KnownTypeMap; 129 | KnownTypeMap m_knownTypes; 130 | 131 | // Maps a type id to the id of a const version of that type. 132 | typedef llvm::DenseMap ConstTypeIdMap; 133 | ConstTypeIdMap m_constTypeIdMap; 134 | 135 | // Map of known namespaces (used to identify a certain namespace as scope) 136 | typedef llvm::DenseMap KnownNamespacesMap; 137 | KnownNamespacesMap m_knownNamespaces; 138 | 139 | // Map of known files (used to identify a certain file, considering it the largest scope a declaration can be in). 140 | typedef llvm::DenseMap KnownFilesMap; 141 | KnownFilesMap m_knownFiles; 142 | 143 | // Map of known template template parameters (used to indentify a template template parameter) 144 | typedef llvm::DenseMap KnownTemplateTemplateParamMap; 145 | KnownTemplateTemplateParamMap m_knowTemplateTemplateParams; 146 | 147 | // Allocator object for entity identifiers 148 | class UidAllocator 149 | { 150 | public: 151 | UidAllocator() : m_uidNext(1) {} 152 | int alloc() { return m_uidNext++; } 153 | private: 154 | int m_uidNext; 155 | }; 156 | UidAllocator m_uid; 157 | 158 | // Dumping configuration bits 159 | DumpBits m_dumpBits; 160 | 161 | // clang Sema instance used to perform semantic analysis 162 | Sema* m_sema; 163 | 164 | private: 165 | 166 | ExtractASTConsumer& operator=(ExtractASTConsumer& other); 167 | }; 168 | } 169 | 170 | #endif //RAW_DUMP_H 171 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Havok. All rights reserved. This file is distributed under the terms 2 | // and conditions defined in file 'LICENSE.txt', which is part of this source code package. 3 | 4 | #pragma warning(push,0) 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #pragma warning(pop) 21 | 22 | #include 23 | #include "extract.h" 24 | 25 | #ifdef _WIN32 26 | #include 27 | static inline bool s_fileNameMatch(const char* path, const char* pattern) 28 | { 29 | static char path_s[MAX_PATH]; // static storage 30 | strcpy(path_s, path); 31 | PathStripPath(path_s); 32 | return (PathMatchSpec(path_s, pattern) == TRUE); 33 | } 34 | #undef GetCurrentDirectory // remove annoying define from windows header 35 | #else 36 | #include 37 | static inline bool s_fileNameMatch(const char* path, const char* pattern) 38 | { 39 | const char* base = basename( const_cast(path) ); 40 | return (fnmatch(pattern, base, 0) == 0); 41 | } 42 | #endif 43 | 44 | namespace Havok 45 | { 46 | // Module loader 47 | class ModuleLoader : public clang::ModuleLoader 48 | { 49 | public: 50 | 51 | virtual ModuleKey loadModule(SourceLocation, IdentifierInfo&, SourceLocation) 52 | { 53 | assert(0); 54 | return 0; 55 | } 56 | }; 57 | // Preprocessor callbacks used to exclude specific included files (with #include) 58 | // When the preprocessor processes a file, it will generate callbacks to this object 59 | // on various events, when an inclusion directive is detected we will simply look 60 | // it up in our set of excluded inclusion, and if something matches we will basically 61 | // override that with an empty buffer. 62 | class FilenamePatternExcluder : public clang::PPCallbacks 63 | { 64 | public: 65 | 66 | FilenamePatternExcluder(clang::Preprocessor& preprocessor, clang::SourceManager& sourceManager) 67 | : PPCallbacks(), m_preprocessor(preprocessor), m_sourceManager(sourceManager) 68 | {} 69 | 70 | ~FilenamePatternExcluder() 71 | {} 72 | 73 | void addExcludedPattern(const std::string& str) 74 | { 75 | m_excludedPatterns.push_back(str); 76 | } 77 | 78 | virtual void InclusionDirective( 79 | SourceLocation, 80 | const Token&, 81 | StringRef fileName, 82 | bool, 83 | const FileEntry* file, 84 | SourceLocation, 85 | StringRef, 86 | StringRef ) 87 | { 88 | m_preprocessor.SetSuppressIncludeNotFoundError(false); 89 | for(unsigned int i = 0; i < m_excludedPatterns.size(); ++i) 90 | { 91 | if(s_fileNameMatch(fileName.str().c_str(), m_excludedPatterns[i].c_str())) 92 | { 93 | if(file) 94 | { 95 | // file was found 96 | m_sourceManager.overrideFileContents(file, llvm::MemoryBuffer::getNewMemBuffer(0), false); 97 | } 98 | else 99 | { 100 | // file was not found (but as it matches one of the excluded patterns we ignore it anyway) 101 | m_preprocessor.SetSuppressIncludeNotFoundError(true); 102 | } 103 | break; 104 | } 105 | } 106 | } 107 | 108 | protected: 109 | 110 | // Included file patterns that will be skipped, these string should contain 111 | // OS-style wildcards to exclude sets of files based on their name. 112 | std::vector m_excludedPatterns; 113 | 114 | // Source manager used to exclude all the specified inclusions. 115 | clang::SourceManager& m_sourceManager; 116 | 117 | // Preprocessor used during parsing of the source 118 | clang::Preprocessor& m_preprocessor; 119 | 120 | private: 121 | FilenamePatternExcluder& operator=(const FilenamePatternExcluder& other); 122 | }; 123 | } 124 | 125 | static llvm::cl::list o_cppDefines(llvm::cl::ZeroOrMore, "D", llvm::cl::desc("Predefined preprocessor constants"), llvm::cl::value_desc("value") ); // Predefined constants 126 | static llvm::cl::list o_includePath(llvm::cl::ZeroOrMore,"I", llvm::cl::desc("Add to the include path"), llvm::cl::value_desc("dirname") ); // Include path directories 127 | static llvm::cl::list o_passAttributes(llvm::cl::ZeroOrMore, "A", llvm::cl::desc("Attributes to pass through to database"), llvm::cl::value_desc("token")); // Output file 128 | static llvm::cl::list o_forceInclude(llvm::cl::ZeroOrMore, "include", llvm::cl::desc("Include before processing"), llvm::cl::value_desc("filename") ); // File included at the beginning of the master file 129 | static llvm::cl::list o_excludeFilenames(llvm::cl::ZeroOrMore, "exclude", llvm::cl::desc("Files to exclude from parsing")); // Files excluded when encountered in an #include directive 130 | static llvm::cl::list o_excludeFilenamePatterns(llvm::cl::ZeroOrMore, "exclude-pattern", llvm::cl::desc("File name patterns to use when excluding additional files")); // File name patterns used to exclude additional files encountered in #include directives 131 | static llvm::cl::list o_inputFilenames(llvm::cl::ZeroOrMore, llvm::cl::Positional, llvm::cl::desc("")); // Input files 132 | static llvm::cl::opt o_resourceDir(llvm::cl::Optional, "resource-dir", llvm::cl::desc("Directory containing standard LLVM includes"), llvm::cl::value_desc("dirname") ); // Directory containing standard LLVM includes 133 | static llvm::cl::opt o_outputFilename(llvm::cl::Required, "o", llvm::cl::desc("Output File (required)")); // Output file 134 | 135 | int main(int argc, char **argv) 136 | { 137 | int exitStatus; 138 | 139 | //_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_EVERY_1024_DF | _CRTDBG_LEAK_CHECK_DF ); 140 | llvm::cl::ParseCommandLineOptions(argc, argv, "Help Text Here", true); 141 | llvm::MemoryBuffer* emptyMemoryBuffer = llvm::MemoryBuffer::getNewMemBuffer(0, "emptyMemoryBuffer"); 142 | { 143 | clang::TextDiagnosticPrinter diagnosticConsumer(llvm::errs(), clang::DiagnosticOptions(), false); 144 | llvm::IntrusiveRefCntPtr diagnosticIDs(new clang::DiagnosticIDs()); 145 | clang::DiagnosticsEngine diagnostics(diagnosticIDs, &diagnosticConsumer, false); 146 | // ignored warnings 147 | diagnostics.setDiagnosticMapping(clang::diag::warn_undefined_internal, clang::diag::MAP_IGNORE, clang::SourceLocation()); //-Wno-undefined-internal 148 | 149 | clang::TargetOptions targetOptions; 150 | targetOptions.Triple = llvm::sys::getHostTriple(); 151 | llvm::IntrusiveRefCntPtr targetInfo( clang::TargetInfo::CreateTargetInfo(diagnostics, targetOptions ) ); 152 | clang::FileSystemOptions filesystemOptions; 153 | clang::FileManager fileManager(filesystemOptions); 154 | clang::SourceManager sourceManager(diagnostics, fileManager); 155 | clang::HeaderSearch headerSearch(fileManager); 156 | Havok::ModuleLoader moduleLoader; 157 | clang::LangOptions langOptions; 158 | langOptions.CPlusPlus = 1; 159 | langOptions.CPlusPlus0x = 0; 160 | langOptions.Bool = 1; 161 | langOptions.ConstStrings = 1; 162 | langOptions.AssumeSaneOperatorNew = 1; 163 | langOptions.ImplicitInt = 0; 164 | langOptions.ElideConstructors = 0; 165 | 166 | clang::Preprocessor preprocessor(diagnostics, langOptions, targetInfo.getPtr(), sourceManager, headerSearch, moduleLoader); 167 | 168 | Havok::FilenamePatternExcluder* filenamePatternExcluder = new Havok::FilenamePatternExcluder(preprocessor, sourceManager); 169 | preprocessor.addPPCallbacks(filenamePatternExcluder); // the preprocessor is now owner of the FilenamePatternExcluder 170 | clang::PreprocessorOptions preprocessorOptions; 171 | clang::HeaderSearchOptions headerSearchOptions; 172 | clang::FrontendOptions frontendOptions; 173 | 174 | std::string errorInfo; 175 | llvm::raw_fd_ostream outstream(o_outputFilename.c_str(), errorInfo); 176 | { 177 | // Gather input files into a memory 178 | std::string mainFileText; 179 | llvm::raw_string_ostream stream(mainFileText); 180 | 181 | // Print the LLVMClangParser working directory 182 | { 183 | llvm::sys::Path cwd = llvm::sys::Path::GetCurrentDirectory(); 184 | outstream << "InvocationWorkingDirectory( path='" << cwd.c_str() << "' )\n"; 185 | } 186 | 187 | // -D 188 | for( llvm::cl::list::iterator iter = o_cppDefines.begin(), end = o_cppDefines.end(); iter != end; ++iter ) 189 | { 190 | std::string::size_type index = iter->find_first_of('='); 191 | std::string macro, value; 192 | if(index != std::string::npos) 193 | { 194 | macro = iter->substr(0, index); 195 | value = iter->substr(index+1, std::string::npos); 196 | } 197 | else 198 | { 199 | macro = (*iter); 200 | } 201 | 202 | stream << "#define " << macro << ' ' << value << '\n'; 203 | outstream << "InvocationDefine( name='" << macro << "', value='" << value << "' )\n"; 204 | } 205 | 206 | // -I 207 | for( std::vector::iterator iter = o_includePath.begin(), end = o_includePath.end(); iter != end; ++iter ) 208 | { 209 | headerSearchOptions.AddPath(*iter, clang::frontend::Angled, true, false, false); 210 | outstream << "InvocationIncludePath( path='" << *iter << "' )\n"; 211 | } 212 | 213 | // -exclude 214 | { 215 | // Do not free buffers associated with the compiler invocation 216 | preprocessorOptions.RetainRemappedFileBuffers = true; 217 | for( std::vector::iterator iter = o_excludeFilenames.begin(), end = o_excludeFilenames.end(); iter != end; ++iter ) 218 | { 219 | preprocessorOptions.addRemappedFile(iter->c_str(), emptyMemoryBuffer); 220 | } 221 | } 222 | 223 | // -exclude-pattern 224 | { 225 | for( std::vector::iterator iter = o_excludeFilenamePatterns.begin(), end = o_excludeFilenamePatterns.end(); iter != end; ++iter ) 226 | { 227 | filenamePatternExcluder->addExcludedPattern(*iter); 228 | } 229 | } 230 | 231 | // -A 232 | for( llvm::cl::list::iterator iter = o_passAttributes.begin(), end = o_passAttributes.end(); iter != end; ++iter ) 233 | { 234 | std::string::size_type index = iter->find_first_of('='); 235 | std::string macro, value; 236 | if(index != std::string::npos) 237 | { 238 | macro = iter->substr(0, index); 239 | value = iter->substr(index+1, std::string::npos); 240 | } 241 | else 242 | { 243 | macro = (*iter); 244 | } 245 | 246 | outstream << "InvocationAttribute( name='" << macro << "', value='" << value << "' )\n"; 247 | } 248 | 249 | for( std::vector::iterator iter = o_forceInclude.begin(), end = o_forceInclude.end(); iter != end; ++iter ) 250 | { 251 | stream << "#include<" << iter->c_str() << ">\n"; 252 | outstream << "InvocationForceInclude( path='" << iter->c_str() << "' )\n"; 253 | } 254 | for( std::vector::iterator iter = o_inputFilenames.begin(), end = o_inputFilenames.end(); iter != end; ++iter ) 255 | { 256 | stream << "#include<" << iter->c_str() << ">\n"; 257 | outstream << "InvocationInput( path='" << iter->c_str() << "' )\n"; 258 | } 259 | stream.flush(); 260 | 261 | llvm::MemoryBuffer* mainBuf = llvm::MemoryBuffer::getMemBufferCopy( llvm::StringRef(mainFileText.c_str(), mainFileText.size()), "masterInputFile" ); 262 | sourceManager.createMainFileIDForMemBuffer(mainBuf); 263 | } 264 | std::string resourceDir = o_resourceDir; 265 | if(!resourceDir.empty()) 266 | { 267 | resourceDir += "/include"; 268 | headerSearchOptions.AddPath(resourceDir, clang::frontend::System, false, false, true); 269 | } 270 | 271 | clang::InitializePreprocessor( preprocessor, clang::PreprocessorOptions(), headerSearchOptions, frontendOptions); 272 | 273 | Havok::ExtractASTConsumer consumer(outstream); 274 | clang::IdentifierTable identifierTable(langOptions); 275 | clang::SelectorTable selectorTable; 276 | clang::Builtin::Context builtinContext; 277 | clang::ASTContext astcontext( langOptions, sourceManager, targetInfo.getPtr(), identifierTable, selectorTable, builtinContext, 0); 278 | 279 | #ifdef _DEBUG 280 | outstream.SetUnbuffered(); 281 | #endif 282 | diagnostics.getClient()->BeginSourceFile(langOptions); 283 | clang::ParseAST(preprocessor, &consumer, astcontext); 284 | diagnostics.getClient()->EndSourceFile(); 285 | exitStatus = diagnostics.hasErrorOccurred() ? 1 : 0; 286 | if(exitStatus == 0) 287 | { 288 | // AST parsing succeeded, proceed with declaration dumping 289 | consumer.dumpAllDeclarations(); 290 | } 291 | else 292 | { 293 | outstream << "## The diagnostic engine returned an error during code parsing.\n"; 294 | } 295 | 296 | outstream.flush(); 297 | } 298 | delete emptyMemoryBuffer; 299 | llvm::llvm_shutdown(); 300 | 301 | return exitStatus; 302 | } 303 | -------------------------------------------------------------------------------- /test1.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) 2012 Havok. All rights reserved. This file is distributed under the terms 3 | // and conditions defined in file 'LICENSE.txt', which is part of this source code package. 4 | 5 | int global; 6 | 7 | int func(float f); 8 | 9 | struct Pair 10 | { 11 | double d; 12 | short e; 13 | }; 14 | --------------------------------------------------------------------------------