consoleDoc.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "console/console.h"
  24. #include "console/ast.h"
  25. #include "core/tAlgorithm.h"
  26. #include "core/strings/findMatch.h"
  27. #include "console/consoleInternal.h"
  28. #include "console/consoleObject.h"
  29. #include "core/stream/fileStream.h"
  30. #include "console/compiler.h"
  31. #include "core/frameAllocator.h"
  32. #include "console/engineAPI.h"
  33. //--- Information pertaining to this page... ------------------
  34. /// @file
  35. ///
  36. /// For specifics on using the consoleDoc functionality, see @ref console_autodoc
  37. ConsoleFunctionGroupBegin(ConsoleDoc, "Console self-documentation functions. These output psuedo C++ suitable for feeeding through Doxygen or another auto documentation tool.");
  38. DefineEngineFunction( dumpConsoleClasses, void, (bool dumpScript, bool dumpEngine), ( true, true ),
  39. "@brief Dumps all declared console classes to the console.\n\n"
  40. "@param dumpScript Optional parameter specifying whether or not classes defined in script should be dumped.\n"
  41. "@param dumpEngine Optional parameter specifying whether or not classes defined in the engine should be dumped.\n"
  42. "@ingroup Logging")
  43. {
  44. Namespace::dumpClasses( dumpScript, dumpEngine );
  45. }
  46. DefineEngineFunction(dumpConsoleFunctions, void, ( bool dumpScript, bool dumpEngine ), ( true, true ),
  47. "@brief Dumps all declared console functions to the console.\n"
  48. "@param dumpScript Optional parameter specifying whether or not functions defined in script should be dumped.\n"
  49. "@param dumpEngine Optional parameter specitying whether or not functions defined in the engine should be dumped.\n"
  50. "@ingroup Logging")
  51. {
  52. Namespace::dumpFunctions( dumpScript, dumpEngine );
  53. }
  54. ConsoleFunctionGroupEnd(ConsoleDoc);
  55. /// Helper table to convert type ids to human readable names.
  56. const char *typeNames[] =
  57. {
  58. "Script",
  59. "string",
  60. "int",
  61. "float",
  62. "void",
  63. "bool",
  64. "",
  65. "",
  66. "unknown_overload"
  67. };
  68. void printClassHeader(const char* usage, const char * className, const char * superClassName, const bool stub)
  69. {
  70. if(stub)
  71. {
  72. Con::printf("/// Stub class");
  73. Con::printf("/// ");
  74. Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No ");
  75. Con::printf("/// information was available for this class.");
  76. }
  77. if((usage != NULL) && strlen(usage))
  78. {
  79. // Copy Usage Document
  80. S32 usageLen = dStrlen( usage ) + 1;
  81. FrameTemp<char> usageStr( usageLen );
  82. dStrcpy( usageStr, usage, usageLen );
  83. // Print Header
  84. Con::printf( "/*!" );
  85. // Print line by line, skipping the @field lines.
  86. //
  87. // fetch first line end
  88. char *newLine = dStrchr( usageStr, '\n' );
  89. char *usagePtr = usageStr;
  90. do
  91. {
  92. // Copy of one line
  93. static char lineStr[2048] = {0};
  94. // Keyword will hold the last keyword (word following '@' or '\') encountered.
  95. static char keyword[8] = {0};
  96. S32 lineLen = 0;
  97. // If not the last line, increment pointer
  98. if( newLine != NULL )
  99. {
  100. *newLine = '\0';
  101. newLine ++;
  102. }
  103. // Copy line and update usagePtr
  104. dStrcpy( lineStr, usagePtr, 2048 );
  105. usagePtr = (newLine != NULL ) ? newLine : usagePtr;
  106. lineLen = dStrlen( lineStr );
  107. // Get the keyword. This is the first word after an '@' or '\'.
  108. const char* tempkw = dStrchr( lineStr, '@' );
  109. if( !tempkw )
  110. tempkw = dStrchr( lineStr, '\\' );
  111. // If we found a new keyword, set it, otherwise, keep using the
  112. // most recently found.
  113. if( tempkw )
  114. {
  115. dStrncpy( keyword, tempkw + 1, 5 );
  116. keyword[5] = '\0';
  117. }
  118. // Print all fields that aren't associated with the 'field' keyword.
  119. if( String::compare( keyword, "field" ) )
  120. Con::printf( "%s", lineStr ); // print lineStr as an unformatted string (otherwise '%' characters in the string could cause problems)
  121. // Fetch next line ending
  122. newLine = dStrchr( usagePtr, '\n' );
  123. } while( newLine != NULL );
  124. // DocBlock Footer
  125. Con::printf( " */" );
  126. }
  127. // Print out appropriate class header
  128. if(superClassName)
  129. Con::printf("class %s : public %s {", className, superClassName ? superClassName : "");
  130. else if(!className)
  131. Con::printf("namespace Global {");
  132. else
  133. Con::printf("class %s {", className);
  134. if(className)
  135. Con::printf(" public:");
  136. }
  137. void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage)
  138. {
  139. if(usage && usage[0] != ';' && usage[0] != 0)
  140. Con::printf(" /*! %s */", usage);
  141. Con::printf(" %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args);
  142. }
  143. void printGroupStart(const char * aName, const char * aDocs)
  144. {
  145. Con::printf("");
  146. Con::printf(" /*! @name %s", aName);
  147. if(aDocs)
  148. {
  149. Con::printf(" ");
  150. Con::printf(" %s", aDocs);
  151. }
  152. Con::printf(" @{ */");
  153. // Add a blank comment in order to make sure groups are parsed properly.
  154. Con::printf(" /*! */");
  155. }
  156. void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs, S32 aElementCount)
  157. {
  158. Con::printf(" /*!");
  159. if(aDocs)
  160. {
  161. Con::printf(" %s", aDocs);
  162. Con::printf(" ");
  163. }
  164. if(isDeprec)
  165. Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined.");
  166. Con::printf(" */");
  167. if (aElementCount == 1)
  168. {
  169. Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName);
  170. }
  171. else
  172. {
  173. Con::printf(" %s %s[%i];", isDeprec ? "deprecated" : aType, aName, aElementCount);
  174. }
  175. }
  176. void printGroupEnd()
  177. {
  178. Con::printf(" /// @}");
  179. Con::printf("");
  180. }
  181. void printClassFooter()
  182. {
  183. Con::printf("};");
  184. Con::printf("");
  185. }
  186. void Namespace::printNamespaceEntries(Namespace * g, bool dumpScript, bool dumpEngine )
  187. {
  188. static bool inGroup = false;
  189. // Go through all the entries.
  190. // Iterate through the methods of the namespace...
  191. for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
  192. {
  193. S32 eType = ewalk->mType;
  194. const char * funcName = ewalk->mFunctionName;
  195. if( ( eType == Entry::ConsoleFunctionType ) && !dumpScript )
  196. continue;
  197. if( ( eType != Entry::ConsoleFunctionType ) && !dumpEngine )
  198. continue;
  199. // If it's a function
  200. if( eType >= Entry::ConsoleFunctionType )
  201. {
  202. if (ewalk->mHeader != NULL)
  203. {
  204. // The function was defined with types, so we can print out the actual return type
  205. printClassMethod(true, ewalk->mHeader->mReturnString, funcName, ewalk->getArgumentsString().c_str(),
  206. ewalk->getDocString().c_str());
  207. }
  208. else
  209. {
  210. printClassMethod(true, typeNames[eType], funcName, (ewalk->getArgumentsString() + "...").c_str(),
  211. ewalk->getDocString().c_str());
  212. }
  213. }
  214. else if(ewalk->mType == Entry::GroupMarker)
  215. {
  216. if(!inGroup)
  217. printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
  218. else
  219. printGroupEnd();
  220. inGroup = !inGroup;
  221. }
  222. else if(ewalk->mType == Entry::ScriptCallbackType)
  223. {
  224. // It's a script callback - emit some sort of appropriate info.
  225. Con::printf(" /*! %s */", ewalk->getDocString().c_str());
  226. Con::printf(" %s;", ewalk->getPrototypeString().c_str());
  227. Con::printf("");
  228. }
  229. else if(ewalk->mFunctionOffset) // If it's a builtin function...
  230. {
  231. String args = ewalk->mCode->getFunctionArgs(ewalk->mFunctionOffset);
  232. printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, args, "");
  233. }
  234. else
  235. {
  236. Con::printf(" // got an unknown thing?? %d", ewalk->mType );
  237. }
  238. }
  239. }
  240. void Namespace::dumpClasses( bool dumpScript, bool dumpEngine )
  241. {
  242. VectorPtr<Namespace*> vec;
  243. trashCache();
  244. vec.reserve( 1024 );
  245. // We use mHashSequence to mark if we have traversed...
  246. // so mark all as zero to start.
  247. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  248. walk->mHashSequence = 0;
  249. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  250. {
  251. VectorPtr<Namespace*> stack;
  252. stack.reserve( 1024 );
  253. // Get all the parents of this namespace... (and mark them as we go)
  254. Namespace *parentWalk = walk;
  255. while(parentWalk)
  256. {
  257. if(parentWalk->mHashSequence != 0)
  258. break;
  259. if(parentWalk->mPackage == 0)
  260. {
  261. parentWalk->mHashSequence = 1; // Mark as traversed.
  262. stack.push_back(parentWalk);
  263. }
  264. parentWalk = parentWalk->mParent;
  265. }
  266. // Load stack into our results vector.
  267. while(stack.size())
  268. {
  269. vec.push_back(stack[stack.size() - 1]);
  270. stack.pop_back();
  271. }
  272. }
  273. // Go through previously discovered classes
  274. U32 i;
  275. for(i = 0; i < vec.size(); i++)
  276. {
  277. const char *className = vec[i]->mName;
  278. const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
  279. // Skip the global namespace, that gets dealt with in dumpFunctions
  280. if(!className) continue;
  281. // If we're just dumping script functions, then we don't want to dump
  282. // a class that only contains script functions. So, we iterate over all
  283. // the functions.
  284. if( !dumpScript )
  285. {
  286. bool found = false;
  287. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  288. {
  289. if( ewalk->mType != Entry::ConsoleFunctionType )
  290. {
  291. found = true;
  292. break;
  293. }
  294. }
  295. // If we don't have engine functions and the namespace name
  296. // doesn't match the class name... then its a script class.
  297. if ( !found && !vec[i]->isClass() )
  298. continue;
  299. }
  300. // And we do the same for engine functions.
  301. if( !dumpEngine )
  302. {
  303. bool found = false;
  304. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  305. {
  306. if( ewalk->mType == Entry::ConsoleFunctionType )
  307. {
  308. found = true;
  309. break;
  310. }
  311. }
  312. if( !found )
  313. continue;
  314. }
  315. // If we hit a class with no members and no classRep, do clever filtering.
  316. if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
  317. {
  318. // Print out a short stub so we get a proper class hierarchy.
  319. if(superClassName) { // Filter hack; we don't want non-inheriting classes...
  320. printClassHeader( NULL, className,superClassName, true);
  321. printClassFooter();
  322. }
  323. continue;
  324. }
  325. // Print the header for the class..
  326. printClassHeader(vec[i]->mUsage, className, superClassName, false);
  327. // Deal with entries.
  328. printNamespaceEntries(vec[i], dumpScript, dumpEngine);
  329. // Deal with the classRep (to get members)...
  330. AbstractClassRep *rep = vec[i]->mClassRep;
  331. AbstractClassRep::FieldList emptyList;
  332. AbstractClassRep::FieldList *parentList = &emptyList;
  333. AbstractClassRep::FieldList *fieldList = &emptyList;
  334. // Since all fields are defined in the engine, if we're not dumping
  335. // engine stuff, than we shouldn't dump the fields.
  336. if(dumpEngine && rep)
  337. {
  338. // Get information about the parent's fields...
  339. AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
  340. if(parentRep)
  341. parentList = &(parentRep->mFieldList);
  342. // Get information about our fields
  343. fieldList = &(rep->mFieldList);
  344. // Go through all our fields...
  345. for(U32 j = 0; j < fieldList->size(); j++)
  346. {
  347. switch((*fieldList)[j].type)
  348. {
  349. case AbstractClassRep::StartArrayFieldType:
  350. case AbstractClassRep::EndArrayFieldType:
  351. break;
  352. case AbstractClassRep::StartGroupFieldType:
  353. printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
  354. break;
  355. case AbstractClassRep::EndGroupFieldType:
  356. printGroupEnd();
  357. break;
  358. default:
  359. case AbstractClassRep::DeprecatedFieldType:
  360. {
  361. // Skip over fields that are already defined in
  362. // our parent class.
  363. if ( parentRep && parentRep->findField( (*fieldList)[j].pFieldname ) )
  364. continue;
  365. bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DeprecatedFieldType);
  366. if(isDeprecated)
  367. {
  368. printClassMember(
  369. true,
  370. "<deprecated>",
  371. (*fieldList)[j].pFieldname,
  372. (*fieldList)[j].pFieldDocs,
  373. (*fieldList)[j].elementCount
  374. );
  375. }
  376. else
  377. {
  378. ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
  379. printClassMember(
  380. false,
  381. cbt ? cbt->getTypeClassName() : "<unknown>",
  382. (*fieldList)[j].pFieldname,
  383. (*fieldList)[j].pFieldDocs,
  384. (*fieldList)[j].elementCount
  385. );
  386. }
  387. }
  388. }
  389. }
  390. }
  391. if( dumpScript )
  392. {
  393. // Print out fields defined in script docs for this namespace.
  394. // These fields are specified by the 'field' keyword in the usage
  395. // string.
  396. // The field type and name.
  397. char fieldName[256];
  398. char fieldDoc[1024];
  399. // Usage string iterator.
  400. const char* field = vec[i]->mUsage;
  401. while( field )
  402. {
  403. // Find the first field keyword.
  404. const char* tempField = dStrstr( field, "@field" );
  405. if( !tempField )
  406. tempField = dStrstr( field, "\\field" );
  407. field = tempField;
  408. if( !field )
  409. break;
  410. // Move to the field name.
  411. field += 7;
  412. // Copy the field type and name. These should both be followed by a
  413. // space so only in this case will we actually store it.
  414. S32 spaceCount = 0;
  415. S32 index = 0;
  416. bool valid = false;
  417. while( field && ( *field != '\n' ) )
  418. {
  419. if( index >= 255 )
  420. break;
  421. if( *field == ' ' )
  422. spaceCount++;
  423. if( spaceCount == 2 )
  424. {
  425. valid = true;
  426. break;
  427. }
  428. fieldName[index++] = *field;
  429. field++;
  430. }
  431. if( !valid )
  432. continue;
  433. fieldName[index] = '\0';
  434. // Now copy from field to the next keyword.
  435. const char* nextKeyword = dStrchr( field, '@' );
  436. if( !nextKeyword )
  437. nextKeyword = dStrchr( field, '\\' );
  438. // Grab the length of the doc string.
  439. S32 docLen = dStrlen( field );
  440. if( nextKeyword )
  441. docLen = nextKeyword - field;
  442. // Make sure it will fit in the buffer.
  443. if( docLen > 1023 )
  444. docLen = 1023;
  445. // Copy.
  446. dStrncpy( fieldDoc, field, docLen );
  447. fieldDoc[docLen] = '\0';
  448. field += docLen;
  449. // Print
  450. Con::printf( " /*!" );
  451. Con::printf( " %s", fieldDoc );
  452. Con::printf( " */" );
  453. Con::printf( " %s;", fieldName );
  454. }
  455. }
  456. // Close the class/namespace.
  457. printClassFooter();
  458. }
  459. }
  460. void Namespace::dumpFunctions( bool dumpScript, bool dumpEngine )
  461. {
  462. // Get the global namespace.
  463. Namespace* g = find(NULL); //->mParent;
  464. printClassHeader(NULL, NULL,NULL, false);
  465. while(g)
  466. {
  467. printNamespaceEntries(g, dumpScript, dumpEngine );
  468. g = g->mParent;
  469. }
  470. printClassFooter();
  471. }