| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "console/console.h"#include "console/ast.h"#include "core/tAlgorithm.h"#include "core/strings/findMatch.h"#include "core/strings/stringUnit.h"#include "console/consoleInternal.h"#include "core/stream/fileStream.h"#include "console/compiler.h"#include "console/simBase.h"#include "console/telnetDebugger.h"#include "sim/netStringTable.h"#include "console/ICallMethod.h"#include "console/stringStack.h"#include "util/messaging/message.h"#include "core/frameAllocator.h"#ifndef TORQUE_TGB_ONLY#include "materials/materialDefinition.h"#include "materials/materialManager.h"#endif// Uncomment to optimize function calls at the expense of potential invalid package lookups//#define COMPILER_OPTIMIZE_FUNCTION_CALLSusing namespace Compiler;enum EvalConstants {   MaxStackSize = 1024,   MethodOnComponent = -2};namespace Con{// Current script file name and root, these are registered as// console variables.extern StringTableEntry gCurrentFile;extern StringTableEntry gCurrentRoot;extern S32 gObjectCopyFailures;}/// Frame data for a foreach/foreach$ loop.struct IterStackRecord{   /// If true, this is a foreach$ loop; if not, it's a foreach loop.   bool mIsStringIter;      /// The iterator variable.   Dictionary::Entry* mVariable;      /// Information for an object iterator loop.   struct ObjectPos   {      /// The set being iterated over.      SimSet* mSet;      /// Current index in the set.      U32 mIndex;   };      /// Information for a string iterator loop.   struct StringPos   {      /// The raw string data on the string stack.      StringStackPtr mString;            /// Current parsing position.      U32 mIndex;   };      union   {      ObjectPos mObj;      StringPos mStr;   } mData;};IterStackRecord iterStack[ MaxStackSize ];F64 floatStack[MaxStackSize];S64 intStack[MaxStackSize];StringStack STR;ConsoleValueStack CSTK;U32 _FLT = 0;     ///< Stack pointer for floatStack.U32 _UINT = 0;    ///< Stack pointer for intStack.U32 _ITER = 0;    ///< Stack pointer for iterStack.namespace Con{   const char *getNamespaceList(Namespace *ns)   {      U32 size = 1;      Namespace * walk;      for(walk = ns; walk; walk = walk->mParent)         size += dStrlen(walk->mName) + 4;      char *ret = Con::getReturnBuffer(size);      ret[0] = 0;      for(walk = ns; walk; walk = walk->mParent)      {         dStrcat(ret, walk->mName);         if(walk->mParent)            dStrcat(ret, " -> ");      }      return ret;   }}//------------------------------------------------------------F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line){   F64 val = dAtof(str);   if(val != 0)      return val;   else if(!dStricmp(str, "true"))      return 1;   else if(!dStricmp(str, "false"))      return 0;   else if(file)   {      Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line);      return 0;   }   return 0;}//------------------------------------------------------------namespace Con{   char *getReturnBuffer(U32 bufferSize)   {      return STR.getReturnBuffer(bufferSize);   }   char *getReturnBuffer( const char *stringToCopy )   {      U32 len = dStrlen( stringToCopy ) + 1;      char *ret = STR.getReturnBuffer( len);      dMemcpy( ret, stringToCopy, len );      return ret;   }   char* getReturnBuffer( const String& str )   {      const U32 size = str.size();      char* ret = STR.getReturnBuffer( size );      dMemcpy( ret, str.c_str(), size );      return ret;   }   char* getReturnBuffer( const StringBuilder& str )   {      char* buffer = Con::getReturnBuffer( str.length() + 1 );      str.copy( buffer );      buffer[ str.length() ] = '\0';            return buffer;   }   char *getArgBuffer(U32 bufferSize)   {      return STR.getArgBuffer(bufferSize);   }   char *getFloatArg(F64 arg)   {      char *ret = STR.getArgBuffer(32);      dSprintf(ret, 32, "%g", arg);      return ret;   }   char *getIntArg(S32 arg)   {      char *ret = STR.getArgBuffer(32);      dSprintf(ret, 32, "%d", arg);      return ret;   }   char* getBoolArg(bool arg)   {      char *ret = STR.getArgBuffer(32);      dSprintf(ret, 32, "%d", arg);      return ret;   }   char *getStringArg( const char *arg )   {      U32 len = dStrlen( arg ) + 1;      char *ret = STR.getArgBuffer( len );      dMemcpy( ret, arg, len );      return ret;   }   char* getStringArg( const String& arg )   {      const U32 size = arg.size();      char* ret = STR.getArgBuffer( size );      dMemcpy( ret, arg.c_str(), size );      return ret;   }}//------------------------------------------------------------inline void ExprEvalState::setCurVarName(StringTableEntry name){   if(name[0] == '$')      currentVariable = globalVars.lookup(name);   else if( getStackDepth() > 0 )      currentVariable = getCurrentFrame().lookup(name);   if(!currentVariable && gWarnUndefinedScriptVariables)	   Con::warnf(ConsoleLogEntry::Script, "Variable referenced before assignment: %s", name);}inline void ExprEvalState::setCurVarNameCreate(StringTableEntry name){   if(name[0] == '$')      currentVariable = globalVars.add(name);   else if( getStackDepth() > 0 )      currentVariable = getCurrentFrame().add(name);   else   {      currentVariable = NULL;      Con::warnf(ConsoleLogEntry::Script, "Accessing local variable in global scope... failed: %s", name);   }}//------------------------------------------------------------inline S32 ExprEvalState::getIntVariable(){   return currentVariable ? currentVariable->getIntValue() : 0;}inline F64 ExprEvalState::getFloatVariable(){   return currentVariable ? currentVariable->getFloatValue() : 0;}inline const char *ExprEvalState::getStringVariable(){   return currentVariable ? currentVariable->getStringValue() : "";}//------------------------------------------------------------inline void ExprEvalState::setIntVariable(S32 val){   AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!");   currentVariable->setIntValue(val);}inline void ExprEvalState::setFloatVariable(F64 val){   AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!");   currentVariable->setFloatValue(val);}inline void ExprEvalState::setStringVariable(const char *val){   AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!");   currentVariable->setStringValue(val);}inline void ExprEvalState::setStringStackPtrVariable(StringStackPtr str){   AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!");   currentVariable->setStringStackPtrValue(str);}inline void ExprEvalState::setCopyVariable(){   if (copyVariable)   {      switch (copyVariable->value.type)      {         case ConsoleValue::TypeInternalInt:            currentVariable->setIntValue(copyVariable->getIntValue());         break;         case ConsoleValue::TypeInternalFloat:            currentVariable->setFloatValue(copyVariable->getFloatValue());         break;         default:            currentVariable->setStringValue(copyVariable->getStringValue());         break;	   }   }}//------------------------------------------------------------// Gets a component of an object's field value or a variable and returns it// in val.static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ){   const char* prevVal = NULL;      // Grab value from object.   if( object && field )      prevVal = object->getDataField( field, array );      // Otherwise, grab from the string stack. The value coming in will always   // be a string because that is how multicomponent variables are handled.   else      prevVal = STR.getStringValue();   // Make sure we got a value.   if ( prevVal && *prevVal )   {      static const StringTableEntry xyzw[] =       {         StringTable->insert( "x" ),         StringTable->insert( "y" ),         StringTable->insert( "z" ),         StringTable->insert( "w" )      };      static const StringTableEntry rgba[] =       {         StringTable->insert( "r" ),         StringTable->insert( "g" ),         StringTable->insert( "b" ),         StringTable->insert( "a" )      };      // Translate xyzw and rgba into the indexed component       // of the variable or field.      if ( subField == xyzw[0] || subField == rgba[0] )         dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") );      else if ( subField == xyzw[1] || subField == rgba[1] )         dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") );      else if ( subField == xyzw[2] || subField == rgba[2] )         dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") );      else if ( subField == xyzw[3] || subField == rgba[3] )         dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") );      else         val[0] = 0;   }   else      val[0] = 0;}// Sets a component of an object's field value based on the sub field. 'x' will// set the first field, 'y' the second, and 'z' the third.static void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ){   // Copy the current string value   char strValue[1024];   dStrncpy( strValue, STR.getStringValue(), 1024 );   char val[1024] = "";   const char* prevVal = NULL;   // Set the value on an object field.   if( object && field )      prevVal = object->getDataField( field, array );   // Set the value on a variable.   else if( gEvalState.currentVariable )      prevVal = gEvalState.getStringVariable();   // Ensure that the variable has a value   if (!prevVal)	   return;   static const StringTableEntry xyzw[] =    {      StringTable->insert( "x" ),      StringTable->insert( "y" ),      StringTable->insert( "z" ),      StringTable->insert( "w" )   };   static const StringTableEntry rgba[] =    {      StringTable->insert( "r" ),      StringTable->insert( "g" ),      StringTable->insert( "b" ),      StringTable->insert( "a" )   };   // Insert the value into the specified    // component of the string.   if ( subField == xyzw[0] || subField == rgba[0] )	  dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") );   else if ( subField == xyzw[1] || subField == rgba[1] )      dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") );   else if ( subField == xyzw[2] || subField == rgba[2] )      dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") );   else if ( subField == xyzw[3] || subField == rgba[3] )      dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") );   if ( val[0] != 0 )   {      // Update the field or variable.      if( object && field )         object->setDataField( field, 0, val );      else if( gEvalState.currentVariable )         gEvalState.setStringVariable( val );   }}ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName, S32 setFrame){#ifdef TORQUE_VALIDATE_STACK   U32 stackStart = STR.mStartStackSize;   U32 consoleStackStart = CSTK.mStackPos;#endif   //Con::printf("CodeBlock::exec(%s,%u)", functionName ? functionName : "??", ip);   static char traceBuffer[1024];   S32 i;      U32 iterDepth = 0;   incRefCount();   F64 *curFloatTable;   char *curStringTable;   S32 curStringTableLen = 0; //clint to ensure we dont overwrite it   STR.clearFunctionOffset(); // ensures arg buffer offset is back to 0   StringTableEntry thisFunctionName = NULL;   bool popFrame = false;   if(argv)   {      // assume this points into a function decl:      U32 fnArgc = code[ip + 2 + 6];      thisFunctionName = CodeToSTE(code, ip);      S32 wantedArgc = getMin(argc-1, fnArgc); // argv[0] is func name      if(gEvalState.traceOn)      {         traceBuffer[0] = 0;         dStrcat(traceBuffer, "Entering ");         if(packageName)         {            dStrcat(traceBuffer, "[");            dStrcat(traceBuffer, packageName);            dStrcat(traceBuffer, "]");         }         if(thisNamespace && thisNamespace->mName)         {            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),               "%s::%s(", thisNamespace->mName, thisFunctionName);         }         else         {            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),               "%s(", thisFunctionName);         }         for(i = 0; i < wantedArgc; i++)         {            dStrcat(traceBuffer, argv[i+1]);            if(i != wantedArgc - 1)               dStrcat(traceBuffer, ", ");         }         dStrcat(traceBuffer, ")");         Con::printf("%s", traceBuffer);      }      gEvalState.pushFrame(thisFunctionName, thisNamespace);      popFrame = true;      for(i = 0; i < wantedArgc; i++)      {         StringTableEntry var = CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2));         gEvalState.setCurVarNameCreate(var);         ConsoleValueRef ref = argv[i+1];         switch(argv[i+1].getType())         {         case ConsoleValue::TypeInternalInt:            gEvalState.setIntVariable(argv[i+1]);            break;         case ConsoleValue::TypeInternalFloat:            gEvalState.setFloatVariable(argv[i+1]);            break;         case ConsoleValue::TypeInternalStringStackPtr:            gEvalState.setStringStackPtrVariable(argv[i+1].getStringStackPtrValue());            break;         case ConsoleValue::TypeInternalStackString:         case ConsoleValue::TypeInternalString:         default:            gEvalState.setStringVariable(argv[i+1]);            break;         }      }      ip = ip + (fnArgc * 2) + (2 + 6 + 1);      curFloatTable = functionFloats;      curStringTable = functionStrings;      curStringTableLen = functionStringsMaxLen;   }   else   {      curFloatTable = globalFloats;      curStringTable = globalStrings;      curStringTableLen = globalStringsMaxLen;      // If requested stack frame isn't available, request a new one      // (this prevents assert failures when creating local      //  variables without a stack frame)      if (gEvalState.getStackDepth() <= setFrame)         setFrame = -1;      // Do we want this code to execute using a new stack frame?      if (setFrame < 0)      {         gEvalState.pushFrame(NULL, NULL);         popFrame = true;      }      else      {         // We want to copy a reference to an existing stack frame         // on to the top of the stack.  Any change that occurs to          // the locals during this new frame will also occur in the          // original frame.         S32 stackIndex = gEvalState.getStackDepth() - setFrame - 1;         gEvalState.pushFrameRef( stackIndex );         popFrame = true;      }   }   // Grab the state of the telenet debugger here once   // so that the push and pop frames are always balanced.   const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected();   if ( telDebuggerOn && setFrame < 0 )      TelDebugger->pushStackFrame();   StringTableEntry var, objParent;   StringTableEntry fnName;   StringTableEntry fnNamespace, fnPackage;   // Add local object creation stack [7/9/2007 Black]   static const U32 objectCreationStackSize = 32;   U32 objectCreationStackIndex = 0;   struct {      SimObject *newObject;      U32 failJump;   } objectCreationStack[ objectCreationStackSize ];      SimObject *currentNewObject = 0;   U32 failJump = 0;   StringTableEntry prevField = NULL;   StringTableEntry curField = NULL;   SimObject *prevObject = NULL;   SimObject *curObject = NULL;   SimObject *saveObject=NULL;   Namespace::Entry *nsEntry;   Namespace *ns;   const char* curFNDocBlock = NULL;   const char* curNSDocBlock = NULL;   const S32 nsDocLength = 128;   char nsDocBlockClass[nsDocLength];   U32 callArgc;   ConsoleValueRef *callArgv;   static char curFieldArray[256];   static char prevFieldArray[256];   CodeBlock *saveCodeBlock = smCurrentCodeBlock;   smCurrentCodeBlock = this;   if(this->name)   {      Con::gCurrentFile = this->name;      Con::gCurrentRoot = this->modPath;   }   const char * val;   StringStackPtr retValue;   // note: anything returned is pushed to CSTK and will be invalidated on the next exec()   ConsoleValueRef returnValue;   // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and   // OP_LOADFIELD_*) to store temporary values for the fields.   static S32 VAL_BUFFER_SIZE = 1024;   FrameTemp<char> valBuffer( VAL_BUFFER_SIZE );   for(;;)   {      U32 instruction = code[ip++];      nsEntry = NULL;breakContinue:      switch(instruction)      {         case OP_FUNC_DECL:            if(!noCalls)            {               fnName       = CodeToSTE(code, ip);               fnNamespace  = CodeToSTE(code, ip+2);               fnPackage    = CodeToSTE(code, ip+4);               bool hasBody = ( code[ ip + 6 ] & 0x01 ) != 0;               U32 lineNumber = code[ ip + 6 ] >> 1;                              Namespace::unlinkPackages();               ns = Namespace::find(fnNamespace, fnPackage);               ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL, lineNumber );// if no body, set the IP to 0               if( curNSDocBlock )               {                  if( fnNamespace == StringTable->lookup( nsDocBlockClass ) )                  {                     char *usageStr = dStrdup( curNSDocBlock );                     usageStr[dStrlen(usageStr)] = '\0';                     ns->mUsage = usageStr;                     ns->mCleanUpUsage = true;                     curNSDocBlock = NULL;                  }               }               Namespace::relinkPackages();               // If we had a docblock, it's definitely not valid anymore, so clear it out.               curFNDocBlock = NULL;               //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip);            }            ip = code[ip + 7];            break;         case OP_CREATE_OBJECT:         {            // Read some useful info.            objParent        = CodeToSTE(code, ip);            bool isDataBlock =          code[ip + 2];            bool isInternal  =          code[ip + 3];            bool isSingleton =          code[ip + 4];            U32  lineNumber  =          code[ip + 5];            failJump         =          code[ip + 6];                                    // If we don't allow calls, we certainly don't allow creating objects!            // Moved this to after failJump is set. Engine was crashing when            // noCalls = true and an object was being created at the beginning of            // a file. ADL.            if(noCalls)            {               ip = failJump;               break;            }            // Push the old info to the stack            //Assert( objectCreationStackIndex < objectCreationStackSize );            objectCreationStack[ objectCreationStackIndex ].newObject = currentNewObject;            objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump;            // Get the constructor information off the stack.            CSTK.getArgcArgv(NULL, &callArgc, &callArgv);            const char *objectName = callArgv[ 2 ];            // Con::printf("Creating object...");            // objectName = argv[1]...            currentNewObject = NULL;            // Are we creating a datablock? If so, deal with case where we override            // an old one.            if(isDataBlock)            {               // Con::printf("  - is a datablock");               // Find the old one if any.               SimObject *db = Sim::getDataBlockGroup()->findObject( objectName );                              // Make sure we're not changing types on ourselves...               if(db && dStricmp(db->getClassName(), callArgv[1]))               {                  Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName);                  ip = failJump;                  STR.popFrame();                  CSTK.popFrame();                  break;               }               // If there was one, set the currentNewObject and move on.               if(db)                  currentNewObject = db;            }            else if (!isInternal)            {               // IF we aren't looking at a local/internal object, then check if                // this object already exists in the global space               AbstractClassRep* rep = AbstractClassRep::findClassRep( objectName );               if (rep != NULL) {                  Con::errorf(ConsoleLogEntry::General, "%s: Cannot name object [%s] the same name as a script class.",                     getFileLine(ip), objectName);                  ip = failJump;                  STR.popFrame();                  break;               }               SimObject *obj = Sim::findObject( (const char*)objectName );               if (obj /*&& !obj->isLocalName()*/)               {                  if ( isSingleton )                  {                     // Make sure we're not trying to change types                     if ( dStricmp( obj->getClassName(), (const char*)callArgv[1] ) != 0 )                     {                        Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].",                           getFileLine(ip), objectName, (const char*)callArgv[1], obj->getClassName());                        ip = failJump;                        STR.popFrame();                        CSTK.popFrame();                        break;                     }                     // We're creating a singleton, so use the found object                     // instead of creating a new object.                     currentNewObject = obj;                  }                  else                  {                     const char* redefineBehavior = Con::getVariable( "$Con::redefineBehavior" );                                          if( dStricmp( redefineBehavior, "replaceExisting" ) == 0 )                     {                        // Save our constructor args as the argv vector is stored on the                        // string stack and may get stomped if deleteObject triggers                        // script execution.                                                ConsoleValueRef savedArgv[ StringStack::MaxArgs ];                        for (int i=0; i<callArgc; i++) {                           savedArgv[i] = callArgv[i];                        }                        //dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc );                                                // Prevent stack value corruption                        CSTK.pushFrame();                        STR.pushFrame();                        // --                        obj->deleteObject();                        obj = NULL;                        // Prevent stack value corruption                        CSTK.popFrame();                        STR.popFrame();                        // --                        //dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc );                        for (int i=0; i<callArgc; i++) {                           callArgv[i] = savedArgv[i];                        }                     }                     else if( dStricmp( redefineBehavior, "renameNew" ) == 0 )                     {                        for( U32 i = 1;; ++ i )                        {                           String newName = String::ToString( "%s%i", objectName, i );                           if( !Sim::findObject( newName ) )                           {                              objectName = StringTable->insert( newName );                              break;                           }                        }                     }                     else if( dStricmp( redefineBehavior, "unnameNew" ) == 0 )                     {                        objectName = StringTable->insert( "" );                     }                     else if( dStricmp( redefineBehavior, "postfixNew" ) == 0 )                     {                        const char* postfix = Con::getVariable( "$Con::redefineBehaviorPostfix" );                        String newName = String::ToString( "%s%s", objectName, postfix );                                                if( Sim::findObject( newName ) )                        {                           Con::errorf( ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].",                              getFileLine(ip), newName.c_str() );                           ip = failJump;                           STR.popFrame();                           CSTK.popFrame();                           break;                        }                        else                           objectName = StringTable->insert( newName );                     }                     else                     {                        Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].",                           getFileLine(ip), objectName);                        ip = failJump;                        STR.popFrame();                        CSTK.popFrame();                        break;                     }                  }               }            }            STR.popFrame();            CSTK.popFrame();                        if(!currentNewObject)            {               // Well, looks like we have to create a new object.               ConsoleObject *object = ConsoleObject::create((const char*)callArgv[1]);               // Deal with failure!               if(!object)               {                  Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine(ip), (const char*)callArgv[1]);                  ip = failJump;                  break;               }               // Do special datablock init if appropros               if(isDataBlock)               {                  SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(object);                  if(dataBlock)                  {                     dataBlock->assignId();                  }                  else                  {                     // They tried to make a non-datablock with a datablock keyword!                     Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine(ip), (const char*)callArgv[1]);                     // Clean up...                     delete object;                     ip = failJump;                     break;                  }               }               // Finally, set currentNewObject to point to the new one.               currentNewObject = dynamic_cast<SimObject *>(object);               // Deal with the case of a non-SimObject.               if(!currentNewObject)               {                  Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", getFileLine(ip), (const char*)callArgv[1]);                  delete object;                  currentNewObject = NULL;                  ip = failJump;                  break;               }               // Set the declaration line               currentNewObject->setDeclarationLine(lineNumber);               // Set the file that this object was created in               currentNewObject->setFilename(name);               // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance)               if(*objParent)               {                  // Find it!                  SimObject *parent;                  if(Sim::findObject(objParent, parent))                  {                     // Con::printf(" - Parent object found: %s", parent->getClassName());                     currentNewObject->setCopySource( parent );                     currentNewObject->assignFieldsFrom( parent );                  }                  else                  {                     if ( Con::gObjectCopyFailures == -1 )                        Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip), objParent, (const char*)callArgv[1]);                     else                        ++Con::gObjectCopyFailures;                     // Fail to create the object.                     delete object;                     currentNewObject = NULL;                     ip = failJump;                     break;                  }               }               // If a name was passed, assign it.               if( objectName[ 0 ] )               {                  if( !isInternal )                     currentNewObject->assignName( objectName );                  else                     currentNewObject->setInternalName( objectName );                  // Set the original name                  currentNewObject->setOriginalName( objectName );               }               // Prevent stack value corruption               CSTK.pushFrame();               STR.pushFrame();               // --               // Do the constructor parameters.               if(!currentNewObject->processArguments(callArgc-3, callArgv+3))               {                  delete currentNewObject;                  currentNewObject = NULL;                  ip = failJump;                  // Prevent stack value corruption                  CSTK.popFrame();                  STR.popFrame();                  // --                  break;               }               // Prevent stack value corruption               CSTK.popFrame();               STR.popFrame();               // --               // If it's not a datablock, allow people to modify bits of it.               if(!isDataBlock)               {                  currentNewObject->setModStaticFields(true);                  currentNewObject->setModDynamicFields(true);               }            }            // Advance the IP past the create info...            ip += 7;            break;         }         case OP_ADD_OBJECT:         {            // See OP_SETCURVAR for why we do this.            curFNDocBlock = NULL;            curNSDocBlock = NULL;                        // Do we place this object at the root?            bool placeAtRoot = code[ip++];            // Con::printf("Adding object %s", currentNewObject->getName());            // Prevent stack value corruption            CSTK.pushFrame();            STR.pushFrame();            // --            // Make sure it wasn't already added, then add it.            if(currentNewObject->isProperlyAdded() == false)            {               bool ret = false;               Message *msg = dynamic_cast<Message *>(currentNewObject);               if(msg)               {                  SimObjectId id = Message::getNextMessageID();                  if(id != 0xffffffff)                     ret = currentNewObject->registerObject(id);                  else                     Con::errorf("%s: No more object IDs available for messages", getFileLine(ip));               }               else                  ret = currentNewObject->registerObject();               if(! ret)               {                  // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields().                  Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName());                  delete currentNewObject;                  currentNewObject = NULL;                  ip = failJump;                  // Prevent stack value corruption                  CSTK.popFrame();                  STR.popFrame();                  // --                  break;               }            }            // Are we dealing with a datablock?            SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(currentNewObject);            static String errorStr;            // If so, preload it.            if(dataBlock && !dataBlock->preload(true, errorStr))            {               Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip),                           currentNewObject->getName(), errorStr.c_str());               dataBlock->deleteObject();               currentNewObject = NULL;               ip = failJump;			                  // Prevent stack value corruption               CSTK.popFrame();               STR.popFrame();               // --               break;            }            // What group will we be added to, if any?            U32 groupAddId = intStack[_UINT];            SimGroup *grp = NULL;            SimSet   *set = NULL;            bool isMessage = dynamic_cast<Message *>(currentNewObject) != NULL;            if(!placeAtRoot || !currentNewObject->getGroup())            {               if(! isMessage)               {                  if(! placeAtRoot)                  {                     // Otherwise just add to the requested group or set.                     if(!Sim::findObject(groupAddId, grp))                        Sim::findObject(groupAddId, set);                  }                                    if(placeAtRoot)                  {                     // Deal with the instantGroup if we're being put at the root or we're adding to a component.                     if( Con::gInstantGroup.isEmpty()                        || !Sim::findObject( Con::gInstantGroup, grp ) )                        grp = Sim::getRootGroup();                  }               }               // If we didn't get a group, then make sure we have a pointer to               // the rootgroup.               if(!grp)                  grp = Sim::getRootGroup();               // add to the parent group               grp->addObject(currentNewObject);                              // If for some reason the add failed, add the object to the               // root group so it won't leak.               if( !currentNewObject->getGroup() )                  Sim::getRootGroup()->addObject( currentNewObject );               // add to any set we might be in               if(set)                  set->addObject(currentNewObject);            }            // store the new object's ID on the stack (overwriting the group/set            // id, if one was given, otherwise getting pushed)            if(placeAtRoot)                intStack[_UINT] = currentNewObject->getId();            else               intStack[++_UINT] = currentNewObject->getId();            // Prevent stack value corruption            CSTK.popFrame();            STR.popFrame();            // --            break;         }         case OP_END_OBJECT:         {            // If we're not to be placed at the root, make sure we clean up            // our group reference.            bool placeAtRoot = code[ip++];            if(!placeAtRoot)               _UINT--;            break;         }         case OP_FINISH_OBJECT:         {            if (currentNewObject)               currentNewObject->onPostAdd();            //Assert( objectCreationStackIndex >= 0 );            // Restore the object info from the stack [7/9/2007 Black]            currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject;            failJump = objectCreationStack[ objectCreationStackIndex ].failJump;            break;         }         case OP_JMPIFFNOT:            if(floatStack[_FLT--])            {               ip++;               break;            }            ip = code[ip];            break;         case OP_JMPIFNOT:            if(intStack[_UINT--])            {               ip++;               break;            }            ip = code[ip];            break;         case OP_JMPIFF:            if(!floatStack[_FLT--])            {               ip++;               break;            }            ip = code[ip];            break;         case OP_JMPIF:            if(!intStack[_UINT--])            {               ip ++;               break;            }            ip = code[ip];            break;         case OP_JMPIFNOT_NP:            if(intStack[_UINT])            {               _UINT--;               ip++;               break;            }            ip = code[ip];            break;         case OP_JMPIF_NP:            if(!intStack[_UINT])            {               _UINT--;               ip++;               break;            }            ip = code[ip];            break;         case OP_JMP:            ip = code[ip];            break;                     // This fixes a bug when not explicitly returning a value.         case OP_RETURN_VOID:      		STR.setStringValue("");      		// We're falling thru here on purpose.                     case OP_RETURN:            retValue = STR.getStringValuePtr();            if( iterDepth > 0 )            {               // Clear iterator state.               while( iterDepth > 0 )               {                  iterStack[ -- _ITER ].mIsStringIter = false;                  -- iterDepth;               }               STR.rewind();               STR.setStringValue( StringStackPtrRef(retValue).getPtr(&STR) ); // Not nice but works.               retValue = STR.getStringValuePtr();            }            // Previously the return value was on the stack and would be returned using STR.getStringValue().            // Now though we need to wrap it in a ConsoleValueRef             returnValue.value = CSTK.pushStringStackPtr(retValue);                           goto execFinished;         case OP_RETURN_FLT:                     if( iterDepth > 0 )            {               // Clear iterator state.               while( iterDepth > 0 )               {                  iterStack[ -- _ITER ].mIsStringIter = false;                  -- iterDepth;               }                           }            returnValue.value = CSTK.pushFLT(floatStack[_FLT]);            _FLT--;                           goto execFinished;         case OP_RETURN_UINT:                     if( iterDepth > 0 )            {               // Clear iterator state.               while( iterDepth > 0 )               {                  iterStack[ -- _ITER ].mIsStringIter = false;                  -- iterDepth;               }            }            returnValue.value = CSTK.pushUINT(intStack[_UINT]);            _UINT--;                           goto execFinished;                     case OP_CMPEQ:            intStack[_UINT+1] = bool(floatStack[_FLT] == floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_CMPGR:            intStack[_UINT+1] = bool(floatStack[_FLT] > floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_CMPGE:            intStack[_UINT+1] = bool(floatStack[_FLT] >= floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_CMPLT:            intStack[_UINT+1] = bool(floatStack[_FLT] < floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_CMPLE:            intStack[_UINT+1] = bool(floatStack[_FLT] <= floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_CMPNE:            intStack[_UINT+1] = bool(floatStack[_FLT] != floatStack[_FLT-1]);            _UINT++;            _FLT -= 2;            break;         case OP_XOR:            intStack[_UINT-1] = intStack[_UINT] ^ intStack[_UINT-1];            _UINT--;            break;         case OP_MOD:            if(  intStack[_UINT-1] != 0 )               intStack[_UINT-1] = intStack[_UINT] % intStack[_UINT-1];            else               intStack[_UINT-1] = 0;            _UINT--;            break;         case OP_BITAND:            intStack[_UINT-1] = intStack[_UINT] & intStack[_UINT-1];            _UINT--;            break;         case OP_BITOR:            intStack[_UINT-1] = intStack[_UINT] | intStack[_UINT-1];            _UINT--;            break;         case OP_NOT:            intStack[_UINT] = !intStack[_UINT];            break;         case OP_NOTF:            intStack[_UINT+1] = !floatStack[_FLT];            _FLT--;            _UINT++;            break;         case OP_ONESCOMPLEMENT:            intStack[_UINT] = ~intStack[_UINT];            break;         case OP_SHR:            intStack[_UINT-1] = intStack[_UINT] >> intStack[_UINT-1];            _UINT--;            break;         case OP_SHL:            intStack[_UINT-1] = intStack[_UINT] << intStack[_UINT-1];            _UINT--;            break;         case OP_AND:            intStack[_UINT-1] = intStack[_UINT] && intStack[_UINT-1];            _UINT--;            break;         case OP_OR:            intStack[_UINT-1] = intStack[_UINT] || intStack[_UINT-1];            _UINT--;            break;         case OP_ADD:            floatStack[_FLT-1] = floatStack[_FLT] + floatStack[_FLT-1];            _FLT--;            break;         case OP_SUB:            floatStack[_FLT-1] = floatStack[_FLT] - floatStack[_FLT-1];            _FLT--;            break;         case OP_MUL:            floatStack[_FLT-1] = floatStack[_FLT] * floatStack[_FLT-1];            _FLT--;            break;         case OP_DIV:            floatStack[_FLT-1] = floatStack[_FLT] / floatStack[_FLT-1];            _FLT--;            break;         case OP_NEG:            floatStack[_FLT] = -floatStack[_FLT];            break;         case OP_SETCURVAR:            var = CodeToSTE(code, ip);            ip += 2;            // If a variable is set, then these must be NULL. It is necessary            // to set this here so that the vector parser can appropriately            // identify whether it's dealing with a vector.            prevField = NULL;            prevObject = NULL;            curObject = NULL;            gEvalState.setCurVarName(var);            // In order to let docblocks work properly with variables, we have            // clear the current docblock when we do an assign. This way it             // won't inappropriately carry forward to following function decls.            curFNDocBlock = NULL;            curNSDocBlock = NULL;            break;         case OP_SETCURVAR_CREATE:            var = CodeToSTE(code, ip);            ip += 2;            // See OP_SETCURVAR            prevField = NULL;            prevObject = NULL;            curObject = NULL;            gEvalState.setCurVarNameCreate(var);            // See OP_SETCURVAR for why we do this.            curFNDocBlock = NULL;            curNSDocBlock = NULL;            break;         case OP_SETCURVAR_ARRAY:            var = STR.getSTValue();            // See OP_SETCURVAR            prevField = NULL;            prevObject = NULL;            curObject = NULL;            gEvalState.setCurVarName(var);            // See OP_SETCURVAR for why we do this.            curFNDocBlock = NULL;            curNSDocBlock = NULL;            break;         case OP_SETCURVAR_ARRAY_CREATE:            var = STR.getSTValue();            // See OP_SETCURVAR            prevField = NULL;            prevObject = NULL;            curObject = NULL;            gEvalState.setCurVarNameCreate(var);            // See OP_SETCURVAR for why we do this.            curFNDocBlock = NULL;            curNSDocBlock = NULL;            break;         case OP_LOADVAR_UINT:            intStack[_UINT+1] = gEvalState.getIntVariable();            _UINT++;            break;         case OP_LOADVAR_FLT:            floatStack[_FLT+1] = gEvalState.getFloatVariable();            _FLT++;            break;         case OP_LOADVAR_STR:            val = gEvalState.getStringVariable();            STR.setStringValue(val);            break;         case OP_LOADVAR_VAR:            // Sets current source of OP_SAVEVAR_VAR            gEvalState.copyVariable = gEvalState.currentVariable;            break;         case OP_SAVEVAR_UINT:            gEvalState.setIntVariable(intStack[_UINT]);            break;         case OP_SAVEVAR_FLT:            gEvalState.setFloatVariable(floatStack[_FLT]);            break;         case OP_SAVEVAR_STR:            gEvalState.setStringVariable(STR.getStringValue());            break;		             case OP_SAVEVAR_VAR:            // this basically handles %var1 = %var2            gEvalState.setCopyVariable();            break;         case OP_SETCUROBJECT:            // Save the previous object for parsing vector fields.            prevObject = curObject;            val = STR.getStringValue();            // Sim::findObject will sometimes find valid objects from            // multi-component strings. This makes sure that doesn't            // happen.            for( const char* check = val; *check; check++ )            {               if( *check == ' ' )               {                  val = "";                  break;               }            }            curObject = Sim::findObject(val);            break;         case OP_SETCUROBJECT_INTERNAL:            ++ip; // To skip the recurse flag if the object wasn't found            if(curObject)            {               SimSet *set = dynamic_cast<SimSet *>(curObject);               if(set)               {                  StringTableEntry intName = StringTable->insert(STR.getStringValue());                  bool recurse = code[ip-1];                  SimObject *obj = set->findObjectByInternalName(intName, recurse);                  intStack[_UINT+1] = obj ? obj->getId() : 0;                  _UINT++;               }               else               {                  Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", getFileLine(ip-2), curObject->getName(), curObject->getClassName());                  intStack[_UINT] = 0;               }            }            break;         case OP_SETCUROBJECT_NEW:            curObject = currentNewObject;            break;         case OP_SETCURFIELD:            // Save the previous field for parsing vector fields.            prevField = curField;            dStrcpy( prevFieldArray, curFieldArray );            curField = CodeToSTE(code, ip);            curFieldArray[0] = 0;            ip += 2;            break;         case OP_SETCURFIELD_ARRAY:            dStrcpy(curFieldArray, STR.getStringValue());            break;         case OP_SETCURFIELD_TYPE:            if(curObject)               curObject->setDataFieldType(code[ip], curField, curFieldArray);            ip++;            break;         case OP_LOADFIELD_UINT:            if(curObject)               intStack[_UINT+1] = U32(dAtoi(curObject->getDataField(curField, curFieldArray)));            else            {               // The field is not being retrieved from an object. Maybe it's               // a special accessor?               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );               intStack[_UINT+1] = dAtoi( valBuffer );            }            _UINT++;            break;         case OP_LOADFIELD_FLT:            if(curObject)               floatStack[_FLT+1] = dAtof(curObject->getDataField(curField, curFieldArray));            else            {               // The field is not being retrieved from an object. Maybe it's               // a special accessor?               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );               floatStack[_FLT+1] = dAtof( valBuffer );            }            _FLT++;            break;         case OP_LOADFIELD_STR:            if(curObject)            {               val = curObject->getDataField(curField, curFieldArray);               STR.setStringValue( val );            }            else            {               // The field is not being retrieved from an object. Maybe it's               // a special accessor?               getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer );               STR.setStringValue( valBuffer );            }            break;         case OP_SAVEFIELD_UINT:            STR.setIntValue(intStack[_UINT]);            if(curObject)               curObject->setDataField(curField, curFieldArray, STR.getStringValue());            else            {               // The field is not being set on an object. Maybe it's               // a special accessor?               setFieldComponent( prevObject, prevField, prevFieldArray, curField );               prevObject = NULL;            }            break;         case OP_SAVEFIELD_FLT:            STR.setFloatValue(floatStack[_FLT]);            if(curObject)               curObject->setDataField(curField, curFieldArray, STR.getStringValue());            else            {               // The field is not being set on an object. Maybe it's               // a special accessor?               setFieldComponent( prevObject, prevField, prevFieldArray, curField );               prevObject = NULL;            }            break;         case OP_SAVEFIELD_STR:            if(curObject)               curObject->setDataField(curField, curFieldArray, STR.getStringValue());            else            {               // The field is not being set on an object. Maybe it's               // a special accessor?               setFieldComponent( prevObject, prevField, prevFieldArray, curField );               prevObject = NULL;            }            break;         case OP_STR_TO_UINT:            intStack[_UINT+1] = STR.getIntValue();            _UINT++;            break;         case OP_STR_TO_FLT:            floatStack[_FLT+1] = STR.getFloatValue();            _FLT++;            break;         case OP_STR_TO_NONE:            // This exists simply to deal with certain typecast situations.            break;         case OP_FLT_TO_UINT:            intStack[_UINT+1] = (S64)floatStack[_FLT];            _FLT--;            _UINT++;            break;         case OP_FLT_TO_STR:            STR.setFloatValue(floatStack[_FLT]);            _FLT--;            break;         case OP_FLT_TO_NONE:            _FLT--;            break;         case OP_UINT_TO_FLT:            floatStack[_FLT+1] = (F32)intStack[_UINT];            _UINT--;            _FLT++;            break;         case OP_UINT_TO_STR:            STR.setIntValue(intStack[_UINT]);            _UINT--;            break;         case OP_UINT_TO_NONE:            _UINT--;            break;         case OP_COPYVAR_TO_NONE:            gEvalState.copyVariable = NULL;            break;         case OP_LOADIMMED_UINT:            intStack[_UINT+1] = code[ip++];            _UINT++;            break;         case OP_LOADIMMED_FLT:            floatStack[_FLT+1] = curFloatTable[code[ip]];            ip++;            _FLT++;            break;                     case OP_TAG_TO_STR:            code[ip-1] = OP_LOADIMMED_STR;            // it's possible the string has already been converted            if(U8(curStringTable[code[ip]]) != StringTagPrefixByte)            {               U32 id = GameAddTaggedString(curStringTable + code[ip]);               dSprintf(curStringTable + code[ip] + 1, 7, "%d", id);               *(curStringTable + code[ip]) = StringTagPrefixByte;            }         case OP_LOADIMMED_STR:            STR.setStringValue(curStringTable + code[ip++]);            break;         case OP_DOCBLOCK_STR:            {               // If the first word of the doc is '\class' or '@class', then this               // is a namespace doc block, otherwise it is a function doc block.               const char* docblock = curStringTable + code[ip++];               const char* sansClass = dStrstr( docblock, "@class" );               if( !sansClass )                  sansClass = dStrstr( docblock, "\\class" );               if( sansClass )               {                  // Don't save the class declaration. Scan past the 'class'                  // keyword and up to the first whitespace.                  sansClass += 7;                  S32 index = 0;                  while( ( *sansClass != ' ' ) && ( *sansClass != '\n' ) && *sansClass && ( index < ( nsDocLength - 1 ) ) )                  {                     nsDocBlockClass[index++] = *sansClass;                     sansClass++;                  }                  nsDocBlockClass[index] = '\0';                  curNSDocBlock = sansClass + 1;               }               else                  curFNDocBlock = docblock;            }            break;         case OP_LOADIMMED_IDENT:            STR.setStringValue(CodeToSTE(code, ip));            ip += 2;            break;         case OP_CALLFUNC_RESOLVE:            // This deals with a function that is potentially living in a namespace.            fnNamespace = CodeToSTE(code, ip+2);            fnName      = CodeToSTE(code, ip);            // Try to look it up.            ns = Namespace::find(fnNamespace);            nsEntry = ns->lookup(fnName);            if(!nsEntry)            {               ip+= 5;               Con::warnf(ConsoleLogEntry::General,                  "%s: Unable to find function %s%s%s",                  getFileLine(ip-7), fnNamespace ? fnNamespace : "",                  fnNamespace ? "::" : "", fnName);               STR.popFrame();               CSTK.popFrame();               break;            }            #ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS            // Now fall through to OP_CALLFUNC...            // Now, rewrite our code a bit (ie, avoid future lookups) and fall            // through to OP_CALLFUNC#ifdef TORQUE_CPU_X64            *((U64*)(code+ip+2)) = ((U64)nsEntry);#else            code[ip+2] = ((U32)nsEntry);#endif            code[ip-1] = OP_CALLFUNC;#endif         case OP_CALLFUNC:         {            // This routingId is set when we query the object as to whether            // it handles this method.  It is set to an enum from the table            // above indicating whether it handles it on a component it owns            // or just on the object.            S32 routingId = 0;            fnName = CodeToSTE(code, ip);            //if this is called from inside a function, append the ip and codeptr            if( gEvalState.getStackDepth() > 0 )            {               gEvalState.getCurrentFrame().code = this;               gEvalState.getCurrentFrame().ip = ip - 1;            }            U32 callType = code[ip+4];            ip += 5;            CSTK.getArgcArgv(fnName, &callArgc, &callArgv);            const char *componentReturnValue = "";            if(callType == FuncCallExprNode::FunctionCall)             {               if( !nsEntry )               {#ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS#ifdef TORQUE_CPU_X64                  nsEntry = ((Namespace::Entry *) *((U64*)(code+ip-3)));#else                  nsEntry = ((Namespace::Entry *) *(code+ip-3));#endif#else                  nsEntry = Namespace::global()->lookup( fnName );#endif                  ns = NULL;               }               ns = NULL;            }            else if(callType == FuncCallExprNode::MethodCall)            {               saveObject = gEvalState.thisObject;               gEvalState.thisObject = Sim::findObject((const char*)callArgv[1]);               if(!gEvalState.thisObject)               {                  // Go back to the previous saved object.                  gEvalState.thisObject = saveObject;                  Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), (const char*)callArgv[1], fnName);                  STR.popFrame();                  CSTK.popFrame();                  STR.setStringValue("");                  break;               }                              bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId);               if( handlesMethod && routingId == MethodOnComponent )               {                  ICallMethod *pComponent = dynamic_cast<ICallMethod *>( gEvalState.thisObject );                  if( pComponent )                     componentReturnValue = pComponent->callMethodArgList( callArgc, callArgv, false );               }                              ns = gEvalState.thisObject->getNamespace();               if(ns)                  nsEntry = ns->lookup(fnName);               else                  nsEntry = NULL;            }            else // it's a ParentCall            {               if(thisNamespace)               {                  ns = thisNamespace->mParent;                  if(ns)                     nsEntry = ns->lookup(fnName);                  else                     nsEntry = NULL;               }               else               {                  ns = NULL;                  nsEntry = NULL;               }            }            Namespace::Entry::CallbackUnion * nsCb = NULL;            const char * nsUsage = NULL;            if (nsEntry)            {               nsCb = &nsEntry->cb;               nsUsage = nsEntry->mUsage;               routingId = 0;            }            if(!nsEntry || noCalls)            {               if(!noCalls && !( routingId == MethodOnComponent ) )               {                  Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-6), fnName);                  if(callType == FuncCallExprNode::MethodCall)                  {                     Con::warnf(ConsoleLogEntry::General, "  Object %s(%d) %s",                           gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "",                           gEvalState.thisObject->getId(), Con::getNamespaceList(ns) );                  }               }               STR.popFrame();               CSTK.popFrame();               if( routingId == MethodOnComponent )                  STR.setStringValue( componentReturnValue );               else                  STR.setStringValue( "" );               break;            }            if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType)            {               ConsoleValueRef ret;               if(nsEntry->mFunctionOffset)                  ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);               STR.popFrame();               // Functions are assumed to return strings, so look ahead to see if we can skip the conversion               if(code[ip] == OP_STR_TO_UINT)               {                  ip++;                  intStack[++_UINT] = (U32)((S32)ret);               }               else if(code[ip] == OP_STR_TO_FLT)               {                  ip++;                  floatStack[++_FLT] = (F32)ret;               }               else if(code[ip] == OP_STR_TO_NONE)               {                  STR.setStringValue(ret.getStringValue());                  ip++;               }               else                  STR.setStringValue((const char*)ret);               // This will clear everything including returnValue               CSTK.popFrame();               //STR.clearFunctionOffset();            }            else            {               const char* nsName = ns? ns->mName: "";#ifndef TORQUE_DEBUG               // [tom, 12/13/2006] This stops tools functions from working in the console,               // which is useful behavior when debugging so I'm ifdefing this out for debug builds.               if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript())               {                  Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-6), nsName, fnName);               }               else#endif               if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs))               {                  Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",                     getFileLine(ip-6), nsName, fnName,                     callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs);                  Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-6), nsEntry->mUsage);                  STR.popFrame();                  CSTK.popFrame();               }               else               {                  switch(nsEntry->mType)                  {                     case Namespace::Entry::StringCallbackType:                     {                        const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv);                        STR.popFrame();                        CSTK.popFrame();                        if(ret != STR.getStringValue())                           STR.setStringValue(ret);                        //else                        //   STR.setLen(dStrlen(ret));                        break;                     }                     case Namespace::Entry::IntCallbackType:                     {                        S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv);                        STR.popFrame();                        CSTK.popFrame();                        if(code[ip] == OP_STR_TO_UINT)                        {                           ip++;                           intStack[++_UINT] = result;                           break;                        }                        else if(code[ip] == OP_STR_TO_FLT)                        {                           ip++;                           floatStack[++_FLT] = result;                           break;                        }                        else if(code[ip] == OP_STR_TO_NONE)                           ip++;                        else                           STR.setIntValue(result);                        break;                     }                     case Namespace::Entry::FloatCallbackType:                     {                        F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv);                        STR.popFrame();                        CSTK.popFrame();                        if(code[ip] == OP_STR_TO_UINT)                        {                           ip++;                           intStack[++_UINT] = (S64)result;                           break;                        }                        else if(code[ip] == OP_STR_TO_FLT)                        {                           ip++;                           floatStack[++_FLT] = result;                           break;                        }                        else if(code[ip] == OP_STR_TO_NONE)                           ip++;                        else                           STR.setFloatValue(result);                        break;                     }                     case Namespace::Entry::VoidCallbackType:                        nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv);                        if( code[ ip ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) )                           Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-6), fnName, functionName);                                                STR.popFrame();                        CSTK.popFrame();                        STR.setStringValue("");                        break;                     case Namespace::Entry::BoolCallbackType:                     {                        bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv);                        STR.popFrame();                        CSTK.popFrame();                        if(code[ip] == OP_STR_TO_UINT)                        {                           ip++;                           intStack[++_UINT] = result;                           break;                        }                        else if(code[ip] == OP_STR_TO_FLT)                        {                           ip++;                           floatStack[++_FLT] = result;                           break;                        }                        else if(code[ip] == OP_STR_TO_NONE)                           ip++;                        else                           STR.setIntValue(result);                        break;                     }                  }               }            }            if(callType == FuncCallExprNode::MethodCall)               gEvalState.thisObject = saveObject;            break;         }         case OP_ADVANCE_STR:            STR.advance();            break;         case OP_ADVANCE_STR_APPENDCHAR:            STR.advanceChar(code[ip++]);            break;         case OP_ADVANCE_STR_COMMA:            STR.advanceChar('_');            break;         case OP_ADVANCE_STR_NUL:            STR.advanceChar(0);            break;         case OP_REWIND_STR:            STR.rewind();            break;         case OP_TERMINATE_REWIND_STR:            STR.rewindTerminate();            break;         case OP_COMPARE_STR:            intStack[++_UINT] = STR.compare();            break;         case OP_PUSH:            STR.push();            CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr());            break;         case OP_PUSH_UINT:            CSTK.pushUINT(intStack[_UINT]);            _UINT--;            break;         case OP_PUSH_FLT:            CSTK.pushFLT(floatStack[_FLT]);            _FLT--;            break;         case OP_PUSH_VAR:            if (gEvalState.currentVariable)               CSTK.pushValue(gEvalState.currentVariable->value);            else               CSTK.pushString("");            break;         case OP_PUSH_FRAME:            STR.pushFrame();            CSTK.pushFrame();            break;         case OP_ASSERT:         {            if( !intStack[_UINT--] )            {               const char *message = curStringTable + code[ip];               U32 breakLine, inst;               findBreakLine( ip - 1, breakLine, inst );               if ( PlatformAssert::processAssert( PlatformAssert::Fatal,                                                    name ? name : "eval",                                                    breakLine,                                                     message ) )               {                  if ( TelDebugger && TelDebugger->isConnected() && breakLine > 0 )                  {                     TelDebugger->breakProcess();                  }                  else                     Platform::debugBreak();               }            }            ip++;            break;         }         case OP_BREAK:         {            //append the ip and codeptr before managing the breakpoint!            AssertFatal( gEvalState.getStackDepth() > 0, "Empty eval stack on break!");            gEvalState.getCurrentFrame().code = this;            gEvalState.getCurrentFrame().ip = ip - 1;            U32 breakLine;            findBreakLine(ip-1, breakLine, instruction);            if(!breakLine)               goto breakContinue;            TelDebugger->executionStopped(this, breakLine);            goto breakContinue;         }                  case OP_ITER_BEGIN_STR:         {            iterStack[ _ITER ].mIsStringIter = true;            /* fallthrough */         }                  case OP_ITER_BEGIN:         {            StringTableEntry varName = CodeToSTE(code, ip);            U32 failIp = code[ ip + 2 ];                        IterStackRecord& iter = iterStack[ _ITER ];                        iter.mVariable = gEvalState.getCurrentFrame().add( varName );                        if( iter.mIsStringIter )            {               iter.mData.mStr.mString = STR.getStringValuePtr();               iter.mData.mStr.mIndex = 0;            }            else            {               // Look up the object.                              SimSet* set;               if( !Sim::findObject( STR.getStringValue(), set ) )               {                  Con::errorf( ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue() );                  Con::errorf( ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?" );                  ip = failIp;                  continue;               }                              // Set up.                              iter.mData.mObj.mSet = set;               iter.mData.mObj.mIndex = 0;            }                        _ITER ++;            iterDepth ++;                        STR.push();                        ip += 3;            break;         }                  case OP_ITER:         {            U32 breakIp = code[ ip ];            IterStackRecord& iter = iterStack[ _ITER - 1 ];                        if( iter.mIsStringIter )            {               const char* str = StringStackPtrRef(iter.mData.mStr.mString).getPtr(&STR);                                             U32 startIndex = iter.mData.mStr.mIndex;               U32 endIndex = startIndex;                              // Break if at end.               if( !str[ startIndex ] )               {                  ip = breakIp;                  continue;               }               // Find right end of current component.                              if( !dIsspace( str[ endIndex ] ) )                  do ++ endIndex;                  while( str[ endIndex ] && !dIsspace( str[ endIndex ] ) );                                 // Extract component.                                 if( endIndex != startIndex )               {                  char savedChar = str[ endIndex ];                  const_cast< char* >( str )[ endIndex ] = '\0'; // We are on the string stack so this is okay.                  iter.mVariable->setStringValue( &str[ startIndex ] );                  const_cast< char* >( str )[ endIndex ] = savedChar;                                 }               else                  iter.mVariable->setStringValue( "" );               // Skip separator.               if( str[ endIndex ] != '\0' )                  ++ endIndex;                              iter.mData.mStr.mIndex = endIndex;            }            else            {               U32 index = iter.mData.mObj.mIndex;               SimSet* set = iter.mData.mObj.mSet;                              if( index >= set->size() )               {                  ip = breakIp;                  continue;               }                              iter.mVariable->setIntValue( set->at( index )->getId() );               iter.mData.mObj.mIndex = index + 1;            }                        ++ ip;            break;         }                  case OP_ITER_END:         {            -- _ITER;            -- iterDepth;                        STR.rewind();                        iterStack[ _ITER ].mIsStringIter = false;            break;         }                  case OP_INVALID:         default:            // error!            goto execFinished;      }   }execFinished:   if ( telDebuggerOn && setFrame < 0 )      TelDebugger->popStackFrame();   if ( popFrame )      gEvalState.popFrame();   if(argv)   {      if(gEvalState.traceOn)      {         traceBuffer[0] = 0;         dStrcat(traceBuffer, "Leaving ");         if(packageName)         {            dStrcat(traceBuffer, "[");            dStrcat(traceBuffer, packageName);            dStrcat(traceBuffer, "]");         }         if(thisNamespace && thisNamespace->mName)         {            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),               "%s::%s() - return %s", thisNamespace->mName, thisFunctionName, STR.getStringValue());         }         else         {            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),               "%s() - return %s", thisFunctionName, STR.getStringValue());         }         Con::printf("%s", traceBuffer);      }   }   smCurrentCodeBlock = saveCodeBlock;   if(saveCodeBlock && saveCodeBlock->name)   {      Con::gCurrentFile = saveCodeBlock->name;      Con::gCurrentRoot = saveCodeBlock->modPath;   }   decRefCount();#ifdef TORQUE_VALIDATE_STACK   AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec");   AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec");#endif   return returnValue;}//------------------------------------------------------------
 |