consoleDoc.cc 18 KB

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