consoleNamespace.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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 "consoleNamespace.h"
  23. #include "platform/platform.h"
  24. #include "console/console.h"
  25. #include "console/ast.h"
  26. #include "collection/findIterator.h"
  27. #include "io/resource/resourceManager.h"
  28. #include "string/findMatch.h"
  29. #include "console/consoleInternal.h"
  30. #include "io/fileStream.h"
  31. #include "console/compiler.h"
  32. #include "consoleNamespace_ScriptBinding.h"
  33. U32 Namespace::mCacheSequence = 0;
  34. DataChunker Namespace::mCacheAllocator;
  35. DataChunker Namespace::mAllocator;
  36. Namespace *Namespace::mNamespaceList = NULL;
  37. Namespace *Namespace::mGlobalNamespace = NULL;
  38. Namespace::Entry::Entry()
  39. {
  40. mCode = NULL;
  41. mType = InvalidFunctionType;
  42. }
  43. void Namespace::Entry::clear()
  44. {
  45. if(mCode)
  46. {
  47. mCode->decRefCount();
  48. mCode = NULL;
  49. }
  50. // Clean up usage strings generated for script functions.
  51. if( ( mType == Namespace::Entry::ScriptFunctionType ) && mUsage )
  52. {
  53. delete mUsage;
  54. mUsage = NULL;
  55. }
  56. }
  57. Namespace::Namespace()
  58. {
  59. mPackage = NULL;
  60. mUsage = NULL;
  61. mCleanUpUsage = false;
  62. mName = NULL;
  63. mParent = NULL;
  64. mNext = NULL;
  65. mEntryList = NULL;
  66. mHashSize = 0;
  67. mHashTable = 0;
  68. mHashSequence = 0;
  69. mRefCountToParent = 0;
  70. mClassRep = 0;
  71. }
  72. Namespace::~Namespace()
  73. {
  74. if( mUsage && mCleanUpUsage )
  75. {
  76. dFree (const_cast <char *> (mUsage));
  77. mUsage = NULL;
  78. mCleanUpUsage = false;
  79. }
  80. }
  81. void Namespace::clearEntries()
  82. {
  83. for(Entry *walk = mEntryList; walk; walk = walk->mNext)
  84. walk->clear();
  85. }
  86. Namespace *Namespace::find(StringTableEntry name, StringTableEntry package)
  87. {
  88. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  89. if(walk->mName == name && walk->mPackage == package)
  90. return walk;
  91. Namespace *ret = (Namespace *) mAllocator.alloc(sizeof(Namespace));
  92. constructInPlace(ret);
  93. ret->mPackage = package;
  94. ret->mName = name;
  95. ret->mNext = mNamespaceList;
  96. mNamespaceList = ret;
  97. return ret;
  98. }
  99. bool Namespace::canTabComplete(const char *prevText, const char *bestMatch, const char *newText, S32 baseLen, bool fForward)
  100. {
  101. // test if it matches the first baseLen chars:
  102. if(dStrnicmp(newText, prevText, baseLen))
  103. return false;
  104. if (fForward)
  105. {
  106. if(!bestMatch)
  107. return dStricmp(newText, prevText) > 0;
  108. else
  109. return (dStricmp(newText, prevText) > 0) &&
  110. (dStricmp(newText, bestMatch) < 0);
  111. }
  112. else
  113. {
  114. if (dStrlen(prevText) == (U32) baseLen)
  115. {
  116. // look for the 'worst match'
  117. if(!bestMatch)
  118. return dStricmp(newText, prevText) > 0;
  119. else
  120. return dStricmp(newText, bestMatch) > 0;
  121. }
  122. else
  123. {
  124. if (!bestMatch)
  125. return (dStricmp(newText, prevText) < 0);
  126. else
  127. return (dStricmp(newText, prevText) < 0) &&
  128. (dStricmp(newText, bestMatch) > 0);
  129. }
  130. }
  131. }
  132. bool Namespace::unlinkClass(Namespace* parent)
  133. {
  134. Namespace* walk = this;
  135. while(walk->mParent && walk->mParent->mName == mName)
  136. walk = walk->mParent;
  137. // Make sure "parent" is the direct parent namespace.
  138. if(walk->mParent && walk->mParent != parent)
  139. {
  140. Con::errorf(ConsoleLogEntry::General, "Namespace::unlinkClass - cannot unlink namespace parent linkage for %s for %s.",
  141. walk->mName, walk->mParent->mName);
  142. return false;
  143. }
  144. // Decrease the reference count. Note that we do this on the bottom-most namespace.
  145. AssertWarn(mRefCountToParent > 0, "Namespace::unlinkClass - reference count to parent is already at 0");
  146. mRefCountToParent--;
  147. // Unlink if the count dropped to zero.
  148. if(mRefCountToParent == 0)
  149. {
  150. walk->mParent = NULL;
  151. trashCache();
  152. }
  153. return true;
  154. }
  155. bool Namespace::classLinkTo(Namespace* parent)
  156. {
  157. Namespace* walk = this;
  158. while(walk->mParent && walk->mParent->mName == mName)
  159. walk = walk->mParent;
  160. // Make sure there is no existing parent namespace.
  161. if(walk->mParent && walk->mParent != parent)
  162. {
  163. Con::errorf(ConsoleLogEntry::General, "Namespace::classLinkTo - cannot change namespace parent linkage of %s from %s to %s.",
  164. walk->mName, walk->mParent->mName, parent->mName);
  165. return false;
  166. }
  167. // Increase the reference count and add the parent namespace.
  168. mRefCountToParent++;
  169. walk->mParent = parent;
  170. trashCache();
  171. return true;
  172. }
  173. void Namespace::buildHashTable()
  174. {
  175. if(mHashSequence == mCacheSequence)
  176. return;
  177. if(!mEntryList && mParent)
  178. {
  179. mParent->buildHashTable();
  180. mHashTable = mParent->mHashTable;
  181. mHashSize = mParent->mHashSize;
  182. mHashSequence = mCacheSequence;
  183. return;
  184. }
  185. U32 entryCount = 0;
  186. Namespace * ns;
  187. for(ns = this; ns; ns = ns->mParent)
  188. for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
  189. if(lookupRecursive(walk->mFunctionName) == walk)
  190. entryCount++;
  191. mHashSize = entryCount + (entryCount >> 1) + 1;
  192. if(!(mHashSize & 1))
  193. mHashSize++;
  194. mHashTable = (Entry **) mCacheAllocator.alloc(sizeof(Entry *) * mHashSize);
  195. for(U32 i = 0; i < mHashSize; i++)
  196. mHashTable[i] = NULL;
  197. for(ns = this; ns; ns = ns->mParent)
  198. {
  199. for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
  200. {
  201. U32 index = HashPointer(walk->mFunctionName) % mHashSize;
  202. while(mHashTable[index] && mHashTable[index]->mFunctionName != walk->mFunctionName)
  203. {
  204. index++;
  205. if(index >= mHashSize)
  206. index = 0;
  207. }
  208. if(!mHashTable[index])
  209. mHashTable[index] = walk;
  210. }
  211. }
  212. mHashSequence = mCacheSequence;
  213. }
  214. void Namespace::init()
  215. {
  216. // create the global namespace
  217. mGlobalNamespace = find(NULL);
  218. }
  219. Namespace *Namespace::global()
  220. {
  221. return mGlobalNamespace;
  222. }
  223. void Namespace::shutdown()
  224. {
  225. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  226. walk->clearEntries();
  227. }
  228. void Namespace::trashCache()
  229. {
  230. mCacheSequence++;
  231. mCacheAllocator.freeBlocks();
  232. }
  233. const char *Namespace::tabComplete(const char *prevText, S32 baseLen, bool fForward)
  234. {
  235. if(mHashSequence != mCacheSequence)
  236. buildHashTable();
  237. const char *bestMatch = NULL;
  238. for(U32 i = 0; i < mHashSize; i++)
  239. if(mHashTable[i] && canTabComplete(prevText, bestMatch, mHashTable[i]->mFunctionName, baseLen, fForward))
  240. bestMatch = mHashTable[i]->mFunctionName;
  241. return bestMatch;
  242. }
  243. Namespace::Entry *Namespace::lookupRecursive(StringTableEntry name)
  244. {
  245. for(Namespace *ns = this; ns; ns = ns->mParent)
  246. for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
  247. if(walk->mFunctionName == name)
  248. return walk;
  249. return NULL;
  250. }
  251. Namespace::Entry *Namespace::lookup(StringTableEntry name)
  252. {
  253. if(mHashSequence != mCacheSequence)
  254. buildHashTable();
  255. U32 index = HashPointer(name) % mHashSize;
  256. while(mHashTable[index] && mHashTable[index]->mFunctionName != name)
  257. {
  258. index++;
  259. if(index >= mHashSize)
  260. index = 0;
  261. }
  262. return mHashTable[index];
  263. }
  264. static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b)
  265. {
  266. const Namespace::Entry* fa = *((Namespace::Entry**)a);
  267. const Namespace::Entry* fb = *((Namespace::Entry**)b);
  268. return dStricmp(fa->mFunctionName, fb->mFunctionName);
  269. }
  270. void Namespace::getEntryList(Vector<Entry *> *vec)
  271. {
  272. if(mHashSequence != mCacheSequence)
  273. buildHashTable();
  274. for(U32 i = 0; i < mHashSize; i++)
  275. if(mHashTable[i])
  276. vec->push_back(mHashTable[i]);
  277. dQsort(vec->address(),vec->size(),sizeof(Namespace::Entry *),compareEntries);
  278. }
  279. Namespace::Entry *Namespace::createLocalEntry(StringTableEntry name)
  280. {
  281. for(Entry *walk = mEntryList; walk; walk = walk->mNext)
  282. {
  283. if(walk->mFunctionName == name)
  284. {
  285. walk->clear();
  286. return walk;
  287. }
  288. }
  289. Entry *ent = (Entry *) mAllocator.alloc(sizeof(Entry));
  290. constructInPlace(ent);
  291. ent->mNamespace = this;
  292. ent->mFunctionName = name;
  293. ent->mNext = mEntryList;
  294. ent->mPackage = mPackage;
  295. mEntryList = ent;
  296. return ent;
  297. }
  298. void Namespace::addFunction(StringTableEntry name, CodeBlock *cb, U32 functionOffset, const char* usage)
  299. {
  300. Entry *ent = createLocalEntry(name);
  301. trashCache();
  302. ent->mUsage = usage;
  303. ent->mCode = cb;
  304. ent->mFunctionOffset = functionOffset;
  305. ent->mCode->incRefCount();
  306. ent->mType = Entry::ScriptFunctionType;
  307. }
  308. void Namespace::addCommand(StringTableEntry name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs)
  309. {
  310. Entry *ent = createLocalEntry(name);
  311. trashCache();
  312. ent->mUsage = usage;
  313. ent->mMinArgs = minArgs;
  314. ent->mMaxArgs = maxArgs;
  315. ent->mType = Entry::StringCallbackType;
  316. ent->cb.mStringCallbackFunc = cb;
  317. }
  318. void Namespace::addCommand(StringTableEntry name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs)
  319. {
  320. Entry *ent = createLocalEntry(name);
  321. trashCache();
  322. ent->mUsage = usage;
  323. ent->mMinArgs = minArgs;
  324. ent->mMaxArgs = maxArgs;
  325. ent->mType = Entry::IntCallbackType;
  326. ent->cb.mIntCallbackFunc = cb;
  327. }
  328. void Namespace::addCommand(StringTableEntry name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs)
  329. {
  330. Entry *ent = createLocalEntry(name);
  331. trashCache();
  332. ent->mUsage = usage;
  333. ent->mMinArgs = minArgs;
  334. ent->mMaxArgs = maxArgs;
  335. ent->mType = Entry::VoidCallbackType;
  336. ent->cb.mVoidCallbackFunc = cb;
  337. }
  338. void Namespace::addCommand(StringTableEntry name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs)
  339. {
  340. Entry *ent = createLocalEntry(name);
  341. trashCache();
  342. ent->mUsage = usage;
  343. ent->mMinArgs = minArgs;
  344. ent->mMaxArgs = maxArgs;
  345. ent->mType = Entry::FloatCallbackType;
  346. ent->cb.mFloatCallbackFunc = cb;
  347. }
  348. void Namespace::addCommand(StringTableEntry name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs)
  349. {
  350. Entry *ent = createLocalEntry(name);
  351. trashCache();
  352. ent->mUsage = usage;
  353. ent->mMinArgs = minArgs;
  354. ent->mMaxArgs = maxArgs;
  355. ent->mType = Entry::BoolCallbackType;
  356. ent->cb.mBoolCallbackFunc = cb;
  357. }
  358. void Namespace::addOverload(const char * name, const char *altUsage)
  359. {
  360. static U32 uid=0;
  361. char buffer[1024];
  362. char lilBuffer[32];
  363. dStrcpy(buffer, name);
  364. dSprintf(lilBuffer, 32, "_%d", uid++);
  365. dStrcat(buffer, lilBuffer);
  366. Entry *ent = createLocalEntry(StringTable->insert( buffer ));
  367. trashCache();
  368. ent->mUsage = altUsage;
  369. ent->mMinArgs = -1;
  370. ent->mMaxArgs = -2;
  371. ent->mType = Entry::OverloadMarker;
  372. ent->cb.mGroupName = name;
  373. }
  374. void Namespace::markGroup(const char* name, const char* usage)
  375. {
  376. static U32 uid=0;
  377. char buffer[1024];
  378. char lilBuffer[32];
  379. dStrcpy(buffer, name);
  380. dSprintf(lilBuffer, 32, "_%d", uid++);
  381. dStrcat(buffer, lilBuffer);
  382. Entry *ent = createLocalEntry(StringTable->insert( buffer ));
  383. trashCache();
  384. if(usage != NULL)
  385. lastUsage = (char*)(ent->mUsage = usage);
  386. else
  387. ent->mUsage = lastUsage;
  388. ent->mMinArgs = -1; // Make sure it explodes if somehow we run this entry.
  389. ent->mMaxArgs = -2;
  390. ent->mType = Entry::GroupMarker;
  391. ent->cb.mGroupName = name;
  392. }
  393. extern S32 executeBlock(StmtNode *block, ExprEvalState *state);
  394. const char *Namespace::Entry::execute(S32 argc, const char **argv, ExprEvalState *state)
  395. {
  396. if(mType == ScriptFunctionType)
  397. {
  398. if(mFunctionOffset)
  399. return mCode->exec(mFunctionOffset, argv[0], mNamespace, argc, argv, false, mPackage);
  400. else
  401. return "";
  402. }
  403. if((mMinArgs && argc < mMinArgs) || (mMaxArgs && argc > mMaxArgs))
  404. {
  405. Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", mNamespace->mName, mFunctionName);
  406. Con::warnf(ConsoleLogEntry::Script, "usage: %s", mUsage);
  407. return "";
  408. }
  409. static char returnBuffer[32];
  410. switch(mType)
  411. {
  412. case StringCallbackType:
  413. return cb.mStringCallbackFunc(state->thisObject, argc, argv);
  414. case IntCallbackType:
  415. dSprintf(returnBuffer, sizeof(returnBuffer), "%d",
  416. cb.mIntCallbackFunc(state->thisObject, argc, argv));
  417. return returnBuffer;
  418. case FloatCallbackType:
  419. dSprintf(returnBuffer, sizeof(returnBuffer), "%.9g",
  420. cb.mFloatCallbackFunc(state->thisObject, argc, argv));
  421. return returnBuffer;
  422. case VoidCallbackType:
  423. cb.mVoidCallbackFunc(state->thisObject, argc, argv);
  424. return "";
  425. case BoolCallbackType:
  426. dSprintf(returnBuffer, sizeof(returnBuffer), "%d",
  427. (U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv));
  428. return returnBuffer;
  429. }
  430. return "";
  431. }
  432. StringTableEntry Namespace::mActivePackages[Namespace::MaxActivePackages];
  433. U32 Namespace::mNumActivePackages = 0;
  434. U32 Namespace::mOldNumActivePackages = 0;
  435. bool Namespace::isPackage(StringTableEntry name)
  436. {
  437. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  438. if(walk->mPackage == name)
  439. return true;
  440. return false;
  441. }
  442. void Namespace::activatePackage(StringTableEntry name)
  443. {
  444. if(mNumActivePackages == MaxActivePackages)
  445. {
  446. Con::printf("ActivatePackage(%s) failed - Max package limit reached: %d", name, MaxActivePackages);
  447. return;
  448. }
  449. if(!name)
  450. return;
  451. // see if this one's already active
  452. for(U32 i = 0; i < mNumActivePackages; i++)
  453. if(mActivePackages[i] == name)
  454. return;
  455. // kill the cache
  456. trashCache();
  457. // find all the package namespaces...
  458. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  459. {
  460. if(walk->mPackage == name)
  461. {
  462. Namespace *parent = Namespace::find(walk->mName);
  463. // hook the parent
  464. walk->mParent = parent->mParent;
  465. parent->mParent = walk;
  466. // now swap the entries:
  467. Entry *ew;
  468. for(ew = parent->mEntryList; ew; ew = ew->mNext)
  469. ew->mNamespace = walk;
  470. for(ew = walk->mEntryList; ew; ew = ew->mNext)
  471. ew->mNamespace = parent;
  472. ew = walk->mEntryList;
  473. walk->mEntryList = parent->mEntryList;
  474. parent->mEntryList = ew;
  475. }
  476. }
  477. mActivePackages[mNumActivePackages++] = name;
  478. }
  479. void Namespace::deactivatePackage(StringTableEntry name)
  480. {
  481. S32 i, j;
  482. for(i = 0; i < (S32)mNumActivePackages; i++)
  483. if(mActivePackages[i] == name)
  484. break;
  485. if(i == mNumActivePackages)
  486. return;
  487. trashCache();
  488. for(j = mNumActivePackages - 1; j >= i; j--)
  489. {
  490. // gotta unlink em in reverse order...
  491. for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
  492. {
  493. if(walk->mPackage == mActivePackages[j])
  494. {
  495. Namespace *parent = Namespace::find(walk->mName);
  496. // hook the parent
  497. parent->mParent = walk->mParent;
  498. walk->mParent = NULL;
  499. // now swap the entries:
  500. Entry *ew;
  501. for(ew = parent->mEntryList; ew; ew = ew->mNext)
  502. ew->mNamespace = walk;
  503. for(ew = walk->mEntryList; ew; ew = ew->mNext)
  504. ew->mNamespace = parent;
  505. ew = walk->mEntryList;
  506. walk->mEntryList = parent->mEntryList;
  507. parent->mEntryList = ew;
  508. }
  509. }
  510. }
  511. mNumActivePackages = i;
  512. }
  513. void Namespace::unlinkPackages()
  514. {
  515. mOldNumActivePackages = mNumActivePackages;
  516. if(!mNumActivePackages)
  517. return;
  518. deactivatePackage(mActivePackages[0]);
  519. }
  520. void Namespace::relinkPackages()
  521. {
  522. if(!mOldNumActivePackages)
  523. return;
  524. for(U32 i = 0; i < mOldNumActivePackages; i++)
  525. activatePackage(mActivePackages[i]);
  526. }