2
0

consoleDoc.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  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. { /* nothing to print */ }
  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. // no longer want to see "virtual" attribute for each "built-in" function.
  153. // (they are all built-in if you dump before loading scripts.)
  154. Con::printf(" %s%s %s(%s) {}", /* isVirtual */ false ? "virtual " : "", retType, methodName, args);
  155. }
  156. // a doxygen "group" or "module" declaration to organize collections arbitrarily
  157. // (doxygen uses the terms "group" and "module" interchangeably)
  158. void printGroupStart(const char * aName, const char * aDocs)
  159. {
  160. Con::printf("");
  161. Con::printf(" /*! @defgroup %s", aName);
  162. if(aDocs)
  163. {
  164. Con::printf(" ");
  165. Con::printf(" %s", aDocs);
  166. }
  167. Con::printf(" @{ */");
  168. }
  169. // a doxygen "member group" declaration, for use within a class to organize methods and variables
  170. void printMemberGroupStart(const char * aName, const char * aDocs)
  171. {
  172. Con::printf("");
  173. Con::printf(" /*! @name %s", aName);
  174. if(aDocs)
  175. {
  176. Con::printf(" ");
  177. Con::printf(" %s", aDocs);
  178. }
  179. Con::printf(" @{ */");
  180. }
  181. void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs)
  182. {
  183. Con::printf(" /*!");
  184. if(aDocs)
  185. {
  186. Con::printf(" %s", aDocs);
  187. Con::printf(" ");
  188. }
  189. if(isDeprec)
  190. Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined.");
  191. Con::printf(" */");
  192. Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName);
  193. }
  194. // see printGroupStart
  195. void printGroupEnd()
  196. {
  197. Con::printf(" /// @}");
  198. Con::printf("");
  199. }
  200. // see printMemberGroupStart
  201. void printMemberGroupEnd()
  202. {
  203. Con::printf(" /// @}");
  204. Con::printf("");
  205. }
  206. void printClassFooter()
  207. {
  208. Con::printf("};");
  209. Con::printf("");
  210. }
  211. void Namespace::printNamespaceEntries(Namespace * g, bool dumpScript, bool dumpEngine )
  212. {
  213. static bool inGroup = false;
  214. // Go through all the entries.
  215. // Iterate through the methods of the namespace...
  216. for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
  217. {
  218. char buffer[1024]; //< This will bite you in the butt someday.
  219. int eType = ewalk->mType;
  220. const char * funcName = ewalk->mFunctionName;
  221. if( ( eType == Entry::ScriptFunctionType ) && !dumpScript )
  222. continue;
  223. if( ( eType != Entry::ScriptFunctionType ) && !dumpEngine )
  224. continue;
  225. // If it's a function
  226. if(eType >= Entry::ScriptFunctionType || eType == Entry::OverloadMarker)
  227. {
  228. if(eType==Entry::OverloadMarker)
  229. {
  230. // Deal with crap from the OverloadMarker case.
  231. // It has no type information so we have to "correct" its type.
  232. // Find the original
  233. eType = 8;
  234. for(Entry *eseek = g->mEntryList; eseek; eseek = eseek->mNext)
  235. {
  236. if(!dStrcmp(eseek->mFunctionName, ewalk->cb.mGroupName))
  237. {
  238. eType = eseek->mType;
  239. break;
  240. }
  241. }
  242. // And correct the name
  243. funcName = ewalk->cb.mGroupName;
  244. }
  245. // A quick note - if your usage field starts with a (, then it's auto-integrated into
  246. // the script docs! Use this HEAVILY!
  247. // We add some heuristics here as well. If you're of the form:
  248. // *.methodName(*)
  249. // then we will also extract parameters.
  250. const char *use = ewalk->mUsage ? ewalk->mUsage : "";
  251. const char *bgn = dStrchr(use, '(');
  252. const char *end = dStrchr(use, ')');
  253. const char *dot = dStrchr(use, '.');
  254. while( *use == ' ' )
  255. use++;
  256. if(use[0] == '(')
  257. {
  258. if(!end)
  259. end = use + 1;
  260. use++;
  261. U32 len = end - use;
  262. dStrncpy(buffer, use, len);
  263. buffer[len] = 0;
  264. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  265. continue; // Skip to next one.
  266. }
  267. // We check to see if they're giving a prototype.
  268. if(dot && bgn && end) // If there's two parentheses, and a dot...
  269. if(dot < bgn && bgn < end) // And they're in the order dot, bgn, end...
  270. {
  271. use++;
  272. U32 len = end - bgn - 1;
  273. dStrncpy(buffer, bgn+1, len);
  274. buffer[len] = 0;
  275. // Then let's do the heuristic-trick
  276. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  277. continue; // Get to next item.
  278. }
  279. // Finally, see if they did it foo(*) style.
  280. char* func_pos = dStrstr(use, funcName);
  281. if((func_pos) && (func_pos < bgn) && (end > bgn))
  282. {
  283. U32 len = end - bgn - 1;
  284. dStrncpy(buffer, bgn+1, len);
  285. buffer[len] = 0;
  286. printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
  287. continue;
  288. }
  289. // Default...
  290. printClassMethod(true, typeNames[eType], funcName, "", ewalk->mUsage);
  291. }
  292. else if(ewalk->mType == Entry::GroupMarker)
  293. {
  294. if(!inGroup)
  295. printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
  296. else
  297. printGroupEnd();
  298. inGroup = !inGroup;
  299. }
  300. else if(ewalk->mFunctionOffset) // If it's a builtin function...
  301. {
  302. ewalk->mCode->getFunctionArgs(buffer, ewalk->mFunctionOffset);
  303. printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, buffer, "");
  304. }
  305. else
  306. {
  307. Con::printf(" // got an unknown thing?? %d", ewalk->mType );
  308. }
  309. }
  310. }
  311. void Namespace::dumpClasses( bool dumpScript, bool dumpEngine )
  312. {
  313. VectorPtr<Namespace*> vec;
  314. trashCache();
  315. vec.reserve( 1024 );
  316. // We use mHashSequence to mark if we have traversed...
  317. // so mark all as zero to start.
  318. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  319. walk->mHashSequence = 0;
  320. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  321. {
  322. VectorPtr<Namespace*> stack;
  323. stack.reserve( 1024 );
  324. // Get all the parents of this namespace... (and mark them as we go)
  325. Namespace *parentWalk = walk;
  326. while(parentWalk)
  327. {
  328. if(parentWalk->mHashSequence != 0)
  329. break;
  330. if(parentWalk->mPackage == 0)
  331. {
  332. parentWalk->mHashSequence = 1; // Mark as traversed.
  333. stack.push_back(parentWalk);
  334. }
  335. parentWalk = parentWalk->mParent;
  336. }
  337. // Load stack into our results vector.
  338. while(stack.size())
  339. {
  340. vec.push_back(stack[stack.size() - 1]);
  341. stack.pop_back();
  342. }
  343. }
  344. // Go through previously discovered classes
  345. U32 i;
  346. for(i = 0; i < (U32)vec.size(); i++)
  347. {
  348. const char *className = vec[i]->mName;
  349. const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
  350. // Skip the global namespace, that gets dealt with in dumpFunctions
  351. if(!className) continue;
  352. // If we're just dumping script functions, then we don't want to dump
  353. // a class that only contains script functions. So, we iterate over all
  354. // the functions.
  355. if( !dumpScript )
  356. {
  357. bool found = false;
  358. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  359. {
  360. if( ewalk->mType != Entry::ScriptFunctionType )
  361. {
  362. found = true;
  363. break;
  364. }
  365. }
  366. if( !found )
  367. continue;
  368. }
  369. // And we do the same for engine functions.
  370. if( !dumpEngine )
  371. {
  372. bool found = false;
  373. for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext)
  374. {
  375. if( ewalk->mType == Entry::ScriptFunctionType )
  376. {
  377. found = true;
  378. break;
  379. }
  380. }
  381. if( !found )
  382. continue;
  383. }
  384. // If we hit a class with no members and no classRep, do clever filtering.
  385. if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
  386. {
  387. // Print out a short stub so we get a proper class hierarchy.
  388. if(superClassName) { // Filter hack; we don't want non-inheriting classes...
  389. printClassHeader( NULL, className,superClassName, true);
  390. printClassFooter();
  391. }
  392. continue;
  393. }
  394. // Print the header for the class..
  395. printClassHeader(vec[i]->mUsage, className, superClassName, false);
  396. // Deal with entries.
  397. printNamespaceEntries(vec[i], dumpScript, dumpEngine);
  398. // Deal with the classRep (to get members)...
  399. AbstractClassRep *rep = vec[i]->mClassRep;
  400. AbstractClassRep::FieldList emptyList;
  401. AbstractClassRep::FieldList *parentList = &emptyList;
  402. AbstractClassRep::FieldList *fieldList = &emptyList;
  403. // Since all fields are defined in the engine, if we're not dumping
  404. // engine stuff, than we shouldn't dump the fields.
  405. if(dumpEngine && rep)
  406. {
  407. // Get information about the parent's fields...
  408. AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
  409. if(parentRep)
  410. parentList = &(parentRep->mFieldList);
  411. // Get information about our fields
  412. fieldList = &(rep->mFieldList);
  413. // Go through all our fields...
  414. for(U32 j = 0; j < (U32)fieldList->size(); j++)
  415. {
  416. switch((*fieldList)[j].type)
  417. {
  418. case AbstractClassRep::StartGroupFieldType:
  419. printMemberGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
  420. break;
  421. case AbstractClassRep::EndGroupFieldType:
  422. printMemberGroupEnd();
  423. break;
  424. default:
  425. case AbstractClassRep::DepricatedFieldType:
  426. {
  427. bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DepricatedFieldType);
  428. if(isDeprecated)
  429. {
  430. printClassMember(
  431. true,
  432. "<deprecated>",
  433. (*fieldList)[j].pFieldname,
  434. (*fieldList)[j].pFieldDocs
  435. );
  436. }
  437. else
  438. {
  439. ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
  440. printClassMember(
  441. false,
  442. cbt ? cbt->getTypeClassName() : "<unknown>",
  443. (*fieldList)[j].pFieldname,
  444. (*fieldList)[j].pFieldDocs
  445. );
  446. }
  447. }
  448. }
  449. }
  450. }
  451. if( dumpScript )
  452. {
  453. // Print out fields defined in script docs for this namespace.
  454. // These fields are specified by the 'field' keyword in the usage
  455. // string.
  456. // The field type and name.
  457. char fieldName[256];
  458. char fieldDoc[1024];
  459. // Usage string iterator.
  460. const char* field = vec[i]->mUsage;
  461. while( field )
  462. {
  463. // Find the first field keyword.
  464. const char* tempField = dStrstr( field, "@field" );
  465. if( !tempField )
  466. tempField = dStrstr( field, "\\field" );
  467. field = tempField;
  468. if( !field )
  469. break;
  470. // Move to the field name.
  471. field += 7;
  472. // Copy the field type and name. These should both be followed by a
  473. // space so only in this case will we actually store it.
  474. S32 spaceCount = 0;
  475. S32 index = 0;
  476. bool valid = false;
  477. while( field && ( *field != '\n' ) )
  478. {
  479. if( index >= 255 )
  480. break;
  481. if( *field == ' ' )
  482. spaceCount++;
  483. if( spaceCount == 2 )
  484. {
  485. valid = true;
  486. break;
  487. }
  488. fieldName[index++] = *field;
  489. field++;
  490. }
  491. if( !valid )
  492. continue;
  493. fieldName[index] = '\0';
  494. // Now copy from field to the next keyword.
  495. const char* nextKeyword = dStrchr( field, '@' );
  496. if( !nextKeyword )
  497. nextKeyword = dStrchr( field, '\\' );
  498. // Grab the length of the doc string.
  499. S32 docLen = dStrlen( field );
  500. if( nextKeyword )
  501. docLen = nextKeyword - field;
  502. // Make sure it will fit in the buffer.
  503. if( docLen > 1023 )
  504. docLen = 1023;
  505. // Copy.
  506. dStrncpy( fieldDoc, field, docLen );
  507. fieldDoc[docLen] = '\0';
  508. field += docLen;
  509. // Print
  510. Con::printf( " /*!" );
  511. Con::printf( " %s", fieldDoc );
  512. Con::printf( " */" );
  513. Con::printf( " %s;", fieldName );
  514. }
  515. }
  516. // Close the class/namespace.
  517. printClassFooter();
  518. }
  519. }
  520. void Namespace::dumpFunctions( bool dumpScript, bool dumpEngine )
  521. {
  522. // Get the global namespace.
  523. Namespace* g = find(NULL); //->mParent;
  524. while(g)
  525. {
  526. printNamespaceEntries(g, dumpScript, dumpEngine );
  527. g = g->mParent;
  528. }
  529. }