consoleDoc.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 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 "collection/findIterator.h"
  26. #include "io/resource/resourceManager.h"
  27. #include "string/findMatch.h"
  28. #include "console/consoleInternal.h"
  29. #include "console/consoleObject.h"
  30. #include "io/fileStream.h"
  31. #include "console/compiler.h"
  32. #include "memory/frameAllocator.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. ConsoleFunction(dumpConsoleClasses, void, 1, 3, "(bool dumpScript = true, bool dumpEngine = true) dumps all declared console classes to the console.\n"
  39. "This will dump all classes and methods that were registered from within the engine, AND from the console via scripts.\n"
  40. "@param dumpScript Specifies whether or not classes defined in script should be dumped.\n"
  41. "@param dumpEngine Specifies whether or not classes defined in the engine should be dumped.")
  42. {
  43. bool dumpScript = true;
  44. if( argc > 1 )
  45. dumpScript = dAtob( argv[1] );
  46. bool dumpEngine = true;
  47. if( argc > 2 )
  48. dumpEngine = dAtob( argv[2] );
  49. Namespace::dumpClasses( dumpScript, dumpEngine );
  50. }
  51. ConsoleFunction(dumpConsoleFunctions, void, 1, 3, "(bool dumpScript = true, bool dumpEngine = true) Dumps all declared console functions to the console.\n"
  52. "This will dump all funtions that were registered from within the engine, AND from the console via scripts.\n"
  53. "@param dumpScript Specifies whether or not functions defined in script should be dumped.\n"
  54. "@param dumpEngine Specifies whether or not functions defined in the engine should be dumped."
  55. "@sa dumpConsoleMethods")
  56. {
  57. bool dumpScript = true;
  58. if( argc > 1 )
  59. dumpScript = dAtob( argv[1] );
  60. bool dumpEngine = true;
  61. if( argc > 2 )
  62. dumpEngine = dAtob( argv[2] );
  63. Namespace::dumpFunctions( dumpScript, dumpEngine );
  64. }
  65. ConsoleFunctionGroupEnd(ConsoleDoc);
  66. /// Helper table to convert type ids to human readable names.
  67. const char *typeNames[] =
  68. {
  69. "Script",
  70. "string",
  71. "int",
  72. "float",
  73. "void",
  74. "bool",
  75. "",
  76. "",
  77. "unknown_overload"
  78. };
  79. void printClassHeader(const char* usage, const char * className, const char * superClassName, const bool stub)
  80. {
  81. if(stub)
  82. {
  83. Con::printf("/// Stub class");
  84. Con::printf("/// ");
  85. Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No ");
  86. Con::printf("/// information was available for this class.");
  87. }
  88. if( usage != NULL )
  89. {
  90. // Copy Usage Document
  91. S32 usageLen = dStrlen( usage );
  92. FrameTemp<char> usageStr( usageLen );
  93. dStrcpy( usageStr, usage );
  94. // Print Header
  95. Con::printf( "/*!" );
  96. // Print line by line, skipping the @field lines.
  97. //
  98. // fetch first line end
  99. char *newLine = dStrchr( usageStr, '\n' );
  100. char *usagePtr = usageStr;
  101. do
  102. {
  103. // Copy of one line
  104. static char lineStr[2048] = {0};
  105. // Keyword will hold the last keyword (word following '@' or '\') encountered.
  106. static char keyword[8] = {0};
  107. S32 lineLen = 0;
  108. // If not the last line, increment pointer
  109. if( newLine != NULL )
  110. {
  111. *newLine = '\0';
  112. newLine ++;
  113. }
  114. // Copy line and update usagePtr
  115. dStrcpy( lineStr, usagePtr );
  116. usagePtr = (newLine != NULL ) ? newLine : usagePtr;
  117. lineLen = dStrlen( lineStr );
  118. // Get the keyword. This is the first word after an '@' or '\'.
  119. const char* tempkw = dStrchr( lineStr, '@' );
  120. if( !tempkw )
  121. tempkw = dStrchr( lineStr, '\\' );
  122. // If we found a new keyword, set it, otherwise, keep using the
  123. // most recently found.
  124. if( tempkw )
  125. {
  126. dStrncpy( keyword, tempkw + 1, 5 );
  127. keyword[5] = '\0';
  128. }
  129. // Print all fields that aren't associated with the 'field' keyword.
  130. if( dStrcmp( keyword, "field" ) )
  131. Con::printf( lineStr );
  132. // Fetch next line ending
  133. newLine = dStrchr( usagePtr, '\n' );
  134. } while( newLine != NULL );
  135. // DocBlock Footer
  136. Con::printf( " */" );
  137. }
  138. // Print out appropriate class header
  139. if(superClassName)
  140. Con::printf("class %s : public %s {", className, superClassName ? superClassName : "");
  141. else if(!className)
  142. Con::printf("namespace Global {");
  143. else
  144. Con::printf("class %s {", className);
  145. if(className)
  146. Con::printf(" public:");
  147. }
  148. void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage)
  149. {
  150. if(usage && usage[0] != ';' && usage[0] != 0)
  151. Con::printf(" /*! %s */", usage);
  152. Con::printf(" %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args);
  153. }
  154. void printGroupStart(const char * aName, const char * aDocs)
  155. {
  156. Con::printf("");
  157. Con::printf(" /*! @name %s", aName);
  158. if(aDocs)
  159. {
  160. Con::printf(" ");
  161. Con::printf(" %s", aDocs);
  162. }
  163. Con::printf(" @{ */");
  164. }
  165. void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs)
  166. {
  167. Con::printf(" /*!");
  168. if(aDocs)
  169. {
  170. Con::printf(" %s", aDocs);
  171. Con::printf(" ");
  172. }
  173. if(isDeprec)
  174. Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined.");
  175. Con::printf(" */");
  176. Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName);
  177. }
  178. void printGroupEnd()
  179. {
  180. Con::printf(" /// @}");
  181. Con::printf("");
  182. }
  183. void printClassFooter()
  184. {
  185. Con::printf("};");
  186. Con::printf("");
  187. }
  188. void Namespace::printNamespaceEntries(Namespace * g, bool dumpScript, bool dumpEngine )
  189. {
  190. static bool inGroup = false;
  191. // Go through all the entries.
  192. // Iterate through the methods of the namespace...
  193. for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
  194. {
  195. char buffer[1024]; //< This will bite you in the butt someday.
  196. int eType = ewalk->mType;
  197. const char * funcName = ewalk->mFunctionName;
  198. if( ( eType == Entry::ScriptFunctionType ) && !dumpScript )
  199. continue;
  200. if( ( eType != Entry::ScriptFunctionType ) && !dumpEngine )
  201. continue;
  202. // If it's a function
  203. if(eType >= Entry::ScriptFunctionType || eType == Entry::OverloadMarker)
  204. {
  205. if(eType==Entry::OverloadMarker)
  206. {
  207. // Deal with crap from the OverloadMarker case.
  208. // It has no type information so we have to "correct" its type.
  209. // Find the original
  210. eType = 8;
  211. for(Entry *eseek = g->mEntryList; eseek; eseek = eseek->mNext)
  212. {
  213. if(!dStrcmp(eseek->mFunctionName, ewalk->cb.mGroupName))
  214. {
  215. eType = eseek->mType;
  216. break;
  217. }
  218. }
  219. // And correct the name
  220. funcName = ewalk->cb.mGroupName;
  221. }
  222. // A quick note - if your usage field starts with a (, then it's auto-integrated into
  223. // the script docs! Use this HEAVILY!
  224. // We add some heuristics here as well. If you're of the form:
  225. // *.methodName(*)
  226. // then we will also extract parameters.
  227. const char *use = ewalk->mUsage ? ewalk->mUsage : "";
  228. const char *bgn = dStrchr(use, '(');
  229. const char *end = dStrchr(use, ')');
  230. const char *dot = dStrchr(use, '.');
  231. while( *use == ' ' )
  232. use++;
  233. if(use[0] == '(')
  234. {
  235. if(!end)
  236. end = use + 1;
  237. use++;
  238. U32 len = end - use;
  239. dStrncpy(buffer, use, len);
  240. buffer[len] = 0;
  241. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  242. continue; // Skip to next one.
  243. }
  244. // We check to see if they're giving a prototype.
  245. if(dot && bgn && end) // If there's two parentheses, and a dot...
  246. if(dot < bgn && bgn < end) // And they're in the order dot, bgn, end...
  247. {
  248. use++;
  249. U32 len = end - bgn - 1;
  250. dStrncpy(buffer, bgn+1, len);
  251. buffer[len] = 0;
  252. // Then let's do the heuristic-trick
  253. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  254. continue; // Get to next item.
  255. }
  256. // Finally, see if they did it foo(*) style.
  257. char* func_pos = dStrstr(use, funcName);
  258. if((func_pos) && (func_pos < bgn) && (end > bgn))
  259. {
  260. U32 len = end - bgn - 1;
  261. dStrncpy(buffer, bgn+1, len);
  262. buffer[len] = 0;
  263. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  264. continue;
  265. }
  266. // Default...
  267. printClassMethod(true, typeNames[eType], funcName, "", ewalk->mUsage);
  268. }
  269. else if(ewalk->mType == Entry::GroupMarker)
  270. {
  271. if(!inGroup)
  272. printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
  273. else
  274. printGroupEnd();
  275. inGroup = !inGroup;
  276. }
  277. else if(ewalk->mFunctionOffset) // If it's a builtin function...
  278. {
  279. ewalk->mCode->getFunctionArgs(buffer, ewalk->mFunctionOffset);
  280. printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, buffer, "");
  281. }
  282. else
  283. {
  284. Con::printf(" // got an unknown thing?? %d", ewalk->mType );
  285. }
  286. }
  287. }
  288. void Namespace::dumpClasses( bool dumpScript, bool dumpEngine )
  289. {
  290. VectorPtr<Namespace*> vec;
  291. trashCache();
  292. vec.reserve( 1024 );
  293. // We use mHashSequence to mark if we have traversed...
  294. // so mark all as zero to start.
  295. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  296. walk->mHashSequence = 0;
  297. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  298. {
  299. VectorPtr<Namespace*> stack;
  300. stack.reserve( 1024 );
  301. // Get all the parents of this namespace... (and mark them as we go)
  302. Namespace *parentWalk = walk;
  303. while(parentWalk)
  304. {
  305. if(parentWalk->mHashSequence != 0)
  306. break;
  307. if(parentWalk->mPackage == 0)
  308. {
  309. parentWalk->mHashSequence = 1; // Mark as traversed.
  310. stack.push_back(parentWalk);
  311. }
  312. parentWalk = parentWalk->mParent;
  313. }
  314. // Load stack into our results vector.
  315. while(stack.size())
  316. {
  317. vec.push_back(stack[stack.size() - 1]);
  318. stack.pop_back();
  319. }
  320. }
  321. // Go through previously discovered classes
  322. U32 i;
  323. for(i = 0; i < (U32)vec.size(); i++)
  324. {
  325. const char *className = vec[i]->mName;
  326. const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
  327. // Skip the global namespace, that gets dealt with in dumpFunctions
  328. if(!className) continue;
  329. // If we're just dumping script functions, then we don't want to dump
  330. // a class that only contains script functions. So, we iterate over all
  331. // the functions.
  332. if( !dumpScript )
  333. {
  334. bool found = false;
  335. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  336. {
  337. if( ewalk->mType != Entry::ScriptFunctionType )
  338. {
  339. found = true;
  340. break;
  341. }
  342. }
  343. if( !found )
  344. continue;
  345. }
  346. // And we do the same for engine functions.
  347. if( !dumpEngine )
  348. {
  349. bool found = false;
  350. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  351. {
  352. if( ewalk->mType == Entry::ScriptFunctionType )
  353. {
  354. found = true;
  355. break;
  356. }
  357. }
  358. if( !found )
  359. continue;
  360. }
  361. // If we hit a class with no members and no classRep, do clever filtering.
  362. if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
  363. {
  364. // Print out a short stub so we get a proper class hierarchy.
  365. if(superClassName) { // Filter hack; we don't want non-inheriting classes...
  366. printClassHeader( NULL, className,superClassName, true);
  367. printClassFooter();
  368. }
  369. continue;
  370. }
  371. // Print the header for the class..
  372. printClassHeader(vec[i]->mUsage, className, superClassName, false);
  373. // Deal with entries.
  374. printNamespaceEntries(vec[i], dumpScript, dumpEngine);
  375. // Deal with the classRep (to get members)...
  376. AbstractClassRep *rep = vec[i]->mClassRep;
  377. AbstractClassRep::FieldList emptyList;
  378. AbstractClassRep::FieldList *parentList = &emptyList;
  379. AbstractClassRep::FieldList *fieldList = &emptyList;
  380. // Since all fields are defined in the engine, if we're not dumping
  381. // engine stuff, than we shouldn't dump the fields.
  382. if(dumpEngine && rep)
  383. {
  384. // Get information about the parent's fields...
  385. AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
  386. if(parentRep)
  387. parentList = &(parentRep->mFieldList);
  388. // Get information about our fields
  389. fieldList = &(rep->mFieldList);
  390. // Go through all our fields...
  391. for(U32 j = 0; j < (U32)fieldList->size(); j++)
  392. {
  393. switch((*fieldList)[j].type)
  394. {
  395. case AbstractClassRep::StartGroupFieldType:
  396. printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
  397. break;
  398. case AbstractClassRep::EndGroupFieldType:
  399. printGroupEnd();
  400. break;
  401. default:
  402. case AbstractClassRep::DepricatedFieldType:
  403. {
  404. bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DepricatedFieldType);
  405. if(isDeprecated)
  406. {
  407. printClassMember(
  408. true,
  409. "<deprecated>",
  410. (*fieldList)[j].pFieldname,
  411. (*fieldList)[j].pFieldDocs
  412. );
  413. }
  414. else
  415. {
  416. ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
  417. printClassMember(
  418. false,
  419. cbt ? cbt->getTypeClassName() : "<unknown>",
  420. (*fieldList)[j].pFieldname,
  421. (*fieldList)[j].pFieldDocs
  422. );
  423. }
  424. }
  425. }
  426. }
  427. }
  428. if( dumpScript )
  429. {
  430. // Print out fields defined in script docs for this namespace.
  431. // These fields are specified by the 'field' keyword in the usage
  432. // string.
  433. // The field type and name.
  434. char fieldName[256];
  435. char fieldDoc[1024];
  436. // Usage string iterator.
  437. const char* field = vec[i]->mUsage;
  438. while( field )
  439. {
  440. // Find the first field keyword.
  441. const char* tempField = dStrstr( field, "@field" );
  442. if( !tempField )
  443. tempField = dStrstr( field, "\\field" );
  444. field = tempField;
  445. if( !field )
  446. break;
  447. // Move to the field name.
  448. field += 7;
  449. // Copy the field type and name. These should both be followed by a
  450. // space so only in this case will we actually store it.
  451. S32 spaceCount = 0;
  452. S32 index = 0;
  453. bool valid = false;
  454. while( field && ( *field != '\n' ) )
  455. {
  456. if( index >= 255 )
  457. break;
  458. if( *field == ' ' )
  459. spaceCount++;
  460. if( spaceCount == 2 )
  461. {
  462. valid = true;
  463. break;
  464. }
  465. fieldName[index++] = *field;
  466. field++;
  467. }
  468. if( !valid )
  469. continue;
  470. fieldName[index] = '\0';
  471. // Now copy from field to the next keyword.
  472. const char* nextKeyword = dStrchr( field, '@' );
  473. if( !nextKeyword )
  474. nextKeyword = dStrchr( field, '\\' );
  475. // Grab the length of the doc string.
  476. S32 docLen = dStrlen( field );
  477. if( nextKeyword )
  478. docLen = nextKeyword - field;
  479. // Make sure it will fit in the buffer.
  480. if( docLen > 1023 )
  481. docLen = 1023;
  482. // Copy.
  483. dStrncpy( fieldDoc, field, docLen );
  484. fieldDoc[docLen] = '\0';
  485. field += docLen;
  486. // Print
  487. Con::printf( " /*!" );
  488. Con::printf( " %s", fieldDoc );
  489. Con::printf( " */" );
  490. Con::printf( " %s;", fieldName );
  491. }
  492. }
  493. // Close the class/namespace.
  494. printClassFooter();
  495. }
  496. }
  497. void Namespace::dumpFunctions( bool dumpScript, bool dumpEngine )
  498. {
  499. // Get the global namespace.
  500. Namespace* g = find(NULL); //->mParent;
  501. printClassHeader(NULL, NULL,NULL, false);
  502. while(g)
  503. {
  504. printNamespaceEntries(g, dumpScript, dumpEngine );
  505. g = g->mParent;
  506. }
  507. printClassFooter();
  508. }