Torque3D Documentation / _generateds / consoleInternal.cpp

consoleInternal.cpp

Engine/source/console/consoleInternal.cpp

More...

Classes:

Public Defines

define

Public Variables

char

Public Functions

bool
canTabComplete(const char * prevText, const char * bestMatch, const char * newText, S32 baseLen, bool fForward)
DefineEngineFunction(activatePackage , void , (String packageName) , "@brief Activates an existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "The activation occurs by updating the namespace linkage of existing functions and methods. " "If the package is already activated the function does <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">nothing.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )
DefineEngineFunction(backtrace , void , () , "@brief Prints the scripting call stack <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">log.\n\n</a>" "Used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trace functions called from within functions. Can help discover what functions were called " "(and not yet exited) before the current point in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n\n</a>" "@ingroup Debugging" )
DefineEngineFunction(deactivatePackage , void , (String packageName) , "@brief Deactivates a previously activated <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "The package is deactivated by removing its namespace linkages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any function or method. " "If there are any packages above this one in the stack they are deactivated as well. " "If the package is not on the stack this function does <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">nothing.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )
DefineEngineFunction(getPackageList , const char * , () , "@brief Returns a space delimited list of the active packages in stack <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">order.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )
DefineEngineFunction(isPackage , bool , (String identifier) , "@brief Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the identifier is the name of a declared <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )

Detailed Description

Public Defines

ST_INIT_SIZE() 15

Public Variables

char scratchBuffer [1024]
char * typeValueEmpty 

Public Functions

canTabComplete(const char * prevText, const char * bestMatch, const char * newText, S32 baseLen, bool fForward)

compareEntries(const void * a, const void * b)

DefineEngineFunction(activatePackage , void , (String packageName) , "@brief Activates an existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "The activation occurs by updating the namespace linkage of existing functions and methods. " "If the package is already activated the function does <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">nothing.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )

DefineEngineFunction(backtrace , void , () , "@brief Prints the scripting call stack <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the console <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">log.\n\n</a>" "Used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trace functions called from within functions. Can help discover what functions were called " "(and not yet exited) before the current point in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n\n</a>" "@ingroup Debugging" )

DefineEngineFunction(deactivatePackage , void , (String packageName) , "@brief Deactivates a previously activated <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "The package is deactivated by removing its namespace linkages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> any function or method. " "If there are any packages above this one in the stack they are deactivated as well. " "If the package is not on the stack this function does <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">nothing.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )

DefineEngineFunction(getPackageList , const char * , () , "@brief Returns a space delimited list of the active packages in stack <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">order.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )

DefineEngineFunction(isPackage , bool , (String identifier) , "@brief Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a2594a51175f310ed96ad6cd7d6514878">if</a> the identifier is the name of a declared <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">package.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Packages\n</a>" )

executeBlock(StmtNode * block, ExprEvalState * state)

HashPointer(StringTableEntry ptr)

varCompare(const void * a, const void * b)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "console/console.h"
  26
  27#include "console/ast.h"
  28#include "core/tAlgorithm.h"
  29
  30#include "core/strings/findMatch.h"
  31#include "console/consoleInternal.h"
  32#include "core/stream/fileStream.h"
  33#include "console/compiler.h"
  34#include "console/engineAPI.h"
  35
  36//#define DEBUG_SPEW
  37
  38#define ST_INIT_SIZE 15
  39
  40static char scratchBuffer[1024];
  41U32 Namespace::mCacheSequence = 0;
  42DataChunker Namespace::mCacheAllocator;
  43DataChunker Namespace::mAllocator;
  44Namespace *Namespace::mNamespaceList = NULL;
  45Namespace *Namespace::mGlobalNamespace = NULL;
  46
  47
  48bool canTabComplete(const char *prevText, const char *bestMatch,
  49               const char *newText, S32 baseLen, bool fForward)
  50{
  51   // test if it matches the first baseLen chars:
  52   if(dStrnicmp(newText, prevText, baseLen))
  53      return false;
  54
  55   if (fForward)
  56   {
  57      if(!bestMatch)
  58         return dStricmp(newText, prevText) > 0;
  59      else
  60         return (dStricmp(newText, prevText) > 0) &&
  61                (dStricmp(newText, bestMatch) < 0);
  62   }
  63   else
  64   {
  65      if (dStrlen(prevText) == (U32) baseLen)
  66      {
  67         // look for the 'worst match'
  68         if(!bestMatch)
  69            return dStricmp(newText, prevText) > 0;
  70         else
  71            return dStricmp(newText, bestMatch) > 0;
  72      }
  73      else
  74      {
  75         if (!bestMatch)
  76            return (dStricmp(newText, prevText)  < 0);
  77         else
  78            return (dStricmp(newText, prevText)  < 0) &&
  79                   (dStricmp(newText, bestMatch) > 0);
  80      }
  81   }
  82}
  83
  84//---------------------------------------------------------------
  85//
  86// Dictionary functions
  87//
  88//---------------------------------------------------------------
  89struct StringValue
  90{
  91   S32 size;
  92   char *val;
  93
  94   operator char *() { return val; }
  95   StringValue &operator=(const char *string);
  96
  97   StringValue() { size = 0; val = NULL; }
  98   ~StringValue() { dFree(val); }
  99};
 100
 101
 102StringValue & StringValue::operator=(const char *string)
 103{
 104   if(!val)
 105   {
 106      val = dStrdup(string);
 107      size = dStrlen(val);
 108   }
 109   else
 110   {
 111      S32 len = dStrlen(string);
 112      if(len < size)
 113         dStrcpy(val, string);
 114      else
 115      {
 116         size = len;
 117         dFree(val);
 118         val = dStrdup(string);
 119      }
 120   }
 121   return *this;
 122}
 123
 124static S32 QSORT_CALLBACK varCompare(const void* a,const void* b)
 125{
 126   return dStricmp( (*((Dictionary::Entry **) a))->name, (*((Dictionary::Entry **) b))->name );
 127}
 128
 129void Dictionary::exportVariables(const char *varString, const char *fileName, bool append)
 130{
 131   const char *searchStr = varString;
 132   Vector<Entry*> sortList(__FILE__, __LINE__);
 133
 134   for(S32 i = 0; i < hashTable->size;i ++)
 135   {
 136      Entry *walk = hashTable->data[i];
 137      while(walk)
 138      {
 139         if(FindMatch::isMatch((char *) searchStr, (char *) walk->name))
 140            sortList.push_back(walk);
 141
 142         walk = walk->nextEntry;
 143      }
 144   }
 145
 146   if(!sortList.size())
 147      return;
 148
 149   dQsort((void *) &sortList[0], sortList.size(), sizeof(Entry *), varCompare);
 150
 151   Vector<Entry *>::iterator s;
 152   char expandBuffer[1024];
 153   FileStream *strm = NULL;
 154
 155   if(fileName)
 156   {
 157      if((strm = FileStream::createAndOpen( fileName, append ? Torque::FS::File::ReadWrite : Torque::FS::File::Write )) == NULL)
 158      {
 159         Con::errorf(ConsoleLogEntry::General, "Unable to open file '%s for writing.", fileName);
 160         return;
 161      }
 162      if(append)
 163         strm->setPosition(strm->getStreamSize());
 164   }
 165
 166   char buffer[1024];
 167   const char *cat = fileName ? "\r\n" : "";
 168
 169   for(s = sortList.begin(); s != sortList.end(); s++)
 170   {
 171      switch((*s)->value.type)
 172      {
 173         case ConsoleValue::TypeInternalInt:
 174            dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->value.ival, cat);
 175            break;
 176         case ConsoleValue::TypeInternalFloat:
 177            dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->value.fval, cat);
 178            break;
 179         default:
 180            expandEscape(expandBuffer, (*s)->getStringValue());
 181            dSprintf(buffer, sizeof(buffer), "%s = \"%s\";%s", (*s)->name, expandBuffer, cat);
 182            break;
 183      }
 184      if(strm)
 185         strm->write(dStrlen(buffer), buffer);
 186      else
 187         Con::printf("%s", buffer);
 188   }
 189   if(strm)
 190      delete strm;
 191}
 192
 193void Dictionary::exportVariables( const char *varString, Vector<String> *names, Vector<String> *values )
 194{
 195   const char *searchStr = varString;
 196   Vector<Entry*> sortList(__FILE__, __LINE__);
 197
 198   for ( S32 i = 0; i < hashTable->size; i++ )
 199   {
 200      Entry *walk = hashTable->data[i];
 201      while ( walk )
 202      {
 203         if ( FindMatch::isMatch( (char*)searchStr, (char*)walk->name ) )
 204            sortList.push_back( walk );
 205
 206         walk = walk->nextEntry;
 207      }
 208   }
 209
 210   if ( !sortList.size() )
 211      return;
 212
 213   dQsort((void *) &sortList[0], sortList.size(), sizeof(Entry *), varCompare);
 214
 215   if ( names )
 216      names->reserve( sortList.size() );
 217   if ( values )
 218      values->reserve( sortList.size() );
 219
 220   char expandBuffer[1024];
 221
 222   Vector<Entry *>::iterator s;
 223
 224   for ( s = sortList.begin(); s != sortList.end(); s++ )
 225   {
 226      if ( names )
 227         names->push_back( String( (*s)->name ) );
 228
 229      if ( values )
 230      {
 231         switch ( (*s)->value.type )
 232         {
 233         case ConsoleValue::TypeInternalInt:
 234            values->push_back( String::ToString( (*s)->value.ival ) );         
 235            break;
 236         case ConsoleValue::TypeInternalFloat:
 237            values->push_back( String::ToString( (*s)->value.fval ) );         
 238            break;
 239         default:         
 240            expandEscape( expandBuffer, (*s)->getStringValue() );
 241            values->push_back( expandBuffer );  
 242            break;
 243         }
 244      }
 245   }
 246}
 247
 248void Dictionary::deleteVariables(const char *varString)
 249{
 250   const char *searchStr = varString;
 251
 252   for(S32 i = 0; i < hashTable->size; i++)
 253   {
 254      Entry *walk = hashTable->data[i];
 255      while(walk)
 256      {
 257         Entry *matchedEntry = (FindMatch::isMatch((char *) searchStr, (char *) walk->name)) ? walk : NULL;
 258         walk = walk->nextEntry;
 259         if (matchedEntry)
 260            remove(matchedEntry); // assumes remove() is a stable remove (will not reorder entries on remove)
 261      }
 262   }
 263}
 264
 265U32 HashPointer(StringTableEntry ptr)
 266{
 267   return (U32)(((dsize_t)ptr) >> 2);
 268}
 269
 270Dictionary::Entry *Dictionary::lookup(StringTableEntry name)
 271{
 272   Entry *walk = hashTable->data[HashPointer(name) % hashTable->size];
 273   while(walk)
 274   {
 275      if(walk->name == name)
 276         return walk;
 277      else
 278         walk = walk->nextEntry;
 279   }
 280
 281   return NULL;
 282}
 283
 284Dictionary::Entry *Dictionary::add(StringTableEntry name)
 285{
 286   // Try to find an existing match.
 287   //printf("Add Variable %s\n", name);
 288   
 289   Entry* ret = lookup( name );
 290   if( ret )
 291      return ret;
 292   
 293   // Rehash if the table get's too crowded.  Be aware that this might
 294   // modify a table that we don't own.
 295
 296   hashTable->count ++;
 297   if( hashTable->count > hashTable->size * 2 )
 298   {
 299      // Allocate a new table.
 300      
 301      const U32 newTableSize = hashTable->size * 4 - 1;
 302      Entry** newTableData = new Entry*[ newTableSize ];
 303      dMemset( newTableData, 0, newTableSize * sizeof( Entry* ) );
 304      
 305      // Move the entries over.
 306      
 307      for( U32 i = 0; i < hashTable->size; ++ i )
 308         for( Entry* entry = hashTable->data[ i ]; entry != NULL; )
 309         {
 310            Entry* next = entry->nextEntry;
 311            U32 index = HashPointer( entry->name ) % newTableSize;
 312            
 313            entry->nextEntry = newTableData[ index ];
 314            newTableData[ index ] = entry;
 315            
 316            entry = next;
 317         }
 318         
 319      // Switch the tables.
 320      
 321      delete[] hashTable->data;
 322      hashTable->data = newTableData;
 323      hashTable->size = newTableSize;
 324   }
 325   
 326   #ifdef DEBUG_SPEW
 327   Platform::outputDebugString( "[ConsoleInternal] Adding entry '%s'", name );
 328   #endif
 329   
 330   // Add the new entry.
 331
 332   ret = hashTable->mChunker.alloc();
 333   constructInPlace( ret, name );
 334   U32 idx = HashPointer(name) % hashTable->size;
 335   ret->nextEntry = hashTable->data[idx];
 336   hashTable->data[idx] = ret;
 337   
 338   return ret;
 339}
 340
 341// deleteVariables() assumes remove() is a stable remove (will not reorder entries on remove)
 342void Dictionary::remove(Dictionary::Entry *ent)
 343{
 344   Entry **walk = &hashTable->data[HashPointer(ent->name) % hashTable->size];
 345   while(*walk != ent)
 346      walk = &((*walk)->nextEntry);
 347      
 348   #ifdef DEBUG_SPEW
 349   Platform::outputDebugString( "[ConsoleInternal] Removing entry '%s'", ent->name );
 350   #endif
 351
 352   *walk = (ent->nextEntry);
 353
 354   destructInPlace( ent );
 355   hashTable->mChunker.free( ent );
 356
 357   hashTable->count--;
 358}
 359
 360Dictionary::Dictionary()
 361   :  hashTable( NULL ),
 362#pragma warning( disable : 4355 )
 363      ownHashTable( this ), // Warning with VC++ but this is safe.
 364#pragma warning( default : 4355 )
 365      exprState( NULL ),
 366      scopeName( NULL ),
 367      scopeNamespace( NULL ),
 368      code( NULL ),
 369      ip( 0 )
 370{
 371}
 372
 373void Dictionary::setState(ExprEvalState *state, Dictionary* ref)
 374{
 375   exprState = state;
 376   
 377   if( ref )
 378   {
 379      hashTable = ref->hashTable;
 380      return;
 381   }
 382
 383   if( !ownHashTable.data )
 384   {
 385      ownHashTable.count = 0;
 386      ownHashTable.size = ST_INIT_SIZE;
 387      ownHashTable.data = new Entry *[ ownHashTable.size ];
 388      
 389      dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) );
 390   }
 391   
 392   hashTable = &ownHashTable;
 393}
 394
 395Dictionary::~Dictionary()
 396{
 397   reset();
 398   if( ownHashTable.data )
 399      delete [] ownHashTable.data;
 400}
 401
 402void Dictionary::reset()
 403{
 404   if( hashTable && hashTable->owner != this )
 405   {
 406      hashTable = NULL;
 407      return;
 408   }
 409      
 410   for( U32 i = 0; i < ownHashTable.size; ++ i )
 411   {
 412      Entry* walk = ownHashTable.data[i];
 413      while( walk )
 414      {
 415         Entry* temp = walk->nextEntry;
 416         destructInPlace( walk );
 417         walk = temp;
 418      }
 419   }
 420
 421   dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) );
 422   ownHashTable.mChunker.freeBlocks( true );
 423   
 424   ownHashTable.count = 0;
 425   hashTable = NULL;
 426   
 427   scopeName = NULL;
 428   scopeNamespace = NULL;
 429   code = NULL;
 430   ip = 0;
 431}
 432
 433
 434const char *Dictionary::tabComplete(const char *prevText, S32 baseLen, bool fForward)
 435{
 436   S32 i;
 437
 438   const char *bestMatch = NULL;
 439   for(i = 0; i < hashTable->size; i++)
 440   {
 441      Entry *walk = hashTable->data[i];
 442      while(walk)
 443      {
 444         if(canTabComplete(prevText, bestMatch, walk->name, baseLen, fForward))
 445            bestMatch = walk->name;
 446         walk = walk->nextEntry;
 447      }
 448   }
 449   return bestMatch;
 450}
 451
 452
 453char *typeValueEmpty = "";
 454
 455Dictionary::Entry::Entry(StringTableEntry in_name)
 456{
 457   name = in_name;
 458   value.type = ConsoleValue::TypeInternalString;
 459   notify = NULL;
 460   nextEntry = NULL;
 461   mUsage = NULL;
 462   mIsConstant = false;
 463
 464   // NOTE: This is data inside a nameless
 465   // union, so we don't need to init the rest.
 466   value.init();
 467}
 468
 469Dictionary::Entry::~Entry()
 470{
 471   value.cleanup();
 472
 473   if ( notify )
 474      delete notify;
 475}
 476
 477const char *Dictionary::getVariable(StringTableEntry name, bool *entValid)
 478{
 479   Entry *ent = lookup(name);
 480   if(ent)
 481   {
 482      if(entValid)
 483         *entValid = true;
 484      return ent->getStringValue();
 485   }
 486   if(entValid)
 487      *entValid = false;
 488
 489   // Warn users when they access a variable that isn't defined.
 490   if(gWarnUndefinedScriptVariables)
 491      Con::warnf(" *** Accessed undefined variable '%s'", name);
 492
 493   return "";
 494}
 495
 496void ConsoleValue::setStringValue(const char * value)
 497{
 498   if (value == NULL) value = typeValueEmpty;
 499
 500   if(type <= ConsoleValue::TypeInternalString)
 501   {
 502      // Let's not remove empty-string-valued global vars from the dict.
 503      // If we remove them, then they won't be exported, and sometimes
 504      // it could be necessary to export such a global.  There are very
 505      // few empty-string global vars so there's no performance-related
 506      // need to remove them from the dict.
 507/*
 508      if(!value[0] && name[0] == '$')
 509      {
 510         gEvalState.globalVars.remove(this);
 511         return;
 512      }
 513*/
 514      if (value == typeValueEmpty)
 515      {
 516         if (bufferLen > 0)
 517         {
 518            dFree(sval);
 519            bufferLen = 0;
 520         }
 521
 522         sval = typeValueEmpty;
 523         fval = 0.f;
 524         ival = 0;
 525         type = TypeInternalString;
 526         return;
 527      }
 528
 529      U32 stringLen = dStrlen(value);
 530
 531      // If it's longer than 256 bytes, it's certainly not a number.
 532      //
 533      // (This decision may come back to haunt you. Shame on you if it
 534      // does.)
 535      if(stringLen < 256)
 536      {
 537         fval = dAtof(value);
 538         ival = dAtoi(value);
 539      }
 540      else
 541      {
 542         fval = 0.f;
 543         ival = 0;
 544      }
 545
 546      // may as well pad to the next cache line
 547      U32 newLen = ((stringLen + 1) + 15) & ~15;
 548     
 549      if(bufferLen == 0)
 550         sval = (char *) dMalloc(newLen);
 551      else if(newLen > bufferLen)
 552         sval = (char *) dRealloc(sval, newLen);
 553
 554      type = TypeInternalString;
 555
 556      bufferLen = newLen;
 557      dStrcpy(sval, value);
 558   }
 559   else
 560      Con::setData(type, dataPtr, 0, 1, &value, enumTable);      
 561}
 562
 563
 564void ConsoleValue::setStackStringValue(const char *value)
 565{
 566   if (value == NULL) value = typeValueEmpty;
 567
 568   if(type <= ConsoleValue::TypeInternalString)
 569   {
 570      // sval might still be temporarily present so we need to check and free it
 571      if (bufferLen > 0)
 572      {
 573         dFree(sval);
 574         bufferLen = 0;
 575      }
 576
 577      if (value == typeValueEmpty)
 578      {
 579         sval = typeValueEmpty;
 580         fval = 0.f;
 581         ival = 0;
 582         type = TypeInternalString;
 583         return;
 584      }
 585
 586      U32 stringLen = dStrlen(value);
 587      if(stringLen < 256)
 588      {
 589         fval = dAtof(value);
 590         ival = dAtoi(value);
 591      }
 592      else
 593      {
 594         fval = 0.f;
 595         ival = 0;
 596      }
 597
 598      type = TypeInternalStackString;
 599      sval = (char*)value;
 600      bufferLen = 0;
 601   }
 602   else
 603      Con::setData(type, dataPtr, 0, 1, &value, enumTable);      
 604}
 605
 606void ConsoleValue::setStringStackPtrValue(StringStackPtr ptrValue)
 607{
 608   if(type <= ConsoleValue::TypeInternalString)
 609   {
 610      const char *value = StringStackPtrRef(ptrValue).getPtr(&STR);
 611      if (bufferLen > 0)
 612      {
 613         dFree(sval);
 614         bufferLen = 0;
 615      }
 616
 617      U32 stringLen = dStrlen(value);
 618      if(stringLen < 256)
 619      {
 620         fval = dAtof(value);
 621         ival = dAtoi(value);
 622      }
 623      else
 624      {
 625         fval = 0.f;
 626         ival = 0;
 627      }
 628
 629      type = TypeInternalStringStackPtr;
 630      sval = (char*)(value - STR.mBuffer);
 631      bufferLen = 0;
 632   }
 633   else
 634   {
 635      const char *value = StringStackPtrRef(ptrValue).getPtr(&STR);
 636      Con::setData(type, dataPtr, 0, 1, &value, enumTable);      
 637   }
 638}
 639
 640S32 Dictionary::getIntVariable(StringTableEntry name, bool *entValid)
 641{
 642   Entry *ent = lookup(name);
 643   if(ent)
 644   {
 645      if(entValid)
 646         *entValid = true;
 647      return ent->getIntValue();
 648   }
 649
 650   if(entValid)
 651      *entValid = false;
 652
 653    return 0;
 654}
 655
 656F32 Dictionary::getFloatVariable(StringTableEntry name, bool *entValid)
 657{
 658   Entry *ent = lookup(name);
 659   if(ent)
 660   {
 661      if(entValid)
 662         *entValid = true;
 663      return ent->getFloatValue();
 664   }
 665
 666   if(entValid)
 667      *entValid = false;
 668
 669   return 0;
 670}
 671
 672void Dictionary::setVariable(StringTableEntry name, const char *value)
 673{
 674   Entry *ent = add(name);
 675   if(!value)
 676      value = "";
 677   ent->setStringValue(value);
 678}
 679
 680Dictionary::Entry* Dictionary::addVariable(  const char *name, 
 681                                             S32 type, 
 682                                             void *dataPtr, 
 683                                             const char* usage )
 684{
 685   AssertFatal( type >= 0, "Dictionary::addVariable - Got bad type!" );
 686
 687   if(name[0] != '$')
 688   {
 689      scratchBuffer[0] = '$';
 690      dStrcpy(scratchBuffer + 1, name);
 691      name = scratchBuffer;
 692   }
 693
 694   Entry *ent = add(StringTable->insert(name));
 695   
 696   if (  ent->value.type <= ConsoleValue::TypeInternalString &&
 697         ent->value.bufferLen > 0 )
 698      dFree(ent->value.sval);
 699
 700   ent->value.type = type;
 701   ent->value.dataPtr = dataPtr;
 702   ent->mUsage = usage;
 703   
 704   // Fetch enum table, if any.
 705   
 706   ConsoleBaseType* conType = ConsoleBaseType::getType( type );
 707   AssertFatal( conType, "Dictionary::addVariable - invalid console type" );
 708   ent->value.enumTable = conType->getEnumTable();
 709   
 710   return ent;
 711}
 712
 713bool Dictionary::removeVariable(StringTableEntry name)
 714{
 715   if( Entry *ent = lookup(name) )
 716   {
 717      remove( ent );
 718      return true;
 719   }
 720   return false;
 721}
 722
 723void Dictionary::addVariableNotify( const char *name, const Con::NotifyDelegate &callback )
 724{
 725   Entry *ent = lookup(StringTable->insert(name));
 726   if ( !ent )
 727    return;
 728
 729   if ( !ent->notify )
 730      ent->notify = new Entry::NotifySignal();
 731
 732   ent->notify->notify( callback );
 733}
 734
 735void Dictionary::removeVariableNotify( const char *name, const Con::NotifyDelegate &callback )
 736{
 737   Entry *ent = lookup(StringTable->insert(name));
 738   if ( ent && ent->notify )
 739      ent->notify->remove( callback );
 740}
 741
 742void Dictionary::validate()
 743{
 744   AssertFatal( ownHashTable.owner == this,
 745      "Dictionary::validate() - Dictionary not owner of own hashtable!" );
 746}
 747
 748void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns)
 749{   
 750   #ifdef DEBUG_SPEW
 751   validate();
 752
 753   Platform::outputDebugString( "[ConsoleInternal] Pushing new frame for '%s' at %i",
 754      frameName, mStackDepth );
 755   #endif
 756   
 757   if( mStackDepth + 1 > stack.size() )
 758   {
 759      #ifdef DEBUG_SPEW
 760      Platform::outputDebugString( "[ConsoleInternal] Growing stack by one frame" );
 761      #endif
 762      
 763      stack.push_back( new Dictionary );
 764   }
 765      
 766   Dictionary& newFrame = *( stack[ mStackDepth ] );
 767   newFrame.setState( this );
 768      
 769   newFrame.scopeName = frameName;
 770   newFrame.scopeNamespace = ns;
 771
 772   mStackDepth ++;
 773   currentVariable = NULL;
 774   
 775   AssertFatal( !newFrame.getCount(), "ExprEvalState::pushFrame - Dictionary not empty!" );
 776   
 777   #ifdef DEBUG_SPEW
 778   validate();
 779   #endif
 780}
 781
 782void ExprEvalState::popFrame()
 783{
 784   AssertFatal( mStackDepth > 0, "ExprEvalState::popFrame - Stack Underflow!" );
 785   
 786   #ifdef DEBUG_SPEW
 787   validate();
 788   
 789   Platform::outputDebugString( "[ConsoleInternal] Popping %sframe at %i",
 790      getCurrentFrame().isOwner() ? "" : "shared ", mStackDepth - 1 );
 791   #endif
 792
 793   mStackDepth --;
 794   stack[ mStackDepth ]->reset();
 795   currentVariable = NULL;
 796
 797   #ifdef DEBUG_SPEW
 798   validate();
 799   #endif
 800}
 801
 802void ExprEvalState::pushFrameRef(S32 stackIndex)
 803{
 804   AssertFatal( stackIndex >= 0 && stackIndex < stack.size(), "You must be asking for a valid frame!" );
 805
 806   #ifdef DEBUG_SPEW
 807   validate();
 808   
 809   Platform::outputDebugString( "[ConsoleInternal] Cloning frame from %i to %i",
 810      stackIndex, mStackDepth );
 811   #endif
 812
 813   if( mStackDepth + 1 > stack.size() )
 814   {
 815      #ifdef DEBUG_SPEW
 816      Platform::outputDebugString( "[ConsoleInternal] Growing stack by one frame" );
 817      #endif
 818      
 819      stack.push_back( new Dictionary );
 820   }
 821
 822   Dictionary& newFrame = *( stack[ mStackDepth ] );
 823   newFrame.setState( this, stack[ stackIndex ] );
 824   
 825   mStackDepth ++;
 826   currentVariable = NULL;
 827   
 828   #ifdef DEBUG_SPEW
 829   validate();
 830   #endif
 831}
 832
 833ExprEvalState::ExprEvalState()
 834{
 835   VECTOR_SET_ASSOCIATION(stack);
 836   globalVars.setState(this);
 837   thisObject = NULL;
 838   traceOn = false;
 839   currentVariable = NULL;
 840   mStackDepth = 0;
 841   stack.reserve( 64 );
 842   mShouldReset = false;
 843   mResetLocked = false;
 844}
 845
 846ExprEvalState::~ExprEvalState()
 847{
 848   // Delete callframes.
 849   
 850   while( !stack.empty() )
 851   {
 852      delete stack.last();
 853      stack.decrement();
 854   }
 855}
 856
 857void ExprEvalState::validate()
 858{
 859   AssertFatal( mStackDepth <= stack.size(),
 860      "ExprEvalState::validate() - Stack depth pointing beyond last stack frame!" );
 861      
 862   for( U32 i = 0; i < stack.size(); ++ i )
 863      stack[ i ]->validate();
 864}
 865
 866DefineEngineFunction(backtrace, void, ( ),,
 867   "@brief Prints the scripting call stack to the console log.\n\n"
 868   "Used to trace functions called from within functions. Can help discover what functions were called "
 869   "(and not yet exited) before the current point in scripts.\n\n"
 870   "@ingroup Debugging")
 871{
 872   U32 totalSize = 1;
 873
 874   for(U32 i = 0; i < gEvalState.getStackDepth(); i++)
 875   {
 876      if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage)  
 877         totalSize += dStrlen(gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage) + 2;  
 878      if(gEvalState.stack[i]->scopeName)  
 879      totalSize += dStrlen(gEvalState.stack[i]->scopeName) + 3;
 880      if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mName)
 881         totalSize += dStrlen(gEvalState.stack[i]->scopeNamespace->mName) + 2;
 882   }
 883
 884   char *buf = Con::getReturnBuffer(totalSize);
 885   buf[0] = 0;
 886   for(U32 i = 0; i < gEvalState.getStackDepth(); i++)
 887   {
 888      dStrcat(buf, "->");
 889      
 890      if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage)  
 891      {  
 892         dStrcat(buf, "[");  
 893         dStrcat(buf, gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage);  
 894         dStrcat(buf, "]");  
 895      }  
 896      if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mName)
 897      {
 898         dStrcat(buf, gEvalState.stack[i]->scopeNamespace->mName);
 899         dStrcat(buf, "::");
 900      }
 901      if(gEvalState.stack[i]->scopeName)  
 902         dStrcat(buf, gEvalState.stack[i]->scopeName);
 903   }
 904
 905   Con::printf("BackTrace: %s", buf);
 906}
 907
 908Namespace::Entry::Entry()
 909{
 910   mCode = NULL;
 911   mType = InvalidFunctionType;
 912   mUsage = NULL;
 913   mHeader = NULL;
 914   mNamespace = NULL;
 915}
 916
 917void Namespace::Entry::clear()
 918{
 919   if(mCode)
 920   {
 921      mCode->decRefCount();
 922      mCode = NULL;
 923   }
 924
 925   // Clean up usage strings generated for script functions.
 926   if( ( mType == Namespace::Entry::ConsoleFunctionType ) && mUsage )
 927   {
 928      dFree(mUsage);
 929      mUsage = NULL;
 930   }
 931}
 932
 933Namespace::Namespace()
 934{
 935   mPackage = NULL;
 936   mUsage = NULL;
 937   mCleanUpUsage = false;
 938   mName = NULL;
 939   mParent = NULL;
 940   mNext = NULL;
 941   mEntryList = NULL;
 942   mHashSize = 0;
 943   mHashTable = 0;
 944   mHashSequence = 0;
 945   mRefCountToParent = 0;
 946   mClassRep = 0;
 947}
 948
 949Namespace::~Namespace()
 950{
 951   clearEntries();
 952   if( mUsage && mCleanUpUsage )
 953   {
 954      dFree(mUsage);
 955      mUsage = NULL;
 956      mCleanUpUsage = false;
 957   }
 958}
 959
 960void Namespace::clearEntries()
 961{
 962   for(Entry *walk = mEntryList; walk; walk = walk->mNext)
 963      walk->clear();
 964}
 965
 966Namespace *Namespace::find(StringTableEntry name, StringTableEntry package)
 967{
 968   if ( name == NULL && package == NULL )
 969      return mGlobalNamespace;
 970
 971   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
 972   {
 973      if(walk->mName == name && walk->mPackage == package)
 974         return walk;
 975   }
 976
 977   Namespace *ret = (Namespace *) mAllocator.alloc(sizeof(Namespace));
 978   constructInPlace(ret);
 979   ret->mPackage = package;
 980   ret->mName = name;
 981   ret->mNext = mNamespaceList;
 982   mNamespaceList = ret;
 983   return ret;
 984}
 985
 986bool Namespace::unlinkClass( Namespace *parent )
 987{
 988   AssertFatal( mPackage == NULL, "Namespace::unlinkClass - Must not be called on a namespace coming from a package!" );
 989
 990   // Skip additions to this namespace coming from packages.
 991
 992   Namespace* walk = getPackageRoot();
 993
 994   // Make sure "parent" is the direct parent namespace.
 995
 996   if( parent != NULL && walk->mParent && walk->mParent != parent )
 997   {
 998      Con::errorf(ConsoleLogEntry::General, "Namespace::unlinkClass - cannot unlink namespace parent linkage for %s for %s.",
 999         walk->mName, walk->mParent->mName);
1000      return false;
1001   }
1002
1003   // Decrease the reference count.  Note that we do this on
1004   // the bottom-most namespace, i.e. the one guaranteed not 
1005   // to come from a package.
1006
1007   mRefCountToParent --;
1008   AssertFatal( mRefCountToParent >= 0, "Namespace::unlinkClass - reference count to parent is less than 0" );
1009
1010   // Unlink if the count dropped to zero.
1011
1012   if( mRefCountToParent == 0 )
1013   {
1014      walk->mParent = NULL;
1015      trashCache();
1016   }
1017
1018   return true;
1019}
1020
1021
1022bool Namespace::classLinkTo(Namespace *parent)
1023{
1024   Namespace* walk = getPackageRoot();
1025
1026   if(walk->mParent && walk->mParent != parent)
1027   {
1028      Con::errorf(ConsoleLogEntry::General, "Error: cannot change namespace parent linkage of %s from %s to %s.",
1029         walk->mName, walk->mParent->mName, parent->mName);
1030      return false;
1031   }
1032
1033   trashCache();
1034   walk->mParent = parent;
1035
1036   mRefCountToParent++;
1037
1038   return true;
1039}
1040
1041void Namespace::buildHashTable()
1042{
1043   if(mHashSequence == mCacheSequence)
1044      return;
1045
1046   if(!mEntryList && mParent)
1047   {
1048      mParent->buildHashTable();
1049      mHashTable = mParent->mHashTable;
1050      mHashSize = mParent->mHashSize;
1051      mHashSequence = mCacheSequence;
1052      return;
1053   }
1054
1055   U32 entryCount = 0;
1056   Namespace * ns;
1057   for(ns = this; ns; ns = ns->mParent)
1058      for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
1059         if(lookupRecursive(walk->mFunctionName) == walk)
1060            entryCount++;
1061
1062   mHashSize = entryCount + (entryCount >> 1) + 1;
1063
1064   if(!(mHashSize & 1))
1065      mHashSize++;
1066
1067   mHashTable = (Entry **) mCacheAllocator.alloc(sizeof(Entry *) * mHashSize);
1068   for(U32 i = 0; i < mHashSize; i++)
1069      mHashTable[i] = NULL;
1070
1071   for(ns = this; ns; ns = ns->mParent)
1072   {
1073      for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
1074      {
1075         U32 index = HashPointer(walk->mFunctionName) % mHashSize;
1076         while(mHashTable[index] && mHashTable[index]->mFunctionName != walk->mFunctionName)
1077         {
1078            index++;
1079            if(index >= mHashSize)
1080               index = 0;
1081         }
1082
1083         if(!mHashTable[index])
1084            mHashTable[index] = walk;
1085      }
1086   }
1087
1088   mHashSequence = mCacheSequence;
1089}
1090
1091void Namespace::getUniqueEntryLists( Namespace *other, VectorPtr<Entry*> *outThisList, VectorPtr<Entry*> *outOtherList )
1092{
1093   // All namespace entries in the common ACR should be
1094   // ignored when checking for duplicate entry names.
1095   static VectorPtr<Namespace::Entry*> commonEntries;
1096   commonEntries.clear();
1097
1098   AbstractClassRep *commonACR = mClassRep->getCommonParent( other->mClassRep );
1099   commonACR->getNameSpace()->getEntryList( &commonEntries );
1100
1101   // Make life easier
1102   VectorPtr<Namespace::Entry*> &thisEntries = *outThisList;
1103   VectorPtr<Namespace::Entry*> &compEntries = *outOtherList;
1104
1105   // Clear, just in case they aren't
1106   thisEntries.clear();
1107   compEntries.clear();
1108
1109   getEntryList( &thisEntries );
1110   other->getEntryList( &compEntries );
1111
1112   // Run through all of the entries in the common ACR, and remove them from
1113   // the other two entry lists
1114   for( NamespaceEntryListIterator itr = commonEntries.begin(); itr != commonEntries.end(); itr++ )
1115   {
1116      // Check this entry list
1117      for( NamespaceEntryListIterator thisItr = thisEntries.begin(); thisItr != thisEntries.end(); thisItr++ )
1118      {
1119         if( *thisItr == *itr )
1120         {
1121            thisEntries.erase( thisItr );
1122            break;
1123         }
1124      }
1125
1126      // Same check for component entry list
1127      for( NamespaceEntryListIterator compItr = compEntries.begin(); compItr != compEntries.end(); compItr++ )
1128      {
1129         if( *compItr == *itr )
1130         {
1131            compEntries.erase( compItr );
1132            break;
1133         }
1134      }
1135   }
1136}
1137
1138void Namespace::init()
1139{
1140   // create the global namespace
1141   mGlobalNamespace = (Namespace *) mAllocator.alloc(sizeof(Namespace));
1142   constructInPlace(mGlobalNamespace);
1143   mGlobalNamespace->mPackage = NULL;
1144   mGlobalNamespace->mName = NULL;
1145   mGlobalNamespace->mNext = NULL;
1146   mNamespaceList = mGlobalNamespace;
1147}
1148
1149Namespace *Namespace::global()
1150{
1151   return mGlobalNamespace;
1152}
1153
1154void Namespace::shutdown()
1155{
1156   // The data chunker will release all memory in one go
1157   // without calling destructors, so we do this manually here.
1158
1159   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
1160      walk->~Namespace();
1161}
1162
1163void Namespace::trashCache()
1164{
1165   mCacheSequence++;
1166   mCacheAllocator.freeBlocks();
1167}
1168
1169const char *Namespace::tabComplete(const char *prevText, S32 baseLen, bool fForward)
1170{
1171   if(mHashSequence != mCacheSequence)
1172      buildHashTable();
1173
1174   const char *bestMatch = NULL;
1175   for(U32 i = 0; i < mHashSize; i++)
1176      if(mHashTable[i] && canTabComplete(prevText, bestMatch, mHashTable[i]->mFunctionName, baseLen, fForward))
1177         bestMatch = mHashTable[i]->mFunctionName;
1178   return bestMatch;
1179}
1180
1181Namespace::Entry *Namespace::lookupRecursive(StringTableEntry name)
1182{
1183   for(Namespace *ns = this; ns; ns = ns->mParent)
1184      for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext)
1185         if(walk->mFunctionName == name)
1186            return walk;
1187
1188   return NULL;
1189}
1190
1191Namespace::Entry *Namespace::lookup(StringTableEntry name)
1192{
1193   if(mHashSequence != mCacheSequence)
1194      buildHashTable();
1195
1196   U32 index = HashPointer(name) % mHashSize;
1197   while(mHashTable[index] && mHashTable[index]->mFunctionName != name)
1198   {
1199      index++;
1200      if(index >= mHashSize)
1201         index = 0;
1202   }
1203   return mHashTable[index];
1204}
1205
1206static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b)
1207{
1208   const Namespace::Entry* fa = *((Namespace::Entry**)a);
1209   const Namespace::Entry* fb = *((Namespace::Entry**)b);
1210
1211   return dStricmp(fa->mFunctionName, fb->mFunctionName);
1212}
1213
1214void Namespace::getEntryList(VectorPtr<Entry*> *vec)
1215{
1216   if(mHashSequence != mCacheSequence)
1217      buildHashTable();
1218
1219   for(U32 i = 0; i < mHashSize; i++)
1220      if(mHashTable[i])
1221         vec->push_back(mHashTable[i]);
1222
1223   dQsort(vec->address(),vec->size(),sizeof(Namespace::Entry *),compareEntries);
1224}
1225
1226Namespace::Entry *Namespace::createLocalEntry(StringTableEntry name)
1227{
1228   for(Entry *walk = mEntryList; walk; walk = walk->mNext)
1229   {
1230      if(walk->mFunctionName == name)
1231      {
1232         walk->clear();
1233         return walk;
1234      }
1235   }
1236
1237   Entry *ent = (Entry *) mAllocator.alloc(sizeof(Entry));
1238   constructInPlace(ent);
1239
1240   ent->mNamespace = this;
1241   ent->mFunctionName = name;
1242   ent->mNext = mEntryList;
1243   ent->mPackage = mPackage;
1244   ent->mToolOnly = false;
1245   mEntryList = ent;
1246   return ent;
1247}
1248
1249void Namespace::addFunction( StringTableEntry name, CodeBlock *cb, U32 functionOffset, const char* usage, U32 lineNumber )
1250{
1251   Entry *ent = createLocalEntry(name);
1252   trashCache();
1253
1254   ent->mUsage = usage;
1255   ent->mCode = cb;
1256   ent->mFunctionOffset = functionOffset;
1257   ent->mCode->incRefCount();
1258   ent->mType = Entry::ConsoleFunctionType;
1259   ent->mFunctionLineNumber = lineNumber;   
1260}
1261
1262void Namespace::addCommand( StringTableEntry name, StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header )
1263{
1264   Entry *ent = createLocalEntry(name);
1265   trashCache();
1266
1267   ent->mUsage = usage;
1268   ent->mHeader = header;
1269   ent->mMinArgs = minArgs;
1270   ent->mMaxArgs = maxArgs;
1271   ent->mToolOnly = isToolOnly;
1272
1273   ent->mType = Entry::StringCallbackType;
1274   ent->cb.mStringCallbackFunc = cb;
1275}
1276
1277void Namespace::addCommand( StringTableEntry name, IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header )
1278{
1279   Entry *ent = createLocalEntry(name);
1280   trashCache();
1281
1282   ent->mUsage = usage;
1283   ent->mHeader = header;
1284   ent->mMinArgs = minArgs;
1285   ent->mMaxArgs = maxArgs;
1286   ent->mToolOnly = isToolOnly;
1287
1288   ent->mType = Entry::IntCallbackType;
1289   ent->cb.mIntCallbackFunc = cb;
1290}
1291
1292void Namespace::addCommand( StringTableEntry name, VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header )
1293{
1294   Entry *ent = createLocalEntry(name);
1295   trashCache();
1296
1297   ent->mUsage = usage;
1298   ent->mHeader = header;
1299   ent->mMinArgs = minArgs;
1300   ent->mMaxArgs = maxArgs;
1301   ent->mToolOnly = isToolOnly;
1302
1303   ent->mType = Entry::VoidCallbackType;
1304   ent->cb.mVoidCallbackFunc = cb;
1305}
1306
1307void Namespace::addCommand( StringTableEntry name, FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header )
1308{
1309   Entry *ent = createLocalEntry(name);
1310   trashCache();
1311
1312   ent->mUsage = usage;
1313   ent->mHeader = header;
1314   ent->mMinArgs = minArgs;
1315   ent->mMaxArgs = maxArgs;
1316   ent->mToolOnly = isToolOnly;
1317
1318   ent->mType = Entry::FloatCallbackType;
1319   ent->cb.mFloatCallbackFunc = cb;
1320}
1321
1322void Namespace::addCommand( StringTableEntry name, BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header )
1323{
1324   Entry *ent = createLocalEntry(name);
1325   trashCache();
1326
1327   ent->mUsage = usage;
1328   ent->mHeader = header;
1329   ent->mMinArgs = minArgs;
1330   ent->mMaxArgs = maxArgs;
1331   ent->mToolOnly = isToolOnly;
1332
1333   ent->mType = Entry::BoolCallbackType;
1334   ent->cb.mBoolCallbackFunc = cb;
1335}
1336
1337void Namespace::addScriptCallback( const char *funcName, const char *usage, ConsoleFunctionHeader* header )
1338{
1339   static U32 uid=0;
1340   char buffer[1024];
1341   char lilBuffer[32];
1342   dStrcpy(buffer, funcName);
1343   dSprintf(lilBuffer, 32, "_%d_cb", uid++);
1344   dStrcat(buffer, lilBuffer);
1345
1346   Entry *ent = createLocalEntry(StringTable->insert( buffer ));
1347   trashCache();
1348
1349   ent->mUsage = usage;
1350   ent->mHeader = header;
1351   ent->mMinArgs = -2;
1352   ent->mMaxArgs = -3;
1353
1354   ent->mType = Entry::ScriptCallbackType;
1355   ent->cb.mCallbackName = funcName;
1356}
1357
1358void Namespace::markGroup(const char* name, const char* usage)
1359{
1360   static U32 uid=0;
1361   char buffer[1024];
1362   char lilBuffer[32];
1363   dStrcpy(buffer, name);
1364   dSprintf(lilBuffer, 32, "_%d", uid++);
1365   dStrcat(buffer, lilBuffer);
1366
1367   Entry *ent = createLocalEntry(StringTable->insert( buffer ));
1368   trashCache();
1369
1370   if(usage != NULL)
1371      lastUsage = (char*)(ent->mUsage = usage);
1372   else
1373      ent->mUsage = lastUsage;
1374
1375   ent->mMinArgs = -1; // Make sure it explodes if somehow we run this entry.
1376   ent->mMaxArgs = -2;
1377
1378   ent->mType = Entry::GroupMarker;
1379   ent->cb.mGroupName = name;
1380}
1381
1382extern S32 executeBlock(StmtNode *block, ExprEvalState *state);
1383
1384ConsoleValueRef Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprEvalState *state)
1385{
1386   STR.clearFunctionOffset();
1387
1388   if(mType == ConsoleFunctionType)
1389   {
1390      if(mFunctionOffset)
1391      {
1392         return mCode->exec(mFunctionOffset, argv[0], mNamespace, argc, argv, false, mPackage);
1393      }
1394      else
1395      {
1396         return ConsoleValueRef();
1397      }
1398   }
1399
1400#ifndef TORQUE_DEBUG
1401   // [tom, 12/13/2006] This stops tools functions from working in the console,
1402   // which is useful behavior when debugging so I'm ifdefing this out for debug builds.
1403   if(mToolOnly && ! Con::isCurrentScriptToolScript())
1404   {
1405      Con::errorf(ConsoleLogEntry::Script, "%s::%s - attempting to call tools only function from outside of tools", mNamespace->mName, mFunctionName);
1406      return ConsoleValueRef();
1407   }
1408#endif
1409
1410   if((mMinArgs && argc < mMinArgs) || (mMaxArgs && argc > mMaxArgs))
1411   {
1412      Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", mNamespace->mName, mFunctionName);
1413      Con::warnf(ConsoleLogEntry::Script, "usage: %s", mUsage);
1414      return ConsoleValueRef();
1415   }
1416
1417   switch(mType)
1418   {
1419      case StringCallbackType:
1420         return ConsoleValueRef::fromValue(CSTK.pushStackString(cb.mStringCallbackFunc(state->thisObject, argc, argv)));
1421      case IntCallbackType:
1422       return ConsoleValueRef::fromValue(CSTK.pushUINT((U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv)));
1423      case FloatCallbackType:
1424       return ConsoleValueRef::fromValue(CSTK.pushFLT((U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv)));
1425      case VoidCallbackType:
1426         cb.mVoidCallbackFunc(state->thisObject, argc, argv);
1427         return ConsoleValueRef();
1428      case BoolCallbackType:
1429       return ConsoleValueRef::fromValue(CSTK.pushUINT((U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv)));
1430   }
1431   
1432   return ConsoleValueRef();
1433}
1434
1435//-----------------------------------------------------------------------------
1436// Doc string code.
1437
1438namespace {
1439
1440   /// Scan the given usage string for an argument list description.  With the
1441   /// old console macros, these were usually included as the first part of the
1442   /// usage string.
1443   bool sFindArgumentListSubstring( const char* usage, const char*& start, const char*& end )
1444   {
1445      if( !usage )
1446         return false;
1447         
1448      const char* ptr = usage;
1449      while( *ptr && *ptr != '(' && *ptr != '\n' ) // Only scan first line of usage string.
1450      {
1451         // Stop on the first alphanumeric character as we expect
1452         // argument lists to precede descriptions.
1453         if( dIsalnum( *ptr ) )
1454            return false;
1455            
1456         ptr ++;
1457      }
1458         
1459      if( *ptr != '(' )
1460         return false;
1461
1462      start = ptr;
1463      ptr ++;
1464      
1465      bool inString = false;
1466      U32 nestingCount = 0;
1467      
1468      while( *ptr && ( *ptr != ')' || nestingCount > 0 || inString ) )
1469      {
1470         if( *ptr == '(' )
1471            nestingCount ++;
1472         else if( *ptr == ')' )
1473            nestingCount --;
1474         else if( *ptr == '"' )
1475            inString = !inString;
1476         else if( *ptr == '\\' && ptr[ 1 ] == '"' )
1477            ptr ++;
1478         ptr ++;
1479      }
1480      
1481      if( *ptr )
1482         ptr ++;
1483      end = ptr;
1484      
1485      return true;
1486   }
1487   
1488   ///
1489   void sParseList( const char* str, Vector< String>& outList )
1490   {
1491      // Skip the initial '( '.
1492      
1493      const char* ptr = str;
1494      while( *ptr && dIsspace( *ptr ) )
1495         ptr ++;
1496      
1497      if( *ptr == '(' )
1498      {
1499         ptr ++;
1500         while( *ptr && dIsspace( *ptr ) )
1501            ptr ++;
1502      }
1503      
1504      // Parse out list items.
1505      
1506      while( *ptr && *ptr != ')' )
1507      {
1508         // Find end of element.
1509         
1510         const char* start = ptr;
1511
1512         bool inString = false;
1513         U32 nestingCount = 0;
1514
1515         while( *ptr && ( ( *ptr != ')' && *ptr != ',' ) || nestingCount > 0 || inString ) )
1516         {
1517            if( *ptr == '(' )
1518               nestingCount ++;
1519            else if( *ptr == ')' )
1520               nestingCount --;
1521            else if( *ptr == '"' )
1522               inString = !inString;
1523            else if( *ptr == '\\' && ptr[ 1 ] == '"' )
1524               ptr ++;
1525            ptr ++;
1526         }
1527            
1528         // Backtrack to remove trailing whitespace.
1529            
1530         const char* end = ptr;
1531         if( *end == ',' || *end == ')' )
1532            end --;
1533         while( end > start && dIsspace( *end ) )
1534            end --;
1535         if( *end )
1536            end ++;
1537            
1538         // Add to list.
1539            
1540         if( start != end )
1541            outList.push_back( String( start, end - start ) );
1542            
1543         // Skip comma and whitespace.
1544         
1545         if( *ptr == ',' )
1546            ptr ++;
1547         while( *ptr && dIsspace( *ptr ) )
1548            ptr ++;
1549      }
1550   }
1551   
1552   ///
1553   void sGetArgNameAndType( const String& str, String& outType, String& outName )
1554   {
1555      if( !str.length() )
1556      {
1557         outType = String::EmptyString;
1558         outName = String::EmptyString;
1559         return;
1560      }
1561      
1562      // Find first non-ID character from right.
1563      
1564      S32 index = str.length() - 1;
1565      while( index >= 0 && ( dIsalnum( str[ index ] ) || str[ index ] == '_' ) )
1566         index --;
1567         
1568      const U32 nameStartIndex = index + 1;
1569      
1570      // Find end of type name by skipping rightmost whitespace inwards.
1571      
1572      while( index >= 0 && dIsspace( str[ index ] ) )
1573         index --;
1574         
1575      //
1576      
1577      outName = String( &( ( const char* ) str )[ nameStartIndex ] );
1578      outType = String( str, index + 1 );
1579   }
1580   
1581   /// Return the type name to show in documentation for the given C++ type.
1582   const char* sGetDocTypeString( const char* nativeType )
1583   {
1584      if( dStrncmp( nativeType, "const ", 6 ) == 0 )
1585         nativeType += 6;
1586
1587      if( dStrcmp( nativeType, "char*" ) == 0 || dStrcmp( nativeType, "char *" ) == 0 )
1588         return "string";
1589      else if( dStrcmp( nativeType, "S32" ) == 0 || dStrcmp( nativeType, "U32" ) == 0 )
1590         return "int";
1591      else if( dStrcmp( nativeType, "F32" ) == 0 )
1592         return "float";
1593         
1594      const U32 length = dStrlen( nativeType );
1595      if( nativeType[ length - 1 ] == '&' || nativeType[ length - 1 ] == '*' )
1596         return StringTable->insertn( nativeType, length - 1 );
1597         
1598      return nativeType;
1599   }
1600}
1601
1602String Namespace::Entry::getBriefDescription( String* outRemainingDocText ) const
1603{
1604   String docString = getDocString();
1605   
1606   S32 newline = docString.find( '\n' );
1607   if( newline == -1 )
1608   {
1609      if( outRemainingDocText )
1610         *outRemainingDocText = String();
1611      return docString;
1612   }
1613      
1614   String brief = docString.substr( 0, newline );
1615   if( outRemainingDocText )
1616      *outRemainingDocText = docString.substr( newline + 1 );
1617      
1618   return brief;
1619}
1620
1621String Namespace::Entry::getDocString() const
1622{
1623   const char* argListStart;
1624   const char* argListEnd;
1625   
1626   if( sFindArgumentListSubstring( mUsage, argListStart, argListEnd ) )
1627   {
1628      // Skip the " - " part present in some old doc strings.
1629      
1630      const char* ptr = argListEnd;
1631      while( *ptr && dIsspace( *ptr ) )
1632         ptr ++;
1633         
1634      if( *ptr == '-' )
1635      {
1636         ptr ++;
1637         while( *ptr && dIsspace( *ptr ) )
1638            ptr ++;
1639      }
1640      
1641      return ptr;
1642   }
1643   
1644   return mUsage;
1645}
1646
1647String Namespace::Entry::getArgumentsString() const
1648{
1649   StringBuilder str;
1650   
1651   if( mHeader )
1652   {
1653      // Parse out the argument list string supplied with the extended
1654      // function header and add default arguments as we go.
1655      
1656      Vector< String> argList;
1657      Vector< String> defaultArgList;
1658      
1659      sParseList( mHeader->mArgString, argList );
1660      sParseList( mHeader->mDefaultArgString, defaultArgList );
1661      
1662      str.append( '(' );
1663      
1664      const U32 numArgs = argList.size();
1665      const U32 numDefaultArgs = defaultArgList.size();
1666      const U32 firstDefaultArgIndex = numArgs - numDefaultArgs;
1667      
1668      for( U32 i = 0; i < numArgs; ++ i )
1669      {
1670         // Add separator if not first arg.
1671         
1672         if( i > 0 )
1673            str.append( ',' );
1674                     
1675         // Add type and name.
1676         
1677         String name;
1678         String type;
1679         
1680         sGetArgNameAndType( argList[ i ], type, name );
1681         
1682         str.append( ' ' );
1683         str.append( sGetDocTypeString( type ) );
1684         str.append( ' ' );
1685         str.append( name );
1686         
1687         // Add default value, if any.
1688         
1689         if( i >= firstDefaultArgIndex )
1690         {
1691            str.append( '=' );
1692            str.append( defaultArgList[ i - firstDefaultArgIndex ] );
1693         }
1694      }
1695      
1696      if( numArgs > 0 )
1697         str.append( ' ' );
1698      str.append( ')' );
1699   }
1700   else
1701   {
1702      // No extended function header.  Try to parse out the argument
1703      // list from the usage string.
1704      
1705      const char* argListStart;
1706      const char* argListEnd;
1707      
1708      if( sFindArgumentListSubstring( mUsage, argListStart, argListEnd ) )
1709         str.append( argListStart, argListEnd - argListStart );
1710      else if( mType == ConsoleFunctionType && mCode )
1711      {
1712         // This isn't correct but the nonsense console stuff is set up such that all
1713         // functions that have no function bodies are keyed to offset 0 to indicate "no code."
1714         // This loses the association with the original function definition so we can't really
1715         // tell here what the actual prototype is except if we searched though the entire opcode
1716         // stream for the corresponding OP_FUNC_DECL (which would require dealing with the
1717         // variable-size instructions).
1718         
1719         if( !mFunctionOffset )
1720            return "()";
1721            
1722         String args = mCode->getFunctionArgs( mFunctionOffset );
1723         if( args.isEmpty() )
1724            return "()";
1725            
1726         str.append( "( " );
1727         str.append( args );
1728         str.append( " )" );
1729      }
1730   }
1731   
1732   return str.end();
1733}
1734
1735String Namespace::Entry::getPrototypeString() const
1736{
1737   StringBuilder str;
1738   
1739   // Start with return type.
1740   
1741   if( mHeader && mHeader->mReturnString )
1742   {
1743      str.append( sGetDocTypeString( mHeader->mReturnString ) );
1744      str.append( ' ' );
1745   }
1746   else
1747      switch( mType )
1748      {
1749         case StringCallbackType:
1750            str.append( "string " );
1751            break;
1752            
1753         case IntCallbackType:
1754            str.append( "int " );
1755            break;
1756
1757         case FloatCallbackType:
1758            str.append( "float " );
1759            break;
1760
1761         case VoidCallbackType:
1762            str.append( "void " );
1763            break;
1764
1765         case BoolCallbackType:
1766            str.append( "bool " );
1767            break;
1768            
1769         case ScriptCallbackType:
1770            break;
1771      }
1772   
1773   // Add function name and arguments.
1774
1775   if( mType == ScriptCallbackType )
1776      str.append( cb.mCallbackName );
1777   else
1778      str.append( mFunctionName );
1779      
1780   str.append( getArgumentsString() );
1781      
1782   return str.end();
1783}
1784
1785//-----------------------------------------------------------------------------
1786
1787StringTableEntry Namespace::mActivePackages[Namespace::MaxActivePackages];
1788U32 Namespace::mNumActivePackages = 0;
1789U32 Namespace::mOldNumActivePackages = 0;
1790
1791bool Namespace::isPackage(StringTableEntry name)
1792{
1793   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
1794      if(walk->mPackage == name)
1795         return true;
1796   return false;
1797}
1798
1799U32 Namespace::getActivePackagesCount()
1800{
1801   return mNumActivePackages;
1802}
1803
1804StringTableEntry Namespace::getActivePackage(U32 index)
1805{
1806   if( index >= mNumActivePackages )
1807      return StringTable->EmptyString();
1808
1809   return mActivePackages[index];
1810}
1811
1812void Namespace::activatePackage(StringTableEntry name)
1813{
1814   if(mNumActivePackages == MaxActivePackages)
1815   {
1816      Con::printf("ActivatePackage(%s) failed - Max package limit reached: %d", name, MaxActivePackages);
1817      return;
1818   }
1819   if(!name)
1820      return;
1821
1822   // see if this one's already active
1823   for(U32 i = 0; i < mNumActivePackages; i++)
1824      if(mActivePackages[i] == name)
1825         return;
1826
1827   // kill the cache
1828   trashCache();
1829
1830   // find all the package namespaces...
1831   for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
1832   {
1833      if(walk->mPackage == name)
1834      {
1835         Namespace *parent = Namespace::find(walk->mName);
1836         // hook the parent
1837         walk->mParent = parent->mParent;
1838         parent->mParent = walk;
1839
1840         // now swap the entries:
1841         Entry *ew;
1842         for(ew = parent->mEntryList; ew; ew = ew->mNext)
1843            ew->mNamespace = walk;
1844
1845         for(ew = walk->mEntryList; ew; ew = ew->mNext)
1846            ew->mNamespace = parent;
1847
1848         ew = walk->mEntryList;
1849         walk->mEntryList = parent->mEntryList;
1850         parent->mEntryList = ew;
1851      }
1852   }
1853   mActivePackages[mNumActivePackages++] = name;
1854}
1855
1856void Namespace::deactivatePackage(StringTableEntry name)
1857{
1858   U32 oldNumActivePackages = mNumActivePackages;
1859
1860   // Remove all packages down to the given one
1861   deactivatePackageStack( name );
1862
1863   // Now add back all packages that followed the given one
1864   if(!oldNumActivePackages)
1865      return;
1866   for(U32 i = mNumActivePackages+1; i < oldNumActivePackages; i++)
1867      activatePackage(mActivePackages[i]);
1868}
1869
1870void Namespace::deactivatePackageStack(StringTableEntry name)
1871{
1872   S32 i, j;
1873   for(i = 0; i < mNumActivePackages; i++)
1874      if(mActivePackages[i] == name)
1875         break;
1876   if(i == mNumActivePackages)
1877      return;
1878
1879   trashCache();
1880
1881   // Remove all packages down to the given one
1882   for(j = mNumActivePackages - 1; j >= i; j--)
1883   {
1884      // gotta unlink em in reverse order...
1885      for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
1886      {
1887         if(walk->mPackage == mActivePackages[j])
1888         {
1889            Namespace *parent = Namespace::find(walk->mName);
1890            // hook the parent
1891            parent->mParent = walk->mParent;
1892            walk->mParent = NULL;
1893
1894            // now swap the entries:
1895            Entry *ew;
1896            for(ew = parent->mEntryList; ew; ew = ew->mNext)
1897               ew->mNamespace = walk;
1898
1899            for(ew = walk->mEntryList; ew; ew = ew->mNext)
1900               ew->mNamespace = parent;
1901
1902            ew = walk->mEntryList;
1903            walk->mEntryList = parent->mEntryList;
1904            parent->mEntryList = ew;
1905         }
1906      }
1907   }
1908   mNumActivePackages = i;
1909}
1910
1911void Namespace::unlinkPackages()
1912{
1913   mOldNumActivePackages = mNumActivePackages;
1914   if(!mNumActivePackages)
1915      return;
1916   deactivatePackageStack(mActivePackages[0]);
1917}
1918
1919void Namespace::relinkPackages()
1920{
1921   if(!mOldNumActivePackages)
1922      return;
1923   for(U32 i = 0; i < mOldNumActivePackages; i++)
1924      activatePackage(mActivePackages[i]);
1925}
1926
1927
1928DefineEngineFunction(isPackage, bool, ( String identifier ),,
1929   "@brief Returns true if the identifier is the name of a declared package.\n\n"
1930   "@ingroup Packages\n")
1931{
1932   StringTableEntry name = StringTable->insert(identifier.c_str());
1933   return Namespace::isPackage(name);
1934}
1935
1936DefineEngineFunction(activatePackage, void, ( String packageName ),,
1937   "@brief Activates an existing package.\n\n"
1938   "The activation occurs by updating the namespace linkage of existing functions and methods. "
1939   "If the package is already activated the function does nothing.\n"
1940   "@ingroup Packages\n")
1941{
1942   StringTableEntry name = StringTable->insert(packageName.c_str());
1943   Namespace::activatePackage(name);
1944}
1945
1946DefineEngineFunction(deactivatePackage, void, ( String packageName ),,
1947   "@brief Deactivates a previously activated package.\n\n"
1948   "The package is deactivated by removing its namespace linkages to any function or method. "
1949   "If there are any packages above this one in the stack they are deactivated as well. "
1950   "If the package is not on the stack this function does nothing.\n"
1951   "@ingroup Packages\n")
1952{
1953   StringTableEntry name = StringTable->insert(packageName.c_str());
1954   Namespace::deactivatePackage(name);
1955}
1956
1957DefineEngineFunction(getPackageList, const char*, (),,
1958   "@brief Returns a space delimited list of the active packages in stack order.\n\n"
1959   "@ingroup Packages\n")
1960{
1961   if( Namespace::getActivePackagesCount() == 0 )
1962      return "";
1963
1964   // Determine size of return buffer
1965   dsize_t buffersize = 0;
1966   for( U32 i = 0; i < Namespace::getActivePackagesCount(); ++i )
1967   {
1968      buffersize += dStrlen(Namespace::getActivePackage(i)) + 1;
1969   }
1970
1971   U32 maxBufferSize = buffersize + 1;
1972   char* returnBuffer = Con::getReturnBuffer(maxBufferSize);
1973   U32 returnLen = 0;
1974   for( U32 i = 0; i < Namespace::getActivePackagesCount(); ++i )
1975   {
1976      dSprintf(returnBuffer + returnLen, maxBufferSize - returnLen, "%s ", Namespace::getActivePackage(i));
1977      returnLen = dStrlen(returnBuffer);
1978   }
1979
1980   // Trim off the last extra space
1981   if (returnLen > 0 && returnBuffer[returnLen - 1] == ' ')
1982      returnBuffer[returnLen - 1] = '\0';
1983
1984   return returnBuffer;
1985}
1986