Browse Source

Merge pull request #495 from JeffProgrammer/tsneo

TorqueScript Interpreter 2.0
Areloch 4 years ago
parent
commit
6487e2eede
100 changed files with 7296 additions and 8066 deletions
  1. 2 2
      Engine/source/T3D/assets/assetImporter.cpp
  2. 18 19
      Engine/source/T3D/gameBase/gameConnection.cpp
  3. 1 1
      Engine/source/Verve/Core/VDataTable.cpp
  4. 9 7
      Engine/source/afx/afxMagicSpell.cpp
  5. 1 1
      Engine/source/afx/afxSelectron.cpp
  6. 3 3
      Engine/source/afx/arcaneFX.cpp
  7. 1 4
      Engine/source/app/mainLoop.cpp
  8. 2 2
      Engine/source/app/net/net.cpp
  9. 2 2
      Engine/source/app/net/tcpObject.cpp
  10. 1 1
      Engine/source/app/net/tcpObject.h
  11. 2 2
      Engine/source/cinterface/cinterface.cpp
  12. 6 1
      Engine/source/console/CMDgram.y
  13. 1056 1055
      Engine/source/console/CMDscan.cpp
  14. 1 0
      Engine/source/console/CMDscan.l
  15. 2 2
      Engine/source/console/ICallMethod.h
  16. 1 1
      Engine/source/console/SimXMLDocument.cpp
  17. 1 1
      Engine/source/console/SimXMLDocument.h
  18. 141 137
      Engine/source/console/ast.h
  19. 98 82
      Engine/source/console/astAlloc.cpp
  20. 275 414
      Engine/source/console/astNodes.cpp
  21. 870 929
      Engine/source/console/cmdgram.cpp
  22. 589 677
      Engine/source/console/codeBlock.cpp
  23. 25 6
      Engine/source/console/codeBlock.h
  24. 0 3018
      Engine/source/console/codeInterpreter.cpp
  25. 0 262
      Engine/source/console/codeInterpreter.h
  26. 2092 21
      Engine/source/console/compiledEval.cpp
  27. 64 0
      Engine/source/console/compiler.cpp
  28. 18 32
      Engine/source/console/compiler.h
  29. 116 286
      Engine/source/console/console.cpp
  30. 286 159
      Engine/source/console/console.h
  31. 5 2
      Engine/source/console/consoleFunctions.cpp
  32. 114 151
      Engine/source/console/consoleInternal.cpp
  33. 167 47
      Engine/source/console/consoleInternal.h
  34. 4 4
      Engine/source/console/consoleLogger.cpp
  35. 1 1
      Engine/source/console/consoleLogger.h
  36. 109 0
      Engine/source/console/consoleValueStack.h
  37. 67 80
      Engine/source/console/engineAPI.h
  38. 2 2
      Engine/source/console/engineDoc.cpp
  39. 104 0
      Engine/source/console/optimizer.cpp
  40. 3 6
      Engine/source/console/sim.h
  41. 29 30
      Engine/source/console/simEvents.cpp
  42. 7 9
      Engine/source/console/simEvents.h
  43. 14 5
      Engine/source/console/simManager.cpp
  44. 8 6
      Engine/source/console/simObject.cpp
  45. 1 1
      Engine/source/console/simObject.h
  46. 1 1
      Engine/source/console/simPersistSet.cpp
  47. 1 1
      Engine/source/console/simPersistSet.h
  48. 5 7
      Engine/source/console/simSet.cpp
  49. 2 2
      Engine/source/console/simSet.h
  50. 1 254
      Engine/source/console/stringStack.cpp
  51. 3 64
      Engine/source/console/stringStack.h
  52. 46 3
      Engine/source/console/telnetDebugger.cpp
  53. 730 113
      Engine/source/console/test/ScriptTest.cpp
  54. 3 0
      Engine/source/console/test/consoleTest.cpp
  55. 3 1
      Engine/source/console/test/engineAPITest.cpp
  56. 1 1
      Engine/source/core/strings/stringFunctions.cpp
  57. 5 0
      Engine/source/core/strings/stringFunctions.h
  58. 10 0
      Engine/source/core/util/tVector.h
  59. 1 1
      Engine/source/environment/waterObject.cpp
  60. 1 1
      Engine/source/environment/waterObject.h
  61. 2 4
      Engine/source/gui/controls/guiGameListMenuCtrl.cpp
  62. 1 1
      Engine/source/gui/core/guiControl.cpp
  63. 1 1
      Engine/source/gui/core/guiControl.h
  64. 1 1
      Engine/source/gui/editor/guiFilterCtrl.cpp
  65. 5 3
      Engine/source/gui/editor/inspector/field.cpp
  66. 4 2
      Engine/source/gui/editor/popupMenu.cpp
  67. 3 3
      Engine/source/gui/worldEditor/worldEditor.cpp
  68. 1 1
      Engine/source/gui/worldEditor/worldEditor.h
  69. 4 3
      Engine/source/platform/types.gcc.h
  70. 10 2
      Engine/source/platform/types.visualc.h
  71. 12 7
      Engine/source/platformWin32/winMath.cpp
  72. 1 1
      Engine/source/sfx/sfxSource.cpp
  73. 1 1
      Engine/source/sfx/sfxSource.h
  74. 1 1
      Engine/source/sfx/sfxTrack.cpp
  75. 1 1
      Engine/source/sfx/sfxTrack.h
  76. 3 3
      Engine/source/shaderGen/GLSL/customFeatureGLSL.cpp
  77. 1 1
      Engine/source/shaderGen/GLSL/customFeatureGLSL.h
  78. 3 3
      Engine/source/shaderGen/HLSL/customFeatureHLSL.cpp
  79. 1 1
      Engine/source/shaderGen/HLSL/customFeatureHLSL.h
  80. 2 2
      Engine/source/shaderGen/customShaderFeature.cpp
  81. 1 1
      Engine/source/shaderGen/customShaderFeature.h
  82. 2 2
      Engine/source/sim/actionMap.cpp
  83. 1 0
      Templates/BaseGame/game/core/clientServer/scripts/server/levelInfo.tscript
  84. 3 0
      Templates/BaseGame/game/core/clientServer/scripts/server/levelLoad.tscript
  85. 1 1
      Templates/BaseGame/game/core/console/guis/console.gui
  86. 1 1
      Templates/BaseGame/game/core/console/scripts/console.tscript
  87. 1 1
      Templates/BaseGame/game/core/postFX/scripts/postFxManager.tscript
  88. 2 2
      Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript
  89. 3 1
      Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript
  90. 83 83
      Templates/BaseGame/game/core/utility/scripts/parseArgs.tscript
  91. 0 3
      Templates/BaseGame/game/core/utility/scripts/scene.tscript
  92. 1 1
      Templates/BaseGame/game/data/ExampleModule/GUIs/ExampleGUI.gui
  93. 1 1
      Templates/BaseGame/game/data/UI/guis/IODropdownDlg.ed.gui
  94. 1 1
      Templates/BaseGame/game/data/UI/guis/RecordingsDlg.gui
  95. 1 1
      Templates/BaseGame/game/data/UI/guis/chooseLevelDlg.gui
  96. 1 1
      Templates/BaseGame/game/data/UI/guis/guiMusicPlayer.gui
  97. 1 1
      Templates/BaseGame/game/data/UI/guis/joinServerMenu.gui
  98. 1 1
      Templates/BaseGame/game/data/UI/guis/loadingGui.gui
  99. 1 1
      Templates/BaseGame/game/data/UI/guis/mainMenu.gui
  100. 1 1
      Templates/BaseGame/game/data/UI/guis/messageBoxDlg.gui

+ 2 - 2
Engine/source/T3D/assets/assetImporter.cpp

@@ -2574,8 +2574,8 @@ void AssetImporter::importAssets(AssetImportObject* assetItem)
             processCommand += childItem->assetType;
             if (isMethod(processCommand.c_str()))
             {
-               ConsoleValueRef importReturnVal = Con::executef(this, processCommand.c_str(), childItem);
-               assetPath = Torque::Path(importReturnVal.getStringValue());
+               const char* importReturnVal = Con::executef(this, processCommand.c_str(), childItem).getString();
+               assetPath = Torque::Path(importReturnVal);
             }
          }
 

+ 18 - 19
Engine/source/T3D/gameBase/gameConnection.cpp

@@ -331,7 +331,7 @@ DefineEngineStringlyVariadicMethod(GameConnection, setConnectArgs, void, 3, 17,
    
    "@see GameConnection::onConnect()\n\n")
 {
-   StringStackWrapper args(argc - 2, argv + 2);
+   ConsoleValueToStringArrayWrapper args(argc - 2, argv + 2);
    object->setConnectArgs(args.count(), args);
 }
 
@@ -373,7 +373,7 @@ void GameConnection::onConnectionEstablished(bool isInitiator)
       mMoveList->init();
       const char *argv[MaxConnectArgs + 2];
       argv[0] = "onConnect";
-      argv[1] = NULL; // Filled in later
+      argv[1] = getIdString();
       for(U32 i = 0; i < mConnectArgc; i++)
          argv[i + 2] = mConnectArgv[i];
       // NOTE: Need to fallback to Con::execute() as IMPLEMENT_CALLBACK does not 
@@ -493,33 +493,32 @@ bool GameConnection::readConnectRequest(BitStream *stream, const char **errorStr
       *errorString = "CR_INVALID_ARGS";
       return false;
    }
-   ConsoleValueRef connectArgv[MaxConnectArgs + 3];
-   ConsoleValue connectArgvValue[MaxConnectArgs + 3];
 
-   for(U32 i = 0; i < mConnectArgc+3; i++)
-   {
-      connectArgv[i].value = &connectArgvValue[i];
-      connectArgvValue[i].init();
-   }
+   char buffer[256];
+   Net::addressToString(getNetAddress(), buffer);
+
+   ConsoleValue connectArgv[MaxConnectArgs + 3];
 
    for(U32 i = 0; i < mConnectArgc; i++)
    {
       char argString[256];
       stream->readString(argString);
       mConnectArgv[i] = dStrdup(argString);
-      connectArgv[i + 3] = mConnectArgv[i];
+      connectArgv[i + 3].setString(argString);
    }
-   connectArgvValue[0].setStackStringValue("onConnectRequest");
-   connectArgvValue[1].setIntValue(0);
-   char buffer[256];
-   Net::addressToString(getNetAddress(), buffer);
-   connectArgvValue[2].setStackStringValue(buffer);
+
+   connectArgv[0].setStringTableEntry("onConnectRequest");
+   connectArgv[1].setInt(0);
+   connectArgv[2].setString(buffer);
 
    // NOTE: Cannot convert over to IMPLEMENT_CALLBACK as it has variable args.
-   const char *ret = Con::execute(this, mConnectArgc + 3, connectArgv);
-   if(ret[0])
+   ConsoleValue returnValue = Con::execute(this, mConnectArgc + 3, connectArgv);
+
+   StringTableEntry returnStr = StringTable->insert(returnValue.getString());
+
+   if(returnStr[0])
    {
-      *errorString = ret;
+      *errorString = returnStr;
       return false;
    }
    return true;
@@ -1088,7 +1087,7 @@ bool GameConnection::readDemoStartBlock(BitStream *stream)
 void GameConnection::demoPlaybackComplete()
 {
    static const char* demoPlaybackArgv[1] = { "demoPlaybackComplete" };
-   static StringStackConsoleWrapper demoPlaybackCmd(1, demoPlaybackArgv);
+   static StringArrayToConsoleValueWrapper demoPlaybackCmd(1, demoPlaybackArgv);
 
    Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(demoPlaybackCmd.argc, demoPlaybackCmd.argv, false));
    Parent::demoPlaybackComplete();

+ 1 - 1
Engine/source/Verve/Core/VDataTable.cpp

@@ -228,7 +228,7 @@ bool VDataTable::getValue( SimObject *pObject, const String &pFieldName, String
         case VDataTable::k_TypeExpression :
             {
                 // Evaluate.
-                pValue = Con::evaluate( fieldValue, false ).getStringValue();
+                pValue = Con::evaluate( fieldValue, false ).getString();
 
             } break;
 

+ 9 - 7
Engine/source/afx/afxMagicSpell.cpp

@@ -2638,23 +2638,25 @@ DefineEngineStringlyVariadicMethod(afxMagicSpell, setTimeFactor, void, 3, 4, "(F
    "@ingroup AFX")
 {
    if (argc == 3)
-      object->setTimeFactor(dAtof(argv[2]));
+      object->setTimeFactor(argv[2].getFloat());
    else
    {
+      F32 value = argv[3].getFloat();
+
       if (dStricmp(argv[2], "overall") == 0)
          object->setTimeFactor(dAtof(argv[3]));
       else if (dStricmp(argv[2], "casting") == 0)
-         object->setTimeFactor(afxMagicSpell::CASTING_PHRASE, dAtof(argv[3]));
+         object->setTimeFactor(afxMagicSpell::CASTING_PHRASE, value);
       else if (dStricmp(argv[2], "launch") == 0)
-         object->setTimeFactor(afxMagicSpell::LAUNCH_PHRASE, dAtof(argv[3]));
+         object->setTimeFactor(afxMagicSpell::LAUNCH_PHRASE, value);
       else if (dStricmp(argv[2], "delivery") == 0)
-         object->setTimeFactor(afxMagicSpell::DELIVERY_PHRASE, dAtof(argv[3]));
+         object->setTimeFactor(afxMagicSpell::DELIVERY_PHRASE, value);
       else if (dStricmp(argv[2], "impact") == 0)
-         object->setTimeFactor(afxMagicSpell::IMPACT_PHRASE, dAtof(argv[3]));
+         object->setTimeFactor(afxMagicSpell::IMPACT_PHRASE, value);
       else if (dStricmp(argv[2], "linger") == 0)
-         object->setTimeFactor(afxMagicSpell::LINGER_PHRASE, dAtof(argv[3]));
+         object->setTimeFactor(afxMagicSpell::LINGER_PHRASE, value);
       else
-         Con::errorf("afxMagicSpell::setTimeFactor() -- unknown spell phrase [%s].", argv[2].getStringValue());
+         Con::errorf("afxMagicSpell::setTimeFactor() -- unknown spell phrase [%s].", argv[2].getString());
    }
 }
 

+ 1 - 1
Engine/source/afx/afxSelectron.cpp

@@ -1070,7 +1070,7 @@ afxSelectron::start_selectron(SceneObject* picked, U8 subcode, SimObject* extra)
   // CALL SCRIPT afxSelectronData::onPreactivate(%params, %extra)
   const char* result = Con::executef(datablock, "onPreactivate",
                                      Con::getIntArg(param_holder->getId()),
-                                     (extra) ? Con::getIntArg(extra->getId()) : "");
+                                     (extra) ? Con::getIntArg(extra->getId()) : "").getString();
   if (result && result[0] != '\0' && !dAtob(result))
   {
 #if defined(TORQUE_DEBUG)

+ 3 - 3
Engine/source/afx/arcaneFX.cpp

@@ -874,7 +874,7 @@ DefineEngineStringlyVariadicFunction(echoThru, const char*, 2, 0, "(string passt
    for (i = 2; i < argc; i++)
       dStrcat(ret, argv[i], len + 1);
 
-   Con::printf("%s -- [%s]", ret, argv[1].getStringValue());
+   Con::printf("%s -- [%s]", ret, argv[1].getString());
    ret[0] = 0;
 
    return argv[1];
@@ -894,7 +894,7 @@ DefineEngineStringlyVariadicFunction(warnThru, const char*, 2, 0, "(string passt
    for(i = 2; i < argc; i++)
       dStrcat(ret, argv[i], len + 1);
 
-   Con::warnf("%s -- [%s]", ret, argv[1].getStringValue());
+   Con::warnf("%s -- [%s]", ret, argv[1].getString());
    ret[0] = 0;
 
    return argv[1];
@@ -914,7 +914,7 @@ DefineEngineStringlyVariadicFunction(errorThru, const char*, 2, 0, "(string pass
    for(i = 2; i < argc; i++)
       dStrcat(ret, argv[i], len + 1);
 
-   Con::errorf("%s -- [%s]", ret, argv[1].getStringValue());
+   Con::errorf("%s -- [%s]", ret, argv[1].getString());
    ret[0] = 0;
 
    return argv[1];

+ 1 - 4
Engine/source/app/mainLoop.cpp

@@ -45,7 +45,6 @@
 #include "console/debugOutputConsumer.h"
 #include "console/consoleTypes.h"
 #include "console/engineAPI.h"
-#include "console/codeInterpreter.h"
 
 #include "gfx/bitmap/gBitmap.h"
 #include "gfx/gFont.h"
@@ -229,9 +228,6 @@ void StandardMainLoop::init()
    ManagedSingleton< ThreadManager >::createSingleton();
    FrameAllocator::init(TORQUE_FRAME_SIZE);      // See comments in torqueConfig.h
 
-   // Initialize the TorqueScript interpreter.
-   CodeInterpreter::init();
-
    // Yell if we can't initialize the network.
    if(!Net::init())
    {
@@ -637,6 +633,7 @@ bool StandardMainLoop::doMainLoop()
 
       ThreadPool::processMainThreadWorkItems();
       Sampler::endFrame();
+      ConsoleValue::resetConversionBuffer();
       PROFILE_END_NAMED(MainLoop);
    }
    

+ 2 - 2
Engine/source/app/net/net.cpp

@@ -254,7 +254,7 @@ DefineEngineStringlyVariadicFunction( commandToServer, void, 2, RemoteCommandEve
    NetConnection *conn = NetConnection::getConnectionToServer();
    if(!conn)
       return;
-   StringStackWrapper args(argc - 1, argv + 1);
+   ConsoleValueToStringArrayWrapper args(argc - 1, argv + 1);
    RemoteCommandEvent::sendRemoteCommand(conn, args.count(), args);
 }
 
@@ -292,7 +292,7 @@ DefineEngineStringlyVariadicFunction( commandToClient, void, 3, RemoteCommandEve
    NetConnection *conn;
    if(!Sim::findObject(argv[1], conn))
       return;
-   StringStackWrapper args(argc - 2, argv + 2);
+   ConsoleValueToStringArrayWrapper args(argc - 2, argv + 2);
    RemoteCommandEvent::sendRemoteCommand(conn, args.count(), args);
 }
 

+ 2 - 2
Engine/source/app/net/tcpObject.cpp

@@ -236,13 +236,13 @@ TCPObject::~TCPObject()
    }
 }
 
-bool TCPObject::processArguments(S32 argc, ConsoleValueRef *argv)
+bool TCPObject::processArguments(S32 argc, ConsoleValue *argv)
 {
    if(argc == 0)
       return true;
    else if(argc == 1)
    {
-      addToTable(NetSocket::fromHandle(dAtoi(argv[0])));
+      addToTable(NetSocket::fromHandle(argv[0].getInt()));
       return true;
    }
    return false;

+ 1 - 1
Engine/source/app/net/tcpObject.h

@@ -83,7 +83,7 @@ public:
    void disconnect();
    State getState() { return mState; }
 
-   bool processArguments(S32 argc, ConsoleValueRef *argv);
+   bool processArguments(S32 argc, ConsoleValue *argv);
    void send(const U8 *buffer, U32 bufferLen);
 
    ///Send an entire file over tcp

+ 2 - 2
Engine/source/cinterface/cinterface.cpp

@@ -55,7 +55,7 @@ bool CInterface::_isMethod(const char* className, const char* methodName) const
    if (mIsMethodCallback)
       return mIsMethodCallback(className, methodName);
 
-   return NULL;
+   return false;
 }
 
 const char* CInterface::_CallFunction(const char* nameSpace, const char* name, const char **argv, int argc, bool *result) const
@@ -93,4 +93,4 @@ TORQUE_API void SetCallbacks(void* ptr, void* methodPtr, void* isMethodPtr, void
    CInterface::GetCInterface().SetCallMethodCallback(methodPtr);
    CInterface::GetCInterface().SetCallIsMethodCallback(isMethodPtr);
    CInterface::GetCInterface().SetMainCallback(mainPtr);
-}
+}

+ 6 - 1
Engine/source/console/CMDgram.y

@@ -456,6 +456,8 @@ expr
       { $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, NULL); }
    | VAR '[' aidx_expr ']'
       { $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, $3 ); }
+   ;
+/*
    | rwDEFINE '(' var_list_decl ')' '{' statement_list '}'
       {
          const U32 bufLen = 64;
@@ -471,7 +473,7 @@ expr
 
          $$ = StrConstNode::alloc( $1.lineNumber, (UTF8*)fName, false );
       }
-   ;
+*/
 
 slot_acc
    : expr '.' IDENT
@@ -551,9 +553,12 @@ funcall_expr
      { $$ = FuncCallExprNode::alloc( $1.lineNumber, $3.value, $1.value, $5, false); }
    | expr '.' IDENT '(' expr_list_decl ')'
       { $1->append($5); $$ = FuncCallExprNode::alloc( $1->dbgLineNumber, $3.value, NULL, $1, true); }
+   ;
+/*
    | expr '(' expr_list_decl ')'
       { $$ = FuncPointerCallExprNode::alloc( $1->dbgLineNumber, $1, $3); }
    ;
+*/
 
 assert_expr
    : rwASSERT '(' expr ')'

File diff suppressed because it is too large
+ 1056 - 1055
Engine/source/console/CMDscan.cpp


+ 1 - 0
Engine/source/console/CMDscan.l

@@ -213,6 +213,7 @@ HEXDIGIT [a-fA-F0-9]
 "true"      { CMDlval.i = MakeToken< int >( 1, lineIndex ); return INTCONST; }
 "false"     { CMDlval.i = MakeToken< int >( 0, lineIndex ); return INTCONST; }
 {VAR}       { return(Sc_ScanVar()); }
+
 {ID}        { return Sc_ScanIdent(); }
 0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
 {INTEGER}   { CMDtext[CMDleng] = 0; CMDlval.i = MakeToken< int >( dAtoi(CMDtext), lineIndex ); return INTCONST; }

+ 2 - 2
Engine/source/console/ICallMethod.h

@@ -27,7 +27,7 @@ class ICallMethod
 {
 public:
    virtual const char* callMethod( S32 argc, const char* methodName, ... ) = 0;
-   virtual const char* callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis = true ) = 0;
+   virtual const char* callMethodArgList( U32 argc, ConsoleValue argv[], bool callThis = true ) = 0;
 };
 
-#endif
+#endif

+ 1 - 1
Engine/source/console/SimXMLDocument.cpp

@@ -159,7 +159,7 @@ SimXMLDocument::~SimXMLDocument()
 // -----------------------------------------------------------------------------
 // Included for completeness.
 // -----------------------------------------------------------------------------
-bool SimXMLDocument::processArguments(S32 argc, ConsoleValueRef *argv)
+bool SimXMLDocument::processArguments(S32 argc, ConsoleValue *argv)
 {
    return argc == 0;
 }

+ 1 - 1
Engine/source/console/SimXMLDocument.h

@@ -56,7 +56,7 @@ class SimXMLDocument: public SimObject
       // tie in to the script language.  The .cc file has more in depth
       // comments on these.
       //-----------------------------------------------------------------------
-      bool processArguments(S32 argc, ConsoleValueRef *argv);
+      bool processArguments(S32 argc, ConsoleValue *argv);
       bool onAdd();
       void onRemove();
       static void initPersistFields();

+ 141 - 137
Engine/source/console/ast.h

@@ -39,8 +39,15 @@ enum TypeReq
    TypeReqNone,
    TypeReqUInt,
    TypeReqFloat,
-   TypeReqString,
-   TypeReqVar
+   TypeReqString
+};
+
+enum ExprNodeName
+{
+   NameExprNode,
+   NameFloatNode,
+   NameIntNode,
+   NameVarNode
 };
 
 /// Representation of a node for the scripting language parser.
@@ -52,7 +59,7 @@ enum TypeReq
 /// each representing a different language construct.
 struct StmtNode
 {
-   StmtNode *mNext;   ///< Next entry in parse tree.
+   StmtNode* next;   ///< Next entry in parse tree.
 
    StmtNode();
    virtual ~StmtNode() {}
@@ -61,8 +68,8 @@ struct StmtNode
    /// @{
 
    ///
-   void append(StmtNode *next);
-   StmtNode *getNext() const { return mNext; }
+   void append(StmtNode* next);
+   StmtNode* getNext() const { return next; }
 
    /// @}
 
@@ -79,93 +86,95 @@ struct StmtNode
    /// @name Breaking
    /// @{
 
-   void addBreakLine(CodeStream &codeStream);
+   void addBreakLine(CodeStream& codeStream);
    /// @}
 
    /// @name Compilation
    /// @{
 
-   virtual U32 compileStmt(CodeStream &codeStream, U32 ip) = 0;
+   virtual U32 compileStmt(CodeStream& codeStream, U32 ip) = 0;
    virtual void setPackage(StringTableEntry packageName);
    /// @}
 };
 
 /// Helper macro
 #ifndef DEBUG_AST_NODES
-#  define DBG_STMT_TYPE(s) virtual String dbgStmtType() const { return String(#s); }
+#  define DBG_STMT_TYPE(s) virtual const char* dbgStmtType() const { return "#s"; }
 #else
 #  define DBG_STMT_TYPE(s) 
 #endif
 
 struct BreakStmtNode : StmtNode
 {
-   static BreakStmtNode *alloc(S32 lineNumber);
+   static BreakStmtNode* alloc(S32 lineNumber);
 
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(BreakStmtNode);
 };
 
 struct ContinueStmtNode : StmtNode
 {
-   static ContinueStmtNode *alloc(S32 lineNumber);
+   static ContinueStmtNode* alloc(S32 lineNumber);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(ContinueStmtNode);
 };
 
 /// A mathematical expression.
 struct ExprNode : StmtNode
 {
+   ExprNode* optimizedNode;
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
 
-   virtual U32 compile(CodeStream &codeStream, U32 ip, TypeReq type) = 0;
+   virtual U32 compile(CodeStream& codeStream, U32 ip, TypeReq type) = 0;
    virtual TypeReq getPreferredType() = 0;
+   virtual ExprNodeName getExprNodeNameEnum() const { return NameExprNode; }
 };
 
 struct ReturnStmtNode : StmtNode
 {
-   ExprNode *expr;
+   ExprNode* expr;
 
-   static ReturnStmtNode *alloc(S32 lineNumber, ExprNode *expr);
+   static ReturnStmtNode* alloc(S32 lineNumber, ExprNode* expr);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(ReturnStmtNode);
 };
 
 struct IfStmtNode : StmtNode
 {
-   ExprNode *testExpr;
-   StmtNode *ifBlock, *elseBlock;
+   ExprNode* testExpr;
+   StmtNode* ifBlock, * elseBlock;
    U32 endifOffset;
    U32 elseOffset;
    bool integer;
    bool propagate;
 
-   static IfStmtNode *alloc(S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagateThrough);
-   void propagateSwitchExpr(ExprNode *left, bool string);
-   ExprNode *getSwitchOR(ExprNode *left, ExprNode *list, bool string);
+   static IfStmtNode* alloc(S32 lineNumber, ExprNode* testExpr, StmtNode* ifBlock, StmtNode* elseBlock, bool propagateThrough);
+   void propagateSwitchExpr(ExprNode* left, bool string);
+   ExprNode* getSwitchOR(ExprNode* left, ExprNode* list, bool string);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(IfStmtNode);
 };
 
 struct LoopStmtNode : StmtNode
 {
-   ExprNode *testExpr;
-   ExprNode *initExpr;
-   ExprNode *endLoopExpr;
-   StmtNode *loopBlock;
+   ExprNode* testExpr;
+   ExprNode* initExpr;
+   ExprNode* endLoopExpr;
+   StmtNode* loopBlock;
    bool isDoLoop;
    U32 breakOffset;
    U32 continueOffset;
    U32 loopBlockStartOffset;
    bool integer;
 
-   static LoopStmtNode *alloc(S32 lineNumber, ExprNode *testExpr, ExprNode *initExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop);
+   static LoopStmtNode* alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* initExpr, ExprNode* endLoopExpr, StmtNode* loopBlock, bool isDoLoop);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(LoopStmtNode);
 };
 
@@ -189,35 +198,38 @@ struct IterStmtNode : StmtNode
 
    static IterStmtNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
 };
 
 /// A binary mathematical expression (ie, left op right).
 struct BinaryExprNode : ExprNode
 {
    S32 op;
-   ExprNode *left;
-   ExprNode *right;
+   ExprNode* left;
+   ExprNode* right;
 };
 
 struct FloatBinaryExprNode : BinaryExprNode
 {
-   static FloatBinaryExprNode *alloc(S32 lineNumber, S32 op, ExprNode *left, ExprNode *right);
+   static FloatBinaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right);
+
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
+
+   bool optimize();
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(FloatBinaryExprNode);
 };
 
 struct ConditionalExprNode : ExprNode
 {
-   ExprNode *testExpr;
-   ExprNode *trueExpr;
-   ExprNode *falseExpr;
+   ExprNode* testExpr;
+   ExprNode* trueExpr;
+   ExprNode* falseExpr;
    bool integer;
-   static ConditionalExprNode *alloc(S32 lineNumber, ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr);
+   static ConditionalExprNode* alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* trueExpr, ExprNode* falseExpr);
 
-   virtual U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   virtual U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    virtual TypeReq getPreferredType();
    DBG_STMT_TYPE(ConditionalExprNode);
 };
@@ -227,11 +239,13 @@ struct IntBinaryExprNode : BinaryExprNode
    TypeReq subType;
    U32 operand;
 
-   static IntBinaryExprNode *alloc(S32 lineNumber, S32 op, ExprNode *left, ExprNode *right);
+   static IntBinaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right);
 
    void getSubTypeOperand();
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   bool optimize();
+
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(IntBinaryExprNode);
 };
@@ -239,9 +253,9 @@ struct IntBinaryExprNode : BinaryExprNode
 struct StreqExprNode : BinaryExprNode
 {
    bool eq;
-   static StreqExprNode *alloc(S32 lineNumber, ExprNode *left, ExprNode *right, bool eq);
+   static StreqExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right, bool eq);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(StreqExprNode);
 };
@@ -249,19 +263,19 @@ struct StreqExprNode : BinaryExprNode
 struct StrcatExprNode : BinaryExprNode
 {
    S32 appendChar;
-   static StrcatExprNode *alloc(S32 lineNumber, ExprNode *left, ExprNode *right, S32 appendChar);
+   static StrcatExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right, S32 appendChar);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(StrcatExprNode);
 };
 
 struct CommaCatExprNode : BinaryExprNode
 {
-   static CommaCatExprNode *alloc(S32 lineNumber, ExprNode *left, ExprNode *right);
+   static CommaCatExprNode* alloc(S32 lineNumber, ExprNode* left, ExprNode* right);
 
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(CommaCatExprNode);
 };
@@ -269,12 +283,12 @@ struct CommaCatExprNode : BinaryExprNode
 struct IntUnaryExprNode : ExprNode
 {
    S32 op;
-   ExprNode *expr;
+   ExprNode* expr;
    bool integer;
 
-   static IntUnaryExprNode *alloc(S32 lineNumber, S32 op, ExprNode *expr);
+   static IntUnaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* expr);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(IntUnaryExprNode);
 };
@@ -282,11 +296,11 @@ struct IntUnaryExprNode : ExprNode
 struct FloatUnaryExprNode : ExprNode
 {
    S32 op;
-   ExprNode *expr;
+   ExprNode* expr;
 
-   static FloatUnaryExprNode *alloc(S32 lineNumber, S32 op, ExprNode *expr);
+   static FloatUnaryExprNode* alloc(S32 lineNumber, S32 op, ExprNode* expr);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(FloatUnaryExprNode);
 };
@@ -294,12 +308,13 @@ struct FloatUnaryExprNode : ExprNode
 struct VarNode : ExprNode
 {
    StringTableEntry varName;
-   ExprNode *arrayIndex;
+   ExprNode* arrayIndex;
 
-   static VarNode *alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex);
+   static VarNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
+   virtual ExprNodeName getExprNodeNameEnum() const { return NameVarNode; }
    DBG_STMT_TYPE(VarNode);
 };
 
@@ -308,10 +323,11 @@ struct IntNode : ExprNode
    S32 value;
    U32 index; // if it's converted to float/string
 
-   static IntNode *alloc(S32 lineNumber, S32 value);
+   static IntNode* alloc(S32 lineNumber, S32 value);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
+   virtual ExprNodeName getExprNodeNameEnum() const { return NameIntNode; }
    DBG_STMT_TYPE(IntNode);
 };
 
@@ -320,24 +336,25 @@ struct FloatNode : ExprNode
    F64 value;
    U32 index;
 
-   static FloatNode *alloc(S32 lineNumber, F64 value);
+   static FloatNode* alloc(S32 lineNumber, F64 value);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
+   virtual ExprNodeName getExprNodeNameEnum() const { return NameFloatNode; }
    DBG_STMT_TYPE(FloatNode);
 };
 
 struct StrConstNode : ExprNode
 {
-   char *str;
+   char* str;
    F64 fVal;
    U32 index;
    bool tag;
    bool doc; // Specifies that this string is a documentation block.
 
-   static StrConstNode *alloc(S32 lineNumber, char *str, bool tag, bool doc = false);
+   static StrConstNode* alloc(S32 lineNumber, char* str, bool tag, bool doc = false);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(StrConstNode);
 };
@@ -348,9 +365,9 @@ struct ConstantNode : ExprNode
    F64 fVal;
    U32 index;
 
-   static ConstantNode *alloc(S32 lineNumber, StringTableEntry value);
+   static ConstantNode* alloc(S32 lineNumber, StringTableEntry value);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(ConstantNode);
 };
@@ -358,13 +375,13 @@ struct ConstantNode : ExprNode
 struct AssignExprNode : ExprNode
 {
    StringTableEntry varName;
-   ExprNode *expr;
-   ExprNode *arrayIndex;
+   ExprNode* expr;
+   ExprNode* arrayIndex;
    TypeReq subType;
 
-   static AssignExprNode *alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr);
+   static AssignExprNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(AssignExprNode);
 };
@@ -373,22 +390,22 @@ struct AssignDecl
 {
    S32 lineNumber;
    S32 token;
-   ExprNode *expr;
+   ExprNode* expr;
    bool integer;
 };
 
 struct AssignOpExprNode : ExprNode
 {
    StringTableEntry varName;
-   ExprNode *expr;
-   ExprNode *arrayIndex;
+   ExprNode* expr;
+   ExprNode* arrayIndex;
    S32 op;
    U32 operand;
    TypeReq subType;
 
-   static AssignOpExprNode *alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op);
+   static AssignOpExprNode* alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr, S32 op);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(AssignOpExprNode);
 };
@@ -396,22 +413,22 @@ struct AssignOpExprNode : ExprNode
 struct TTagSetStmtNode : StmtNode
 {
    StringTableEntry tag;
-   ExprNode *valueExpr;
-   ExprNode *stringExpr;
+   ExprNode* valueExpr;
+   ExprNode* stringExpr;
 
-   static TTagSetStmtNode *alloc(S32 lineNumber, StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr);
+   static TTagSetStmtNode* alloc(S32 lineNumber, StringTableEntry tag, ExprNode* valueExpr, ExprNode* stringExpr);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    DBG_STMT_TYPE(TTagSetStmtNode);
 };
 
 struct TTagDerefNode : ExprNode
 {
-   ExprNode *expr;
+   ExprNode* expr;
 
-   static TTagDerefNode *alloc(S32 lineNumber, ExprNode *expr);
+   static TTagDerefNode* alloc(S32 lineNumber, ExprNode* expr);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(TTagDerefNode);
 };
@@ -420,9 +437,9 @@ struct TTagExprNode : ExprNode
 {
    StringTableEntry tag;
 
-   static TTagExprNode *alloc(S32 lineNumber, StringTableEntry tag);
+   static TTagExprNode* alloc(S32 lineNumber, StringTableEntry tag);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(TTagExprNode);
 };
@@ -431,42 +448,31 @@ struct FuncCallExprNode : ExprNode
 {
    StringTableEntry funcName;
    StringTableEntry nameSpace;
-   ExprNode *args;
+   ExprNode* args;
    U32 callType;
    enum {
       FunctionCall,
+      StaticCall,
       MethodCall,
       ParentCall
    };
 
-   static FuncCallExprNode *alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot);
+   static FuncCallExprNode* alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode* args, bool dot);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(FuncCallExprNode);
 };
 
-struct FuncPointerCallExprNode : ExprNode
-{
-   ExprNode *funcPointer;
-   ExprNode *args;
-
-   static FuncPointerCallExprNode *alloc(S32 lineNumber, ExprNode *stmt, ExprNode *args);
-
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
-   TypeReq getPreferredType();
-   DBG_STMT_TYPE(FuncPointerCallExprNode);
-};
-
 struct AssertCallExprNode : ExprNode
 {
-   ExprNode *testExpr;
-   const char *message;
+   ExprNode* testExpr;
+   const char* message;
    U32 messageIndex;
 
-   static AssertCallExprNode *alloc(S32 lineNumber, ExprNode *testExpr, const char *message);
+   static AssertCallExprNode* alloc(S32 lineNumber, ExprNode* testExpr, const char* message);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(AssertCallExprNode);
 };
@@ -474,19 +480,19 @@ struct AssertCallExprNode : ExprNode
 struct SlotDecl
 {
    S32              lineNumber;
-   ExprNode         *object;
+   ExprNode* object;
    StringTableEntry slotName;
-   ExprNode         *array;
+   ExprNode* array;
 };
 
 struct SlotAccessNode : ExprNode
 {
-   ExprNode *objectExpr, *arrayExpr;
+   ExprNode* objectExpr, * arrayExpr;
    StringTableEntry slotName;
 
-   static SlotAccessNode *alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName);
+   static SlotAccessNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(SlotAccessNode);
 };
@@ -494,101 +500,99 @@ struct SlotAccessNode : ExprNode
 struct InternalSlotDecl
 {
    S32              lineNumber;
-   ExprNode         *object;
-   ExprNode         *slotExpr;
+   ExprNode* object;
+   ExprNode* slotExpr;
    bool             recurse;
 };
 
 struct InternalSlotAccessNode : ExprNode
 {
-   ExprNode *objectExpr, *slotExpr;
+   ExprNode* objectExpr, * slotExpr;
    bool recurse;
 
-   static InternalSlotAccessNode *alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *slotExpr, bool recurse);
+   static InternalSlotAccessNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* slotExpr, bool recurse);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(InternalSlotAccessNode);
 };
 
 struct SlotAssignNode : ExprNode
 {
-   ExprNode *objectExpr, *arrayExpr;
+   ExprNode* objectExpr, * arrayExpr;
    StringTableEntry slotName;
-   ExprNode *valueExpr;
+   ExprNode* valueExpr;
    U32 typeID;
 
-   static SlotAssignNode *alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr, U32 typeID = -1);
+   static SlotAssignNode* alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName, ExprNode* valueExpr, U32 typeID = -1);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(SlotAssignNode);
 };
 
 struct SlotAssignOpNode : ExprNode
 {
-   ExprNode *objectExpr, *arrayExpr;
+   ExprNode* objectExpr, * arrayExpr;
    StringTableEntry slotName;
    S32 op;
-   ExprNode *valueExpr;
+   ExprNode* valueExpr;
    U32 operand;
    TypeReq subType;
 
-   static SlotAssignOpNode *alloc(S32 lineNumber, ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr);
+   static SlotAssignOpNode* alloc(S32 lineNumber, ExprNode* objectExpr, StringTableEntry slotName, ExprNode* arrayExpr, S32 op, ExprNode* valueExpr);
 
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(SlotAssignOpNode);
 };
 
 struct ObjectDeclNode : ExprNode
 {
-   ExprNode *classNameExpr;
+   ExprNode* classNameExpr;
    StringTableEntry parentObject;
-   ExprNode *objectNameExpr;
-   ExprNode *argList;
-   SlotAssignNode *slotDecls;
-   ObjectDeclNode *subObjects;
+   ExprNode* objectNameExpr;
+   ExprNode* argList;
+   SlotAssignNode* slotDecls;
+   ObjectDeclNode* subObjects;
    bool isDatablock;
    U32 failOffset;
    bool isClassNameInternal;
    bool isSingleton;
 
-   static ObjectDeclNode *alloc(S32 lineNumber, ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool isDatablock, bool classNameInternal, bool isSingleton);
+   static ObjectDeclNode* alloc(S32 lineNumber, ExprNode* classNameExpr, ExprNode* objectNameExpr, ExprNode* argList, StringTableEntry parentObject, SlotAssignNode* slotDecls, ObjectDeclNode* subObjects, bool isDatablock, bool classNameInternal, bool isSingleton);
 
    U32 precompileSubObject(bool);
-   U32 compile(CodeStream &codeStream, U32 ip, TypeReq type);
-   U32 compileSubObject(CodeStream &codeStream, U32 ip, bool);
+   U32 compile(CodeStream& codeStream, U32 ip, TypeReq type);
+   U32 compileSubObject(CodeStream& codeStream, U32 ip, bool);
    TypeReq getPreferredType();
    DBG_STMT_TYPE(ObjectDeclNode);
 };
 
 struct ObjectBlockDecl
 {
-   SlotAssignNode *slots;
-   ObjectDeclNode *decls;
+   SlotAssignNode* slots;
+   ObjectDeclNode* decls;
 };
 
 struct FunctionDeclStmtNode : StmtNode
 {
    StringTableEntry fnName;
-   VarNode *args;
-   StmtNode *stmts;
+   VarNode* args;
+   StmtNode* stmts;
    StringTableEntry nameSpace;
    StringTableEntry package;
    U32 endOffset;
    U32 argc;
 
-   static FunctionDeclStmtNode *alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts);
+   static FunctionDeclStmtNode* alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode* args, StmtNode* stmts);
 
-   U32 compileStmt(CodeStream &codeStream, U32 ip);
+   U32 compileStmt(CodeStream& codeStream, U32 ip);
    void setPackage(StringTableEntry packageName);
    DBG_STMT_TYPE(FunctionDeclStmtNode);
 };
 
-extern StmtNode *gStatementList;
-extern StmtNode *gAnonFunctionList;
-extern U32 gAnonFunctionID;
+extern StmtNode* gStatementList;
 extern ExprEvalState gEvalState;
 
 #endif

+ 98 - 82
Engine/source/console/astAlloc.cpp

@@ -1,5 +1,5 @@
 //-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
+// Copyright (c) 2013 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
@@ -20,7 +20,6 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
-#include "platform/platform.h"
 #include "console/console.h"
 #include "console/compiler.h"
 #include "console/consoleInternal.h"
@@ -38,25 +37,25 @@ using namespace Compiler;
 
 //------------------------------------------------------------
 
-BreakStmtNode *BreakStmtNode::alloc(S32 lineNumber)
+BreakStmtNode* BreakStmtNode::alloc(S32 lineNumber)
 {
-   BreakStmtNode *ret = (BreakStmtNode *)consoleAlloc(sizeof(BreakStmtNode));
+   BreakStmtNode* ret = (BreakStmtNode*)consoleAlloc(sizeof(BreakStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    return ret;
 }
 
-ContinueStmtNode *ContinueStmtNode::alloc(S32 lineNumber)
+ContinueStmtNode* ContinueStmtNode::alloc(S32 lineNumber)
 {
-   ContinueStmtNode *ret = (ContinueStmtNode *)consoleAlloc(sizeof(ContinueStmtNode));
+   ContinueStmtNode* ret = (ContinueStmtNode*)consoleAlloc(sizeof(ContinueStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    return ret;
 }
 
-ReturnStmtNode *ReturnStmtNode::alloc(S32 lineNumber, ExprNode *expr)
+ReturnStmtNode* ReturnStmtNode::alloc(S32 lineNumber, ExprNode* expr)
 {
-   ReturnStmtNode *ret = (ReturnStmtNode *)consoleAlloc(sizeof(ReturnStmtNode));
+   ReturnStmtNode* ret = (ReturnStmtNode*)consoleAlloc(sizeof(ReturnStmtNode));
    constructInPlace(ret);
    ret->expr = expr;
    ret->dbgLineNumber = lineNumber;
@@ -64,9 +63,9 @@ ReturnStmtNode *ReturnStmtNode::alloc(S32 lineNumber, ExprNode *expr)
    return ret;
 }
 
-IfStmtNode *IfStmtNode::alloc(S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagate)
+IfStmtNode* IfStmtNode::alloc(S32 lineNumber, ExprNode* testExpr, StmtNode* ifBlock, StmtNode* elseBlock, bool propagate)
 {
-   IfStmtNode *ret = (IfStmtNode *)consoleAlloc(sizeof(IfStmtNode));
+   IfStmtNode* ret = (IfStmtNode*)consoleAlloc(sizeof(IfStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
 
@@ -78,9 +77,9 @@ IfStmtNode *IfStmtNode::alloc(S32 lineNumber, ExprNode *testExpr, StmtNode *ifBl
    return ret;
 }
 
-LoopStmtNode *LoopStmtNode::alloc(S32 lineNumber, ExprNode *initExpr, ExprNode *testExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop)
+LoopStmtNode* LoopStmtNode::alloc(S32 lineNumber, ExprNode* initExpr, ExprNode* testExpr, ExprNode* endLoopExpr, StmtNode* loopBlock, bool isDoLoop)
 {
-   LoopStmtNode *ret = (LoopStmtNode *)consoleAlloc(sizeof(LoopStmtNode));
+   LoopStmtNode* ret = (LoopStmtNode*)consoleAlloc(sizeof(LoopStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    ret->testExpr = testExpr;
@@ -111,11 +110,12 @@ IterStmtNode* IterStmtNode::alloc(S32 lineNumber, StringTableEntry varName, Expr
    return ret;
 }
 
-FloatBinaryExprNode *FloatBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode *left, ExprNode *right)
+FloatBinaryExprNode* FloatBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
 {
-   FloatBinaryExprNode *ret = (FloatBinaryExprNode *)consoleAlloc(sizeof(FloatBinaryExprNode));
+   FloatBinaryExprNode* ret = (FloatBinaryExprNode*)consoleAlloc(sizeof(FloatBinaryExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
 
    ret->op = op;
    ret->left = left;
@@ -124,11 +124,12 @@ FloatBinaryExprNode *FloatBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode
    return ret;
 }
 
-IntBinaryExprNode *IntBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode *left, ExprNode *right)
+IntBinaryExprNode* IntBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
 {
-   IntBinaryExprNode *ret = (IntBinaryExprNode *)consoleAlloc(sizeof(IntBinaryExprNode));
+   IntBinaryExprNode* ret = (IntBinaryExprNode*)consoleAlloc(sizeof(IntBinaryExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
 
    ret->op = op;
    ret->left = left;
@@ -137,11 +138,12 @@ IntBinaryExprNode *IntBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode *le
    return ret;
 }
 
-StreqExprNode *StreqExprNode::alloc(S32 lineNumber, ExprNode *left, ExprNode *right, bool eq)
+StreqExprNode* StreqExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, bool eq)
 {
-   StreqExprNode *ret = (StreqExprNode *)consoleAlloc(sizeof(StreqExprNode));
+   StreqExprNode* ret = (StreqExprNode*)consoleAlloc(sizeof(StreqExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->left = left;
    ret->right = right;
    ret->eq = eq;
@@ -149,11 +151,12 @@ StreqExprNode *StreqExprNode::alloc(S32 lineNumber, ExprNode *left, ExprNode *ri
    return ret;
 }
 
-StrcatExprNode *StrcatExprNode::alloc(S32 lineNumber, ExprNode *left, ExprNode *right, S32 appendChar)
+StrcatExprNode* StrcatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, int appendChar)
 {
-   StrcatExprNode *ret = (StrcatExprNode *)consoleAlloc(sizeof(StrcatExprNode));
+   StrcatExprNode* ret = (StrcatExprNode*)consoleAlloc(sizeof(StrcatExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->left = left;
    ret->right = right;
    ret->appendChar = appendChar;
@@ -161,61 +164,67 @@ StrcatExprNode *StrcatExprNode::alloc(S32 lineNumber, ExprNode *left, ExprNode *
    return ret;
 }
 
-CommaCatExprNode *CommaCatExprNode::alloc(S32 lineNumber, ExprNode *left, ExprNode *right)
+CommaCatExprNode* CommaCatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right)
 {
-   CommaCatExprNode *ret = (CommaCatExprNode *)consoleAlloc(sizeof(CommaCatExprNode));
+   CommaCatExprNode* ret = (CommaCatExprNode*)consoleAlloc(sizeof(CommaCatExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->left = left;
    ret->right = right;
 
    return ret;
 }
 
-IntUnaryExprNode *IntUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode *expr)
+IntUnaryExprNode* IntUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
 {
-   IntUnaryExprNode *ret = (IntUnaryExprNode *)consoleAlloc(sizeof(IntUnaryExprNode));
+   IntUnaryExprNode* ret = (IntUnaryExprNode*)consoleAlloc(sizeof(IntUnaryExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->op = op;
    ret->expr = expr;
    return ret;
 }
 
-FloatUnaryExprNode *FloatUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode *expr)
+FloatUnaryExprNode* FloatUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
 {
-   FloatUnaryExprNode *ret = (FloatUnaryExprNode *)consoleAlloc(sizeof(FloatUnaryExprNode));
+   FloatUnaryExprNode* ret = (FloatUnaryExprNode*)consoleAlloc(sizeof(FloatUnaryExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->op = op;
    ret->expr = expr;
    return ret;
 }
 
-VarNode *VarNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex)
+VarNode* VarNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex)
 {
-   VarNode *ret = (VarNode *)consoleAlloc(sizeof(VarNode));
+   VarNode* ret = (VarNode*)consoleAlloc(sizeof(VarNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->varName = varName;
    ret->arrayIndex = arrayIndex;
    return ret;
 }
 
-IntNode *IntNode::alloc(S32 lineNumber, S32 value)
+IntNode* IntNode::alloc(S32 lineNumber, S32 value)
 {
-   IntNode *ret = (IntNode *)consoleAlloc(sizeof(IntNode));
+   IntNode* ret = (IntNode*)consoleAlloc(sizeof(IntNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->value = value;
    return ret;
 }
 
-ConditionalExprNode *ConditionalExprNode::alloc(S32 lineNumber, ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr)
+ConditionalExprNode* ConditionalExprNode::alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* trueExpr, ExprNode* falseExpr)
 {
-   ConditionalExprNode *ret = (ConditionalExprNode *)consoleAlloc(sizeof(ConditionalExprNode));
+   ConditionalExprNode* ret = (ConditionalExprNode*)consoleAlloc(sizeof(ConditionalExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->testExpr = testExpr;
    ret->trueExpr = trueExpr;
    ret->falseExpr = falseExpr;
@@ -223,44 +232,50 @@ ConditionalExprNode *ConditionalExprNode::alloc(S32 lineNumber, ExprNode *testEx
    return ret;
 }
 
-FloatNode *FloatNode::alloc(S32 lineNumber, F64 value)
+FloatNode* FloatNode::alloc(S32 lineNumber, F64 value)
 {
-   FloatNode *ret = (FloatNode *)consoleAlloc(sizeof(FloatNode));
+   FloatNode* ret = (FloatNode*)consoleAlloc(sizeof(FloatNode));
    constructInPlace(ret);
 
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->value = value;
    return ret;
 }
 
-StrConstNode *StrConstNode::alloc(S32 lineNumber, char *str, bool tag, bool doc)
+StrConstNode* StrConstNode::alloc(S32 lineNumber, char* str, bool tag, bool doc)
 {
-   StrConstNode *ret = (StrConstNode *)consoleAlloc(sizeof(StrConstNode));
+   StrConstNode* ret = (StrConstNode*)consoleAlloc(sizeof(StrConstNode));
    constructInPlace(ret);
+   S32 len = dStrlen(str);
+
    ret->dbgLineNumber = lineNumber;
-   dsize_t retStrLen = dStrlen(str) + 1;
-   ret->str = (char *)consoleAlloc(retStrLen);
+   ret->optimizedNode = NULL;
+   ret->str = (char*)consoleAlloc(len + 1);
    ret->tag = tag;
    ret->doc = doc;
-   dStrcpy(ret->str, str, retStrLen);
+   dStrcpy(ret->str, str, len + 1);
+   ret->str[len] = '\0';
 
    return ret;
 }
 
-ConstantNode *ConstantNode::alloc(S32 lineNumber, StringTableEntry value)
+ConstantNode* ConstantNode::alloc(S32 lineNumber, StringTableEntry value)
 {
-   ConstantNode *ret = (ConstantNode *)consoleAlloc(sizeof(ConstantNode));
+   ConstantNode* ret = (ConstantNode*)consoleAlloc(sizeof(ConstantNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->value = value;
    return ret;
 }
 
-AssignExprNode *AssignExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr)
+AssignExprNode* AssignExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr)
 {
-   AssignExprNode *ret = (AssignExprNode *)consoleAlloc(sizeof(AssignExprNode));
+   AssignExprNode* ret = (AssignExprNode*)consoleAlloc(sizeof(AssignExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->varName = varName;
    ret->expr = expr;
    ret->arrayIndex = arrayIndex;
@@ -268,11 +283,12 @@ AssignExprNode *AssignExprNode::alloc(S32 lineNumber, StringTableEntry varName,
    return ret;
 }
 
-AssignOpExprNode *AssignOpExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op)
+AssignOpExprNode* AssignOpExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr, S32 op)
 {
-   AssignOpExprNode *ret = (AssignOpExprNode *)consoleAlloc(sizeof(AssignOpExprNode));
+   AssignOpExprNode* ret = (AssignOpExprNode*)consoleAlloc(sizeof(AssignOpExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->varName = varName;
    ret->expr = expr;
    ret->arrayIndex = arrayIndex;
@@ -280,9 +296,9 @@ AssignOpExprNode *AssignOpExprNode::alloc(S32 lineNumber, StringTableEntry varNa
    return ret;
 }
 
-TTagSetStmtNode *TTagSetStmtNode::alloc(S32 lineNumber, StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr)
+TTagSetStmtNode* TTagSetStmtNode::alloc(S32 lineNumber, StringTableEntry tag, ExprNode* valueExpr, ExprNode* stringExpr)
 {
-   TTagSetStmtNode *ret = (TTagSetStmtNode *)consoleAlloc(sizeof(TTagSetStmtNode));
+   TTagSetStmtNode* ret = (TTagSetStmtNode*)consoleAlloc(sizeof(TTagSetStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    ret->tag = tag;
@@ -291,59 +307,54 @@ TTagSetStmtNode *TTagSetStmtNode::alloc(S32 lineNumber, StringTableEntry tag, Ex
    return ret;
 }
 
-TTagDerefNode *TTagDerefNode::alloc(S32 lineNumber, ExprNode *expr)
+TTagDerefNode* TTagDerefNode::alloc(S32 lineNumber, ExprNode* expr)
 {
-   TTagDerefNode *ret = (TTagDerefNode *)consoleAlloc(sizeof(TTagDerefNode));
+   TTagDerefNode* ret = (TTagDerefNode*)consoleAlloc(sizeof(TTagDerefNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->expr = expr;
    return ret;
 }
 
-TTagExprNode *TTagExprNode::alloc(S32 lineNumber, StringTableEntry tag)
+TTagExprNode* TTagExprNode::alloc(S32 lineNumber, StringTableEntry tag)
 {
-   TTagExprNode *ret = (TTagExprNode *)consoleAlloc(sizeof(TTagExprNode));
+   TTagExprNode* ret = (TTagExprNode*)consoleAlloc(sizeof(TTagExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->tag = tag;
    return ret;
 }
 
-FuncCallExprNode *FuncCallExprNode::alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot)
+FuncCallExprNode* FuncCallExprNode::alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode* args, bool dot)
 {
-   FuncCallExprNode *ret = (FuncCallExprNode *)consoleAlloc(sizeof(FuncCallExprNode));
+   FuncCallExprNode* ret = (FuncCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->funcName = funcName;
    ret->nameSpace = nameSpace;
    ret->args = args;
    if (dot)
       ret->callType = MethodCall;
-   else
+   else if (nameSpace != NULL)
    {
-      if (nameSpace && !dStricmp(nameSpace, "Parent"))
+      if (dStricmp(nameSpace, "Parent") == 0)
          ret->callType = ParentCall;
       else
-         ret->callType = FunctionCall;
+         ret->callType = StaticCall;
    }
+   else
+      ret->callType = FunctionCall;
    return ret;
 }
 
-FuncPointerCallExprNode *FuncPointerCallExprNode::alloc(S32 lineNumber, ExprNode *funcPointer, ExprNode *args)
-{
-   FuncPointerCallExprNode *ret = (FuncPointerCallExprNode *)consoleAlloc(sizeof(FuncPointerCallExprNode));
-   constructInPlace(ret);
-   ret->dbgLineNumber = lineNumber;
-   ret->funcPointer = funcPointer;
-   ret->args = args;
-   return ret;
-}
-
-AssertCallExprNode *AssertCallExprNode::alloc(S32 lineNumber, ExprNode *testExpr, const char *message)
+AssertCallExprNode* AssertCallExprNode::alloc(S32 lineNumber, ExprNode* testExpr, const char* message)
 {
-#ifdef TORQUE_ENABLE_SCRIPTASSERTS      
+#ifdef TORQUE_ENABLE_SCRIPTASSERTS
 
-   AssertCallExprNode *ret = (AssertCallExprNode *)consoleAlloc(sizeof(FuncCallExprNode));
+   AssertCallExprNode* ret = (AssertCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    ret->testExpr = testExpr;
@@ -357,10 +368,11 @@ AssertCallExprNode *AssertCallExprNode::alloc(S32 lineNumber, ExprNode *testExpr
 #endif
 }
 
-SlotAccessNode *SlotAccessNode::alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName)
+SlotAccessNode* SlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName)
 {
-   SlotAccessNode *ret = (SlotAccessNode *)consoleAlloc(sizeof(SlotAccessNode));
+   SlotAccessNode* ret = (SlotAccessNode*)consoleAlloc(sizeof(SlotAccessNode));
    constructInPlace(ret);
+   ret->optimizedNode = NULL;
    ret->dbgLineNumber = lineNumber;
    ret->objectExpr = objectExpr;
    ret->arrayExpr = arrayExpr;
@@ -368,22 +380,24 @@ SlotAccessNode *SlotAccessNode::alloc(S32 lineNumber, ExprNode *objectExpr, Expr
    return ret;
 }
 
-InternalSlotAccessNode *InternalSlotAccessNode::alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *slotExpr, bool recurse)
+InternalSlotAccessNode* InternalSlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* slotExpr, bool recurse)
 {
-   InternalSlotAccessNode *ret = (InternalSlotAccessNode *)consoleAlloc(sizeof(InternalSlotAccessNode));
+   InternalSlotAccessNode* ret = (InternalSlotAccessNode*)consoleAlloc(sizeof(InternalSlotAccessNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->objectExpr = objectExpr;
    ret->slotExpr = slotExpr;
    ret->recurse = recurse;
    return ret;
 }
 
-SlotAssignNode *SlotAssignNode::alloc(S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr, U32 typeID /* = -1 */)
+SlotAssignNode* SlotAssignNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName, ExprNode* valueExpr, U32 typeID /* = -1 */)
 {
-   SlotAssignNode *ret = (SlotAssignNode *)consoleAlloc(sizeof(SlotAssignNode));
+   SlotAssignNode* ret = (SlotAssignNode*)consoleAlloc(sizeof(SlotAssignNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->objectExpr = objectExpr;
    ret->arrayExpr = arrayExpr;
    ret->slotName = slotName;
@@ -392,11 +406,12 @@ SlotAssignNode *SlotAssignNode::alloc(S32 lineNumber, ExprNode *objectExpr, Expr
    return ret;
 }
 
-SlotAssignOpNode *SlotAssignOpNode::alloc(S32 lineNumber, ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr)
+SlotAssignOpNode* SlotAssignOpNode::alloc(S32 lineNumber, ExprNode* objectExpr, StringTableEntry slotName, ExprNode* arrayExpr, S32 op, ExprNode* valueExpr)
 {
-   SlotAssignOpNode *ret = (SlotAssignOpNode *)consoleAlloc(sizeof(SlotAssignOpNode));
+   SlotAssignOpNode* ret = (SlotAssignOpNode*)consoleAlloc(sizeof(SlotAssignOpNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->objectExpr = objectExpr;
    ret->arrayExpr = arrayExpr;
    ret->slotName = slotName;
@@ -405,11 +420,12 @@ SlotAssignOpNode *SlotAssignOpNode::alloc(S32 lineNumber, ExprNode *objectExpr,
    return ret;
 }
 
-ObjectDeclNode *ObjectDeclNode::alloc(S32 lineNumber, ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool isDatablock, bool classNameInternal, bool isSingleton)
+ObjectDeclNode* ObjectDeclNode::alloc(S32 lineNumber, ExprNode* classNameExpr, ExprNode* objectNameExpr, ExprNode* argList, StringTableEntry parentObject, SlotAssignNode* slotDecls, ObjectDeclNode* subObjects, bool isDatablock, bool classNameInternal, bool isSingleton)
 {
-   ObjectDeclNode *ret = (ObjectDeclNode *)consoleAlloc(sizeof(ObjectDeclNode));
+   ObjectDeclNode* ret = (ObjectDeclNode*)consoleAlloc(sizeof(ObjectDeclNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
+   ret->optimizedNode = NULL;
    ret->classNameExpr = classNameExpr;
    ret->objectNameExpr = objectNameExpr;
    ret->argList = argList;
@@ -422,13 +438,13 @@ ObjectDeclNode *ObjectDeclNode::alloc(S32 lineNumber, ExprNode *classNameExpr, E
    if (parentObject)
       ret->parentObject = parentObject;
    else
-      ret->parentObject = StringTable->EmptyString();
+      ret->parentObject = StringTable->insert("");
    return ret;
 }
 
-FunctionDeclStmtNode *FunctionDeclStmtNode::alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts)
+FunctionDeclStmtNode* FunctionDeclStmtNode::alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode* args, StmtNode* stmts)
 {
-   FunctionDeclStmtNode *ret = (FunctionDeclStmtNode *)consoleAlloc(sizeof(FunctionDeclStmtNode));
+   FunctionDeclStmtNode* ret = (FunctionDeclStmtNode*)consoleAlloc(sizeof(FunctionDeclStmtNode));
    constructInPlace(ret);
    ret->dbgLineNumber = lineNumber;
    ret->fnName = fnName;

File diff suppressed because it is too large
+ 275 - 414
Engine/source/console/astNodes.cpp


File diff suppressed because it is too large
+ 870 - 929
Engine/source/console/cmdgram.cpp


+ 589 - 677
Engine/source/console/codeBlock.cpp

@@ -405,6 +405,26 @@ bool CodeBlock::read(StringTableEntry fileName, Stream &st)
       for (i = 0; i < size; i++)
          st.read(&functionFloats[i]);
    }
+
+   // Variable register mapping table
+   st.read(&size);
+   if (size)
+   {
+      for (i = 0; i < size; i++)
+      {
+         StringTableEntry fnName = st.readSTString();
+
+         U32 count;
+         st.read(&count);
+         for (U32 j = 0; j < count; j++)
+         {
+            StringTableEntry varName = st.readSTString();
+
+            variableRegisterTable.localVarToRegister[fnName].varList.push_back(varName);
+         }
+      }
+   }
+
    U32 codeLength;
    st.read(&codeLength);
    st.read(&lineBreakPairCount);
@@ -477,7 +497,6 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
    STEtoCode = compileSTEtoCode;
 
    gStatementList = NULL;
-   gAnonFunctionList = NULL;
 
    // Set up the parser.
    smCurrentParser = getParserForFile(fileName);
@@ -487,17 +506,6 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
    smCurrentParser->setScanBuffer(script, fileName);
    smCurrentParser->restart(NULL);
    smCurrentParser->parse();
-   if (gStatementList)
-   {
-      if (gAnonFunctionList)
-      {
-         // Prepend anonymous functions to statement list, so they're defined already when
-         // the statements run.
-         gAnonFunctionList->append(gStatementList);
-         gStatementList = gAnonFunctionList;
-      }
-   }
-
 
    if (gSyntaxError)
    {
@@ -532,7 +540,7 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
       lastIp = 0;
    }
 
-   codeStream.emit(OP_RETURN);
+   codeStream.emit(OP_RETURN_VOID);
    codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
 
    lineBreakPairCount = codeStream.getNumLineBreaks();
@@ -545,6 +553,9 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
    getGlobalFloatTable().write(st);
    getFunctionFloatTable().write(st);
 
+   // write variable mapping table
+   getFunctionVariableMappingTable().write(st);
+
    if (lastIp != codeSize)
       Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
 
@@ -575,11 +586,9 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con
    st.close();
 
    return true;
-
-
 }
 
-ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, S32 setFrame)
+ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, S32 setFrame)
 {
    AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread");
 
@@ -620,7 +629,6 @@ ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *in
       addToCodeList();
 
    gStatementList = NULL;
-   gAnonFunctionList = NULL;
 
    // Set up the parser.
    smCurrentParser = getParserForFile(fileName);
@@ -630,21 +638,11 @@ ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *in
    smCurrentParser->setScanBuffer(string, fileName);
    smCurrentParser->restart(NULL);
    smCurrentParser->parse();
-   if (gStatementList)
-   {
-      if (gAnonFunctionList)
-      {
-         // Prepend anonymous functions to statement list, so they're defined already when
-         // the statements run.
-         gAnonFunctionList->append(gStatementList);
-         gStatementList = gAnonFunctionList;
-      }
-   }
 
    if (!gStatementList)
    {
       delete this;
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
 
    resetTables();
@@ -665,20 +663,27 @@ ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *in
    globalFloats = getGlobalFloatTable().build();
    functionFloats = getFunctionFloatTable().build();
 
-   codeStream.emit(OP_RETURN);
-   codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
+   variableRegisterTable = getFunctionVariableMappingTable().copy();
 
-   //dumpInstructions(0, false);
+   codeStream.emit(OP_RETURN_VOID);
+   codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
 
    consoleAllocReset();
 
+#ifndef TORQUE_SHIPPING
+   if (Con::getBoolVariable("$Debug::DumpByteCode"))
+   {
+      dumpInstructions();
+   }
+#endif
+
    if (lineBreakPairCount && fileName)
       calcBreakList();
 
    if (lastIp + 1 != codeSize)
       Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp);
 
-   return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
+   return std::move(exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame));
 }
 
 //-------------------------------------------------------------------------
@@ -701,10 +706,14 @@ String CodeBlock::getFunctionArgs(U32 ip)
 {
    StringBuilder str;
 
+   StringTableEntry fnName = CodeToSTE(code, ip);
+   StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
+   StringTableEntry fnNsName = StringTable->insert(avar("%s::%s", fnNamespace, fnName));
+
    U32 fnArgc = code[ip + 8];
    for (U32 i = 0; i < fnArgc; ++i)
    {
-      StringTableEntry var = CodeToSTE(code, ip + (i * 2) + 9);
+      StringTableEntry var = variableRegisterTable.localVarToRegister[fnNsName].varList[i];
 
       if (i != 0)
          str.append(", ");
@@ -739,775 +748,678 @@ void CodeBlock::dumpInstructions(U32 startIp, bool upToReturn)
       switch (code[ip++])
       {
 
-         case OP_FUNC_DECL:
-         {
-            StringTableEntry fnName = CodeToSTE(code, ip);
-            StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
-            StringTableEntry fnPackage = CodeToSTE(code, ip + 4);
-            bool hasBody = bool(code[ip + 6]);
-            U32 newIp = code[ip + 7];
-            U32 argc = code[ip + 8];
-            endFuncIp = newIp;
+      case OP_FUNC_DECL:
+      {
+         StringTableEntry fnName = CodeToSTE(code, ip);
+         StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
+         StringTableEntry fnPackage = CodeToSTE(code, ip + 4);
+         bool hasBody = bool(code[ip + 6]);
+         U32 newIp = code[ip + 7];
+         U32 argc = code[ip + 8];
+         U32 regCount = code[ip + 9];
+         endFuncIp = newIp;
+
+         Con::printf("%i: OP_FUNC_DECL stk=+0 name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i regCount=%i",
+            ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc, regCount);
+
+         // Skip args.
+
+         ip += 10 + argc;
+         smInFunction = true;
+         break;
+      }
 
-            Con::printf("%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i",
-               ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc);
+      case OP_CREATE_OBJECT:
+      {
+         StringTableEntry objParent = CodeToSTE(code, ip);
+         bool isDataBlock = code[ip + 2];
+         bool isInternal = code[ip + 3];
+         bool isSingleton = code[ip + 4];
+         U32  lineNumber = code[ip + 5];
+         U32 failJump = code[ip + 6];
 
-            // Skip args.
+         Con::printf("%i: OP_CREATE_OBJECT stk=+0 objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i",
+            ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump);
 
-            ip += 9 + (argc * 2);
-            smInFunction = true;
-            break;
-         }
+         ip += 7;
+         break;
+      }
 
-         case OP_CREATE_OBJECT:
-         {
-            StringTableEntry objParent = CodeToSTE(code, ip);
-            bool isDataBlock = code[ip + 2];
-            bool isInternal = code[ip + 3];
-            bool isSingleton = code[ip + 4];
-            U32  lineNumber = code[ip + 5];
-            U32 failJump = code[ip + 6];
+      case OP_ADD_OBJECT:
+      {
+         bool placeAtRoot = code[ip++];
+         const char* stk = placeAtRoot ? "+1" : "0";
+         Con::printf("%i: OP_ADD_OBJECT stk=%s placeAtRoot=%i", ip - 1, stk, placeAtRoot);
+         break;
+      }
 
-            Con::printf("%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i",
-               ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump);
+      case OP_END_OBJECT:
+      {
+         bool placeAtRoot = code[ip++];
+         const char* stk = placeAtRoot ? "-1" : "0";
+         Con::printf("%i: OP_END_OBJECT stk=%s placeAtRoot=%i", ip - 1, stk, placeAtRoot);
+         break;
+      }
 
-            ip += 7;
-            break;
-         }
+      case OP_FINISH_OBJECT:
+      {
+         Con::printf("%i: OP_FINISH_OBJECT", ip - 1);
+         break;
+      }
 
-         case OP_ADD_OBJECT:
-         {
-            bool placeAtRoot = code[ip++];
-            Con::printf("%i: OP_ADD_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot);
-            break;
-         }
+      case OP_JMPIFFNOT:
+      {
+         Con::printf("%i: OP_JMPIFFNOT stk=-1 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_END_OBJECT:
-         {
-            bool placeAtRoot = code[ip++];
-            Con::printf("%i: OP_END_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot);
-            break;
-         }
+      case OP_JMPIFNOT:
+      {
+         Con::printf("%i: OP_JMPIFNOT stk=-1 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_FINISH_OBJECT:
-         {
-            Con::printf("%i: OP_FINISH_OBJECT", ip - 1);
-            break;
-         }
+      case OP_JMPIFF:
+      {
+         Con::printf("%i: OP_JMPIFF stk=-1 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_JMPIFFNOT:
-         {
-            Con::printf("%i: OP_JMPIFFNOT ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+      case OP_JMPIF:
+      {
+         Con::printf("%i: OP_JMPIF stk=-1 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_JMPIFNOT:
-         {
-            Con::printf("%i: OP_JMPIFNOT ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+      case OP_JMPIFNOT_NP:
+      {
+         Con::printf("%i: OP_JMPIFNOT_NP stk=-1 or 0 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_JMPIFF:
-         {
-            Con::printf("%i: OP_JMPIFF ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+      case OP_JMPIF_NP:
+      {
+         Con::printf("%i: OP_JMPIF_NP stk=-1 or 0 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_JMPIF:
-         {
-            Con::printf("%i: OP_JMPIF ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+      case OP_JMP:
+      {
+         Con::printf("%i: OP_JMP stk=0 ip=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_JMPIFNOT_NP:
-         {
-            Con::printf("%i: OP_JMPIFNOT_NP ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+      case OP_RETURN_VOID:
+      {
+         Con::printf("%i: OP_RETURN_VOID stk=0", ip - 1);
 
-         case OP_JMPIF_NP:
-         {
-            Con::printf("%i: OP_JMPIF_NP ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+         if (upToReturn)
+            return;
 
-         case OP_JMP:
-         {
-            Con::printf("%i: OP_JMP ip=%i", ip - 1, code[ip]);
-            ++ip;
-            break;
-         }
+         break;
+      }
 
-         case OP_RETURN:
-         {
-            Con::printf("%i: OP_RETURN", ip - 1);
+      case OP_RETURN:
+      {
+         Con::printf("%i: OP_RETURN stk=-1", ip - 1);
 
-            if (upToReturn)
-               return;
+         if (upToReturn)
+            return;
 
-            break;
-         }
+         break;
+      }
 
-         case OP_RETURN_VOID:
-         {
-            Con::printf("%i: OP_RETURNVOID", ip - 1);
+      case OP_RETURN_UINT:
+      {
+         Con::printf("%i: OP_RETURNUINT stk=-1", ip - 1);
 
-            if (upToReturn)
-               return;
+         if (upToReturn)
+            return;
 
-            break;
-         }
+         break;
+      }
 
-         case OP_RETURN_UINT:
-         {
-            Con::printf("%i: OP_RETURNUINT", ip - 1);
+      case OP_RETURN_FLT:
+      {
+         Con::printf("%i: OP_RETURNFLT stk=-1", ip - 1);
 
-            if (upToReturn)
-               return;
+         if (upToReturn)
+            return;
 
-            break;
-         }
+         break;
+      }
 
-         case OP_RETURN_FLT:
-         {
-            Con::printf("%i: OP_RETURNFLT", ip - 1);
+      case OP_CMPEQ:
+      {
+         Con::printf("%i: OP_CMPEQ stk=-1", ip - 1);
+         break;
+      }
 
-            if (upToReturn)
-               return;
+      case OP_CMPGR:
+      {
+         Con::printf("%i: OP_CMPGR stk=-1", ip - 1);
+         break;
+      }
 
-            break;
-         }
+      case OP_CMPGE:
+      {
+         Con::printf("%i: OP_CMPGE stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPEQ:
-         {
-            Con::printf("%i: OP_CMPEQ", ip - 1);
-            break;
-         }
+      case OP_CMPLT:
+      {
+         Con::printf("%i: OP_CMPLT stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPGR:
-         {
-            Con::printf("%i: OP_CMPGR", ip - 1);
-            break;
-         }
+      case OP_CMPLE:
+      {
+         Con::printf("%i: OP_CMPLE stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPGE:
-         {
-            Con::printf("%i: OP_CMPGE", ip - 1);
-            break;
-         }
+      case OP_CMPNE:
+      {
+         Con::printf("%i: OP_CMPNE stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPLT:
-         {
-            Con::printf("%i: OP_CMPLT", ip - 1);
-            break;
-         }
+      case OP_XOR:
+      {
+         Con::printf("%i: OP_XOR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPLE:
-         {
-            Con::printf("%i: OP_CMPLE", ip - 1);
-            break;
-         }
+      case OP_MOD:
+      {
+         Con::printf("%i: OP_MOD stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_CMPNE:
-         {
-            Con::printf("%i: OP_CMPNE", ip - 1);
-            break;
-         }
+      case OP_BITAND:
+      {
+         Con::printf("%i: OP_BITAND stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_XOR:
-         {
-            Con::printf("%i: OP_XOR", ip - 1);
-            break;
-         }
+      case OP_BITOR:
+      {
+         Con::printf("%i: OP_BITOR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_MOD:
-         {
-            Con::printf("%i: OP_MOD", ip - 1);
-            break;
-         }
+      case OP_NOT:
+      {
+         Con::printf("%i: OP_NOT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_BITAND:
-         {
-            Con::printf("%i: OP_BITAND", ip - 1);
-            break;
-         }
+      case OP_NOTF:
+      {
+         Con::printf("%i: OP_NOTF stk=0", ip - 1);
+         break;
+      }
 
-         case OP_BITOR:
-         {
-            Con::printf("%i: OP_BITOR", ip - 1);
-            break;
-         }
+      case OP_ONESCOMPLEMENT:
+      {
+         Con::printf("%i: OP_ONESCOMPLEMENT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_NOT:
-         {
-            Con::printf("%i: OP_NOT", ip - 1);
-            break;
-         }
+      case OP_SHR:
+      {
+         Con::printf("%i: OP_SHR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_NOTF:
-         {
-            Con::printf("%i: OP_NOTF", ip - 1);
-            break;
-         }
+      case OP_SHL:
+      {
+         Con::printf("%i: OP_SHL stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_ONESCOMPLEMENT:
-         {
-            Con::printf("%i: OP_ONESCOMPLEMENT", ip - 1);
-            break;
-         }
+      case OP_AND:
+      {
+         Con::printf("%i: OP_AND stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_SHR:
-         {
-            Con::printf("%i: OP_SHR", ip - 1);
-            break;
-         }
+      case OP_OR:
+      {
+         Con::printf("%i: OP_OR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_SHL:
-         {
-            Con::printf("%i: OP_SHL", ip - 1);
-            break;
-         }
+      case OP_ADD:
+      {
+         Con::printf("%i: OP_ADD stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_AND:
-         {
-            Con::printf("%i: OP_AND", ip - 1);
-            break;
-         }
+      case OP_SUB:
+      {
+         Con::printf("%i: OP_SUB stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_OR:
-         {
-            Con::printf("%i: OP_OR", ip - 1);
-            break;
-         }
+      case OP_MUL:
+      {
+         Con::printf("%i: OP_MUL stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_ADD:
-         {
-            Con::printf("%i: OP_ADD", ip - 1);
-            break;
-         }
+      case OP_DIV:
+      {
+         Con::printf("%i: OP_DIV stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_SUB:
-         {
-            Con::printf("%i: OP_SUB", ip - 1);
-            break;
-         }
+      case OP_NEG:
+      {
+         Con::printf("%i: OP_NEG stk=0", ip - 1);
+         break;
+      }
 
-         case OP_MUL:
-         {
-            Con::printf("%i: OP_MUL", ip - 1);
-            break;
-         }
-
-         case OP_DIV:
-         {
-            Con::printf("%i: OP_DIV", ip - 1);
-            break;
-         }
-
-         case OP_NEG:
-         {
-            Con::printf("%i: OP_NEG", ip - 1);
-            break;
-         }
-
-         case OP_INC:
-         {
-            Con::printf("%i: OP_INC varName=%s", ip - 1, CodeToSTE(code, ip));
-            ip += 2;
-            break;
-         }
-
-         case OP_DEC:
-         {
-            Con::printf("%i: OP_DEC varName=%s", ip - 1, CodeToSTE(code, ip));
-            ip += 2;
-            break;
-         }
-
-         case OP_SETCURVAR:
-         {
-            StringTableEntry var = CodeToSTE(code, ip);
-
-            Con::printf("%i: OP_SETCURVAR var=%s", ip - 1, var);
-            ip += 2;
-            break;
-         }
-
-         case OP_SETCURVAR_CREATE:
-         {
-            StringTableEntry var = CodeToSTE(code, ip);
-
-            Con::printf("%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var);
-            ip += 2;
-            break;
-         }
-
-         case OP_SETCURVAR_ARRAY:
-         {
-            Con::printf("%i: OP_SETCURVAR_ARRAY", ip - 1);
-            break;
-         }
-
-         case OP_SETCURVAR_ARRAY_VARLOOKUP:
-         {
-            StringTableEntry arrayName = CodeToSTE(code, ip);
-            StringTableEntry arrayLookup = CodeToSTE(code, ip + 2);
-
-            Con::printf("%i: OP_SETCURVAR_ARRAY_VARLOOKUP arrayName=%s arrayLookup=%s", ip - 1, arrayName, arrayLookup);
-            ip += 4;
-            break;
-         }
-
-         case OP_SETCURVAR_ARRAY_CREATE:
-         {
-            Con::printf("%i: OP_SETCURVAR_ARRAY_CREATE", ip - 1);
-            break;
-         }
-
-         case OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP:
-         {
-            StringTableEntry arrayName = CodeToSTE(code, ip);
-            StringTableEntry arrayLookup = CodeToSTE(code, ip + 2);
-
-            Con::printf("%i: OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP arrayName=%s arrayLookup=%s", ip - 1, arrayName, arrayLookup);
-            ip += 4;
-            break;
-         }
+      case OP_INC:
+      {
+         Con::printf("%i: OP_INC stk=0 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADVAR_UINT:
-         {
-            Con::printf("%i: OP_LOADVAR_UINT", ip - 1);
-            break;
-         }
+      case OP_SETCURVAR:
+      {
+         StringTableEntry var = CodeToSTE(code, ip);
 
-         case OP_LOADVAR_FLT:
-         {
-            Con::printf("%i: OP_LOADVAR_FLT", ip - 1);
-            break;
-         }
+         Con::printf("%i: OP_SETCURVAR stk=0 var=%s", ip - 1, var);
+         ip += 2;
+         break;
+      }
 
-         case OP_LOADVAR_STR:
-         {
-            Con::printf("%i: OP_LOADVAR_STR", ip - 1);
-            break;
-         }
+      case OP_SETCURVAR_CREATE:
+      {
+         StringTableEntry var = CodeToSTE(code, ip);
 
-         case OP_LOADVAR_VAR:
-         {
-            Con::printf("%i: OP_LOADVAR_VAR", ip - 1);
-            break;
-         }
+         Con::printf("%i: OP_SETCURVAR_CREATE stk=0 var=%s", ip - 1, var);
+         ip += 2;
+         break;
+      }
 
-         case OP_SAVEVAR_UINT:
-         {
-            Con::printf("%i: OP_SAVEVAR_UINT", ip - 1);
-            break;
-         }
+      case OP_SETCURVAR_ARRAY:
+      {
+         Con::printf("%i: OP_SETCURVAR_ARRAY stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SAVEVAR_FLT:
-         {
-            Con::printf("%i: OP_SAVEVAR_FLT", ip - 1);
-            break;
-         }
+      case OP_SETCURVAR_ARRAY_CREATE:
+      {
+         Con::printf("%i: OP_SETCURVAR_ARRAY_CREATE stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SAVEVAR_STR:
-         {
-            Con::printf("%i: OP_SAVEVAR_STR", ip - 1);
-            break;
-         }
+      case OP_LOADVAR_UINT:
+      {
+         Con::printf("%i: OP_LOADVAR_UINT stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_SAVEVAR_VAR:
-         {
-            Con::printf("%i: OP_SAVEVAR_VAR", ip - 1);
-            break;
-         }
+      case OP_LOADVAR_FLT:
+      {
+         Con::printf("%i: OP_LOADVAR_FLT stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_SETCUROBJECT:
-         {
-            Con::printf("%i: OP_SETCUROBJECT", ip - 1);
-            break;
-         }
+      case OP_LOADVAR_STR:
+      {
+         Con::printf("%i: OP_LOADVAR_STR stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_SETCUROBJECT_NEW:
-         {
-            Con::printf("%i: OP_SETCUROBJECT_NEW", ip - 1);
-            break;
-         }
+      case OP_SAVEVAR_UINT:
+      {
+         Con::printf("%i: OP_SAVEVAR_UINT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SETCUROBJECT_INTERNAL:
-         {
-            Con::printf("%i: OP_SETCUROBJECT_INTERNAL", ip - 1);
-            ++ip;
-            break;
-         }
+      case OP_SAVEVAR_FLT:
+      {
+         Con::printf("%i: OP_SAVEVAR_FLT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SETCURFIELD:
-         {
-            StringTableEntry curField = CodeToSTE(code, ip);
-            Con::printf("%i: OP_SETCURFIELD field=%s", ip - 1, curField);
-            ip += 2;
-            break;
-         }
+      case OP_SAVEVAR_STR:
+      {
+         Con::printf("%i: OP_SAVEVAR_STR stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SETCURFIELD_ARRAY:
-         {
-            Con::printf("%i: OP_SETCURFIELD_ARRAY", ip - 1);
-            break;
-         }
+      case OP_LOAD_LOCAL_VAR_UINT:
+      {
+         Con::printf("%i: OP_LOAD_LOCAL_VAR_UINT stk=+1 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_SETCURFIELD_ARRAY_VAR:
-         {
-            StringTableEntry var = CodeToSTE(code, ip);
-            Con::printf("%i: OP_SETCURFIELD_ARRAY_VAR var=%s", ip - 1, var);
-            ip += 2;
-            break;
-         }
+      case OP_LOAD_LOCAL_VAR_FLT:
+      {
+         Con::printf("%i: OP_LOAD_LOCAL_VAR_FLT stk=+1 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_SETCURFIELD_THIS:
-         {
-            StringTableEntry curField = CodeToSTE(code, ip);
-            Con::printf("%i: OP_SETCURFIELD_THIS field=%s", ip - 1, curField);
-            ip += 2;
-            break;
-         }
+      case OP_LOAD_LOCAL_VAR_STR:
+      {
+         Con::printf("%i: OP_LOAD_LOCAL_VAR_STR stk=+1 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_SETCURFIELD_TYPE:
-         {
-            U32 type = code[ip];
-            Con::printf("%i: OP_SETCURFIELD_TYPE type=%i", ip - 1, type);
-            ++ip;
-            break;
-         }
+      case OP_SAVE_LOCAL_VAR_UINT:
+      {
+         Con::printf("%i: OP_SAVE_LOCAL_VAR_UINT stk=0 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADFIELD_UINT:
-         {
-            Con::printf("%i: OP_LOADFIELD_UINT", ip - 1);
-            break;
-         }
+      case OP_SAVE_LOCAL_VAR_FLT:
+      {
+         Con::printf("%i: OP_SAVE_LOCAL_VAR_FLT stk=0 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADFIELD_FLT:
-         {
-            Con::printf("%i: OP_LOADFIELD_FLT", ip - 1);
-            break;
-         }
+      case OP_SAVE_LOCAL_VAR_STR:
+      {
+         Con::printf("%i: OP_SAVE_LOCAL_VAR_STR stk=0 reg=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADFIELD_STR:
-         {
-            Con::printf("%i: OP_LOADFIELD_STR", ip - 1);
-            break;
-         }
+      case OP_SETCUROBJECT:
+      {
+         Con::printf("%i: OP_SETCUROBJECT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SAVEFIELD_UINT:
-         {
-            Con::printf("%i: OP_SAVEFIELD_UINT", ip - 1);
-            break;
-         }
+      case OP_SETCUROBJECT_NEW:
+      {
+         Con::printf("%i: OP_SETCUROBJECT_NEW stk=0", ip - 1);
+         break;
+      }
 
-         case OP_SAVEFIELD_FLT:
-         {
-            Con::printf("%i: OP_SAVEFIELD_FLT", ip - 1);
-            break;
-         }
+      case OP_SETCUROBJECT_INTERNAL:
+      {
+         Con::printf("%i: OP_SETCUROBJECT_INTERNAL stk=0", ip - 1);
+         ++ip;
+         break;
+      }
 
-         case OP_SAVEFIELD_STR:
-         {
-            Con::printf("%i: OP_SAVEFIELD_STR", ip - 1);
-            break;
-         }
+      case OP_SETCURFIELD:
+      {
+         StringTableEntry curField = CodeToSTE(code, ip);
+         Con::printf("%i: OP_SETCURFIELD stk=0 field=%s", ip - 1, curField);
+         ip += 2;
+         break;
+      }
 
-         case OP_STR_TO_UINT:
-         {
-            Con::printf("%i: OP_STR_TO_UINT", ip - 1);
-            break;
-         }
+      case OP_SETCURFIELD_ARRAY:
+      {
+         Con::printf("%i: OP_SETCURFIELD_ARRAY stk=0", ip - 1);
+         break;
+      }
 
-         case OP_STR_TO_FLT:
-         {
-            Con::printf("%i: OP_STR_TO_FLT", ip - 1);
-            break;
-         }
+      case OP_SETCURFIELD_TYPE:
+      {
+         U32 type = code[ip];
+         Con::printf("%i: OP_SETCURFIELD_TYPE stk=0 type=%i", ip - 1, type);
+         ++ip;
+         break;
+      }
 
-         case OP_STR_TO_NONE:
-         {
-            Con::printf("%i: OP_STR_TO_NONE", ip - 1);
-            break;
-         }
+      case OP_LOADFIELD_UINT:
+      {
+         Con::printf("%i: OP_LOADFIELD_UINT stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_FLT_TO_UINT:
-         {
-            Con::printf("%i: OP_FLT_TO_UINT", ip - 1);
-            break;
-         }
+      case OP_LOADFIELD_FLT:
+      {
+         Con::printf("%i: OP_LOADFIELD_FLT stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_FLT_TO_STR:
-         {
-            Con::printf("%i: OP_FLT_TO_STR", ip - 1);
-            break;
-         }
+      case OP_LOADFIELD_STR:
+      {
+         Con::printf("%i: OP_LOADFIELD_STR stk=+1", ip - 1);
+         break;
+      }
 
-         case OP_FLT_TO_NONE:
-         {
-            Con::printf("%i: OP_FLT_TO_NONE", ip - 1);
-            break;
-         }
+      case OP_SAVEFIELD_UINT:
+      {
+         Con::printf("%i: OP_SAVEFIELD_UINT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_UINT_TO_FLT:
-         {
-            Con::printf("%i: OP_SAVEFIELD_STR", ip - 1);
-            break;
-         }
+      case OP_SAVEFIELD_FLT:
+      {
+         Con::printf("%i: OP_SAVEFIELD_FLT stk=0", ip - 1);
+         break;
+      }
 
-         case OP_UINT_TO_STR:
-         {
-            Con::printf("%i: OP_UINT_TO_STR", ip - 1);
-            break;
-         }
+      case OP_SAVEFIELD_STR:
+      {
+         Con::printf("%i: OP_SAVEFIELD_STR stk=0", ip - 1);
+         break;
+      }
 
-         case OP_UINT_TO_NONE:
-         {
-            Con::printf("%i: OP_UINT_TO_NONE", ip - 1);
-            break;
-         }
+      case OP_POP_STK:
+      {
+         Con::printf("%i: OP_POP_STK stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_COPYVAR_TO_NONE:
-         {
-            Con::printf("%i: OP_COPYVAR_TO_NONE", ip - 1);
-            break;
-         }
+      case OP_LOADIMMED_UINT:
+      {
+         U32 val = code[ip];
+         Con::printf("%i: OP_LOADIMMED_UINT stk=+1 val=%i", ip - 1, val);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADIMMED_UINT:
-         {
-            U32 val = code[ip];
-            Con::printf("%i: OP_LOADIMMED_UINT val=%i", ip - 1, val);
-            ++ip;
-            break;
-         }
+      case OP_LOADIMMED_FLT:
+      {
+         F64 val = (smInFunction ? functionFloats : globalFloats)[code[ip]];
+         Con::printf("%i: OP_LOADIMMED_FLT stk=+1 val=%f", ip - 1, val);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADIMMED_FLT:
-         {
-            F64 val = (smInFunction ? functionFloats : globalFloats)[code[ip]];
-            Con::printf("%i: OP_LOADIMMED_FLT val=%f", ip - 1, val);
-            ++ip;
-            break;
-         }
+      case OP_TAG_TO_STR:
+      {
+         const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
+         Con::printf("%i: OP_TAG_TO_STR stk=0 str=%s", ip - 1, str);
+         Con::printf("    OP_LOADIMMED_STR stk=+1 (fallthrough)");
+         ++ip;
+         break;
+      }
 
-         case OP_TAG_TO_STR:
-         {
-            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
-            Con::printf("%i: OP_TAG_TO_STR str=%s", ip - 1, str);
-            ++ip;
-            break;
-         }
+      case OP_LOADIMMED_STR:
+      {
+         const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
+         Con::printf("%i: OP_LOADIMMED_STR stk=+1 str=%s", ip - 1, str);
+         ++ip;
+         break;
+      }
 
-         case OP_LOADIMMED_STR:
-         {
-            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
-            Con::printf("%i: OP_LOADIMMED_STR str=%s", ip - 1, str);
-            ++ip;
-            break;
-         }
+      case OP_DOCBLOCK_STR:
+      {
+         const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
+         Con::printf("%i: OP_DOCBLOCK_STR stk=0 str=%s", ip - 1, str);
+         ++ip;
+         break;
+      }
 
-         case OP_DOCBLOCK_STR:
-         {
-            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ip];
-            Con::printf("%i: OP_DOCBLOCK_STR str=%s", ip - 1, str);
-            ++ip;
-            break;
-         }
+      case OP_LOADIMMED_IDENT:
+      {
+         StringTableEntry str = CodeToSTE(code, ip);
+         Con::printf("%i: OP_LOADIMMED_IDENT stk=+1 str=%s", ip - 1, str);
+         ip += 2;
+         break;
+      }
 
-         case OP_LOADIMMED_IDENT:
-         {
-            StringTableEntry str = CodeToSTE(code, ip);
-            Con::printf("%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str);
-            ip += 2;
-            break;
-         }
+      case OP_CALLFUNC:
+      {
+         StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
+         StringTableEntry fnName = CodeToSTE(code, ip);
+         U32 callType = code[ip + 4];
 
-         case OP_CALLFUNC_RESOLVE:
+         StringTableEntry callTypeName;
+         switch (callType)
          {
-            StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
-            StringTableEntry fnName = CodeToSTE(code, ip);
-            U32 callType = code[ip + 2];
-
-            Con::printf("%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
-               callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
-               : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall");
-
-            ip += 5;
-            break;
+         case FuncCallExprNode::FunctionCall: callTypeName = "FunctionCall"; break;
+         case FuncCallExprNode::MethodCall:   callTypeName = "MethodCall"; break;
+         case FuncCallExprNode::ParentCall:   callTypeName = "ParentCall"; break;
+         case FuncCallExprNode::StaticCall:   callTypeName = "StaticCall"; break;
          }
 
-         case OP_CALLFUNC:
-         {
-            StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
-            StringTableEntry fnName = CodeToSTE(code, ip);
-            U32 callType = code[ip + 4];
-
-            Con::printf("%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
-               callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
-               : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall");
-
-            ip += 5;
-            break;
-         }
+         Con::printf("%i: OP_CALLFUNC stk=+1 name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, callTypeName);
 
-         case OP_CALLFUNC_POINTER:
-         {
-            Con::printf("%i: OP_CALLFUNC_POINTER", ip - 1);
-            break;
-         }
+         ip += 5;
+         break;
+      }
 
-         case OP_CALLFUNC_THIS:
-         {
-            StringTableEntry fnName = CodeToSTE(code, ip);
-            Con::printf("%i: OP_CALLFUNC_THIS name=%s ", ip - 1, fnName);
+      case OP_ADVANCE_STR_APPENDCHAR:
+      {
+         char ch = code[ip];
+         Con::printf("%i: OP_ADVANCE_STR_APPENDCHAR stk=0 char=%c", ip - 1, ch);
+         ++ip;
+         break;
+      }
 
-            ip += 2;
-            break;
-         }
+      case OP_REWIND_STR:
+      {
+         Con::printf("%i: OP_REWIND_STR stk=0", ip - 1);
+         Con::printf("    OP_TERMINATE_REWIND_STR stk=-1 (fallthrough)");
+         break;
+      }
 
-         case OP_ADVANCE_STR:
-         {
-            Con::printf("%i: OP_ADVANCE_STR", ip - 1);
-            break;
-         }
+      case OP_TERMINATE_REWIND_STR:
+      {
+         Con::printf("%i: OP_TERMINATE_REWIND_STR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_ADVANCE_STR_APPENDCHAR:
-         {
-            char ch = code[ip];
-            Con::printf("%i: OP_ADVANCE_STR_APPENDCHAR char=%c", ip - 1, ch);
-            ++ip;
-            break;
-         }
+      case OP_COMPARE_STR:
+      {
+         Con::printf("%i: OP_COMPARE_STR stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_ADVANCE_STR_COMMA:
-         {
-            Con::printf("%i: OP_ADVANCE_STR_COMMA", ip - 1);
-            break;
-         }
+      case OP_PUSH:
+      {
+         Con::printf("%i: OP_PUSH stk=-1", ip - 1);
+         break;
+      }
 
-         case OP_ADVANCE_STR_NUL:
-         {
-            Con::printf("%i: OP_ADVANCE_STR_NUL", ip - 1);
-            break;
-         }
+      case OP_PUSH_FRAME:
+      {
+         Con::printf("%i: OP_PUSH_FRAME stk=0 count=%i", ip - 1, code[ip]);
+         ++ip;
+         break;
+      }
 
-         case OP_REWIND_STR:
-         {
-            Con::printf("%i: OP_REWIND_STR", ip - 1);
-            break;
-         }
+      case OP_ASSERT:
+      {
+         const char* message = (smInFunction ? functionStrings : globalStrings) + code[ip];
+         Con::printf("%i: OP_ASSERT stk=-1 message=%s", ip - 1, message);
+         ++ip;
+         break;
+      }
 
-         case OP_TERMINATE_REWIND_STR:
-         {
-            Con::printf("%i: OP_TERMINATE_REWIND_STR", ip - 1);
-            break;
-         }
+      case OP_BREAK:
+      {
+         Con::printf("%i: OP_BREAK stk=0", ip - 1);
+         break;
+      }
 
-         case OP_COMPARE_STR:
+      case OP_ITER_BEGIN:
+      {
+         bool isGlobal = code[ip];
+         if (isGlobal)
          {
-            Con::printf("%i: OP_COMPARE_STR", ip - 1);
-            break;
-         }
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            U32 failIp = code[ip + 3];
 
-         case OP_PUSH:
-         {
-            Con::printf("%i: OP_PUSH", ip - 1);
-            break;
-         }
+            Con::printf("%i: OP_ITER_BEGIN stk=0 varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
 
-         case OP_PUSH_UINT:
-         {
-            Con::printf("%i: OP_PUSH_UINT", ip - 1);
-            break;
+            ip += 4;
          }
-
-         case OP_PUSH_FLT:
+         else
          {
-            Con::printf("%i: OP_PUSH_FLT", ip - 1);
-            break;
-         }
+            S32 reg = code[ip + 1];
+            U32 failIp = code[ip + 2];
 
-         case OP_PUSH_VAR:
-         {
-            Con::printf("%i: OP_PUSH_VAR", ip - 1);
-            break;
-         }
+            Con::printf("%i: OP_ITER_BEGIN stk=0 varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
 
-         case OP_PUSH_THIS:
-         {
-            Con::printf("%i: OP_PUSH_THIS varName=%s", ip - 1, CodeToSTE(code, ip));
-            ip += 2;
-            break;
+            ip += 3;
          }
+         break;
+      }
 
-         case OP_PUSH_FRAME:
+      case OP_ITER_BEGIN_STR:
+      {
+         bool isGlobal = code[ip];
+         if (isGlobal)
          {
-            Con::printf("%i: OP_PUSH_FRAME", ip - 1);
-            break;
-         }
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            U32 failIp = code[ip + 3];
 
-         case OP_ASSERT:
-         {
-            const char* message = (smInFunction ? functionStrings : globalStrings) + code[ip];
-            Con::printf("%i: OP_ASSERT message=%s", ip - 1, message);
-            ++ip;
-            break;
-         }
+            Con::printf("%i: OP_ITER_BEGIN_STR stk=0 varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
+            Con::printf("    OP_ITER_BEGIN stk=0 (fallthrough)");
 
-         case OP_BREAK:
-         {
-            Con::printf("%i: OP_BREAK", ip - 1);
-            break;
+            ip += 4;
          }
-
-         case OP_ITER_BEGIN:
+         else
          {
-            StringTableEntry varName = CodeToSTE(code, ip);
+            S32 reg = code[ip + 1];
             U32 failIp = code[ip + 2];
 
-            Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
+            Con::printf("%i: OP_ITER_BEGIN_STR stk=0 varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
+            Con::printf("    OP_ITER_BEGIN stk=0 (fallthrough)");
 
             ip += 3;
-            break;
          }
 
-         case OP_ITER_BEGIN_STR:
-         {
-            StringTableEntry varName = CodeToSTE(code, ip);
-            U32 failIp = code[ip + 2];
-
-            Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
-
-            ip += 3;
-            break;
-         }
+         break;
+      }
 
-         case OP_ITER:
-         {
-            U32 breakIp = code[ip];
+      case OP_ITER:
+      {
+         U32 breakIp = code[ip];
 
-            Con::printf("%i: OP_ITER breakIp=%i", ip - 1, breakIp);
+         Con::printf("%i: OP_ITER stk=0 breakIp=%i", ip - 1, breakIp);
 
-            ++ip;
-            break;
-         }
+         ++ip;
+         break;
+      }
 
-         case OP_ITER_END:
-         {
-            Con::printf("%i: OP_ITER_END", ip - 1);
-            break;
-         }
+      case OP_ITER_END:
+      {
+         Con::printf("%i: OP_ITER_END stk=-1", ip - 1);
+         break;
+      }
 
-         default:
-            Con::printf("%i: !!INVALID!!", ip - 1);
-            break;
+      default:
+         Con::printf("%i: !!INVALID!!", ip - 1);
+         break;
       }
    }
 

+ 25 - 6
Engine/source/console/codeBlock.h

@@ -23,19 +23,36 @@
 #ifndef _CODEBLOCK_H_
 #define _CODEBLOCK_H_
 
+#include <vector>
+#include <unordered_map>
+
+struct CompilerLocalVariableToRegisterMappingTable
+{
+   struct RemappingTable
+   {
+      std::vector<StringTableEntry> varList;
+   };
+
+   std::unordered_map<StringTableEntry, RemappingTable> localVarToRegister;
+
+   void add(StringTableEntry functionName, StringTableEntry namespaceName, StringTableEntry varName);
+   S32 lookup(StringTableEntry namespaceName, StringTableEntry functionName, StringTableEntry varName);
+   CompilerLocalVariableToRegisterMappingTable copy();
+   void reset();
+   void write(Stream& stream);
+};
+
 #include "console/compiler.h"
 #include "console/consoleParser.h"
 
 class Stream;
 class ConsoleValue;
-class ConsoleValueRef;
 
 /// Core TorqueScript code management class.
 ///
 /// This class represents a block of code, usually mapped directly to a file.
 class CodeBlock
 {
-   friend class CodeInterpreter;
 private:
    static CodeBlock* smCodeBlockList;
    static CodeBlock* smCurrentCodeBlock;
@@ -78,6 +95,8 @@ public:
    U32 codeSize;
    U32 *code;
 
+   CompilerLocalVariableToRegisterMappingTable variableRegisterTable;
+
    U32 refCount;
    U32 lineBreakPairCount;
    U32 *lineBreakPairs;
@@ -130,7 +149,7 @@ public:
    /// with, zero being the top of the stack. If the the index is
    /// -1 a new frame is created. If the index is out of range the
    /// top stack frame is used.
-   ConsoleValueRef compileExec(StringTableEntry fileName, const char *script,
+   ConsoleValue compileExec(StringTableEntry fileName, const char *script,
       bool noCalls, S32 setFrame = -1);
 
    /// Executes the existing code in the CodeBlock. The return string is any 
@@ -148,9 +167,9 @@ public:
    /// -1 a new frame is created. If the index is out of range the
    /// top stack frame is used.
    /// @param packageName The code package name or null.
-   ConsoleValueRef exec(U32 offset, const char *fnName, Namespace *ns, U32 argc,
-      ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName,
+   ConsoleValue exec(U32 offset, const char *fnName, Namespace *ns, U32 argc,
+      ConsoleValue *argv, bool noCalls, StringTableEntry packageName,
       S32 setFrame = -1);
 };
 
-#endif
+#endif

+ 0 - 3018
Engine/source/console/codeInterpreter.cpp

@@ -1,3018 +0,0 @@
-//-----------------------------------------------------------------------------
-// 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.
-//-----------------------------------------------------------------------------
-
-//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
-// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
-// Copyright (C) 2015 Faust Logic, Inc.
-//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
-
-#include "console/codeInterpreter.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/strings/findMatch.h"
-#include "core/strings/stringUnit.h"
-#include "console/console.h"
-#include "console/consoleInternal.h"
-#include "cinterface/cinterface.h"
-
-//#define TORQUE_VALIDATE_STACK
-
-using namespace Compiler;
-
-enum EvalConstants
-{
-   MaxStackSize = 1024,
-   FieldBufferSizeString = 2048,
-   FieldBufferSizeNumeric = 128,
-   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;
-}
-
-// 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"), 128);
-
-      else if (subField == xyzw[1] || subField == rgba[1])
-         dStrcpy(val, StringUnit::getUnit(prevVal, 1, " \t\n"), 128);
-
-      else if (subField == xyzw[2] || subField == rgba[2])
-         dStrcpy(val, StringUnit::getUnit(prevVal, 2, " \t\n"), 128);
-
-      else if (subField == xyzw[3] || subField == rgba[3])
-         dStrcpy(val, StringUnit::getUnit(prevVal, 3, " \t\n"), 128);
-
-      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"), 128);
-
-   else if (subField == xyzw[1] || subField == rgba[1])
-      dStrcpy(val, StringUnit::setUnit(prevVal, 1, strValue, " \t\n"), 128);
-
-   else if (subField == xyzw[2] || subField == rgba[2])
-      dStrcpy(val, StringUnit::setUnit(prevVal, 2, strValue, " \t\n"), 128);
-
-   else if (subField == xyzw[3] || subField == rgba[3])
-      dStrcpy(val, StringUnit::setUnit(prevVal, 3, strValue, " \t\n"), 128);
-
-   if (val[0] != 0)
-   {
-      // Update the field or variable.
-      if (object && field)
-         object->setDataField(field, 0, val);
-      else if (gEvalState.currentVariable)
-         gEvalState.setStringVariable(val);
-   }
-}
-extern ExprEvalState gEvalState;
-
-char sTraceBuffer[1024];
-
-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.
-
-IterStackRecord iterStack[MaxStackSize];
-
-F64 floatStack[MaxStackSize];
-S64 intStack[MaxStackSize];
-
-char curFieldArray[256];
-char prevFieldArray[256];
-
-typedef OPCodeReturn(CodeInterpreter::*OpFn)(U32&);
-
-static OpFn gOpCodeArray[MAX_OP_CODELEN];
-
-CodeInterpreter::CodeInterpreter(CodeBlock *cb) :
-   mCodeBlock(cb),
-   mIterDepth(0),
-   mCurFloatTable(nullptr),
-   mCurStringTable(nullptr),
-   mThisFunctionName(nullptr),
-   mPopFrame(false),
-   mObjectCreationStackIndex(0),
-   mCurrentNewObject(nullptr),
-   mFailJump(0),
-   mPrevField(nullptr),
-   mCurField(nullptr),
-   mPrevObject(nullptr),
-   mCurObject(nullptr),
-   mSaveObject(nullptr),
-   mThisObject(nullptr),
-   mNSEntry(nullptr),
-   mCurFNDocBlock(nullptr),
-   mCurNSDocBlock(nullptr),
-   mCallArgc(0),
-   mCallArgv(nullptr),
-   mSaveCodeBlock(nullptr),
-   mCurrentInstruction(0)
-{
-   dMemset(&mExec, 0, sizeof(mExec));
-   dMemset(&mObjectCreationStack, 0, sizeof(mObjectCreationStack));
-   dMemset(&mNSDocBlockClass, 0, sizeof(mNSDocBlockClass));
-}
-
-CodeInterpreter::~CodeInterpreter()
-{
-}
-
-void CodeInterpreter::init()
-{
-   gOpCodeArray[OP_FUNC_DECL] = &CodeInterpreter::op_func_decl;
-   gOpCodeArray[OP_CREATE_OBJECT] = &CodeInterpreter::op_create_object;
-   gOpCodeArray[OP_ADD_OBJECT] = &CodeInterpreter::op_add_object;
-   gOpCodeArray[OP_END_OBJECT] = &CodeInterpreter::op_end_object;
-   gOpCodeArray[OP_FINISH_OBJECT] = &CodeInterpreter::op_finish_object;
-   gOpCodeArray[OP_JMPIFFNOT] = &CodeInterpreter::op_jmpiffnot;
-   gOpCodeArray[OP_JMPIFNOT] = &CodeInterpreter::op_jmpifnot;
-   gOpCodeArray[OP_JMPIFF] = &CodeInterpreter::op_jmpiff;
-   gOpCodeArray[OP_JMPIF] = &CodeInterpreter::op_jmpif;
-   gOpCodeArray[OP_JMPIFNOT_NP] = &CodeInterpreter::op_jmpifnot_np;
-   gOpCodeArray[OP_JMPIF_NP] = &CodeInterpreter::op_jmpif_np;
-   gOpCodeArray[OP_JMP] = &CodeInterpreter::op_jmp;
-   gOpCodeArray[OP_RETURN] = &CodeInterpreter::op_return;
-   gOpCodeArray[OP_RETURN_VOID] = &CodeInterpreter::op_return_void;
-   gOpCodeArray[OP_RETURN_FLT] = &CodeInterpreter::op_return_flt;
-   gOpCodeArray[OP_RETURN_UINT] = &CodeInterpreter::op_return_uint;
-   gOpCodeArray[OP_CMPEQ] = &CodeInterpreter::op_cmpeq;
-   gOpCodeArray[OP_CMPGR] = &CodeInterpreter::op_cmpgr;
-   gOpCodeArray[OP_CMPGE] = &CodeInterpreter::op_cmpge;
-   gOpCodeArray[OP_CMPLT] = &CodeInterpreter::op_cmplt;
-   gOpCodeArray[OP_CMPLE] = &CodeInterpreter::op_cmple;
-   gOpCodeArray[OP_CMPNE] = &CodeInterpreter::op_cmpne;
-   gOpCodeArray[OP_XOR] = &CodeInterpreter::op_xor;
-   gOpCodeArray[OP_MOD] = &CodeInterpreter::op_mod;
-   gOpCodeArray[OP_BITAND] = &CodeInterpreter::op_bitand;
-   gOpCodeArray[OP_BITOR] = &CodeInterpreter::op_bitor;
-   gOpCodeArray[OP_NOT] = &CodeInterpreter::op_not;
-   gOpCodeArray[OP_NOTF] = &CodeInterpreter::op_notf;
-   gOpCodeArray[OP_ONESCOMPLEMENT] = &CodeInterpreter::op_onescomplement;
-   gOpCodeArray[OP_SHR] = &CodeInterpreter::op_shr;
-   gOpCodeArray[OP_SHL] = &CodeInterpreter::op_shl;
-   gOpCodeArray[OP_AND] = &CodeInterpreter::op_and;
-   gOpCodeArray[OP_OR] = &CodeInterpreter::op_or;
-   gOpCodeArray[OP_ADD] = &CodeInterpreter::op_add;
-   gOpCodeArray[OP_SUB] = &CodeInterpreter::op_sub;
-   gOpCodeArray[OP_MUL] = &CodeInterpreter::op_mul;
-   gOpCodeArray[OP_DIV] = &CodeInterpreter::op_div;
-   gOpCodeArray[OP_NEG] = &CodeInterpreter::op_neg;
-   gOpCodeArray[OP_INC] = &CodeInterpreter::op_inc;
-   gOpCodeArray[OP_DEC] = &CodeInterpreter::op_dec;
-   gOpCodeArray[OP_SETCURVAR] = &CodeInterpreter::op_setcurvar;
-   gOpCodeArray[OP_SETCURVAR_CREATE] = &CodeInterpreter::op_setcurvar_create;
-   gOpCodeArray[OP_SETCURVAR_ARRAY] = &CodeInterpreter::op_setcurvar_array;
-   gOpCodeArray[OP_SETCURVAR_ARRAY_VARLOOKUP] = &CodeInterpreter::op_setcurvar_array_varlookup;
-   gOpCodeArray[OP_SETCURVAR_ARRAY_CREATE] = &CodeInterpreter::op_setcurvar_array_create;
-   gOpCodeArray[OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP] = &CodeInterpreter::op_setcurvar_array_create_varlookup;
-   gOpCodeArray[OP_LOADVAR_UINT] = &CodeInterpreter::op_loadvar_uint;
-   gOpCodeArray[OP_LOADVAR_FLT] = &CodeInterpreter::op_loadvar_flt;
-   gOpCodeArray[OP_LOADVAR_STR] = &CodeInterpreter::op_loadvar_str;
-   gOpCodeArray[OP_LOADVAR_VAR] = &CodeInterpreter::op_loadvar_var;
-   gOpCodeArray[OP_SAVEVAR_UINT] = &CodeInterpreter::op_savevar_uint;
-   gOpCodeArray[OP_SAVEVAR_FLT] = &CodeInterpreter::op_savevar_flt;
-   gOpCodeArray[OP_SAVEVAR_STR] = &CodeInterpreter::op_savevar_str;
-   gOpCodeArray[OP_SAVEVAR_VAR] = &CodeInterpreter::op_savevar_var;
-   gOpCodeArray[OP_SETCUROBJECT] = &CodeInterpreter::op_setcurobject;
-   gOpCodeArray[OP_SETCUROBJECT_INTERNAL] = &CodeInterpreter::op_setcurobject_internal;
-   gOpCodeArray[OP_SETCUROBJECT_NEW] = &CodeInterpreter::op_setcurobject_new;
-   gOpCodeArray[OP_SETCURFIELD] = &CodeInterpreter::op_setcurfield;
-   gOpCodeArray[OP_SETCURFIELD_ARRAY] = &CodeInterpreter::op_setcurfield_array;
-   gOpCodeArray[OP_SETCURFIELD_TYPE] = &CodeInterpreter::op_setcurfield_type;
-   gOpCodeArray[OP_SETCURFIELD_ARRAY_VAR] = &CodeInterpreter::op_setcurfield_array_var;
-   gOpCodeArray[OP_SETCURFIELD_THIS] = &CodeInterpreter::op_setcurfield_this;
-   gOpCodeArray[OP_LOADFIELD_UINT] = &CodeInterpreter::op_loadfield_uint;
-   gOpCodeArray[OP_LOADFIELD_FLT] = &CodeInterpreter::op_loadfield_flt;
-   gOpCodeArray[OP_LOADFIELD_STR] = &CodeInterpreter::op_loadfield_str;
-   gOpCodeArray[OP_SAVEFIELD_UINT] = &CodeInterpreter::op_savefield_uint;
-   gOpCodeArray[OP_SAVEFIELD_FLT] = &CodeInterpreter::op_savefield_flt;
-   gOpCodeArray[OP_SAVEFIELD_STR] = &CodeInterpreter::op_savefield_str;
-   gOpCodeArray[OP_STR_TO_UINT] = &CodeInterpreter::op_str_to_uint;
-   gOpCodeArray[OP_STR_TO_FLT] = &CodeInterpreter::op_str_to_flt;
-   gOpCodeArray[OP_STR_TO_NONE] = &CodeInterpreter::op_str_to_none;
-   gOpCodeArray[OP_FLT_TO_UINT] = &CodeInterpreter::op_flt_to_uint;
-   gOpCodeArray[OP_FLT_TO_STR] = &CodeInterpreter::op_flt_to_str;
-   gOpCodeArray[OP_FLT_TO_NONE] = &CodeInterpreter::op_flt_to_none;
-   gOpCodeArray[OP_UINT_TO_FLT] = &CodeInterpreter::op_uint_to_flt;
-   gOpCodeArray[OP_UINT_TO_STR] = &CodeInterpreter::op_uint_to_str;
-   gOpCodeArray[OP_UINT_TO_NONE] = &CodeInterpreter::op_uint_to_none;
-   gOpCodeArray[OP_COPYVAR_TO_NONE] = &CodeInterpreter::op_copyvar_to_none;
-   gOpCodeArray[OP_LOADIMMED_UINT] = &CodeInterpreter::op_loadimmed_uint;
-   gOpCodeArray[OP_LOADIMMED_FLT] = &CodeInterpreter::op_loadimmed_flt;
-   gOpCodeArray[OP_TAG_TO_STR] = &CodeInterpreter::op_tag_to_str;
-   gOpCodeArray[OP_LOADIMMED_STR] = &CodeInterpreter::op_loadimmed_str;
-   gOpCodeArray[OP_DOCBLOCK_STR] = &CodeInterpreter::op_docblock_str;
-   gOpCodeArray[OP_LOADIMMED_IDENT] = &CodeInterpreter::op_loadimmed_ident;
-   gOpCodeArray[OP_CALLFUNC_RESOLVE] = &CodeInterpreter::op_callfunc_resolve;
-   gOpCodeArray[OP_CALLFUNC] = &CodeInterpreter::op_callfunc;
-   gOpCodeArray[OP_CALLFUNC_POINTER] = &CodeInterpreter::op_callfunc_pointer;
-   gOpCodeArray[OP_CALLFUNC_THIS] = &CodeInterpreter::op_callfunc_this;
-   gOpCodeArray[OP_ADVANCE_STR] = &CodeInterpreter::op_advance_str;
-   gOpCodeArray[OP_ADVANCE_STR_APPENDCHAR] = &CodeInterpreter::op_advance_str_appendchar;
-   gOpCodeArray[OP_ADVANCE_STR_COMMA] = &CodeInterpreter::op_advance_str_comma;
-   gOpCodeArray[OP_ADVANCE_STR_NUL] = &CodeInterpreter::op_advance_str_nul;
-   gOpCodeArray[OP_REWIND_STR] = &CodeInterpreter::op_rewind_str;
-   gOpCodeArray[OP_TERMINATE_REWIND_STR] = &CodeInterpreter::op_terminate_rewind_str;
-   gOpCodeArray[OP_COMPARE_STR] = &CodeInterpreter::op_compare_str;
-   gOpCodeArray[OP_PUSH] = &CodeInterpreter::op_push;
-   gOpCodeArray[OP_PUSH_UINT] = &CodeInterpreter::op_push_uint;
-   gOpCodeArray[OP_PUSH_FLT] = &CodeInterpreter::op_push_flt;
-   gOpCodeArray[OP_PUSH_VAR] = &CodeInterpreter::op_push_var;
-   gOpCodeArray[OP_PUSH_THIS] = &CodeInterpreter::op_push_this;
-   gOpCodeArray[OP_PUSH_FRAME] = &CodeInterpreter::op_push_frame;
-   gOpCodeArray[OP_ASSERT] = &CodeInterpreter::op_assert;
-   gOpCodeArray[OP_BREAK] = &CodeInterpreter::op_break;
-   gOpCodeArray[OP_ITER_BEGIN_STR] = &CodeInterpreter::op_iter_begin_str;
-   gOpCodeArray[OP_ITER_BEGIN] = &CodeInterpreter::op_iter_begin;
-   gOpCodeArray[OP_ITER] = &CodeInterpreter::op_iter;
-   gOpCodeArray[OP_ITER_END] = &CodeInterpreter::op_iter_end;
-   gOpCodeArray[OP_INVALID] = &CodeInterpreter::op_invalid;
-}
-
-ConsoleValueRef CodeInterpreter::exec(U32 ip,
-                                      StringTableEntry functionName,
-                                      Namespace *thisNamespace,
-                                      U32 argc, 
-                                      ConsoleValueRef *argv,
-                                      bool noCalls,
-                                      StringTableEntry packageName,
-                                      S32 setFrame) 
-{
-   mExec.functionName = functionName;
-   mExec.thisNamespace = thisNamespace;
-   mExec.argc = argc;
-   mExec.argv = argv;
-   mExec.noCalls = noCalls;
-   mExec.packageName = packageName;
-   mExec.setFrame = setFrame;
-
-   mCodeBlock->incRefCount();
-
-   mPopFrame = false;
-
-#ifdef TORQUE_VALIDATE_STACK
-   U32 stackStart = STR.mStartStackSize;
-#endif
-
-   STR.clearFunctionOffset(); // ensures arg buffer offset is back to 0
-
-   // Lets load up our function arguments.
-   parseArgs(ip);
-
-   // 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();
-
-   mSaveCodeBlock = CodeBlock::smCurrentCodeBlock;
-   CodeBlock::smCurrentCodeBlock = mCodeBlock;
-   if (mCodeBlock->name)
-   {
-      Con::gCurrentFile = mCodeBlock->name;
-      Con::gCurrentRoot = mCodeBlock->modPath;
-   }
-
-   U32 *code = mCodeBlock->code;
-
-   while (true)
-   {
-      mCurrentInstruction = code[ip++];
-      mNSEntry = nullptr;
-
-#ifdef TORQUE_VALIDATE_STACK
-      // OP Code check.
-      AssertFatal(mCurrentInstruction < MAX_OP_CODELEN, "Invalid OP code in script interpreter");
-#endif
-
-   breakContinueLabel:
-      OPCodeReturn ret = (this->*gOpCodeArray[mCurrentInstruction])(ip);
-      if (ret == OPCodeReturn::exitCode)
-         break;
-      else if (ret == OPCodeReturn::breakContinue)
-         goto breakContinueLabel;
-   }
-
-   if (telDebuggerOn && setFrame < 0)
-      TelDebugger->popStackFrame();
-
-   if (mPopFrame)
-      gEvalState.popFrame();
-
-   if (argv)
-   {
-      if (gEvalState.traceOn)
-      {
-         sTraceBuffer[0] = 0;
-         dStrcat(sTraceBuffer, "Leaving ", 1024);
-
-         if (packageName)
-         {
-            dStrcat(sTraceBuffer, "[", 1024);
-            dStrcat(sTraceBuffer, packageName, 1024);
-            dStrcat(sTraceBuffer, "]", 1024);
-         }
-         if (thisNamespace && thisNamespace->mName)
-         {
-            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
-               "%s::%s() - return %s", thisNamespace->mName, mThisFunctionName, STR.getStringValue());
-         }
-         else
-         {
-            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
-               "%s() - return %s", mThisFunctionName, STR.getStringValue());
-         }
-         Con::printf("%s", sTraceBuffer);
-      }
-   }
-
-   CodeBlock::smCurrentCodeBlock = mSaveCodeBlock;
-   if (mSaveCodeBlock && mSaveCodeBlock->name)
-   {
-      Con::gCurrentFile = mSaveCodeBlock->name;
-      Con::gCurrentRoot = mSaveCodeBlock->modPath;
-   }
-
-   mCodeBlock->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 mReturnValue;
-}
-
-void CodeInterpreter::parseArgs(U32 &ip)
-{
-   U32 *code = mCodeBlock->code;
-
-   if (mExec.argv)
-   {
-      U32 fnArgc = code[ip + 2 + 6];
-      mThisFunctionName = Compiler::CodeToSTE(code, ip);
-      S32 wantedArgc = getMin(mExec.argc - 1, fnArgc); // argv[0] is func name
-      if (gEvalState.traceOn)
-      {
-         sTraceBuffer[0] = 0;
-         dStrcat(sTraceBuffer, "Entering ", 1024);
-
-         if (mExec.packageName)
-         {
-            dStrcat(sTraceBuffer, "[", 1024);
-            dStrcat(sTraceBuffer, mExec.packageName, 1024);
-            dStrcat(sTraceBuffer, "]", 1024);
-         }
-         if (mExec.thisNamespace && mExec.thisNamespace->mName)
-         {
-            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
-               "%s::%s(", mExec.thisNamespace->mName, mThisFunctionName);
-         }
-         else
-         {
-            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
-               "%s(", mThisFunctionName);
-         }
-         for (S32 i = 0; i < wantedArgc; i++)
-         {
-            dStrcat(sTraceBuffer, mExec.argv[i + 1], 1024);
-            if (i != wantedArgc - 1)
-               dStrcat(sTraceBuffer, ", ", 1024);
-         }
-         dStrcat(sTraceBuffer, ")", 1024);
-         Con::printf("%s", sTraceBuffer);
-      }
-
-      gEvalState.pushFrame(mThisFunctionName, mExec.thisNamespace);
-      mPopFrame = true;
-
-      StringTableEntry thisPointer = StringTable->insert("%this");
-
-      for (S32 i = 0; i < wantedArgc; i++)
-      {
-         StringTableEntry var = Compiler::CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2));
-         gEvalState.setCurVarNameCreate(var);
-
-         ConsoleValueRef ref = mExec.argv[i + 1];
-
-         switch (ref.getType())
-         {
-         case ConsoleValue::TypeInternalInt:
-            gEvalState.setIntVariable(ref);
-            break;
-         case ConsoleValue::TypeInternalFloat:
-            gEvalState.setFloatVariable(ref);
-            break;
-         case ConsoleValue::TypeInternalStringStackPtr:
-            gEvalState.setStringStackPtrVariable(ref.getStringStackPtrValue());
-            break;
-         case ConsoleValue::TypeInternalStackString:
-         case ConsoleValue::TypeInternalString:
-         default:
-            gEvalState.setStringVariable(ref);
-            break;
-         }
-
-         if (var == thisPointer)
-         {
-            // %this gets optimized as it is flagged as a constant.
-            // Since it is guarenteed to be constant, we can then perform optimizations.
-            gEvalState.currentVariable->mIsConstant = true;
-
-            // Store a reference to the this pointer object.
-            mThisObject = Sim::findObject(gEvalState.getStringVariable());
-         }
-      }
-
-      ip = ip + (fnArgc * 2) + (2 + 6 + 1);
-      mCurFloatTable = mCodeBlock->functionFloats;
-      mCurStringTable = mCodeBlock->functionStrings;
-   }
-   else
-   {
-      mCurFloatTable = mCodeBlock->globalFloats;
-      mCurStringTable = mCodeBlock->globalStrings;
-
-      // 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() <= mExec.setFrame)
-         mExec.setFrame = -1;
-
-      // Do we want this code to execute using a new stack frame?
-      if (mExec.setFrame < 0)
-      {
-         gEvalState.pushFrame(NULL, NULL);
-         mPopFrame = 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() - mExec.setFrame - 1;
-         gEvalState.pushFrameRef(stackIndex);
-         mPopFrame = true;
-      }
-   }
-}
-
-OPCodeReturn CodeInterpreter::op_func_decl(U32 &ip)
-{
-   U32 *code = mCodeBlock->code;
-
-   if (!mExec.noCalls)
-   {
-      StringTableEntry fnName = CodeToSTE(code, ip);
-      StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
-      StringTableEntry fnPackage = CodeToSTE(code, ip + 4);
-      bool hasBody = (code[ip + 6] & 0x01) != 0;
-      U32 lineNumber = code[ip + 6] >> 1;
-
-      Namespace::unlinkPackages();
-      Namespace *ns = Namespace::find(fnNamespace, fnPackage);
-      ns->addFunction(fnName, mCodeBlock, hasBody ? ip : 0, mCurFNDocBlock ? dStrdup(mCurFNDocBlock) : NULL, lineNumber);// if no body, set the IP to 0
-      if (mCurNSDocBlock)
-      {
-         // If we have a docblock before we declare the function in the script file,
-         // this will attempt to set the doc block to the function.
-         // See OP_DOCBLOCK_STR
-         if (fnNamespace == StringTable->lookup(mNSDocBlockClass))
-         {
-            char *usageStr = dStrdup(mCurNSDocBlock);
-            usageStr[dStrlen(usageStr)] = '\0';
-            ns->mUsage = usageStr;
-            ns->mCleanUpUsage = true;
-            mCurNSDocBlock = NULL;
-         }
-      }
-      Namespace::relinkPackages();
-
-      // If we had a docblock, it's definitely not valid anymore, so clear it out.
-      mCurFNDocBlock = NULL;
-
-      //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip);
-   }
-   ip = code[ip + 7];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_create_object(U32 &ip)
-{
-   U32 *code = mCodeBlock->code;
-
-   // Read some useful info.
-   StringTableEntry objParent = CodeToSTE(code, ip);
-   bool isDataBlock = code[ip + 2];
-   bool isInternal = code[ip + 3];
-   bool isSingleton = code[ip + 4];
-   U32  lineNumber = code[ip + 5];
-   mFailJump = 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 (mExec.noCalls)
-   {
-      ip = mFailJump;
-      return OPCodeReturn::success;
-   }
-
-   // Push the old info to the stack
-   //Assert( objectCreationStackIndex < objectCreationStackSize );
-   mObjectCreationStack[mObjectCreationStackIndex].newObject = mCurrentNewObject;
-   mObjectCreationStack[mObjectCreationStackIndex++].failJump = mFailJump;
-
-   // Get the constructor information off the stack.
-   CSTK.getArgcArgv(NULL, &mCallArgc, &mCallArgv);
-   const char *objectName = mCallArgv[2];
-
-   // Con::printf("Creating object...");
-
-   // objectName = argv[1]...
-   mCurrentNewObject = 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(), mCallArgv[1]))
-      {
-         Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", mCodeBlock->getFileLine(ip), objectName);
-         ip = mFailJump;
-         STR.popFrame();
-         CSTK.popFrame();
-         return OPCodeReturn::success;
-      }
-
-      // If there was one, set the currentNewObject and move on.
-      if (db)
-         mCurrentNewObject = 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.",
-            mCodeBlock->getFileLine(ip), objectName);
-         ip = mFailJump;
-         STR.popFrame();
-         CSTK.popFrame();
-         return OPCodeReturn::success;
-      }
-
-      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*)mCallArgv[1]) != 0)
-            {
-               Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].",
-                  mCodeBlock->getFileLine(ip), objectName, (const char*)mCallArgv[1], obj->getClassName());
-               ip = mFailJump;
-               STR.popFrame();
-               CSTK.popFrame();
-               return OPCodeReturn::success;
-            }
-
-            // We're creating a singleton, so use the found object
-            // instead of creating a new object.
-            mCurrentNewObject = 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< mCallArgc; i++) {
-                  savedArgv[i] = mCallArgv[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<mCallArgc; i++) {
-                  mCallArgv[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].",
-                     mCodeBlock->getFileLine(ip), newName.c_str());
-                  ip = mFailJump;
-                  STR.popFrame();
-                  CSTK.popFrame();
-                  return OPCodeReturn::success;
-               }
-               else
-                  objectName = StringTable->insert(newName);
-            }
-            else
-            {
-               Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].",
-                  mCodeBlock->getFileLine(ip), objectName);
-               ip = mFailJump;
-               STR.popFrame();
-               CSTK.popFrame();
-               return OPCodeReturn::success;
-            }
-         }
-      }
-   }
-
-   STR.popFrame();
-   CSTK.popFrame();
-
-   if (!mCurrentNewObject)
-   {
-      // Well, looks like we have to create a new object.
-      ConsoleObject *object = ConsoleObject::create((const char*)mCallArgv[1]);
-
-      // Deal with failure!
-      if (!object)
-      {
-         Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
-         ip = mFailJump;
-         return OPCodeReturn::success;
-      }
-
-      // 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.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
-            // Clean up...
-            delete object;
-            mCurrentNewObject = NULL;
-            ip = mFailJump;
-            return OPCodeReturn::success;
-         }
-      }
-
-      // Finally, set currentNewObject to point to the new one.
-      mCurrentNewObject = dynamic_cast<SimObject *>(object);
-
-      // Deal with the case of a non-SimObject.
-      if (!mCurrentNewObject)
-      {
-         Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
-         delete object;
-         mCurrentNewObject = NULL;
-         ip = mFailJump;
-         return OPCodeReturn::success;
-      }
-
-      // Set the declaration line
-      mCurrentNewObject->setDeclarationLine(lineNumber);
-
-      // Set the file that this object was created in
-      mCurrentNewObject->setFilename(mCodeBlock->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());
-
-            mCurrentNewObject->setCopySource(parent);
-            mCurrentNewObject->assignFieldsFrom(parent);
-
-            // copy any substitution statements
-            SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
-            if (parent_db)
-            {
-               SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(mCurrentNewObject);
-               if (currentNewObject_db)
-                  currentNewObject_db->copySubstitutionsFrom(parent_db);
-            }
-         }
-         else
-         {
-            if (Con::gObjectCopyFailures == -1)
-               Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", mCodeBlock->getFileLine(ip), objParent, (const char*)mCallArgv[1]);
-            else
-               ++Con::gObjectCopyFailures;
-
-            // Fail to create the object.
-            delete object;
-            mCurrentNewObject = NULL;
-            ip = mFailJump;
-            return OPCodeReturn::success;
-         }
-      }
-
-      // If a name was passed, assign it.
-      if (objectName[0])
-      {
-         if (!isInternal)
-            mCurrentNewObject->assignName(objectName);
-         else
-            mCurrentNewObject->setInternalName(objectName);
-
-         // Set the original name
-         mCurrentNewObject->setOriginalName(objectName);
-      }
-
-      // Prevent stack value corruption
-      CSTK.pushFrame();
-      STR.pushFrame();
-      // --
-
-      // Do the constructor parameters.
-      if (!mCurrentNewObject->processArguments(mCallArgc - 3, mCallArgv + 3))
-      {
-         delete mCurrentNewObject;
-         mCurrentNewObject = NULL;
-         ip = mFailJump;
-
-         // Prevent stack value corruption
-         CSTK.popFrame();
-         STR.popFrame();
-         // --
-         return OPCodeReturn::success;
-      }
-
-      // Prevent stack value corruption
-      CSTK.popFrame();
-      STR.popFrame();
-      // --
-
-      // If it's not a datablock, allow people to modify bits of it.
-      if (!isDataBlock)
-      {
-         mCurrentNewObject->setModStaticFields(true);
-         mCurrentNewObject->setModDynamicFields(true);
-      }
-   }
-   else
-   {
-      mCurrentNewObject->reloadReset(); // AFX (reload-reset)
-                                       // 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());
-
-            // temporarily block name change
-            SimObject::preventNameChanging = true;
-            mCurrentNewObject->setCopySource(parent);
-            mCurrentNewObject->assignFieldsFrom(parent);
-            // restore name changing
-            SimObject::preventNameChanging = false;
-
-            // copy any substitution statements
-            SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
-            if (parent_db)
-            {
-               SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(mCurrentNewObject);
-               if (currentNewObject_db)
-                  currentNewObject_db->copySubstitutionsFrom(parent_db);
-            }
-         }
-         else
-            Con::errorf(ConsoleLogEntry::General, "%d: Unable to find parent object %s for %s.", lineNumber, objParent, (const char*)mCallArgv[1]);
-      }
-   }
-
-   // Advance the IP past the create info...
-   ip += 7;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_add_object(U32 &ip)
-{
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   // Do we place this object at the root?
-   bool placeAtRoot = mCodeBlock->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 (mCurrentNewObject->isProperlyAdded() == false)
-   {
-      bool ret = false;
-
-      Message *msg = dynamic_cast<Message *>(mCurrentNewObject);
-      if (msg)
-      {
-         SimObjectId id = Message::getNextMessageID();
-         if (id != 0xffffffff)
-            ret = mCurrentNewObject->registerObject(id);
-         else
-            Con::errorf("%s: No more object IDs available for messages", mCodeBlock->getFileLine(ip));
-      }
-      else
-         ret = mCurrentNewObject->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.", mCodeBlock->getFileLine(ip), mCurrentNewObject->getName(), mCurrentNewObject->getClassName());
-         delete mCurrentNewObject;
-         mCurrentNewObject = NULL;
-         ip = mFailJump;
-         // Prevent stack value corruption
-         CSTK.popFrame();
-         STR.popFrame();
-         // --
-         return OPCodeReturn::success;
-      }
-   }
-
-   // Are we dealing with a datablock?
-   SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(mCurrentNewObject);
-   static String errorStr;
-
-   // If so, preload it.
-   if (dataBlock && !dataBlock->preload(true, errorStr))
-   {
-      Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", mCodeBlock->getFileLine(ip),
-         mCurrentNewObject->getName(), errorStr.c_str());
-      dataBlock->deleteObject();
-      mCurrentNewObject = NULL;
-      ip = mFailJump;
-
-      // Prevent stack value corruption
-      CSTK.popFrame();
-      STR.popFrame();
-      // --
-      return OPCodeReturn::success;
-   }
-
-   // What group will we be added to, if any?
-   U32 groupAddId = intStack[_UINT];
-   SimGroup *grp = NULL;
-   SimSet   *set = NULL;
-   bool isMessage = dynamic_cast<Message *>(mCurrentNewObject) != NULL;
-
-   if (!placeAtRoot || !mCurrentNewObject->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(mCurrentNewObject);
-
-      // If for some reason the add failed, add the object to the
-      // root group so it won't leak.
-      if (!mCurrentNewObject->getGroup())
-         Sim::getRootGroup()->addObject(mCurrentNewObject);
-
-      // add to any set we might be in
-      if (set)
-         set->addObject(mCurrentNewObject);
-   }
-
-   // 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] = mCurrentNewObject->getId();
-   else
-      intStack[++_UINT] = mCurrentNewObject->getId();
-
-   // Prevent stack value corruption
-   CSTK.popFrame();
-   STR.popFrame();
-   // --
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_end_object(U32 &ip)
-{
-   // If we're not to be placed at the root, make sure we clean up
-   // our group reference.
-   bool placeAtRoot = mCodeBlock->code[ip++];
-   if (!placeAtRoot)
-      _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_finish_object(U32 &ip)
-{
-   if (mCurrentNewObject)
-      mCurrentNewObject->onPostAdd();
-
-   //Assert( objectCreationStackIndex >= 0 );
-   // Restore the object info from the stack [7/9/2007 Black]
-   mCurrentNewObject = mObjectCreationStack[--mObjectCreationStackIndex].newObject;
-   mFailJump = mObjectCreationStack[mObjectCreationStackIndex].failJump;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmpiffnot(U32 &ip)
-{
-   if (floatStack[_FLT--])
-   {
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-
-OPCodeReturn CodeInterpreter::op_jmpifnot(U32 &ip)
-{
-   if (intStack[_UINT--])
-   {
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmpiff(U32 &ip)
-{
-   if (!floatStack[_FLT--])
-   {
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmpif(U32 &ip)
-{
-   if (!intStack[_UINT--])
-   {
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmpifnot_np(U32 &ip)
-{
-   if (intStack[_UINT])
-   {
-      _UINT--;
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmpif_np(U32 &ip)
-{
-   if (!intStack[_UINT])
-   {
-      _UINT--;
-      ip++;
-      return OPCodeReturn::success;
-   }
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_jmp(U32 &ip)
-{
-   ip = mCodeBlock->code[ip];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_return_void(U32 &ip)
-{
-   STR.setStringValue("");
-   // We're falling thru here on purpose.
-
-   OPCodeReturn ret = op_return(ip);
-
-   return ret;
-}
-
-OPCodeReturn CodeInterpreter::op_return(U32 &ip)
-{
-   StringStackPtr retValue = STR.getStringValuePtr();
-
-   if (mIterDepth > 0)
-   {
-      // Clear iterator state.
-      while (mIterDepth > 0)
-      {
-         iterStack[--_ITER].mIsStringIter = false;
-         --mIterDepth;
-      }
-
-      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 
-   mReturnValue.value = CSTK.pushStringStackPtr(retValue);
-
-   return OPCodeReturn::exitCode;
-}
-
-OPCodeReturn CodeInterpreter::op_return_flt(U32 &ip)
-{
-   if (mIterDepth > 0)
-   {
-      // Clear iterator state.
-      while (mIterDepth > 0)
-      {
-         iterStack[--_ITER].mIsStringIter = false;
-         --mIterDepth;
-      }
-
-   }
-
-   mReturnValue.value = CSTK.pushFLT(floatStack[_FLT]);
-   _FLT--;
-
-   return OPCodeReturn::exitCode;
-}
-
-OPCodeReturn CodeInterpreter::op_return_uint(U32 &ip)
-{
-   if (mIterDepth > 0)
-   {
-      // Clear iterator state.
-      while (mIterDepth > 0)
-      {
-         iterStack[--_ITER].mIsStringIter = false;
-         --mIterDepth;
-      }
-   }
-
-   mReturnValue.value = CSTK.pushUINT(intStack[_UINT]);
-   _UINT--;
-
-   return OPCodeReturn::exitCode;
-}
-
-OPCodeReturn CodeInterpreter::op_cmpeq(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] == floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_cmpgr(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] > floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_cmpge(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] >= floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_cmplt(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] < floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_cmple(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] <= floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_cmpne(U32 &ip)
-{
-   intStack[_UINT + 1] = bool(floatStack[_FLT] != floatStack[_FLT - 1]);
-   _UINT++;
-   _FLT -= 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_xor(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] ^ intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_mod(U32 &ip)
-{
-   if (intStack[_UINT - 1] != 0)
-      intStack[_UINT - 1] = intStack[_UINT] % intStack[_UINT - 1];
-   else
-      intStack[_UINT - 1] = 0;
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_bitand(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] & intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_bitor(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] | intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_not(U32 &ip)
-{
-   intStack[_UINT] = !intStack[_UINT];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_notf(U32 &ip)
-{
-   intStack[_UINT + 1] = !floatStack[_FLT];
-   _FLT--;
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_onescomplement(U32 &ip)
-{
-   intStack[_UINT] = ~intStack[_UINT];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_shr(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] >> intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_shl(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] << intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_and(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] && intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_or(U32 &ip)
-{
-   intStack[_UINT - 1] = intStack[_UINT] || intStack[_UINT - 1];
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_add(U32 &ip)
-{
-   floatStack[_FLT - 1] = floatStack[_FLT] + floatStack[_FLT - 1];
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_sub(U32 &ip)
-{
-   floatStack[_FLT - 1] = floatStack[_FLT] - floatStack[_FLT - 1];
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_mul(U32 &ip)
-{
-   floatStack[_FLT - 1] = floatStack[_FLT] * floatStack[_FLT - 1];
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_div(U32 &ip)
-{
-   floatStack[_FLT - 1] = floatStack[_FLT] / floatStack[_FLT - 1];
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_neg(U32 &ip)
-{
-   floatStack[_FLT] = -floatStack[_FLT];
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_inc(U32 &ip)
-{
-   StringTableEntry var = CodeToSTE(mCodeBlock->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.
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarNameCreate(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.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   F64 val = gEvalState.getFloatVariable() + 1.0;
-   gEvalState.setFloatVariable(val);
-
-   // We gotta push val onto the stack. What if we have
-   // more expressions that have to use this.
-   // If we don't, we send out an op code to pop it.
-   floatStack[_FLT + 1] = val;
-   _FLT++;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_dec(U32 &ip)
-{
-   StringTableEntry var = CodeToSTE(mCodeBlock->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.
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarNameCreate(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.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   F64 val = gEvalState.getFloatVariable() - 1.0;
-   gEvalState.setFloatVariable(val);
-
-   // We gotta push val onto the stack. What if we have
-   // more expressions that have to use this.
-   // If we don't, we send out an op code to pop it.
-   floatStack[_FLT + 1] = val;
-   _FLT++;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar(U32 &ip)
-{
-   StringTableEntry var = CodeToSTE(mCodeBlock->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.
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = 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.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar_create(U32 &ip)
-{
-   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
-   ip += 2;
-
-   // See OP_SETCURVAR
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarNameCreate(var);
-
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar_array(U32 &ip)
-{
-   StringTableEntry var = STR.getSTValue();
-
-   // See OP_SETCURVAR
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarName(var);
-
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar_array_varlookup(U32 &ip)
-{
-   StringTableEntry arrayName = CodeToSTE(mCodeBlock->code, ip);
-   StringTableEntry arrayLookup = CodeToSTE(mCodeBlock->code, ip + 2);
-   ip += 4;
-
-   STR.setStringValue(arrayName);
-   STR.advance();
-
-   // See OP_SETCURVAR
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   // resolve arrayLookup to get the 'value'
-   // Note: we have to setCurVarNameCreate in case the var doesn't exist.
-   // this won't cause much of a performance hit since vars are hashed.
-   gEvalState.setCurVarNameCreate(arrayLookup);
-   StringTableEntry hash = gEvalState.getStringVariable();
-
-   STR.setStringValue(hash);
-   STR.rewind();
-
-   // Generate new array name.
-   StringTableEntry var = STR.getSTValue();
-   gEvalState.setCurVarName(var);
-
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar_array_create(U32 &ip)
-{
-   StringTableEntry var = STR.getSTValue();
-
-   // See OP_SETCURVAR
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarNameCreate(var);
-
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurvar_array_create_varlookup(U32 &ip)
-{
-   StringTableEntry arrayName = CodeToSTE(mCodeBlock->code, ip);
-   StringTableEntry arrayLookup = CodeToSTE(mCodeBlock->code, ip + 2);
-   ip += 4;
-
-   // See OP_SETCURVAR
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   STR.setStringValue(arrayName);
-   STR.advance();
-
-   // resolve arrayLookup to get the 'value'
-   // Note: we have to setCurVarNameCreate in case the var doesn't exist.
-   // this won't cause much of a performance hit since vars are hashed.
-   gEvalState.setCurVarNameCreate(arrayLookup);
-   StringTableEntry hash = gEvalState.getStringVariable();
-
-   STR.setStringValue(hash);
-   STR.rewind();
-
-   // Generate new array name.
-   StringTableEntry var = STR.getSTValue();
-   gEvalState.setCurVarNameCreate(var);
-
-   // See OP_SETCURVAR for why we do this.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadvar_uint(U32 &ip)
-{
-   intStack[_UINT + 1] = gEvalState.getIntVariable();
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadvar_flt(U32 &ip)
-{
-   floatStack[_FLT + 1] = gEvalState.getFloatVariable();
-   _FLT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadvar_str(U32 &ip)
-{
-   StringTableEntry val = gEvalState.getStringVariable();
-   STR.setStringValue(val);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadvar_var(U32 &ip)
-{
-   // Sets current source of OP_SAVEVAR_VAR
-   gEvalState.copyVariable = gEvalState.currentVariable;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savevar_uint(U32 &ip)
-{
-   gEvalState.setIntVariable(intStack[_UINT]);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savevar_flt(U32 &ip)
-{
-   gEvalState.setFloatVariable(floatStack[_FLT]);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savevar_str(U32 &ip)
-{
-   gEvalState.setStringVariable(STR.getStringValue());
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savevar_var(U32 &ip)
-{
-   // this basically handles %var1 = %var2
-   gEvalState.setCopyVariable();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurobject(U32 &ip)
-{
-   // Save the previous object for parsing vector fields.
-   mPrevObject = mCurObject;
-   StringTableEntry 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;
-      }
-   }
-   mCurObject = Sim::findObject(val);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurobject_internal(U32 &ip)
-{
-   ++ip; // To skip the recurse flag if the object wasn't found
-   if (mCurObject)
-   {
-      SimSet *set = dynamic_cast<SimSet *>(mCurObject);
-      if (set)
-      {
-         StringTableEntry intName = StringTable->insert(STR.getStringValue());
-         bool recurse = mCodeBlock->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.", mCodeBlock->getFileLine(ip - 2), mCurObject->getName(), mCurObject->getClassName());
-         intStack[_UINT] = 0;
-      }
-   }
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurobject_new(U32 &ip)
-{
-   mCurObject = mCurrentNewObject;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurfield(U32 &ip)
-{
-   // Save the previous field for parsing vector fields.
-   mPrevField = mCurField;
-   dStrcpy(prevFieldArray, curFieldArray, 256);
-   mCurField = CodeToSTE(mCodeBlock->code, ip);
-   curFieldArray[0] = 0;
-   ip += 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurfield_array(U32 &ip)
-{
-   dStrcpy(curFieldArray, STR.getStringValue(), 256);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurfield_type(U32 &ip)
-{
-   if (mCurObject)
-      mCurObject->setDataFieldType(mCodeBlock->code[ip], mCurField, curFieldArray);
-   ip++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurfield_array_var(U32 &ip)
-{
-   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
-   ip += 2;
-
-   // We set the current var name (create it as well in case if it doesn't exist,
-   // otherwise we will crash).
-   gEvalState.setCurVarNameCreate(var);
-
-   // Then load the var and copy the contents to the current field array
-   dStrncpy(curFieldArray, gEvalState.currentVariable->getStringValue(), sizeof(curFieldArray));
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_setcurfield_this(U32 &ip)
-{
-   // set the 'this pointer' as the current object.
-   mCurObject = mThisObject;
-
-   mPrevField = mCurField;
-   dStrcpy(prevFieldArray, curFieldArray, 256);
-   mCurField = CodeToSTE(mCodeBlock->code, ip);
-   curFieldArray[0] = 0;
-   ip += 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadfield_uint(U32 &ip)
-{
-   if (mCurObject)
-      intStack[_UINT + 1] = U32(dAtoi(mCurObject->getDataField(mCurField, curFieldArray)));
-   else
-   {
-      // The field is not being retrieved from an object. Maybe it's
-      // a special accessor?
-      char buff[FieldBufferSizeNumeric];
-      memset(buff, 0, sizeof(buff));
-      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
-      intStack[_UINT + 1] = dAtoi(buff);
-   }
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadfield_flt(U32 &ip)
-{
-   if (mCurObject)
-      floatStack[_FLT + 1] = dAtof(mCurObject->getDataField(mCurField, curFieldArray));
-   else
-   {
-      // The field is not being retrieved from an object. Maybe it's
-      // a special accessor?
-      char buff[FieldBufferSizeNumeric];
-      memset(buff, 0, sizeof(buff));
-      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
-      floatStack[_FLT + 1] = dAtof(buff);
-   }
-   _FLT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadfield_str(U32 &ip)
-{
-   if (mCurObject)
-   {
-      StringTableEntry val = mCurObject->getDataField(mCurField, curFieldArray);
-      STR.setStringValue(val);
-   }
-   else
-   {
-      // The field is not being retrieved from an object. Maybe it's
-      // a special accessor?
-      char buff[FieldBufferSizeString];
-      memset(buff, 0, sizeof(buff));
-      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
-      STR.setStringValue(buff);
-   }
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savefield_uint(U32 &ip)
-{
-   STR.setIntValue(intStack[_UINT]);
-   if (mCurObject)
-      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
-   else
-   {
-      // The field is not being set on an object. Maybe it's
-      // a special accessor?
-      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
-      mPrevObject = NULL;
-   }
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savefield_flt(U32 &ip)
-{
-   STR.setFloatValue(floatStack[_FLT]);
-   if (mCurObject)
-      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
-   else
-   {
-      // The field is not being set on an object. Maybe it's
-      // a special accessor?
-      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
-      mPrevObject = NULL;
-   }
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_savefield_str(U32 &ip)
-{
-   if (mCurObject)
-      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
-   else
-   {
-      // The field is not being set on an object. Maybe it's
-      // a special accessor?
-      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
-      mPrevObject = NULL;
-   }
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_str_to_uint(U32 &ip)
-{
-   intStack[_UINT + 1] = STR.getIntValue();
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_str_to_flt(U32 &ip)
-{
-   floatStack[_FLT + 1] = STR.getFloatValue();
-   _FLT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_str_to_none(U32 &ip)
-{
-   // This exists simply to deal with certain typecast situations.
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_flt_to_uint(U32 &ip)
-{
-   intStack[_UINT + 1] = (S64)floatStack[_FLT];
-   _FLT--;
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_flt_to_str(U32 &ip)
-{
-   STR.setFloatValue(floatStack[_FLT]);
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_flt_to_none(U32 &ip)
-{
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_uint_to_flt(U32 &ip)
-{
-   floatStack[_FLT + 1] = (F32)intStack[_UINT];
-   _UINT--;
-   _FLT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_uint_to_str(U32 &ip)
-{
-   STR.setIntValue(intStack[_UINT]);
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_uint_to_none(U32 &ip)
-{
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_copyvar_to_none(U32 &ip)
-{
-   gEvalState.copyVariable = NULL;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadimmed_uint(U32 &ip)
-{
-   intStack[_UINT + 1] = mCodeBlock->code[ip++];
-   _UINT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadimmed_flt(U32 &ip)
-{
-   floatStack[_FLT + 1] = mCurFloatTable[mCodeBlock->code[ip]];
-   ip++;
-   _FLT++;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_tag_to_str(U32 &ip)
-{
-   mCodeBlock->code[ip - 1] = OP_LOADIMMED_STR;
-   // it's possible the string has already been converted
-   if (U8(mCurStringTable[mCodeBlock->code[ip]]) != StringTagPrefixByte)
-   {
-      U32 id = GameAddTaggedString(mCurStringTable + mCodeBlock->code[ip]);
-      dSprintf(mCurStringTable + mCodeBlock->code[ip] + 1, 7, "%d", id);
-      *(mCurStringTable + mCodeBlock->code[ip]) = StringTagPrefixByte;
-   }
-
-   // Fallthrough
-   OPCodeReturn ret = op_loadimmed_str(ip);
-
-   return ret;
-}
-
-OPCodeReturn CodeInterpreter::op_loadimmed_str(U32 &ip)
-{
-   STR.setStringValue(mCurStringTable + mCodeBlock->code[ip++]);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_docblock_str(U32 &ip)
-{
-   // 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 = mCurStringTable + mCodeBlock->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)))
-      {
-         mNSDocBlockClass[index++] = *sansClass;
-         sansClass++;
-      }
-      mNSDocBlockClass[index] = '\0';
-
-      mCurNSDocBlock = sansClass + 1;
-   }
-   else
-      mCurFNDocBlock = docblock;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_loadimmed_ident(U32 &ip)
-{
-   STR.setStringValue(CodeToSTE(mCodeBlock->code, ip));
-   ip += 2;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_callfunc_resolve(U32 &ip)
-{
-   // This deals with a function that is potentially living in a namespace.
-   StringTableEntry fnNamespace = CodeToSTE(mCodeBlock->code, ip + 2);
-   StringTableEntry fnName = CodeToSTE(mCodeBlock->code, ip);
-
-   // Try to look it up.
-   mNSEntry = Namespace::find(fnNamespace)->lookup(fnName);
-   if (!CInterface::GetCInterface().isMethod(fnNamespace, fnName) && !mNSEntry)
-   {
-      ip += 5;
-      Con::warnf(ConsoleLogEntry::General,
-         "%s: Unable to find function %s%s%s",
-         mCodeBlock->getFileLine(ip - 7), fnNamespace ? fnNamespace : "",
-         fnNamespace ? "::" : "", fnName);
-      STR.popFrame();
-      CSTK.popFrame();
-      return OPCodeReturn::success;
-   }
-
-   // Fallthrough to op_callfunc_resolve
-   OPCodeReturn ret = op_callfunc(ip);
-
-   return ret;
-}
-
-OPCodeReturn CodeInterpreter::op_callfunc(U32 &ip)
-{
-   // 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;
-
-   U32 *code = mCodeBlock->code;
-
-   StringTableEntry fnNamespace = CodeToSTE(mCodeBlock->code, ip + 2);
-   StringTableEntry fnName = CodeToSTE(code, ip);
-
-   //if this is called from inside a function, append the ip and codeptr
-   if (gEvalState.getStackDepth() > 0)
-   {
-      gEvalState.getCurrentFrame().code = mCodeBlock;
-      gEvalState.getCurrentFrame().ip = ip - 1;
-   }
-
-   U32 callType = code[ip + 4];
-
-   ip += 5;
-   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
-
-   const char *componentReturnValue = "";
-   Namespace *ns = NULL;
-
-   bool cFunctionRes = false;
-   const char* cRetRes = NULL;
-
-   if (callType == FuncCallExprNode::FunctionCall)
-   {
-      if (!mNSEntry)
-         mNSEntry = Namespace::global()->lookup(fnName);
-
-      StringStackWrapper args(mCallArgc, mCallArgv);
-      cRetRes = CInterface::CallFunction(fnNamespace, fnName, args.argv + 1, args.argc - 1, &cFunctionRes);
-   }
-   else if (callType == FuncCallExprNode::MethodCall)
-   {
-      mSaveObject = gEvalState.thisObject;
-      gEvalState.thisObject = Sim::findObject((const char*)mCallArgv[1]);
-      if (!gEvalState.thisObject)
-      {
-         // Go back to the previous saved object.
-         gEvalState.thisObject = mSaveObject;
-
-         Con::warnf(ConsoleLogEntry::General, "%s: Unable to find object: '%s' attempting to call function '%s'", mCodeBlock->getFileLine(ip - 4), (const char*)mCallArgv[1], fnName);
-         STR.popFrame();
-         CSTK.popFrame();
-         STR.setStringValue("");
-         return OPCodeReturn::success;
-      }
-
-      bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName, &routingId);
-      if (handlesMethod && routingId == MethodOnComponent)
-      {
-         ICallMethod *pComponent = dynamic_cast<ICallMethod *>(gEvalState.thisObject);
-         if (pComponent)
-            componentReturnValue = pComponent->callMethodArgList(mCallArgc, mCallArgv, false);
-      }
-
-      ns = gEvalState.thisObject->getNamespace();
-      if (ns)
-         mNSEntry = ns->lookup(fnName);
-      else
-         mNSEntry = NULL;
-
-      StringStackWrapper args(mCallArgc, mCallArgv);
-      cRetRes = CInterface::CallMethod(gEvalState.thisObject, fnName, args.argv + 2, args.argc - 2, &cFunctionRes);
-   }
-   else // it's a ParentCall
-   {
-      if (mExec.thisNamespace)
-      {
-         ns = mExec.thisNamespace->mParent;
-         if (ns)
-            mNSEntry = ns->lookup(fnName);
-         else
-            mNSEntry = NULL;
-      }
-      else
-      {
-         ns = NULL;
-         mNSEntry = NULL;
-      }
-   }
-
-   Namespace::Entry::CallbackUnion * nsCb = NULL;
-   const char * nsUsage = NULL;
-   if (mNSEntry)
-   {
-      nsCb = &mNSEntry->cb;
-      nsUsage = mNSEntry->mUsage;
-      routingId = 0;
-   }
-   if (!cFunctionRes && (!mNSEntry || mExec.noCalls))
-   {
-      if (!mExec.noCalls && !(routingId == MethodOnComponent))
-      {
-         if (callType == FuncCallExprNode::MethodCall)
-         {
-            if (gEvalState.thisObject != NULL)
-            {
-               // Try to use the name instead of the id
-               StringTableEntry name = gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : gEvalState.thisObject->getIdString();
-               Con::warnf(ConsoleLogEntry::General, "%s: Unknown method %s.%s Namespace List: %s", mCodeBlock->getFileLine(ip - 6), name, fnName, Con::getNamespaceList(ns));
-            }
-            else
-            {
-               // NULL.
-               Con::warnf(ConsoleLogEntry::General, "%s: Unknown method NULL.%s", mCodeBlock->getFileLine(ip - 6), fnName);
-            }
-         }
-         else if (callType == FuncCallExprNode::ParentCall)
-         {
-            Con::warnf(ConsoleLogEntry::General, "%s: Unknown parent call %s.", mCodeBlock->getFileLine(ip - 6), fnName);
-         }
-         else 
-         {
-            Con::warnf(ConsoleLogEntry::General, "%s: Unknown function %s.", mCodeBlock->getFileLine(ip - 6), fnName);
-         }
-      }
-      STR.popFrame();
-      CSTK.popFrame();
-
-      if (routingId == MethodOnComponent)
-         STR.setStringValue(componentReturnValue);
-      else
-         STR.setStringValue("");
-      return OPCodeReturn::success;
-   }
-
-   // ConsoleFunctionType is for any function defined by script.
-   // Any 'callback' type is an engine function that is exposed to script.
-   if (cFunctionRes || mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
-   {
-      ConsoleValue retVal;
-      ConsoleValueRef ret;
-      if (cFunctionRes)
-      {
-         retVal.init();
-         ret.value = &retVal;
-         retVal.setStackStringValue(cRetRes);
-      }
-      else if (mNSEntry->mFunctionOffset)
-      {
-         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->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 (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
-      {
-         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
-      }
-      else
-#endif
-      if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
-      {
-         Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
-            mCodeBlock->getFileLine(ip - 6), nsName, fnName,
-            mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
-         Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
-         STR.popFrame();
-         CSTK.popFrame();
-      }
-      else
-      {
-         switch (mNSEntry->mType)
-         {
-         case Namespace::Entry::StringCallbackType:
-         {
-            const char *ret = mNSEntry->cb.mStringCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-            STR.popFrame();
-            CSTK.popFrame();
-            if (ret != STR.getStringValue())
-               STR.setStringValue(ret);
-            //else
-            //   sSTR.setLen(dStrlen(ret));
-            break;
-         }
-         case Namespace::Entry::IntCallbackType:
-         {
-            S32 result = mNSEntry->cb.mIntCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-            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 = mNSEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-            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:
-            mNSEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-            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.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
-
-            STR.popFrame();
-            CSTK.popFrame();
-            STR.setStringValue("");
-            break;
-         case Namespace::Entry::BoolCallbackType:
-         {
-            bool result = mNSEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-            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 = mSaveObject;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_callfunc_pointer(U32 &ip)
-{
-   // get function name. This is the 'function pointer'.
-   StringTableEntry fnName = StringTable->insert(STR.getStringValue());
-
-   U32 *code = mCodeBlock->code;
-
-   mNSEntry = Namespace::global()->lookup(fnName);
-
-   //if this is called from inside a function, append the ip and codeptr
-   if (gEvalState.getStackDepth() > 0)
-   {
-      gEvalState.getCurrentFrame().code = mCodeBlock;
-      gEvalState.getCurrentFrame().ip = ip - 1;
-   }
-
-   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
-
-
-   if (!mNSEntry || mExec.noCalls)
-   {
-      if (!mExec.noCalls)
-      {
-         Con::warnf(ConsoleLogEntry::General, "%s: Unknown function %s.", mCodeBlock->getFileLine(ip - 6), fnName);
-      }
-      STR.popFrame();
-      CSTK.popFrame();
-
-      STR.setStringValue("");
-      return OPCodeReturn::success;
-   }
-
-   // ConsoleFunctionType is for any function defined by script.
-   // Any 'callback' type is an engine function that is exposed to script.
-   if (mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
-   {
-      ConsoleValueRef ret;
-      if (mNSEntry->mFunctionOffset)
-         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->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 = "";
-
-#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 (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
-      {
-         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
-      }
-      else
-#endif
-         if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
-         {
-            Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
-               mCodeBlock->getFileLine(ip - 6), nsName, fnName,
-               mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
-            Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
-            STR.popFrame();
-            CSTK.popFrame();
-         }
-         else
-         {
-            switch (mNSEntry->mType)
-            {
-            case Namespace::Entry::StringCallbackType:
-            {
-               const char *ret = mNSEntry->cb.mStringCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-               STR.popFrame();
-               CSTK.popFrame();
-               if (ret != STR.getStringValue())
-                  STR.setStringValue(ret);
-               //else
-               //   sSTR.setLen(dStrlen(ret));
-               break;
-            }
-            case Namespace::Entry::IntCallbackType:
-            {
-               S32 result = mNSEntry->cb.mIntCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-               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 = mNSEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-               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:
-               mNSEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-               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.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
-
-               STR.popFrame();
-               CSTK.popFrame();
-               STR.setStringValue("");
-               break;
-            case Namespace::Entry::BoolCallbackType:
-            {
-               bool result = mNSEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
-               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;
-            }
-            }
-         }
-   }
-
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_callfunc_this(U32 &ip)
-{
-   U32 *code = mCodeBlock->code;
-
-   StringTableEntry fnName = CodeToSTE(code, ip);
-
-   //if this is called from inside a function, append the ip and codeptr
-   if (gEvalState.getStackDepth() > 0)
-   {
-      gEvalState.getCurrentFrame().code = mCodeBlock;
-      gEvalState.getCurrentFrame().ip = ip - 1;
-   }
-
-   ip += 2;
-   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
-
-   Namespace *ns = mThisObject ? mThisObject->getNamespace() : NULL;
-   if (ns)
-      mNSEntry = ns->lookup(fnName);
-   else
-      mNSEntry = NULL;
-
-   if (!mNSEntry || mExec.noCalls)
-   {
-      if (!mExec.noCalls)
-      {
-         if (mThisObject)
-         {
-            // Try to use the name instead of the id
-            StringTableEntry name = mThisObject->getName() ? mThisObject->getName() : mThisObject->getIdString();
-            Con::warnf(ConsoleLogEntry::General, "%s: Unknown method %s.%s Namespace List: %s", mCodeBlock->getFileLine(ip - 6), name, fnName, Con::getNamespaceList(ns));
-         }
-         else
-         {
-            // At least let the scripter know that they access the object.
-            Con::warnf(ConsoleLogEntry::General, "%s: Unknown method NULL.%s", mCodeBlock->getFileLine(ip - 6), fnName);
-         }
-      }
-      STR.popFrame();
-      CSTK.popFrame();
-
-      STR.setStringValue("");
-      return OPCodeReturn::success;
-   }
-
-   // ConsoleFunctionType is for any function defined by script.
-   // Any 'callback' type is an engine function that is exposed to script.
-   if (mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
-   {
-      ConsoleValueRef ret;
-      if (mNSEntry->mFunctionOffset)
-         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->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 (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
-      {
-         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
-      }
-      else
-#endif
-         if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
-         {
-            Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
-               mCodeBlock->getFileLine(ip - 6), nsName, fnName,
-               mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
-            Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
-            STR.popFrame();
-            CSTK.popFrame();
-         }
-         else
-         {
-            switch (mNSEntry->mType)
-            {
-            case Namespace::Entry::StringCallbackType:
-            {
-               const char *ret = mNSEntry->cb.mStringCallbackFunc(mThisObject, mCallArgc, mCallArgv);
-               STR.popFrame();
-               CSTK.popFrame();
-               if (ret != STR.getStringValue())
-                  STR.setStringValue(ret);
-               //else
-               //   sSTR.setLen(dStrlen(ret));
-               break;
-            }
-            case Namespace::Entry::IntCallbackType:
-            {
-               S32 result = mNSEntry->cb.mIntCallbackFunc(mThisObject, mCallArgc, mCallArgv);
-               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 = mNSEntry->cb.mFloatCallbackFunc(mThisObject, mCallArgc, mCallArgv);
-               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:
-               mNSEntry->cb.mVoidCallbackFunc(mThisObject, mCallArgc, mCallArgv);
-               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.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
-
-               STR.popFrame();
-               CSTK.popFrame();
-               STR.setStringValue("");
-               break;
-            case Namespace::Entry::BoolCallbackType:
-            {
-               bool result = mNSEntry->cb.mBoolCallbackFunc(mThisObject, mCallArgc, mCallArgv);
-               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;
-            }
-            }
-         }
-   }
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_advance_str(U32 &ip)
-{
-   STR.advance();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_advance_str_appendchar(U32 &ip)
-{
-   STR.advanceChar(mCodeBlock->code[ip++]);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_advance_str_comma(U32 &ip)
-{
-   STR.advanceChar('_');
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_advance_str_nul(U32 &ip)
-{
-   STR.advanceChar(0);
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_rewind_str(U32 &ip)
-{
-   STR.rewind();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_terminate_rewind_str(U32 &ip)
-{
-   STR.rewindTerminate();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_compare_str(U32 &ip)
-{
-   intStack[++_UINT] = STR.compare();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push(U32 &ip)
-{
-   STR.push();
-   CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr());
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push_uint(U32 &ip)
-{
-   CSTK.pushUINT(intStack[_UINT]);
-   _UINT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push_flt(U32 &ip)
-{
-   CSTK.pushFLT(floatStack[_FLT]);
-   _FLT--;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push_var(U32 &ip)
-{
-   if (gEvalState.currentVariable)
-      CSTK.pushValue(gEvalState.currentVariable->value);
-   else
-      CSTK.pushString("");
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push_this(U32 &ip)
-{
-   StringTableEntry varName = CodeToSTE(mCodeBlock->code, ip);
-   ip += 2;
-
-   // shorthand OP_SETCURVAR
-
-   // 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.
-   mPrevField = NULL;
-   mPrevObject = NULL;
-   mCurObject = NULL;
-
-   gEvalState.setCurVarName(varName);
-
-   // 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.
-   mCurFNDocBlock = NULL;
-   mCurNSDocBlock = NULL;
-
-   // shorthand OP_LOADVAR_STR (since objs can be by name we can't assume uint)
-   STR.setStringValue(gEvalState.getStringVariable());
-
-   // shorthand OP_PUSH
-   STR.push();
-   CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr());
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_push_frame(U32 &ip)
-{
-   STR.pushFrame();
-   CSTK.pushFrame();
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_assert(U32 &ip)
-{
-   if (!intStack[_UINT--])
-   {
-      const char *message = mCurStringTable + mCodeBlock->code[ip];
-
-      U32 breakLine, inst;
-      mCodeBlock->findBreakLine(ip - 1, breakLine, inst);
-
-      if (PlatformAssert::processAssert(PlatformAssert::Fatal,
-         mCodeBlock->name ? mCodeBlock->name : "eval",
-         breakLine,
-         message))
-      {
-         if (TelDebugger && TelDebugger->isConnected() && breakLine > 0)
-         {
-            TelDebugger->breakProcess();
-         }
-         else
-            Platform::debugBreak();
-      }
-   }
-
-   ip++;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_break(U32 &ip)
-{
-   //append the ip and codeptr before managing the breakpoint!
-   AssertFatal(gEvalState.getStackDepth() > 0, "Empty eval stack on break!");
-   gEvalState.getCurrentFrame().code = mCodeBlock;
-   gEvalState.getCurrentFrame().ip = ip - 1;
-
-   U32 breakLine;
-   mCodeBlock->findBreakLine(ip - 1, breakLine, mCurrentInstruction);
-   if (!breakLine)
-      return OPCodeReturn::breakContinue;
-   TelDebugger->executionStopped(mCodeBlock, breakLine);
-   return OPCodeReturn::breakContinue;
-}
-
-OPCodeReturn CodeInterpreter::op_iter_begin_str(U32 &ip)
-{
-   iterStack[_ITER].mIsStringIter = true;
-
-   // Emulate fallthrough:
-   OPCodeReturn fallthrough = op_iter_begin(ip);
-
-   return fallthrough;
-}
-
-OPCodeReturn CodeInterpreter::op_iter_begin(U32 &ip)
-{
-   StringTableEntry varName = CodeToSTE(mCodeBlock->code, ip);
-   U32 failIp = mCodeBlock->code[ip + 2];
-
-   IterStackRecord& iter = iterStack[_ITER];
-
-   if (varName[0] == '$')
-      iter.mVariable = gEvalState.globalVars.add(varName);
-   else
-      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;
-         return OPCodeReturn::success;
-      }
-
-      // Set up.
-
-      iter.mData.mObj.mSet = set;
-      iter.mData.mObj.mIndex = 0;
-   }
-
-   _ITER++;
-   mIterDepth++;
-
-   STR.push();
-
-   ip += 3;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_iter(U32 &ip)
-{
-   U32 breakIp = mCodeBlock->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;
-         return OPCodeReturn::success; // continue in old interpreter
-      }
-
-      // 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;
-         return OPCodeReturn::success; // continue in old interpreter
-      }
-
-      iter.mVariable->setIntValue(set->at(index)->getId());
-      iter.mData.mObj.mIndex = index + 1;
-   }
-
-   ++ip;
-
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_iter_end(U32 &ip)
-{
-   --_ITER;
-   --mIterDepth;
-   STR.rewind();
-   iterStack[_ITER].mIsStringIter = false;
-   return OPCodeReturn::success;
-}
-
-OPCodeReturn CodeInterpreter::op_invalid(U32 &ip)
-{
-   // Invalid does nothing.
-   return OPCodeReturn::exitCode;
-}

+ 0 - 262
Engine/source/console/codeInterpreter.h

@@ -1,262 +0,0 @@
-//-----------------------------------------------------------------------------
-// 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.
-//-----------------------------------------------------------------------------
-
-#ifndef _CODEINTERPRETER_H_
-#define _CODEINTERPRETER_H_
-
-#include "console/codeBlock.h"
-#include "console/console.h"
-#include "console/consoleInternal.h"
-
-/// 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;
-};
-
-enum OPCodeReturn
-{
-   exitCode = -1,
-   success = 0,
-   breakContinue = 1
-};
-
-class CodeInterpreter
-{
-public:
-   CodeInterpreter(CodeBlock *cb);
-   ~CodeInterpreter();
-
-   ConsoleValueRef exec(U32 ip, 
-                        StringTableEntry functionName,
-                        Namespace *thisNamespace,
-                        U32 argc, 
-                        ConsoleValueRef *argv, 
-                        bool noCalls, 
-                        StringTableEntry packageName, 
-                        S32 setFrame);
-
-   static void init();
-
-   // Methods
-private:
-   void parseArgs(U32 &ip);
-
-   /// Group op codes
-   /// @{
-
-   OPCodeReturn op_func_decl(U32 &ip);
-   OPCodeReturn op_create_object(U32 &ip);
-   OPCodeReturn op_add_object(U32 &ip);
-   OPCodeReturn op_end_object(U32 &ip);
-   OPCodeReturn op_finish_object(U32 &ip);
-   OPCodeReturn op_jmpiffnot(U32 &ip);
-   OPCodeReturn op_jmpifnot(U32 &ip);
-   OPCodeReturn op_jmpiff(U32 &ip);
-   OPCodeReturn op_jmpif(U32 &ip);
-   OPCodeReturn op_jmpifnot_np(U32 &ip);
-   OPCodeReturn op_jmpif_np(U32 &ip);
-   OPCodeReturn op_jmp(U32 &ip);
-   OPCodeReturn op_return_void(U32 &ip);
-   OPCodeReturn op_return(U32 &ip);
-   OPCodeReturn op_return_flt(U32 &ip);
-   OPCodeReturn op_return_uint(U32 &ip);
-   OPCodeReturn op_cmpeq(U32 &ip);
-   OPCodeReturn op_cmpgr(U32 &ip);
-   OPCodeReturn op_cmpge(U32 &ip);
-   OPCodeReturn op_cmplt(U32 &ip);
-   OPCodeReturn op_cmple(U32 &ip);
-   OPCodeReturn op_cmpne(U32 &ip);
-   OPCodeReturn op_xor(U32 &ip);
-   OPCodeReturn op_mod(U32 &ip);
-   OPCodeReturn op_bitand(U32 &ip);
-   OPCodeReturn op_bitor(U32 &ip);
-   OPCodeReturn op_not(U32 &ip);
-   OPCodeReturn op_notf(U32 &ip);
-   OPCodeReturn op_onescomplement(U32 &ip);
-   OPCodeReturn op_shr(U32 &ip);
-   OPCodeReturn op_shl(U32 &ip);
-   OPCodeReturn op_and(U32 &ip);
-   OPCodeReturn op_or(U32 &ip);
-   OPCodeReturn op_add(U32 &ip);
-   OPCodeReturn op_sub(U32 &ip);
-   OPCodeReturn op_mul(U32 &ip);
-   OPCodeReturn op_div(U32 &ip);
-   OPCodeReturn op_neg(U32 &ip);
-   OPCodeReturn op_inc(U32 &ip);
-   OPCodeReturn op_dec(U32 &ip);
-   OPCodeReturn op_setcurvar(U32 &ip);
-   OPCodeReturn op_setcurvar_create(U32 &ip);
-   OPCodeReturn op_setcurvar_array(U32 &ip);
-   OPCodeReturn op_setcurvar_array_varlookup(U32 &ip);
-   OPCodeReturn op_setcurvar_array_create(U32 &ip);
-   OPCodeReturn op_setcurvar_array_create_varlookup(U32 &ip);
-   OPCodeReturn op_loadvar_uint(U32 &ip);
-   OPCodeReturn op_loadvar_flt(U32 &ip);
-   OPCodeReturn op_loadvar_str(U32 &ip);
-   OPCodeReturn op_loadvar_var(U32 &ip);
-   OPCodeReturn op_savevar_uint(U32 &ip);
-   OPCodeReturn op_savevar_flt(U32 &ip);
-   OPCodeReturn op_savevar_str(U32 &ip);
-   OPCodeReturn op_savevar_var(U32 &ip);
-   OPCodeReturn op_setcurobject(U32 &ip);
-   OPCodeReturn op_setcurobject_internal(U32 &ip);
-   OPCodeReturn op_setcurobject_new(U32 &ip);
-   OPCodeReturn op_setcurfield(U32 &ip);
-   OPCodeReturn op_setcurfield_array(U32 &ip);
-   OPCodeReturn op_setcurfield_type(U32 &ip);
-   OPCodeReturn op_setcurfield_this(U32 &ip);
-   OPCodeReturn op_setcurfield_array_var(U32 &ip);
-   OPCodeReturn op_loadfield_uint(U32 &ip);
-   OPCodeReturn op_loadfield_flt(U32 &ip);
-   OPCodeReturn op_loadfield_str(U32 &ip);
-   OPCodeReturn op_savefield_uint(U32 &ip);
-   OPCodeReturn op_savefield_flt(U32 &ip);
-   OPCodeReturn op_savefield_str(U32 &ip);
-   OPCodeReturn op_str_to_uint(U32 &ip);
-   OPCodeReturn op_str_to_flt(U32 &ip);
-   OPCodeReturn op_str_to_none(U32 &ip);
-   OPCodeReturn op_flt_to_uint(U32 &ip);
-   OPCodeReturn op_flt_to_str(U32 &ip);
-   OPCodeReturn op_flt_to_none(U32 &ip);
-   OPCodeReturn op_uint_to_flt(U32 &ip);
-   OPCodeReturn op_uint_to_str(U32 &ip);
-   OPCodeReturn op_uint_to_none(U32 &ip);
-   OPCodeReturn op_copyvar_to_none(U32 &ip);
-   OPCodeReturn op_loadimmed_uint(U32 &ip);
-   OPCodeReturn op_loadimmed_flt(U32 &ip);
-   OPCodeReturn op_tag_to_str(U32 &ip);
-   OPCodeReturn op_loadimmed_str(U32 &ip);
-   OPCodeReturn op_docblock_str(U32 &ip);
-   OPCodeReturn op_loadimmed_ident(U32 &ip);
-   OPCodeReturn op_callfunc_resolve(U32 &ip);
-   OPCodeReturn op_callfunc(U32 &ip);
-   OPCodeReturn op_callfunc_pointer(U32 &ip);
-   OPCodeReturn op_callfunc_this(U32 &ip);
-   OPCodeReturn op_advance_str(U32 &ip);
-   OPCodeReturn op_advance_str_appendchar(U32 &ip);
-   OPCodeReturn op_advance_str_comma(U32 &ip);
-   OPCodeReturn op_advance_str_nul(U32 &ip);
-   OPCodeReturn op_rewind_str(U32 &ip);
-   OPCodeReturn op_terminate_rewind_str(U32 &ip);
-   OPCodeReturn op_compare_str(U32 &ip);
-   OPCodeReturn op_push(U32 &ip);
-   OPCodeReturn op_push_uint(U32 &ip);
-   OPCodeReturn op_push_flt(U32 &ip);
-   OPCodeReturn op_push_var(U32 &ip);
-   OPCodeReturn op_push_this(U32 &ip);
-   OPCodeReturn op_push_frame(U32 &ip);
-   OPCodeReturn op_assert(U32 &ip);
-   OPCodeReturn op_break(U32 &ip);
-   OPCodeReturn op_iter_begin_str(U32 &ip);
-   OPCodeReturn op_iter_begin(U32 &ip);
-   OPCodeReturn op_iter(U32 &ip);
-   OPCodeReturn op_iter_end(U32 &ip);
-   OPCodeReturn op_invalid(U32 &ip);
-
-   /// @}
-
-private:
-   CodeBlock *mCodeBlock;
-
-   /// Group exec arguments.
-   struct
-   {
-      StringTableEntry functionName;
-      Namespace *thisNamespace;
-      U32 argc;
-      ConsoleValueRef *argv;
-      bool noCalls;
-      StringTableEntry packageName;
-      S32 setFrame;
-   } mExec;
-
-   U32 mIterDepth;
-   F64 *mCurFloatTable;
-   char *mCurStringTable;
-   StringTableEntry mThisFunctionName;
-   bool mPopFrame;
-
-   // Add local object creation stack [7/9/2007 Black]
-   static const U32 objectCreationStackSize = 32;
-   U32 mObjectCreationStackIndex;
-   struct 
-   {
-      SimObject *newObject;
-      U32 failJump;
-   } mObjectCreationStack[objectCreationStackSize];
-
-   SimObject *mCurrentNewObject;
-   U32 mFailJump;
-   StringTableEntry mPrevField;
-   StringTableEntry mCurField;
-   SimObject *mPrevObject;
-   SimObject *mCurObject;
-   SimObject *mSaveObject;
-   SimObject *mThisObject;
-   Namespace::Entry *mNSEntry;
-   StringTableEntry mCurFNDocBlock;
-   StringTableEntry mCurNSDocBlock;
-   U32 mCallArgc;
-   ConsoleValueRef *mCallArgv;
-   CodeBlock *mSaveCodeBlock;
-
-   // note: anything returned is pushed to CSTK and will be invalidated on the next exec()
-   ConsoleValueRef mReturnValue;
-
-   U32 mCurrentInstruction;
-
-   static const S32 nsDocLength = 128;
-   char mNSDocBlockClass[nsDocLength];
-};
-
-#endif

+ 2092 - 21
Engine/source/console/compiledEval.cpp

@@ -1,5 +1,7 @@
 //-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
+// Copyright (c) 2013 GarageGames, LLC
+// Copyright (c) 2015 Faust Logic, Inc.
+// Copyright (c) 2021 TGEMIT Authors & Contributors
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to
@@ -40,8 +42,8 @@
 #include "util/messaging/message.h"
 #include "core/frameAllocator.h"
 
-#include "console/codeInterpreter.h"
 #include "console/returnBuffer.h"
+#include "console/consoleValueStack.h"
 
 #ifndef TORQUE_TGB_ONLY
 #include "materials/materialDefinition.h"
@@ -50,13 +52,96 @@
 
 using namespace Compiler;
 
+enum EvalConstants
+{
+   MaxStackSize = 1024,
+   FieldBufferSizeString = 2048,
+   FieldBufferSizeNumeric = 128,
+   ConcatBufferInitialSize = 8192,
+   MethodOnComponent = -2
+};
+
+/// 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;
+
+   /// True if the variable referenced is a global
+   bool mIsGlobalVariable;
+
+   union
+   {
+
+      /// The iterator variable if we are a global variable
+      Dictionary::Entry* mVariable;
+
+      /// The register variable if we are a local variable
+      S32 mRegister;
+   } mVar;
+
+   /// 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.
+      const char* mString;
+
+      /// Current parsing position.
+      U32 mIndex;
+   };
+   union
+   {
+      ObjectPos mObj;
+      StringPos mStr;
+   } mData;
+};
+
+ConsoleValueStack<4096> gCallStack;
+
+StringStack STR;
+
+IterStackRecord iterStack[MaxStackSize];
+U32 _ITER = 0;    ///< Stack pointer for iterStack.
+
+ConsoleValue stack[MaxStackSize];
+S32 _STK = 0;
+
+char curFieldArray[256];
+char prevFieldArray[256];
+
+const char* tsconcat(const char* strA, const char* strB, S32& outputLen)
+{
+   S32 lenA = dStrlen(strA);
+   S32 lenB = dStrlen(strB);
+
+   S32 len = lenA + lenB + 1;
+
+   char* concatBuffer = (char*)dMalloc(len);
+
+   concatBuffer[len - 1] = '\0';
+   memcpy(concatBuffer, strA, lenA);
+   memcpy(concatBuffer + lenA, strB, lenB);
+
+   outputLen = lenA + lenB;
+   return concatBuffer;
+}
+
 namespace Con
 {
    // Current script file name and root, these are registered as
    // console variables.
    extern StringTableEntry gCurrentFile;
    extern StringTableEntry gCurrentRoot;
-   extern S32 gObjectCopyFailures;
 }
 
 namespace Con
@@ -79,6 +164,120 @@ namespace Con
    }
 }
 
+static void getFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[], S32 currentLocalRegister)
+{
+   const char* prevVal = NULL;
+
+   if (object && field)
+      prevVal = object->getDataField(field, array);
+   else if (currentLocalRegister != -1)
+      prevVal = gEvalState.getLocalStringVariable(currentLocalRegister);
+   else if (gEvalState.currentVariable)
+      prevVal = gEvalState.getStringVariable();
+
+   // 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"), 128);
+
+      else if (subField == xyzw[1] || subField == rgba[1])
+         dStrcpy(val, StringUnit::getUnit(prevVal, 1, " \t\n"), 128);
+
+      else if (subField == xyzw[2] || subField == rgba[2])
+         dStrcpy(val, StringUnit::getUnit(prevVal, 2, " \t\n"), 128);
+
+      else if (subField == xyzw[3] || subField == rgba[3])
+         dStrcpy(val, StringUnit::getUnit(prevVal, 3, " \t\n"), 128);
+
+      else
+         val[0] = 0;
+   }
+   else
+      val[0] = 0;
+}
+
+static void setFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, S32 currentLocalRegister)
+{
+   // Copy the current string value
+   char strValue[1024];
+   dStrncpy(strValue, stack[_STK].getString(), 1024);
+
+   char val[1024] = "";
+   const char* prevVal = NULL;
+
+   if (object && field)
+      prevVal = object->getDataField(field, array);
+   else if (currentLocalRegister != -1)
+      prevVal = gEvalState.getLocalStringVariable(currentLocalRegister);
+   // 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"), 128);
+
+   else if (subField == xyzw[1] || subField == rgba[1])
+      dStrcpy(val, StringUnit::setUnit(prevVal, 1, strValue, " \t\n"), 128);
+
+   else if (subField == xyzw[2] || subField == rgba[2])
+      dStrcpy(val, StringUnit::setUnit(prevVal, 2, strValue, " \t\n"), 128);
+
+   else if (subField == xyzw[3] || subField == rgba[3])
+      dStrcpy(val, StringUnit::setUnit(prevVal, 3, strValue, " \t\n"), 128);
+
+   if (val[0] != 0)
+   {
+      // Update the field or variable.
+      if (object && field)
+         object->setDataField(field, 0, val);
+      else if (currentLocalRegister != -1)
+         gEvalState.setLocalStringVariable(currentLocalRegister, val, dStrlen(val));
+      else if (gEvalState.currentVariable)
+         gEvalState.setStringVariable(val);
+   }
+}
+
 //------------------------------------------------------------
 
 F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line)
@@ -239,38 +438,1910 @@ void ExprEvalState::setStringVariable(const char *val)
    currentVariable->setStringValue(val);
 }
 
-void ExprEvalState::setStringStackPtrVariable(StringStackPtr str)
+//-----------------------------------------------------------------------------
+
+enum class FloatOperation
 {
-   AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!");
-   currentVariable->setStringStackPtrValue(str);
+   Add,
+   Sub,
+   Mul,
+   Div,
+
+   LT,
+   LE,
+   GR,
+   GE,
+   EQ,
+   NE
+};
+
+template<FloatOperation Op>
+TORQUE_NOINLINE void doSlowMathOp()
+{
+   ConsoleValue& a = stack[_STK];
+   ConsoleValue& b = stack[_STK - 1];
+
+   // Arithmetic
+   if constexpr (Op == FloatOperation::Add)
+      stack[_STK - 1].setFloat(a.getFloat() + b.getFloat());
+   else if constexpr (Op == FloatOperation::Sub)
+      stack[_STK - 1].setFloat(a.getFloat() - b.getFloat());
+   else if constexpr (Op == FloatOperation::Mul)
+      stack[_STK - 1].setFloat(a.getFloat() * b.getFloat());
+   else if constexpr (Op == FloatOperation::Div)
+      stack[_STK - 1].setFloat(a.getFloat() / b.getFloat());
+
+   // Logical
+   if constexpr (Op == FloatOperation::LT)
+      stack[_STK - 1].setInt(a.getFloat() < b.getFloat());
+   if constexpr (Op == FloatOperation::LE)
+      stack[_STK - 1].setInt(a.getFloat() <= b.getFloat());
+   if constexpr (Op == FloatOperation::GR)
+      stack[_STK - 1].setInt(a.getFloat() > b.getFloat());
+   if constexpr (Op == FloatOperation::GE)
+      stack[_STK - 1].setInt(a.getFloat() >= b.getFloat());
+   if constexpr (Op == FloatOperation::EQ)
+      stack[_STK - 1].setInt(a.getFloat() == b.getFloat());
+   if constexpr (Op == FloatOperation::NE)
+      stack[_STK - 1].setInt(a.getFloat() != b.getFloat());
+
+   _STK--;
+}
+
+template<FloatOperation Op>
+TORQUE_FORCEINLINE void doFloatMathOperation()
+{
+   ConsoleValue& a = stack[_STK];
+   ConsoleValue& b = stack[_STK - 1];
+
+   S32 fastIf = (a.getType() == ConsoleValueType::cvFloat) & (b.getType() == ConsoleValueType::cvFloat);
+   if (fastIf)
+   {
+      // Arithmetic
+      if constexpr (Op == FloatOperation::Add)
+         stack[_STK - 1].setFastFloat(a.getFastFloat() + b.getFastFloat());
+      if constexpr (Op == FloatOperation::Sub)
+         stack[_STK - 1].setFastFloat(a.getFastFloat() - b.getFastFloat());
+      if constexpr (Op == FloatOperation::Mul)
+         stack[_STK - 1].setFastFloat(a.getFastFloat() * b.getFastFloat());
+      if constexpr (Op == FloatOperation::Div)
+         stack[_STK - 1].setFastFloat(a.getFastFloat() / b.getFastFloat());
+
+      // Logical
+      if constexpr (Op == FloatOperation::LT)
+         stack[_STK - 1].setFastInt(a.getFastFloat() < b.getFastFloat());
+      if constexpr (Op == FloatOperation::LE)
+         stack[_STK - 1].setFastInt(a.getFastFloat() <= b.getFastFloat());
+      if constexpr (Op == FloatOperation::GR)
+         stack[_STK - 1].setFastInt(a.getFastFloat() > b.getFastFloat());
+      if constexpr (Op == FloatOperation::GE)
+         stack[_STK - 1].setFastInt(a.getFastFloat() >= b.getFastFloat());
+      if constexpr (Op == FloatOperation::EQ)
+         stack[_STK - 1].setFastInt(a.getFastFloat() == b.getFastFloat());
+      if constexpr (Op == FloatOperation::NE)
+         stack[_STK - 1].setFastInt(a.getFastFloat() != b.getFastFloat());
+
+      _STK--;
+   }
+   else
+   {
+      doSlowMathOp<Op>();
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+enum class IntegerOperation
+{
+   BitAnd,
+   BitOr,
+   Xor,
+   LShift,
+   RShift,
+
+   LogicalAnd,
+   LogicalOr
+};
+
+template<IntegerOperation Op>
+TORQUE_NOINLINE void doSlowIntegerOp()
+{
+   ConsoleValue& a = stack[_STK];
+   ConsoleValue& b = stack[_STK - 1];
+
+   // Bitwise Op
+   if constexpr (Op == IntegerOperation::BitAnd)
+      stack[_STK - 1].setInt(a.getInt() & b.getInt());
+   if constexpr (Op == IntegerOperation::BitOr)
+      stack[_STK - 1].setInt(a.getInt() | b.getInt());
+   if constexpr (Op == IntegerOperation::Xor)
+      stack[_STK - 1].setInt(a.getInt() ^ b.getInt());
+   if constexpr (Op == IntegerOperation::LShift)
+      stack[_STK - 1].setInt(a.getInt() << b.getInt());
+   if constexpr (Op == IntegerOperation::RShift)
+      stack[_STK - 1].setInt(a.getInt() >> b.getInt());
+
+   // Logical Op
+   if constexpr (Op == IntegerOperation::LogicalAnd)
+      stack[_STK - 1].setInt(a.getInt() && b.getInt());
+   if constexpr (Op == IntegerOperation::LogicalOr)
+      stack[_STK - 1].setInt(a.getInt() || b.getInt());
+
+   _STK--;
 }
 
-void ExprEvalState::setCopyVariable()
+template<IntegerOperation Op>
+TORQUE_FORCEINLINE void doIntOperation()
 {
-   if (copyVariable)
+   ConsoleValue& a = stack[_STK];
+   ConsoleValue& b = stack[_STK - 1];
+
+   if (a.isNumberType() && b.isNumberType())
    {
-      switch (copyVariable->value.type)
+      // Bitwise Op
+      if constexpr (Op == IntegerOperation::BitAnd)
+         stack[_STK - 1].setFastInt(a.getFastInt() & b.getFastInt());
+      if constexpr (Op == IntegerOperation::BitOr)
+         stack[_STK - 1].setFastInt(a.getFastInt() | b.getFastInt());
+      if constexpr (Op == IntegerOperation::Xor)
+         stack[_STK - 1].setFastInt(a.getFastInt() ^ b.getFastInt());
+      if constexpr (Op == IntegerOperation::LShift)
+         stack[_STK - 1].setFastInt(a.getFastInt() << b.getFastInt());
+      if constexpr (Op == IntegerOperation::RShift)
+         stack[_STK - 1].setFastInt(a.getFastInt() >> b.getFastInt());
+
+      // Logical Op
+      if constexpr (Op == IntegerOperation::LogicalAnd)
+         stack[_STK - 1].setFastInt(a.getFastInt() && b.getFastInt());
+      if constexpr (Op == IntegerOperation::LogicalOr)
+         stack[_STK - 1].setFastInt(a.getFastInt() || b.getFastInt());
+
+      _STK--;
+   }
+   else
+   {
+      doSlowIntegerOp<Op>();
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+U32 gExecCount = 0;
+ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNamespace, U32 argc, ConsoleValue* argv, bool noCalls, StringTableEntry packageName, S32 setFrame)
+{
+#ifdef TORQUE_DEBUG
+   U32 stackStart = _STK;
+   gExecCount++;
+#endif
+
+   const dsize_t TRACE_BUFFER_SIZE = 1024;
+   static char traceBuffer[TRACE_BUFFER_SIZE];
+   U32 i;
+
+   U32 iterDepth = 0;
+   ConsoleValue returnValue;
+
+   incRefCount();
+   F64* curFloatTable;
+   char* curStringTable;
+   S32 curStringTableLen = 0; //clint to ensure we dont overwrite it
+
+   StringTableEntry thisFunctionName = NULL;
+   bool popFrame = false;
+   if (argv)
+   {
+      // assume this points into a function decl:
+      U32 fnArgc = code[ip + 2 + 6];
+      U32 regCount = code[ip + 2 + 7];
+      thisFunctionName = CodeToSTE(code, ip);
+      S32 wantedArgc = getMin(argc - 1, fnArgc); // argv[0] is func name
+      if (gEvalState.traceOn)
+      {
+         traceBuffer[0] = 0;
+         dStrcat(traceBuffer, "Entering ", TRACE_BUFFER_SIZE);
+         if (packageName)
+         {
+            dStrcat(traceBuffer, "[", TRACE_BUFFER_SIZE);
+            dStrcat(traceBuffer, packageName, TRACE_BUFFER_SIZE);
+            dStrcat(traceBuffer, "]", TRACE_BUFFER_SIZE);
+         }
+         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].getString(), TRACE_BUFFER_SIZE);
+            if (i != wantedArgc - 1)
+               dStrcat(traceBuffer, ", ", TRACE_BUFFER_SIZE);
+         }
+         dStrcat(traceBuffer, ")", TRACE_BUFFER_SIZE);
+         Con::printf("%s", traceBuffer);
+      }
+      gEvalState.pushFrame(thisFunctionName, thisNamespace, regCount);
+      popFrame = true;
+      for (i = 0; i < wantedArgc; i++)
+      {
+         S32 reg = code[ip + (2 + 6 + 1 + 1) + i];
+         ConsoleValue& value = argv[i + 1];
+         gEvalState.moveConsoleValue(reg, std::move(value));
+      }
+      ip = ip + fnArgc + (2 + 6 + 1 + 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, 0);
+         gCallStack.pushFrame(0);
+         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.getTopOfStack() - 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;
+   U32 failJump;
+   StringTableEntry fnName;
+   StringTableEntry fnNamespace, fnPackage;
+
+   static const U32 objectCreationStackSize = 32;
+   U32 objectCreationStackIndex = 0;
+   struct {
+      SimObject* newObject;
+      U32 failJump;
+   } objectCreationStack[objectCreationStackSize];
+
+   SimObject* currentNewObject = 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];
+
+   S32 callArgc;
+   ConsoleValue* 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;
+   S32 reg;
+   S32 currentRegister = -1;
+
+   // 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++];
+   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();
+            if (fnNamespace == NULL && fnPackage == NULL)
+               ns = Namespace::global();
+            else
+               ns = Namespace::find(fnNamespace, fnPackage);
+            ns->addFunction(fnName, this, hasBody ? ip : 0);// 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.
+         gCallStack.argvc(NULL, callArgc, &callArgv);
+         AssertFatal(callArgc - 3 >= 0, avar("Call Arg needs at least 3, only has %d", callArgc));
+         const char* objectName = callArgv[2].getString();
+
+         // 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].getString()))
+            {
+               Con::errorf(ConsoleLogEntry::General, "Cannot re-declare data block %s with a different class.", objectName);
+               ip = failJump;
+               gCallStack.popFrame();
+               break;
+            }
+
+            // If there was one, set the currentNewObject and move on.
+            if (db)
+               currentNewObject = db;
+         }
+         else if (!isInternal)
+         {
+            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;
+               gCallStack.popFrame();
+               break;
+            }
+
+            SimObject* obj = Sim::findObject((const char*)objectName);
+            if (obj)
+            {
+               if (isSingleton)
+               {
+                  // Make sure we're not trying to change types
+                  if (dStricmp(obj->getClassName(), callArgv[1].getString()) != 0)
+                  {
+                     Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].",
+                        getFileLine(ip), objectName, callArgv[1].getString(), obj->getClassName());
+                     ip = failJump;
+                     gCallStack.popFrame();
+                     break;
+                  }
+
+                  // We're creating a singleton, so use the found object instead of creating a new object.
+                  currentNewObject = obj;
+                  Con::warnf("%s: Singleton Object was already created with name %s. Using existing object.",
+                     getFileLine(ip), objectName);
+               }
+            }
+         }
+
+         gCallStack.popFrame();
+
+         if (!currentNewObject)
+         {
+            // Well, looks like we have to create a new object.
+            ConsoleObject* object = ConsoleObject::create(callArgv[1].getString());
+
+            // Deal with failure!
+            if (!object)
+            {
+               Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine(ip - 1), callArgv[1].getString());
+               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 - 1), callArgv[1].getString());
+
+                  // Clean up...
+                  delete object;
+                  currentNewObject = NULL;
+                  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 - 1), callArgv[1].getString());
+               delete object;
+               ip = failJump;
+               break;
+            }
+
+            // Set the declaration line
+            currentNewObject->setDeclarationLine(lineNumber);
+
+            // Set the file that this object was created in
+            currentNewObject->setFilename(this->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);
+
+                  // copy any substitution statements
+                  SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
+                  if (parent_db)
+                  {
+                     SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(currentNewObject);
+                     if (currentNewObject_db)
+                        currentNewObject_db->copySubstitutionsFrom(parent_db);
+                  }
+               }
+               else
+               {
+                  Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip - 1), objParent, callArgv[1].getString());
+                  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 );
+            }
+
+            // Do the constructor parameters.
+            if (!currentNewObject->processArguments(callArgc - 3, callArgv + 3))
+            {
+               delete currentNewObject;
+               currentNewObject = NULL;
+               ip = failJump;
+               break;
+            }
+
+            // If it's not a datablock, allow people to modify bits of it.
+            if (!isDataBlock)
+            {
+               currentNewObject->setModStaticFields(true);
+               currentNewObject->setModDynamicFields(true);
+            }
+         }
+         else
+         {
+            currentNewObject->reloadReset(); // AFX (reload-reset)
+
+            // 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());
+
+                  // temporarily block name change
+                  SimObject::preventNameChanging = true;
+                  currentNewObject->setCopySource(parent);
+                  currentNewObject->assignFieldsFrom(parent);
+                  // restore name changing
+                  SimObject::preventNameChanging = false;
+
+                  // copy any substitution statements
+                  SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
+                  if (parent_db)
+                  {
+                     SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(currentNewObject);
+                     if (currentNewObject_db)
+                        currentNewObject_db->copySubstitutionsFrom(parent_db);
+                  }
+               }
+               else
+               {
+                  Con::errorf(ConsoleLogEntry::General, "%d: Unable to find parent object %s for %s.", lineNumber, objParent, callArgv[1].getString());
+               }
+            }
+         }
+
+         // 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());
+
+         // Make sure it wasn't already added, then add it.
+         if (currentNewObject == NULL)
+         {
+            break;
+         }
+
+         bool isMessage = dynamic_cast<Message*>(currentNewObject) != NULL;
+
+         if (currentNewObject->isProperlyAdded() == false)
+         {
+            bool ret = false;
+            if (isMessage)
+            {
+               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 - 2), currentNewObject->getName(), currentNewObject->getClassName());
+               delete currentNewObject;
+               currentNewObject = NULL;
+               ip = failJump;
+               break;
+            }
+         }
+
+         // Are we dealing with a datablock?
+         SimDataBlock* dataBlock = dynamic_cast<SimDataBlock*>(currentNewObject);
+         String errorStr;
+
+         // If so, preload it.
+         if (dataBlock && !dataBlock->preload(true, errorStr))
+         {
+            Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip - 2),
+               currentNewObject->getName(), errorStr.c_str());
+            dataBlock->deleteObject();
+            ip = failJump;
+            break;
+         }
+
+         // What group will we be added to, if any?
+         U32 groupAddId = (U32)stack[_STK].getInt();
+         SimGroup* grp = NULL;
+         SimSet* set = 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() == NULL)
+               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)
+         S32 id = currentNewObject->getId();
+         if (placeAtRoot)
+            stack[_STK].setInt(id);
+         else
+            stack[++_STK].setInt(id);
+
+         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)
+            _STK--;
+         break;
+      }
+
+      case OP_FINISH_OBJECT:
       {
-         case ConsoleValue::TypeInternalInt:
-            currentVariable->setIntValue(copyVariable->getIntValue());
+         if (currentNewObject)
+            currentNewObject->onPostAdd();
+
+         AssertFatal( objectCreationStackIndex >= 0, "Object Stack is empty." );
+         // Restore the object info from the stack [7/9/2007 Black]
+         currentNewObject = objectCreationStack[--objectCreationStackIndex].newObject;
+         failJump = objectCreationStack[objectCreationStackIndex].failJump;
+         break;
+      }
+
+      case OP_JMPIFFNOT:
+         if (stack[_STK--].getFloat())
+         {
+            ip++;
             break;
-         case ConsoleValue::TypeInternalFloat:
-            currentVariable->setFloatValue(copyVariable->getFloatValue());
+         }
+         ip = code[ip];
+         break;
+      case OP_JMPIFNOT:
+         if (stack[_STK--].getInt())
+         {
+            ip++;
             break;
-         default:
-            currentVariable->setStringValue(copyVariable->getStringValue());
+         }
+         ip = code[ip];
+         break;
+      case OP_JMPIFF:
+         if (!stack[_STK--].getFloat())
+         {
+            ip++;
             break;
+         }
+         ip = code[ip];
+         break;
+      case OP_JMPIF:
+         if (!stack[_STK--].getFloat())
+         {
+            ip++;
+            break;
+         }
+         ip = code[ip];
+         break;
+      case OP_JMPIFNOT_NP:
+         if (stack[_STK].getInt())
+         {
+            _STK--;
+            ip++;
+            break;
+         }
+         ip = code[ip];
+         break;
+      case OP_JMPIF_NP:
+         if (!stack[_STK].getInt())
+         {
+            _STK--;
+            ip++;
+            break;
+         }
+         ip = code[ip];
+         break;
+      case OP_JMP:
+         ip = code[ip];
+         break;
+
+      case OP_RETURN_VOID:
+      {
+         if (iterDepth > 0)
+         {
+            // Clear iterator state.
+            while (iterDepth > 0)
+            {
+               iterStack[--_ITER].mIsStringIter = false;
+               --iterDepth;
+
+               _STK--; // this is a pop from foreach()
+            }
+         }
+
+         returnValue.setEmptyString();
+
+         goto execFinished;
+      }
+
+      case OP_RETURN:
+      {
+         returnValue = std::move(stack[_STK]);
+         _STK--;
+
+         // Clear iterator state.
+         while (iterDepth > 0)
+         {
+            iterStack[--_ITER].mIsStringIter = false;
+            --iterDepth;
+
+            _STK--;
+         }
+
+         goto execFinished;
+      }
+      case OP_RETURN_FLT:
+         returnValue.setFloat(stack[_STK].getFloat());
+         _STK--;
+
+         // Clear iterator state.
+         while (iterDepth > 0)
+         {
+            iterStack[--_ITER].mIsStringIter = false;
+            --iterDepth;
+
+            _STK--;
+         }
+
+         goto execFinished;
+
+      case OP_RETURN_UINT:
+         returnValue.setInt(stack[_STK].getInt());
+         _STK--;
+
+         // Clear iterator state.
+         while (iterDepth > 0)
+         {
+            iterStack[--_ITER].mIsStringIter = false;
+            --iterDepth;
+
+            _STK--;
+         }
+
+         goto execFinished;
+
+      case OP_CMPEQ:
+         doFloatMathOperation<FloatOperation::EQ>();
+         break;
+
+      case OP_CMPGR:
+         doFloatMathOperation<FloatOperation::GR>();
+         break;
+
+      case OP_CMPGE:
+         doFloatMathOperation<FloatOperation::GE>();
+         break;
+
+      case OP_CMPLT:
+         doFloatMathOperation<FloatOperation::LT>();
+         break;
+
+      case OP_CMPLE:
+         doFloatMathOperation<FloatOperation::LE>();
+         break;
+
+      case OP_CMPNE:
+         doFloatMathOperation<FloatOperation::NE>();
+         break;
+
+      case OP_XOR:
+         doIntOperation<IntegerOperation::Xor>();
+         break;
+
+      case OP_BITAND:
+         doIntOperation<IntegerOperation::BitAnd>();
+         break;
+
+      case OP_BITOR:
+         doIntOperation<IntegerOperation::BitOr>();
+         break;
+
+      case OP_NOT:
+         stack[_STK].setInt(!stack[_STK].getInt());
+         break;
+
+      case OP_NOTF:
+         stack[_STK].setInt(!stack[_STK].getFloat());
+         break;
+
+      case OP_ONESCOMPLEMENT:
+         stack[_STK].setInt(~stack[_STK].getInt());
+         break;
+
+      case OP_SHR:
+         doIntOperation<IntegerOperation::RShift>();
+         break;
+
+      case OP_SHL:
+         doIntOperation<IntegerOperation::LShift>();
+         break;
+
+      case OP_AND:
+         doIntOperation<IntegerOperation::LogicalAnd>();
+         break;
+
+      case OP_OR:
+         doIntOperation<IntegerOperation::LogicalOr>();
+         break;
+
+      case OP_ADD:
+         doFloatMathOperation<FloatOperation::Add>();
+         break;
+
+      case OP_SUB:
+         doFloatMathOperation<FloatOperation::Sub>();
+         break;
+
+      case OP_MUL:
+         doFloatMathOperation<FloatOperation::Mul>();
+         break;
+
+      case OP_DIV:
+         doFloatMathOperation<FloatOperation::Div>();
+         break;
+
+      case OP_MOD:
+      {
+         S64 divisor = stack[_STK - 1].getInt();
+         if (divisor != 0)
+            stack[_STK - 1].setInt(stack[_STK].getInt() % divisor);
+         else
+            stack[_STK - 1].setInt(0);
+         _STK--;
+         break;
+      }
+
+      case OP_NEG:
+         stack[_STK].setFloat(-stack[_STK].getFloat());
+         break;
+
+      case OP_INC:
+         reg = code[ip++];
+         currentRegister = reg;
+         gEvalState.setLocalFloatVariable(reg, gEvalState.getLocalFloatVariable(reg) + 1.0);
+         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;
+
+         // Used for local variable caching of what is active...when we
+         // set a global, we aren't active
+         currentRegister = -1;
+
+         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;
+
+         // Used for local variable caching of what is active...when we
+         // set a global, we aren't active
+         currentRegister = -1;
+
+         gEvalState.setCurVarNameCreate(var);
+
+         // See OP_SETCURVAR for why we do this.
+         curFNDocBlock = NULL;
+         curNSDocBlock = NULL;
+         break;
+
+      case OP_SETCURVAR_ARRAY:
+         var = StringTable->insert(stack[_STK].getString());
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         // Used for local variable caching of what is active...when we
+         // set a global, we aren't active
+         currentRegister = -1;
+
+         gEvalState.setCurVarName(var);
+
+         // See OP_SETCURVAR for why we do this.
+         curFNDocBlock = NULL;
+         curNSDocBlock = NULL;
+         break;
+
+      case OP_SETCURVAR_ARRAY_CREATE:
+         var = StringTable->insert(stack[_STK].getString());
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         // Used for local variable caching of what is active...when we
+         // set a global, we aren't active
+         currentRegister = -1;
+
+         gEvalState.setCurVarNameCreate(var);
+
+         // See OP_SETCURVAR for why we do this.
+         curFNDocBlock = NULL;
+         curNSDocBlock = NULL;
+         break;
+
+      case OP_LOADVAR_UINT:
+         currentRegister = -1;
+         stack[_STK + 1].setInt(gEvalState.getIntVariable());
+         _STK++;
+         break;
+
+      case OP_LOADVAR_FLT:
+         currentRegister = -1;
+         stack[_STK + 1].setFloat(gEvalState.getFloatVariable());
+         _STK++;
+         break;
+
+      case OP_LOADVAR_STR:
+         currentRegister = -1;
+         stack[_STK + 1].setString(gEvalState.getStringVariable());
+         _STK++;
+         break;
+
+      case OP_SAVEVAR_UINT:
+         gEvalState.setIntVariable(stack[_STK].getInt());
+         break;
+
+      case OP_SAVEVAR_FLT:
+         gEvalState.setFloatVariable(stack[_STK].getFloat());
+         break;
+
+      case OP_SAVEVAR_STR:
+         gEvalState.setStringVariable(stack[_STK].getString());
+         break;
+
+      case OP_LOAD_LOCAL_VAR_UINT:
+         reg = code[ip++];
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         stack[_STK + 1].setInt(gEvalState.getLocalIntVariable(reg));
+         _STK++;
+         break;
+
+      case OP_LOAD_LOCAL_VAR_FLT:
+         reg = code[ip++];
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         stack[_STK + 1].setFloat(gEvalState.getLocalFloatVariable(reg));
+         _STK++;
+         break;
+
+      case OP_LOAD_LOCAL_VAR_STR:
+         reg = code[ip++];
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         val = gEvalState.getLocalStringVariable(reg);
+         stack[_STK + 1].setString(val);
+         _STK++;
+         break;
+
+      case OP_SAVE_LOCAL_VAR_UINT:
+         reg = code[ip++];
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         gEvalState.setLocalIntVariable(reg, stack[_STK].getInt());
+         break;
+
+      case OP_SAVE_LOCAL_VAR_FLT:
+         reg = code[ip++];
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         gEvalState.setLocalFloatVariable(reg, stack[_STK].getFloat());
+         break;
+
+      case OP_SAVE_LOCAL_VAR_STR:
+         reg = code[ip++];
+         val = stack[_STK].getString();
+         currentRegister = reg;
+
+         // See OP_SETCURVAR
+         prevField = NULL;
+         prevObject = NULL;
+         curObject = NULL;
+
+         gEvalState.setLocalStringVariable(reg, val, (S32)dStrlen(val));
+         break;
+
+      case OP_SETCUROBJECT:
+         // Save the previous object for parsing vector fields.
+         prevObject = curObject;
+         val = stack[_STK].getString();
+
+         // 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 wasnt found
+         if (curObject)
+         {
+            SimGroup* group = dynamic_cast<SimGroup*>(curObject);
+            if (group)
+            {
+               StringTableEntry intName = StringTable->insert(stack[_STK].getString());
+               bool recurse = code[ip - 1];
+               SimObject* obj = group->findObjectByInternalName(intName, recurse);
+               stack[_STK].setInt(obj ? obj->getId() : 0);
+            }
+            else
+            {
+               Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-group %s of class %s.", getFileLine(ip - 2), curObject->getName(), curObject->getClassName());
+               stack[_STK].setInt(0);
+            }
+         }
+         else
+         {
+            Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use ->, but the group object wasn't found.", getFileLine(ip - 2));
+            stack[_STK].setInt(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, 256);
+         curField = CodeToSTE(code, ip);
+         curFieldArray[0] = 0;
+         ip += 2;
+         break;
+
+      case OP_SETCURFIELD_ARRAY:
+         dStrcpy(curFieldArray, stack[_STK].getString(), 256);
+         break;
+
+      case OP_SETCURFIELD_TYPE:
+         if(curObject)
+            curObject->setDataFieldType(code[ip], curField, curFieldArray);
+         ip++;
+         break;
+
+      case OP_LOADFIELD_UINT:
+         if (curObject)
+            stack[_STK + 1].setInt(dAtol(curObject->getDataField(curField, curFieldArray)));
+         else
+         {
+            // The field is not being retrieved from an object. Maybe it's
+            // a special accessor?
+            char buff[FieldBufferSizeNumeric];
+            memset(buff, 0, sizeof(buff));
+            getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
+            stack[_STK + 1].setInt(dAtol(buff));
+         }
+         _STK++;
+         break;
+
+      case OP_LOADFIELD_FLT:
+         if (curObject)
+            stack[_STK + 1].setFloat(dAtod(curObject->getDataField(curField, curFieldArray)));
+         else
+         {
+            // The field is not being retrieved from an object. Maybe it's
+            // a special accessor?
+            char buff[FieldBufferSizeNumeric];
+            memset(buff, 0, sizeof(buff));
+            getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
+            stack[_STK + 1].setFloat(dAtod(buff));
+         }
+         _STK++;
+         break;
+
+      case OP_LOADFIELD_STR:
+         if (curObject)
+         {
+            val = curObject->getDataField(curField, curFieldArray);
+            stack[_STK + 1].setString(val);
+         }
+         else
+         {
+            // The field is not being retrieved from an object. Maybe it's
+            // a special accessor?
+            char buff[FieldBufferSizeString];
+            memset(buff, 0, sizeof(buff));
+            getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
+            stack[_STK + 1].setString(buff);
+         }
+         _STK++;
+         break;
+
+      case OP_SAVEFIELD_UINT:
+         if (curObject)
+            curObject->setDataField(curField, curFieldArray, stack[_STK].getString());
+         else
+         {
+            // The field is not being set on an object. Maybe it's a special accessor?
+            setFieldComponent(prevObject, prevField, prevFieldArray, curField, currentRegister);
+            prevObject = NULL;
+         }
+         break;
+
+      case OP_SAVEFIELD_FLT:
+         if (curObject)
+            curObject->setDataField(curField, curFieldArray, stack[_STK].getString());
+         else
+         {
+            // The field is not being set on an object. Maybe it's a special accessor?
+            setFieldComponent(prevObject, prevField, prevFieldArray, curField, currentRegister);
+            prevObject = NULL;
+         }
+         break;
+
+      case OP_SAVEFIELD_STR:
+         if (curObject)
+            curObject->setDataField(curField, curFieldArray, stack[_STK].getString());
+         else
+         {
+            // The field is not being set on an object. Maybe it's a special accessor?
+            setFieldComponent(prevObject, prevField, prevFieldArray, curField, currentRegister);
+            prevObject = NULL;
+         }
+         break;
+
+      case OP_POP_STK:
+         _STK--;
+         break;
+
+      case OP_LOADIMMED_UINT:
+         stack[_STK + 1].setInt(code[ip++]);
+         _STK++;
+         break;
+
+      case OP_LOADIMMED_FLT:
+         stack[_STK + 1].setFloat(curFloatTable[code[ip++]]);
+         _STK++;
+         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;
+         }
+         TORQUE_CASE_FALLTHROUGH;
+
+      case OP_LOADIMMED_STR:
+         stack[_STK + 1].setString(curStringTable + code[ip++]);
+         _STK ++;
+         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:
+         stack[_STK + 1].setString(CodeToSTE(code, ip));
+         _STK++;
+         ip += 2;
+         break;
+
+      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);
+         fnNamespace = CodeToSTE(code, ip + 2);
+         U32 callType = code[ip + 4];
+
+         //if this is called from inside a function, append the ip and codeptr
+         if (!gEvalState.stack.empty())
+         {
+            gEvalState.getCurrentFrame().code = this;
+            gEvalState.getCurrentFrame().ip = ip - 1;
+         }
+
+         ip += 5;
+         gCallStack.argvc(fnName, callArgc, &callArgv);
+
+         if (callType == FuncCallExprNode::FunctionCall)
+         {
+            // Note: This works even if the function was in a package. Reason being is when
+            // activatePackage() is called, it swaps the namespaceEntry into the global namespace
+            // (and reverts it when deactivatePackage is called). Method or Static related ones work
+            // as expected, as the namespace is resolved on the fly.
+            nsEntry = Namespace::global()->lookup(fnName);
+            if (!nsEntry)
+            {
+               Con::warnf(ConsoleLogEntry::General,
+                  "%s: Unable to find function %s",
+                  getFileLine(ip - 4), fnName);
+
+               gCallStack.popFrame();
+               stack[_STK + 1].setEmptyString();
+               _STK++;
+               break;
+            }
+         }
+         else if (callType == FuncCallExprNode::StaticCall)
+         {
+            // Try to look it up.
+            ns = Namespace::find(fnNamespace);
+            nsEntry = ns->lookup(fnName);
+            if (!nsEntry)
+            {
+               Con::warnf(ConsoleLogEntry::General,
+                  "%s: Unable to find function %s%s%s",
+                  getFileLine(ip - 4), fnNamespace ? fnNamespace : "",
+                  fnNamespace ? "::" : "", fnName);
+
+               gCallStack.popFrame();
+               stack[_STK + 1].setEmptyString();
+               _STK++;
+               break;
+            }
+         }
+         else if (callType == FuncCallExprNode::MethodCall)
+         {
+            saveObject = gEvalState.thisObject;
+
+            // Optimization: If we're an integer, we can lookup the value by SimObjectId
+            const ConsoleValue& simObjectLookupValue = callArgv[1];
+            if (simObjectLookupValue.getType() == ConsoleValueType::cvInteger)
+               gEvalState.thisObject = Sim::findObject(static_cast<SimObjectId>(simObjectLookupValue.getFastInt()));
+            else
+            {
+               SimObject *foundObject = Sim::findObject(simObjectLookupValue.getString());
+
+               // Optimization: If we're not an integer, let's make it so that the fast path exists
+               // on the first argument of the method call (speeds up future usage of %this, for example)
+               if (foundObject != NULL)
+                  callArgv[1].setInt(static_cast<S64>(foundObject->getId()));
+
+               gEvalState.thisObject = foundObject;
+            }
+
+            if (gEvalState.thisObject == NULL)
+            {
+               Con::warnf(
+                  ConsoleLogEntry::General,
+                  "%s: Unable to find object: '%s' attempting to call function '%s'",
+                  getFileLine(ip - 6),
+                  simObjectLookupValue.getString(),
+                  fnName
+               );
+
+               gCallStack.popFrame();
+               stack[_STK + 1].setEmptyString();
+               _STK++;
+               break;
+            }
+
+            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;
+            }
+         }
+
+         if (!nsEntry || noCalls)
+         {
+            if (!noCalls)
+            {
+               Con::warnf(ConsoleLogEntry::General, "%s: Unknown command %s.", getFileLine(ip - 4), 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));
+               }
+            }
+            gCallStack.popFrame();
+            stack[_STK + 1].setEmptyString();
+            _STK++;
+            break;
+         }
+         if (nsEntry->mType == Namespace::Entry::ConsoleFunctionType)
+         {
+            if (nsEntry->mFunctionOffset)
+            {
+               ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);
+               stack[_STK + 1] = std::move(returnFromFn);
+            }
+            else // no body
+               stack[_STK + 1].setEmptyString();
+            _STK++;
+
+            gCallStack.popFrame();
+         }
+         else
+         {
+            if ((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs))
+            {
+               const char* nsName = ns ? ns->mName : "";
+               Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments.", getFileLine(ip - 4), nsName, fnName);
+               Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip - 4), nsEntry->mUsage);
+               gCallStack.popFrame();
+               stack[_STK + 1].setEmptyString();
+               _STK++;
+            }
+            else
+            {
+               switch (nsEntry->mType)
+               {
+               case Namespace::Entry::StringCallbackType:
+               {
+                  const char* result = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv);
+                  gCallStack.popFrame();
+                  stack[_STK + 1].setString(result);
+                  _STK++;
+                  break;
+               }
+               case Namespace::Entry::IntCallbackType:
+               {
+                  S64 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv);
+                  gCallStack.popFrame();
+
+                  if (code[ip] == OP_POP_STK)
+                  {
+                     ip++;
+                     break;
+                  }
+
+                  stack[_STK + 1].setInt(result);
+                  _STK++;
+                  break;
+               }
+               case Namespace::Entry::FloatCallbackType:
+               {
+                  F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv);
+                  gCallStack.popFrame();
+
+                  if (code[ip] == OP_POP_STK)
+                  {
+                     ip++;
+                     break;
+                  }
+
+                  stack[_STK + 1].setFloat(result);
+                  _STK++;
+                  break;
+               }
+               case Namespace::Entry::VoidCallbackType:
+               {
+                  nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv);
+                  gCallStack.popFrame();
+
+                  if (code[ip] == OP_POP_STK)
+                  {
+                     ip++;
+                     break;
+                  }
+
+                  Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip - 4), fnName, functionName);
+                  stack[_STK + 1].setEmptyString();
+                  _STK++;
+
+                  break;
+               }
+               case Namespace::Entry::BoolCallbackType:
+               {
+                  bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv);
+                  gCallStack.popFrame();
+
+                  if (code[ip] == OP_POP_STK)
+                  {
+                     ip++;
+                     break;
+                  }
+
+                  stack[_STK + 1].setBool(result);
+                  _STK++;
+
+                  break;
+               }
+               }
+            }
+         }
+
+         if (callType == FuncCallExprNode::MethodCall)
+            gEvalState.thisObject = saveObject;
+         break;
+      }
+
+      case OP_ADVANCE_STR_APPENDCHAR:
+      {
+         char buff[2];
+         buff[0] = (char)code[ip++];
+         buff[1] = '\0';
+
+         S32 len;
+         const char* concat = tsconcat(stack[_STK].getString(), buff, len);
+
+         stack[_STK].setStringRef(concat, len);
+         break;
+      }
+
+      case OP_REWIND_STR:
+         TORQUE_CASE_FALLTHROUGH;
+      case OP_TERMINATE_REWIND_STR:
+      {
+         S32 len;
+         const char* concat = tsconcat(stack[_STK - 1].getString(), stack[_STK].getString(), len);
+
+         stack[_STK - 1].setStringRef(concat, len);
+         _STK--;
+         break;
+      }
+
+      case OP_COMPARE_STR:
+         stack[_STK - 1].setBool(!dStricmp(stack[_STK].getString(), stack[_STK - 1].getString()));
+         _STK--;
+         break;
+
+      case OP_PUSH:
+         gCallStack.push(std::move(stack[_STK--]));
+         break;
+
+      case OP_PUSH_FRAME:
+         gCallStack.pushFrame(code[ip++]);
+         break;
+
+      case OP_ASSERT:
+      {
+         if (!stack[_STK--].getBool())
+         {
+            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.stack.empty(), "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;
+         TORQUE_CASE_FALLTHROUGH;
+      }
+
+      case OP_ITER_BEGIN:
+      {
+         bool isGlobal = code[ip];
+
+         U32 failIp = code[ip + (isGlobal ? 3 : 2)];
+
+         IterStackRecord& iter = iterStack[_ITER];
+         iter.mIsGlobalVariable = isGlobal;
+
+         if (isGlobal)
+         {
+            StringTableEntry varName = CodeToSTE(code, ip + 1);
+            iter.mVar.mVariable = gEvalState.globalVars.add(varName);
+         }
+         else
+         {
+            iter.mVar.mRegister = code[ip + 1];
+         }
+
+         if (iter.mIsStringIter)
+         {
+            iter.mData.mStr.mString = stack[_STK].getString();
+            iter.mData.mStr.mIndex = 0;
+         }
+         else
+         {
+            // Look up the object.
+
+            SimSet* set;
+            if (!Sim::findObject(stack[_STK].getString(), set))
+            {
+               Con::errorf(ConsoleLogEntry::General, "No SimSet object '%s'", stack[_STK].getString());
+               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++;
+
+         ip += isGlobal ? 4 : 3;
+         break;
+      }
+
+      case OP_ITER:
+      {
+         U32 breakIp = code[ip];
+         IterStackRecord& iter = iterStack[_ITER - 1];
+
+         if (iter.mIsStringIter)
+         {
+            const char* str = iter.mData.mStr.mString;
+
+            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.
+
+               if (iter.mIsGlobalVariable)
+                  iter.mVar.mVariable->setStringValue(&str[startIndex]);
+               else
+                  gEvalState.setLocalStringVariable(iter.mVar.mRegister, &str[startIndex], endIndex - startIndex);
+
+               const_cast<char*>(str)[endIndex] = savedChar;
+            }
+            else
+            {
+               if (iter.mIsGlobalVariable)
+                  iter.mVar.mVariable->setStringValue("");
+               else
+                  gEvalState.setLocalStringVariable(iter.mVar.mRegister, "", 0);
+            }
+
+            // 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;
+            }
+
+            SimObjectId id = set->at(index)->getId();
+
+            if (iter.mIsGlobalVariable)
+               iter.mVar.mVariable->setIntValue(id);
+            else
+               gEvalState.setLocalIntVariable(iter.mVar.mRegister, id);
+
+            iter.mData.mObj.mIndex = index + 1;
+         }
+
+         ++ip;
+         break;
+      }
+
+      case OP_ITER_END:
+      {
+         --_ITER;
+         --iterDepth;
+
+         _STK--;
+
+         iterStack[_ITER].mIsStringIter = false;
+         break;
+      }
+
+      case OP_INVALID:
+         TORQUE_CASE_FALLTHROUGH;
+      default:
+         // error!
+         AssertISV(false, "Invalid OPCode Processed!");
+         goto execFinished;
       }
    }
-}
+execFinished:
 
-//------------------------------------------------------------
+   if (telDebuggerOn && setFrame < 0)
+      TelDebugger->popStackFrame();
 
+   if (popFrame)
+   {
+      gEvalState.popFrame();
+   }
 
-ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName, S32 setFrame)
-{
-   CodeInterpreter interpreter(this);
-   return interpreter.exec(ip, functionName, thisNamespace, argc, argv, noCalls, packageName, setFrame);
+   if (argv)
+   {
+      if (gEvalState.traceOn)
+      {
+         traceBuffer[0] = 0;
+         dStrcat(traceBuffer, "Leaving ", TRACE_BUFFER_SIZE);
+
+         if (packageName)
+         {
+            dStrcat(traceBuffer, "[", TRACE_BUFFER_SIZE);
+            dStrcat(traceBuffer, packageName, TRACE_BUFFER_SIZE);
+            dStrcat(traceBuffer, "]", TRACE_BUFFER_SIZE);
+         }
+         if (thisNamespace && thisNamespace->mName)
+         {
+            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),
+               "%s::%s() - return %s", thisNamespace->mName, thisFunctionName, returnValue.getString());
+         }
+         else
+         {
+            dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer),
+               "%s() - return %s", thisFunctionName, returnValue.getString());
+         }
+         Con::printf("%s", traceBuffer);
+      }
+   }
+   else
+   {
+      delete[] const_cast<char*>(globalStrings);
+      delete[] globalFloats;
+      globalStrings = NULL;
+      globalFloats = NULL;
+   }
+   smCurrentCodeBlock = saveCodeBlock;
+   if (saveCodeBlock && saveCodeBlock->name)
+   {
+      Con::gCurrentFile = saveCodeBlock->name;
+      Con::gCurrentRoot = saveCodeBlock->modPath;
+   }
+
+   decRefCount();
+
+#ifdef TORQUE_DEBUG
+   AssertFatal(!(_STK > stackStart), "String stack not popped enough in script exec");
+   AssertFatal(!(_STK < stackStart), "String stack popped too much in script exec");
+#endif
+
+   return std::move(returnValue);
 }
 
 //------------------------------------------------------------

+ 64 - 0
Engine/source/console/compiler.cpp

@@ -60,6 +60,7 @@ namespace Compiler
    CompilerFloatTable  *gCurrentFloatTable, gGlobalFloatTable, gFunctionFloatTable;
    DataChunker          gConsoleAllocator;
    CompilerIdentTable   gIdentTable;
+   CompilerLocalVariableToRegisterMappingTable gFunctionVariableMappingTable;
 
    //------------------------------------------------------------
 
@@ -92,6 +93,8 @@ namespace Compiler
    CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; }
    CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; }
 
+   CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable() { return gFunctionVariableMappingTable; }
+
    void setCurrentStringTable(CompilerStringTable* cst) { gCurrentStringTable = cst; }
 
    CompilerFloatTable *getCurrentFloatTable() { return gCurrentFloatTable; }
@@ -117,6 +120,7 @@ namespace Compiler
       getFunctionFloatTable().reset();
       getFunctionStringTable().reset();
       getIdentTable().reset();
+      getFunctionVariableMappingTable().reset();
    }
 
    void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); }
@@ -208,6 +212,66 @@ void CompilerStringTable::write(Stream &st)
 
 //------------------------------------------------------------
 
+void CompilerLocalVariableToRegisterMappingTable::add(StringTableEntry functionName, StringTableEntry namespaceName, StringTableEntry varName)
+{
+   StringTableEntry funcLookupTableName = StringTable->insert(avar("%s::%s", namespaceName, functionName));
+
+   localVarToRegister[funcLookupTableName].varList.push_back(varName);;
+}
+
+S32 CompilerLocalVariableToRegisterMappingTable::lookup(StringTableEntry namespaceName, StringTableEntry functionName, StringTableEntry varName)
+{
+   StringTableEntry funcLookupTableName = StringTable->insert(avar("%s::%s", namespaceName, functionName));
+
+   auto functionPosition = localVarToRegister.find(funcLookupTableName);
+   if (functionPosition != localVarToRegister.end())
+   {
+      const auto& table = localVarToRegister[funcLookupTableName].varList;
+      auto varPosition = std::find(table.begin(), table.end(), varName);
+      if (varPosition != table.end())
+      {
+         return std::distance(table.begin(), varPosition);
+      }
+   }
+
+   Con::errorf("Unable to find local variable %s in function name %s", varName, funcLookupTableName);
+   return -1;
+}
+
+CompilerLocalVariableToRegisterMappingTable CompilerLocalVariableToRegisterMappingTable::copy()
+{
+   // Trivilly copyable as its all plain old data and using STL containers... (We want a deep copy though!)
+   CompilerLocalVariableToRegisterMappingTable table;
+   table.localVarToRegister = localVarToRegister;
+   return table;
+}
+
+void CompilerLocalVariableToRegisterMappingTable::reset()
+{
+   localVarToRegister.clear();
+}
+
+void CompilerLocalVariableToRegisterMappingTable::write(Stream& stream)
+{
+   stream.write((U32)localVarToRegister.size());
+
+   for (const auto& pair : localVarToRegister)
+   {
+      StringTableEntry functionName = pair.first;
+      stream.writeString(functionName);
+
+      const auto& localVariableTableForFunction = localVarToRegister[functionName].varList;
+      stream.write((U32)localVariableTableForFunction.size());
+
+      for (const StringTableEntry& varName : localVariableTableForFunction)
+      {
+         stream.writeString(varName);
+      }
+   }
+}
+
+//------------------------------------------------------------
+
 U32 CompilerFloatTable::add(F64 value)
 {
    Entry **walk;

+ 18 - 32
Engine/source/console/compiler.h

@@ -44,6 +44,8 @@ class DataChunker;
 #include "core/util/tVector.h"
 #endif
 
+//------------------------------------------------------------
+
 namespace Compiler
 {
    /// The opcodes for the TorqueScript VM.
@@ -69,6 +71,7 @@ namespace Compiler
       OP_RETURN_FLT,
       OP_RETURN_UINT,
 
+
       OP_CMPEQ,
       OP_CMPGR,
       OP_CMPGE,
@@ -93,26 +96,28 @@ namespace Compiler
       OP_MUL,
       OP_DIV,
       OP_NEG,
-
       OP_INC,
-      OP_DEC,
 
       OP_SETCURVAR,
       OP_SETCURVAR_CREATE,
       OP_SETCURVAR_ARRAY,
-      OP_SETCURVAR_ARRAY_VARLOOKUP,
       OP_SETCURVAR_ARRAY_CREATE,
-      OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP,
 
       OP_LOADVAR_UINT,// 40
       OP_LOADVAR_FLT,
       OP_LOADVAR_STR,
-      OP_LOADVAR_VAR,
 
       OP_SAVEVAR_UINT,
       OP_SAVEVAR_FLT,
       OP_SAVEVAR_STR,
-      OP_SAVEVAR_VAR,
+
+      OP_LOAD_LOCAL_VAR_UINT,
+      OP_LOAD_LOCAL_VAR_FLT,
+      OP_LOAD_LOCAL_VAR_STR,
+
+      OP_SAVE_LOCAL_VAR_UINT,
+      OP_SAVE_LOCAL_VAR_FLT,
+      OP_SAVE_LOCAL_VAR_STR,
 
       OP_SETCUROBJECT,
       OP_SETCUROBJECT_NEW,
@@ -121,8 +126,6 @@ namespace Compiler
       OP_SETCURFIELD,
       OP_SETCURFIELD_ARRAY, // 50
       OP_SETCURFIELD_TYPE,
-      OP_SETCURFIELD_ARRAY_VAR,
-      OP_SETCURFIELD_THIS,
 
       OP_LOADFIELD_UINT,
       OP_LOADFIELD_FLT,
@@ -132,43 +135,25 @@ namespace Compiler
       OP_SAVEFIELD_FLT,
       OP_SAVEFIELD_STR,
 
-      OP_STR_TO_UINT,
-      OP_STR_TO_FLT,
-      OP_STR_TO_NONE,  // 60
-      OP_FLT_TO_UINT,
-      OP_FLT_TO_STR,
-      OP_FLT_TO_NONE,
-      OP_UINT_TO_FLT,
-      OP_UINT_TO_STR,
-      OP_UINT_TO_NONE,
-      OP_COPYVAR_TO_NONE,
+      OP_POP_STK,
 
       OP_LOADIMMED_UINT,
       OP_LOADIMMED_FLT,
       OP_TAG_TO_STR,
       OP_LOADIMMED_STR, // 70
-      OP_DOCBLOCK_STR,
+      OP_DOCBLOCK_STR,  // 76
       OP_LOADIMMED_IDENT,
 
-      OP_CALLFUNC_RESOLVE,
       OP_CALLFUNC,
-      OP_CALLFUNC_POINTER,
-      OP_CALLFUNC_THIS,
 
-      OP_ADVANCE_STR,
       OP_ADVANCE_STR_APPENDCHAR,
-      OP_ADVANCE_STR_COMMA,
-      OP_ADVANCE_STR_NUL,
       OP_REWIND_STR,
-      OP_TERMINATE_REWIND_STR,  // 80
+      OP_TERMINATE_REWIND_STR,
+
       OP_COMPARE_STR,
 
-      OP_PUSH,          // String
-      OP_PUSH_UINT,     // Integer
-      OP_PUSH_FLT,      // Float
-      OP_PUSH_VAR,      // Variable
-      OP_PUSH_THIS,     // This pointer
-      OP_PUSH_FRAME,    // Frame
+      OP_PUSH,
+      OP_PUSH_FRAME,
 
       OP_ASSERT,
       OP_BREAK,
@@ -269,6 +254,7 @@ namespace Compiler
    CompilerStringTable *getCurrentStringTable();
    CompilerStringTable &getGlobalStringTable();
    CompilerStringTable &getFunctionStringTable();
+   CompilerLocalVariableToRegisterMappingTable& getFunctionVariableMappingTable();
 
    void setCurrentStringTable(CompilerStringTable* cst);
 

+ 116 - 286
Engine/source/console/console.cpp

@@ -41,9 +41,40 @@
 #include "platform/threads/mutex.h"
 #include "core/util/journal/journal.h"
 #include "cinterface/cinterface.h"
+#include "console/consoleValueStack.h"
 
 extern StringStack STR;
-extern ConsoleValueStack CSTK;
+extern ConsoleValueStack<4096> gCallStack;
+
+Vector<ConsoleValue::ConversionBuffer> ConsoleValue::sConversionBuffer;
+
+void ConsoleValue::init()
+{
+   sConversionBuffer.reserve(8192);
+}
+
+void ConsoleValue::resetConversionBuffer()
+{
+   sConversionBuffer.resetAndTreatAsScratchBuffer();
+}
+
+char* ConsoleValue::convertToBuffer() const
+{
+   ConversionBuffer conversion;
+   
+   if (type == ConsoleValueType::cvFloat)
+      dSprintf(conversion.buffer, ConversionBufferStride, "%.9g", f);
+   else
+      dSprintf(conversion.buffer, ConversionBufferStride, "%lld", i);
+
+   sConversionBuffer.push_back(std::move(conversion));
+   return sConversionBuffer.last().buffer;
+}
+
+const char* ConsoleValue::getConsoleData() const
+{
+   return Con::getData(type, ct->dataPtr, 0, ct->enumTable);
+}
 
 ConsoleDocFragment* ConsoleDocFragment::smFirst;
 ExprEvalState gEvalState;
@@ -271,8 +302,6 @@ StringTableEntry gCurrentFile;
 StringTableEntry gCurrentRoot;
 /// @}
 
-S32 gObjectCopyFailures = -1;
-
 bool alwaysUseDebugOutput = true;
 bool useTimestamp = false;
 bool useRealTimestamp = false;
@@ -327,6 +356,7 @@ void init()
 
    // Setup the console types.
    ConsoleBaseType::initialize();
+   ConsoleValue::init();
 
    // And finally, the ACR...
    AbstractClassRep::initialize();
@@ -344,10 +374,6 @@ void init()
    addVariable( "instantGroup", TypeRealString, &gInstantGroup, "The group that objects will be added to when they are created.\n"
       "@ingroup Console\n");
 
-   addVariable("Con::objectCopyFailures", TypeS32, &gObjectCopyFailures, "If greater than zero then it counts the number of object creation "
-      "failures based on a missing copy object and does not report an error..\n"
-      "@ingroup Console\n");   
-
    // Current script file name and root
    addVariable( "Con::File", TypeString, &gCurrentFile, "The currently executing script file.\n"
       "@ingroup FileSystem\n");
@@ -1474,7 +1500,7 @@ bool executeFile(const char* fileName, bool noCalls, bool journalScript)
    return ret;
 }
 
-ConsoleValueRef evaluate(const char* string, bool echo, const char *fileName)
+ConsoleValue evaluate(const char* string, bool echo, const char *fileName)
 {
    ConsoleStackFrameSaver stackSaver;
    stackSaver.save();
@@ -1491,11 +1517,11 @@ ConsoleValueRef evaluate(const char* string, bool echo, const char *fileName)
       fileName = StringTable->insert(fileName);
 
    CodeBlock *newCodeBlock = new CodeBlock();
-   return newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0);
+   return std::move(newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0));
 }
 
 //------------------------------------------------------------------------------
-ConsoleValueRef evaluatef(const char* string, ...)
+ConsoleValue evaluatef(const char* string, ...)
 {
    ConsoleStackFrameSaver stackSaver;
    stackSaver.save();
@@ -1512,36 +1538,41 @@ ConsoleValueRef evaluatef(const char* string, ...)
 //------------------------------------------------------------------------------
 
 // Internal execute for global function which does not save the stack
-ConsoleValueRef _internalExecute(S32 argc, ConsoleValueRef argv[])
+ConsoleValue _internalExecute(S32 argc, ConsoleValue argv[])
 {
+   StringTableEntry funcName = StringTable->insert(argv[0].getString());
+
    const char** argv_str = static_cast<const char**>(malloc((argc - 1) * sizeof(char *)));
    for (int i = 0; i < argc - 1; i++)
    {
-      argv_str[i] = argv[i + 1];
+      argv_str[i] = argv[i + 1].getString();
    }
    bool result;
-   const char* methodRes = CInterface::CallFunction(NULL, argv[0], argv_str, argc - 1, &result);
+   const char* methodRes = CInterface::CallFunction(NULL, funcName, argv_str, argc - 1, &result);
    free(argv_str);
    if (result)
    {
-      return ConsoleValueRef::fromValue(CSTK.pushString(methodRes));
+      ConsoleValue ret;
+      ret.setString(methodRes);
+      return std::move(ret);
    }
    
    Namespace::Entry *ent;
-   StringTableEntry funcName = StringTable->insert(argv[0]);
+   
    ent = Namespace::global()->lookup(funcName);
 
    if(!ent)
    {
-      warnf(ConsoleLogEntry::Script, "%s: Unknown command.", (const char*)argv[0]);
+      warnf(ConsoleLogEntry::Script, "%s: Unknown command.", funcName);
 
       STR.clearFunctionOffset();
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
-   return ent->execute(argc, argv, &gEvalState);
+
+   return std::move(ent->execute(argc, argv, &gEvalState));
 }
 
-ConsoleValueRef execute(S32 argc, ConsoleValueRef argv[])
+ConsoleValue execute(S32 argc, ConsoleValue argv[])
 {
 #ifdef TORQUE_MULTITHREAD
    if(isMainThread())
@@ -1563,23 +1594,23 @@ ConsoleValueRef execute(S32 argc, ConsoleValueRef argv[])
 #endif
 }
 
-ConsoleValueRef execute(S32 argc, const char *argv[])
+ConsoleValue execute(S32 argc, const char *argv[])
 {
    ConsoleStackFrameSaver stackSaver;
    stackSaver.save();
-   StringStackConsoleWrapper args(argc, argv);
-   return execute(args.count(), args);
+   StringArrayToConsoleValueWrapper args(argc, argv);
+   return std::move(execute(args.count(), args));
 }
 
 //------------------------------------------------------------------------------
 
 // Internal execute for object method which does not save the stack
-ConsoleValueRef _internalExecute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly)
+static ConsoleValue _internalExecute(SimObject *object, S32 argc, ConsoleValue argv[], bool thisCallOnly)
 {
    if(argc < 2)
    {
       STR.clearFunctionOffset();
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
 
    // [neo, 10/05/2007 - #3010]
@@ -1590,35 +1621,36 @@ ConsoleValueRef _internalExecute(SimObject *object, S32 argc, ConsoleValueRef ar
       ICallMethod *com = dynamic_cast<ICallMethod *>(object);
       if(com)
       {
-         STR.pushFrame();
-         CSTK.pushFrame();
+         gCallStack.pushFrame(0);
          com->callMethodArgList(argc, argv, false);
-         STR.popFrame();
-         CSTK.popFrame();
+         gCallStack.popFrame();
       }
    }
 
+   StringTableEntry funcName = StringTable->insert(argv[0].getString());
+
    const char** argv_str = static_cast<const char**>(malloc((argc - 2) * sizeof(char *)));
    for (int i = 0; i < argc - 2; i++)
    {
-      argv_str[i] = argv[i + 2];
+      argv_str[i] = argv[i + 2].getString();
    }
    bool result;
-   const char* methodRes = CInterface::CallMethod(object, argv[0], argv_str, argc - 2, &result);
+   const char* methodRes = CInterface::CallMethod(object, funcName, argv_str, argc - 2, &result);
 
    free(argv_str);
 
    if (result)
    {
-      return ConsoleValueRef::fromValue(CSTK.pushString(methodRes));
+      ConsoleValue val;
+      val.setString(methodRes);
+      return val;
    }
 
    if(object->getNamespace())
    {
       U32 ident = object->getId();
-      ConsoleValueRef oldIdent(argv[1]);
-
-      StringTableEntry funcName = StringTable->insert(argv[0]);
+      const char* oldIdent = dStrdup(argv[1].getString());
+      
       Namespace::Entry *ent = object->getNamespace()->lookup(funcName);
 
       if(ent == NULL)
@@ -1626,37 +1658,35 @@ ConsoleValueRef _internalExecute(SimObject *object, S32 argc, ConsoleValueRef ar
          //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId());
 
          STR.clearFunctionOffset();
-         return ConsoleValueRef();
+         return std::move(ConsoleValue());
       }
 
       // Twiddle %this argument
-      ConsoleValue func_ident;
-      func_ident.setIntValue((S32)ident);
-      argv[1] = ConsoleValueRef::fromValue(&func_ident);
+      argv[1].setInt(ident);
 
       SimObject *save = gEvalState.thisObject;
       gEvalState.thisObject = object;
-      ConsoleValueRef ret = ent->execute(argc, argv, &gEvalState);
+      ConsoleValue ret = std::move(ent->execute(argc, argv, &gEvalState));
       gEvalState.thisObject = save;
 
       // Twiddle it back
-      argv[1] = oldIdent;
+      argv[1].setString(oldIdent);
+      dFree(oldIdent);
 
       return ret;
    }
 
-   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]);
+   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), funcName);
    STR.clearFunctionOffset();
-   return ConsoleValueRef();
+   return std::move(ConsoleValue());
 }
 
-
-ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly)
+ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool thisCallOnly)
 {
    if(argc < 2)
    {
       STR.clearFunctionOffset();
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
 
    ConsoleStackFrameSaver stackSaver;
@@ -1666,7 +1696,7 @@ ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], boo
    {
       if (isMainThread())
       {
-         return _internalExecute(object, argc, argv, thisCallOnly);
+         return std::move(_internalExecute(object, argc, argv, thisCallOnly));
       }
       else
       {
@@ -1676,34 +1706,34 @@ ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], boo
       }
    }
 
-   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]);
+   warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), argv[0].getString());
    STR.clearFunctionOffset();
-   return ConsoleValueRef();
+   return std::move(ConsoleValue());
 }
 
-ConsoleValueRef execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly)
+ConsoleValue execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly)
 {
    ConsoleStackFrameSaver stackSaver;
    stackSaver.save();
-   StringStackConsoleWrapper args(argc, argv);
-   return execute(object, args.count(), args, thisCallOnly);
+   StringArrayToConsoleValueWrapper args(argc, argv);
+   return std::move(execute(object, args.count(), args, thisCallOnly));
 }
 
-inline ConsoleValueRef _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValueRef *argv)
+inline ConsoleValue _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValue *argv)
 {
    const U32 maxArg = 12;
-   AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)");
+   AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)");
    AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more.");
-   return execute(obj, argc, argv);
+   return std::move(execute(obj, argc, argv));
 }
 
 //------------------------------------------------------------------------------
-inline ConsoleValueRef _executef(S32 checkArgc, S32 argc, ConsoleValueRef *argv)
+inline ConsoleValue _executef(S32 checkArgc, S32 argc, ConsoleValue *argv)
 {
    const U32 maxArg = 10;
    AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()");
    AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more.");
-   return execute(argc, argv);
+   return std::move(execute(argc, argv));
 }
 
 //------------------------------------------------------------------------------
@@ -1901,15 +1931,9 @@ StringTableEntry getModNameFromPath(const char *path)
 void postConsoleInput( RawData data )
 {
    // Schedule this to happen at the next time event.
-   ConsoleValue values[2];
-   ConsoleValueRef argv[2];
-
-   values[0].init();
-   values[0].setStringValue("eval");
-   values[1].init();
-   values[1].setStringValue((const char*)data.data);
-   argv[0].value = &values[0];
-   argv[1].value = &values[1];
+   ConsoleValue argv[2];
+   argv[0].setString("eval");
+   argv[1].setString(reinterpret_cast<const char*>(data.data));
 
    Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, argv, false));
 }
@@ -2524,70 +2548,20 @@ DefineEngineFunction( logWarning, void, ( const char* message ),,
 
 //------------------------------------------------------------------------------
 
-extern ConsoleValueStack CSTK;
-
-ConsoleValueRef::ConsoleValueRef(const ConsoleValueRef &ref)
-{
-   value = ref.value;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(const ConsoleValueRef &newValue)
-{
-   value = newValue.value;
-   return *this;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(const char *newValue)
-{
-   AssertFatal(value, "value should not be NULL");
-   value->setStringValue(newValue);
-   return *this;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(S32 newValue)
-{
-   AssertFatal(value, "value should not be NULL");
-   value->setIntValue(newValue);
-   return *this;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(U32 newValue)
-{
-   AssertFatal(value, "value should not be NULL");
-   value->setIntValue(newValue);
-   return *this;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(F32 newValue)
-{
-   AssertFatal(value, "value should not be NULL");
-   value->setFloatValue(newValue);
-   return *this;
-}
-
-ConsoleValueRef& ConsoleValueRef::operator=(F64 newValue)
-{
-   AssertFatal(value, "value should not be NULL");
-   value->setFloatValue(newValue);
-   return *this;
-}
-
-//------------------------------------------------------------------------------
-
-StringStackWrapper::StringStackWrapper(int targc, ConsoleValueRef targv[])
+ConsoleValueToStringArrayWrapper::ConsoleValueToStringArrayWrapper(int targc, ConsoleValue *targv)
 {
    argv = new const char*[targc];
    argc = targc;
 
-   for (int i=0; i<targc; i++)
+   for (S32 i = 0; i < targc; i++)
    {
-      argv[i] = dStrdup(targv[i]);
+      argv[i] = dStrdup(targv[i].getString());
    }
 }
 
-StringStackWrapper::~StringStackWrapper()
+ConsoleValueToStringArrayWrapper::~ConsoleValueToStringArrayWrapper()
 {
-   for (int i=0; i<argc; i++)
+   for (S32 i = 0; i< argc; i++)
    {
       dFree(argv[i]);
    }
@@ -2595,201 +2569,58 @@ StringStackWrapper::~StringStackWrapper()
 }
 
 
-StringStackConsoleWrapper::StringStackConsoleWrapper(int targc, const char** targ)
+StringArrayToConsoleValueWrapper::StringArrayToConsoleValueWrapper(int targc, const char** targv)
 {
-   argv = new ConsoleValueRef[targc];
-   argvValue = new ConsoleValue[targc];
+   argv = new ConsoleValue[targc]();
    argc = targc;
 
-   for (int i=0; i<targc; i++) {
-      argvValue[i].init();
-      argv[i].value = &argvValue[i];
-      argvValue[i].setStackStringValue(targ[i]);
-   }
-}
-
-StringStackConsoleWrapper::~StringStackConsoleWrapper()
-{
-   for (int i=0; i<argc; i++)
-   {
-      argv[i] = 0;
-   }
-   delete[] argv;
-   delete[] argvValue;
-}
-
-//------------------------------------------------------------------------------
-
-S32 ConsoleValue::getSignedIntValue()
-{
-   if(type <= TypeInternalString)
-      return (S32)fval;
-   else
-      return dAtoi(Con::getData(type, dataPtr, 0, enumTable));
-}
-
-U32 ConsoleValue::getIntValue()
-{
-   if(type <= TypeInternalString)
-      return ival;
-   else
-      return dAtoi(Con::getData(type, dataPtr, 0, enumTable));
-}
-
-F32 ConsoleValue::getFloatValue()
-{
-   if(type <= TypeInternalString)
-      return fval;
-   else
-      return dAtof(Con::getData(type, dataPtr, 0, enumTable));
-}
-
-const char *ConsoleValue::getStringValue()
-{
-   if(type == TypeInternalString || type == TypeInternalStackString)
-      return sval;
-   else if (type == TypeInternalStringStackPtr)
-      return STR.mBuffer + (uintptr_t)sval;
-   else
-   {
-      // We need a string representation, so lets create one
-      const char *internalValue = NULL;
-
-      if(type == TypeInternalFloat)
-         internalValue = Con::getData(TypeF32, &fval, 0);
-      else if(type == TypeInternalInt)
-         internalValue = Con::getData(TypeS32, &ival, 0);
-      else
-         return Con::getData(type, dataPtr, 0, enumTable); // We can't save sval here since it is the same as dataPtr
-
-      if (!internalValue)
-         return "";
-
-      U32 stringLen = dStrlen(internalValue);
-      U32 newLen = ((stringLen + 1) + 15) & ~15; // pad upto next cache line
-      
-      if (bufferLen == 0)
-         sval = (char *) dMalloc(newLen);
-      else if(newLen > bufferLen)
-         sval = (char *) dRealloc(sval, newLen);
-
-      dStrcpy(sval, internalValue, newLen);
-      bufferLen = newLen;
-
-      return sval;
-   }
-}
-
-StringStackPtr ConsoleValue::getStringStackPtr()
-{
-   if (type == TypeInternalStringStackPtr)
-      return (uintptr_t)sval;
-   else
-      return (uintptr_t)-1;
-}
-
-bool ConsoleValue::getBoolValue()
-{
-   if(type == TypeInternalString || type == TypeInternalStackString || type == TypeInternalStringStackPtr)
-      return dAtob(getStringValue());
-   if(type == TypeInternalFloat)
-      return fval > 0;
-   else if(type == TypeInternalInt)
-      return ival > 0;
-   else {
-      const char *value = Con::getData(type, dataPtr, 0, enumTable);
-      return dAtob(value);
-   }
-}
-
-void ConsoleValue::setIntValue(S32 val)
-{
-   setFloatValue(val);
-}
-
-void ConsoleValue::setIntValue(U32 val)
-{
-   if(type <= TypeInternalString)
-   {
-      fval = (F32)val;
-      ival = val;
-      if(bufferLen > 0)
-      {
-         dFree(sval);
-         bufferLen = 0;
-      }
-
-      sval = typeValueEmpty;
-      type = TypeInternalInt;
-   }
-   else
+   for (int i=0; i<targc; i++)
    {
-      const char *dptr = Con::getData(TypeS32, &val, 0);
-      Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
+      argv[i].setString(targv[i]);
    }
 }
 
-void ConsoleValue::setBoolValue(bool val)
-{
-   return setIntValue(val ? 1 : 0);
-}
-
-void ConsoleValue::setFloatValue(F32 val)
+StringArrayToConsoleValueWrapper::~StringArrayToConsoleValueWrapper()
 {
-   if(type <= TypeInternalString)
-   {
-      fval = val;
-      ival = static_cast<U32>(val);
-      if(bufferLen > 0)
-      {
-         dFree(sval);
-         bufferLen = 0;
-      }
-      sval = typeValueEmpty;
-      type = TypeInternalFloat;
-   }
-   else
-   {
-      const char *dptr = Con::getData(TypeF32, &val, 0);
-      Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
-   }
+   delete[] argv;
 }
 
 //------------------------------------------------------------------------------
 
-ConsoleValueRef _BaseEngineConsoleCallbackHelper::_exec()
+ConsoleValue _BaseEngineConsoleCallbackHelper::_exec()
 {
-   ConsoleValueRef returnValue;
    if( mThis )
    {
       // Cannot invoke callback until object has been registered
-      if (mThis->isProperlyAdded()) {
-         returnValue = Con::_internalExecute( mThis, mArgc, mArgv, false );
-      } else {
-         STR.clearFunctionOffset();
-         returnValue = ConsoleValueRef();
+      if (mThis->isProperlyAdded())
+      {
+         ConsoleValue returnValue = Con::_internalExecute( mThis, mArgc, mArgv, false );
+         mArgc = mInitialArgc; // reset
+         return std::move(returnValue);
       }
+
+      STR.clearFunctionOffset();
+      mArgc = mInitialArgc; // reset
+      return std::move(ConsoleValue());
    }
-   else
-      returnValue = Con::_internalExecute( mArgc, mArgv );
 
+   ConsoleValue returnValue = std::move(Con::_internalExecute( mArgc, mArgv ));
    mArgc = mInitialArgc; // reset args
-   return returnValue;
+   return std::move(returnValue);
 }
 
-ConsoleValueRef _BaseEngineConsoleCallbackHelper::_execLater(SimConsoleThreadExecEvent *evt)
+ConsoleValue _BaseEngineConsoleCallbackHelper::_execLater(SimConsoleThreadExecEvent *evt)
 {
    mArgc = mInitialArgc; // reset args
    Sim::postEvent((SimObject*)Sim::getRootGroup(), evt, Sim::getCurrentTime());
-   return evt->getCB().waitForResult();
+   return std::move(evt->getCB().waitForResult());
 }
 
 //------------------------------------------------------------------------------
 
 void ConsoleStackFrameSaver::save()
 {
-   CSTK.pushFrame();
-   STR.pushFrame();
+   gCallStack.pushFrame(0);
    mSaved = true;
 }
 
@@ -2797,7 +2628,6 @@ void ConsoleStackFrameSaver::restore()
 {
    if (mSaved)
    {
-      CSTK.popFrame();
-      STR.popFrame();
+      gCallStack.popFrame();
    }
 }

+ 286 - 159
Engine/source/console/console.h

@@ -36,6 +36,7 @@
 
 #include "core/util/str.h"
 #include "core/util/journal/journaledSignal.h"
+#include "core/stringTable.h"
 
 class SimObject;
 class Namespace;
@@ -117,188 +118,313 @@ struct ConsoleLogEntry
 typedef const char *StringTableEntry;
 extern char *typeValueEmpty;
 
-class ConsoleValue
+enum ConsoleValueType
 {
-public:
+   cvInteger = -4,
+   cvFloat = -3,
+   cvString = -2,
+   cvSTEntry = -1,
+   cvConsoleValueType = 0
+};
 
-   enum
+struct ConsoleValueConsoleType
+{
+   void* dataPtr;
+   EnumTable* enumTable;
+};
+
+class ConsoleValue
+{
+   union
    {
-      TypeInternalInt = -5,
-      TypeInternalFloat = -4,
-      TypeInternalStringStackPtr = -3,
-      TypeInternalStackString = -2,
-      TypeInternalString = -1,
+      F64   f;
+      S64   i;
+      char* s;
+      void* data;
+      ConsoleValueConsoleType* ct;
    };
 
    S32 type;
 
-public:
+   enum Constants
+   {
+      ConversionBufferStride = 32
+   };
+
+   struct ConversionBuffer
+   {
+      char buffer[ConversionBufferStride];
+   };
+   
+   static Vector<ConversionBuffer> sConversionBuffer;
 
-   // NOTE: This is protected to ensure no one outside
-   // of this structure is messing with it.
+   char* convertToBuffer() const;
 
-#pragma warning( push )
-#pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union
+   TORQUE_FORCEINLINE bool hasAllocatedData() const
+   {
+      return (type == ConsoleValueType::cvString || isConsoleType()) && data != NULL;
+   }
 
-   // An variable is either a real dynamic type or
-   // its one exposed from C++ using a data pointer.
-   //
-   // We use this nameless union and struct setup
-   // to optimize the memory usage.
-   union
+   const char* getConsoleData() const;
+
+   TORQUE_FORCEINLINE void cleanupData()
    {
-      struct
+      if (hasAllocatedData())
       {
-         char *sval;
-         U32 ival;  // doubles as strlen when type is TypeInternalString
-         F32 fval;
-         U32 bufferLen;
-      };
+         dFree(data);
+         data = NULL;
+      }
+   }
+
+   TORQUE_FORCEINLINE void _move(ConsoleValue&& ref) noexcept
+   {
+      type = ref.type;
 
-      struct
+      switch (ref.type)
       {
-         /// The real data pointer.
-         void *dataPtr;
+      case cvInteger:
+         i = ref.i;
+         break;
+      case cvFloat:
+         f = ref.f;
+         break;
+      case cvSTEntry:
+         TORQUE_CASE_FALLTHROUGH;
+      case cvString:
+         s = ref.s;
+         break;
+      default:
+         data = ref.data;
+         break;
+      }
 
-         /// The enum lookup table for enumerated types.
-         const EnumTable *enumTable;
-      };
-   };
+      ref.data = NULL;
+      ref.setEmptyString();
+   }
+
+public:
+   ConsoleValue()
+   {
+      type = ConsoleValueType::cvSTEntry;
+      s = const_cast<char*>(StringTable->EmptyString());
+   }
+
+   ConsoleValue(ConsoleValue&& ref) noexcept
+   {
+      _move(std::move(ref));
+   }
+
+   TORQUE_FORCEINLINE ConsoleValue& operator=(ConsoleValue&& ref) noexcept
+   {
+      _move(std::move(ref));
+      return *this;
+   }
+
+   ConsoleValue(const ConsoleValue&) = delete;
+   ConsoleValue& operator=(const ConsoleValue&) = delete;
+
+   TORQUE_FORCEINLINE ~ConsoleValue()
+   {
+      cleanupData();
+   }
+
+   TORQUE_FORCEINLINE void reset()
+   {
+      setEmptyString();
+   }
+
+   TORQUE_FORCEINLINE F64 getFloat() const
+   {
+      if (type == ConsoleValueType::cvFloat)
+         return f;
+      if (type == ConsoleValueType::cvInteger)
+         return i;
+      if (isStringType())
+         return dAtof(s);
+      return dAtof(getConsoleData());
+   }
+
+   TORQUE_FORCEINLINE S64 getInt() const
+   {
+      if (type == ConsoleValueType::cvInteger)
+         return i;
+      if (type == ConsoleValueType::cvFloat)
+         return f;
+      if (isStringType())
+         return dAtoi(s);
+      return dAtoi(getConsoleData());
+   }
+
+   TORQUE_FORCEINLINE const char* getString() const
+   {
+      if (isStringType())
+         return s;
+      if (isNumberType())
+         return convertToBuffer();
+      return getConsoleData();
+   }
+
+   TORQUE_FORCEINLINE operator const char* () const
+   {
+      return getString();
+   }
+
+   TORQUE_FORCEINLINE bool getBool() const
+   {
+      if (type == ConsoleValueType::cvInteger)
+         return (bool)i;
+      if (type == ConsoleValueType::cvFloat)
+         return (bool)f;
+      if (isStringType())
+         return dAtob(s);
+      return dAtob(getConsoleData());
+   }
+
+   TORQUE_FORCEINLINE void setFloat(const F64 val)
+   {
+      cleanupData();
+      type = ConsoleValueType::cvFloat;
+      f = val;
+   }
 
-   U32 getIntValue();
-   S32 getSignedIntValue();
-   F32 getFloatValue();
-   const char *getStringValue();
-   StringStackPtr getStringStackPtr();
-   bool getBoolValue();
-
-   void setIntValue(U32 val);
-   void setIntValue(S32 val);
-   void setFloatValue(F32 val);
-   void setStringValue(const char *value);
-   void setStackStringValue(const char *value);
-   void setStringStackPtrValue(StringStackPtr ptr);
-   void setBoolValue(bool val);
-
-   void init()
+   TORQUE_FORCEINLINE void setInt(const S64 val)
    {
-      ival = 0;
-      fval = 0;
-      sval = typeValueEmpty;
-      bufferLen = 0;
-      type = TypeInternalString;
+      cleanupData();
+      type = ConsoleValueType::cvInteger;
+      i = val;
    }
 
-   void cleanup()
+   TORQUE_FORCEINLINE void setString(const char* val)
    {
-      if ((type <= TypeInternalString) && (bufferLen > 0))
+      setString(val, val != NULL ? dStrlen(val) : 0);
+   }
+
+   TORQUE_FORCEINLINE void setString(const char* val, S32 len)
+   {
+      if (len == 0)
       {
-         dFree(sval);
-         bufferLen = 0;
+         setEmptyString();
+         return;
       }
-      sval = typeValueEmpty;
-      type = ConsoleValue::TypeInternalString;
-      ival = 0;
-      fval = 0;
+
+      cleanupData();
+
+      type = ConsoleValueType::cvString;
+
+      s = (char*)dMalloc(static_cast<dsize_t>(len) + 1);
+      s[len] = '\0';
+      dStrcpy(s, val, static_cast<dsize_t>(len) + 1);
    }
-   ConsoleValue() { init(); };
-   ~ConsoleValue() { cleanup(); };
-};
 
-// Proxy class for console variables
-// Can point to existing console variables,
-// or act like a free floating value.
-class ConsoleValueRef
-{
-public:
-   ConsoleValue *value;
-
-   ConsoleValueRef() : value(0) { ; }
-   ~ConsoleValueRef() { ; }
-
-   ConsoleValueRef(const ConsoleValueRef &ref);
-
-   static ConsoleValueRef fromValue(ConsoleValue *value) { ConsoleValueRef ref; ref.value = value; return ref; }
-
-   const char *getStringValue() { return value ? value->getStringValue() : ""; }
-   StringStackPtr getStringStackPtrValue() { return value ? value->getStringStackPtr() : 0; }
-
-   inline U32 getIntValue() { return value ? value->getIntValue() : 0; }
-   inline S32 getSignedIntValue() { return value ? value->getSignedIntValue() : 0; }
-   inline F32 getFloatValue() { return value ? value->getFloatValue() : 0.0f; }
-   inline bool getBoolValue() { return value ? value->getBoolValue() : false; }
-
-   inline operator const char*() { return getStringValue(); }
-   inline operator String() { return String(getStringValue()); }
-   inline operator U32() { return getIntValue(); }
-   inline operator S32() { return getSignedIntValue(); }
-   inline operator F32() { return getFloatValue(); }
-   inline operator bool() { return getBoolValue(); }
-
-   inline bool isStringStackPtr() { return value ? value->type == ConsoleValue::TypeInternalStringStackPtr : false; }
-   inline bool isString() { return value ? value->type >= ConsoleValue::TypeInternalStringStackPtr : true; }
-   inline bool isInt() { return value ? value->type == ConsoleValue::TypeInternalInt : false; }
-   inline bool isFloat() { return value ? value->type == ConsoleValue::TypeInternalFloat : false; }
-   inline S32 getType() { return value ? value->type : -1; }
-
-   // Note: operators replace value
-   ConsoleValueRef& operator=(const ConsoleValueRef &other);
-   ConsoleValueRef& operator=(const char *newValue);
-   ConsoleValueRef& operator=(U32 newValue);
-   ConsoleValueRef& operator=(S32 newValue);
-   ConsoleValueRef& operator=(F32 newValue);
-   ConsoleValueRef& operator=(F64 newValue);
-};
+   TORQUE_FORCEINLINE void setStringRef(const char* ref, S32 len)
+   {
+      cleanupData();
+      type = ConsoleValueType::cvString;
+      s = const_cast<char*>(ref);
+   }
 
-// Overrides to allow ConsoleValueRefs to be directly converted to S32&F32
+   TORQUE_FORCEINLINE void setBool(const bool val)
+   {
+      cleanupData();
+      type = ConsoleValueType::cvInteger;
+      i = (int)val;
+   }
 
-inline S32 dAtoi(ConsoleValueRef &ref)
-{
-   return ref.getSignedIntValue();
-}
+   TORQUE_FORCEINLINE void setStringTableEntry(StringTableEntry val)
+   {
+      cleanupData();
+      type = ConsoleValueType::cvSTEntry;
+      s = const_cast<char*>(val);
+   }
 
-inline F32 dAtof(ConsoleValueRef &ref)
-{
-   return ref.getFloatValue();
-}
+   TORQUE_FORCEINLINE void setEmptyString()
+   {
+      setStringTableEntry(StringTable->EmptyString());
+   }
 
-inline bool dAtob(ConsoleValue &ref)
-{
-   return ref.getBoolValue();
-}
+   TORQUE_FORCEINLINE void setConsoleData(S32 consoleType, void* dataPtr, const EnumTable* enumTable)
+   {
+      cleanupData();
+      type = ConsoleValueType::cvSTEntry;
+      ct = new ConsoleValueConsoleType{ dataPtr, const_cast<EnumTable*>(enumTable) };
+   }
+
+   TORQUE_FORCEINLINE S32 getType() const
+   {
+      return type;
+   }
+
+   TORQUE_FORCEINLINE bool isStringType() const
+   {
+      return type == ConsoleValueType::cvString || type == ConsoleValueType::cvSTEntry;
+   }
+
+   TORQUE_FORCEINLINE bool isNumberType() const
+   {
+      return type == ConsoleValueType::cvFloat || type == ConsoleValueType::cvInteger;
+   }
+
+   TORQUE_FORCEINLINE bool isConsoleType() const
+   {
+      return type >= ConsoleValueType::cvConsoleValueType;
+   }
 
+   TORQUE_FORCEINLINE void setFastFloat(F64 flt)
+   {
+      type = ConsoleValueType::cvFloat;
+      f = flt;
+   }
+
+   TORQUE_FORCEINLINE F64 getFastFloat() const
+   {
+      return f;
+   }
+
+   TORQUE_FORCEINLINE void setFastInt(S64 flt)
+   {
+      type = ConsoleValueType::cvInteger;
+      i = flt;
+   }
+
+   TORQUE_FORCEINLINE S64 getFastInt() const
+   {
+      return i;
+   }
+
+   static void init();
+   static void resetConversionBuffer();
+};
 
 // Transparently converts ConsoleValue[] to const char**
-class StringStackWrapper
+class ConsoleValueToStringArrayWrapper
 {
 public:
    const char **argv;
-   int argc;
+   S32 argc;
 
-   StringStackWrapper(int targc, ConsoleValueRef targv[]);
-   ~StringStackWrapper();
+   ConsoleValueToStringArrayWrapper(int targc, ConsoleValue* targv);
+   ~ConsoleValueToStringArrayWrapper();
 
-   const char* operator[](int idx) { return argv[idx]; }
+   const char* operator[](S32 idx) { return argv[idx]; }
    operator const char**() { return argv; }
 
-   int count() { return argc; }
+   S32 count() { return argc; }
 };
 
 // Transparently converts const char** to ConsoleValue
-class StringStackConsoleWrapper
+class StringArrayToConsoleValueWrapper
 {
 public:
-   ConsoleValue *argvValue;
-   ConsoleValueRef *argv;
-   int argc;
+   ConsoleValue *argv;
+   S32 argc;
 
-   StringStackConsoleWrapper(int targc, const char **targv);
-   ~StringStackConsoleWrapper();
+   StringArrayToConsoleValueWrapper(int targc, const char **targv);
+   ~StringArrayToConsoleValueWrapper();
 
-   ConsoleValueRef& operator[](int idx) { return argv[idx]; }
-   operator ConsoleValueRef*() { return argv; }
+   ConsoleValue& operator[](int idx) { return argv[idx]; }
+   operator ConsoleValue*() { return argv; }
 
-   int count() { return argc; }
+   S32 count() { return argc; }
 };
 
 /// @defgroup console_callbacks Scripting Engine Callbacks
@@ -319,11 +445,11 @@ public:
 /// @{
 
 ///
-typedef const char * (*StringCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]);
-typedef S32(*IntCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]);
-typedef F32(*FloatCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]);
-typedef void(*VoidCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); // We have it return a value so things don't break..
-typedef bool(*BoolCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]);
+typedef const char * (*StringCallback)(SimObject *obj, S32 argc, ConsoleValue argv[]);
+typedef S32(*IntCallback)(SimObject *obj, S32 argc, ConsoleValue argv[]);
+typedef F32(*FloatCallback)(SimObject *obj, S32 argc, ConsoleValue argv[]);
+typedef void(*VoidCallback)(SimObject *obj, S32 argc, ConsoleValue argv[]); // We have it return a value so things don't break..
+typedef bool(*BoolCallback)(SimObject *obj, S32 argc, ConsoleValue argv[]);
 
 typedef void(*ConsumerCallback)(U32 level, const char *consoleLine);
 /// @}
@@ -376,7 +502,8 @@ namespace Con
       /// 10/14/14 - jamesu - 47->48 Added opcodes to reduce reliance on strings in function calls
       /// 10/07/17 - JTH - 48->49 Added opcode for function pointers and revamp of interpreter 
       ///                         from switch to function calls.
-      DSOVersion = 49,
+      /// 09/04/21 - JTH - 49->50 Rewrite of interpreter
+      DSOVersion = 50,
 
       MaxLineLength = 512,  ///< Maximum length of a line of console input.
       MaxDataTypes = 256    ///< Maximum number of registered data types.
@@ -786,8 +913,8 @@ namespace Con
    /// char* result = execute(2, argv);
    /// @endcode
    /// NOTE: this function restores the console stack on return.
-   ConsoleValueRef execute(S32 argc, const char* argv[]);
-   ConsoleValueRef execute(S32 argc, ConsoleValueRef argv[]);
+   ConsoleValue execute(S32 argc, const char* argv[]);
+   ConsoleValue execute(S32 argc, ConsoleValue argv[]);
 
    /// Call a Torque Script member function of a SimObject from C/C++ code.
    /// @param object    Object on which to execute the method call.
@@ -802,8 +929,8 @@ namespace Con
    /// char* result = execute(mysimobject, 3, argv);
    /// @endcode
    /// NOTE: this function restores the console stack on return.
-   ConsoleValueRef execute(SimObject *object, S32 argc, const char* argv[], bool thisCallOnly = false);
-   ConsoleValueRef execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly = false);
+   ConsoleValue execute(SimObject *object, S32 argc, const char* argv[], bool thisCallOnly = false);
+   ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool thisCallOnly = false);
 
    /// Executes a script file and compiles it for use in script.
    ///
@@ -821,13 +948,13 @@ namespace Con
    /// @param  echo     Should we echo the string to the console?
    /// @param  fileName Indicate what file this code is coming from; used in error reporting and such.
    /// NOTE: This function restores the console stack on return.
-   ConsoleValueRef evaluate(const char* string, bool echo = false, const char *fileName = NULL);
+   ConsoleValue evaluate(const char* string, bool echo = false, const char *fileName = NULL);
 
    /// Evaluate an arbitrary line of script.
    ///
    /// This wraps dVsprintf(), so you can substitute parameters into the code being executed.
    /// NOTE: This function restores the console stack on return.
-   ConsoleValueRef evaluatef(const char* string, ...);
+   ConsoleValue evaluatef(const char* string, ...);
 
    /// @}
 
@@ -915,10 +1042,10 @@ namespace Con
    /// @see _EngineConsoleExecCallbackHelper
    ///
    template<typename R, typename ...ArgTs>
-   ConsoleValueRef executef(R r, ArgTs ...argTs)
+   ConsoleValue executef(R r, ArgTs ...argTs)
    {
       _EngineConsoleExecCallbackHelper<R> callback(r);
-      return callback.template call<ConsoleValueRef>(argTs...);
+      return std::move(callback.template call<ConsoleValue>(argTs...));
    }
    /// }
 };
@@ -1192,9 +1319,9 @@ public:
       static ConsoleConstructor cfg_ConsoleFunctionGroup_##groupName##_GroupBegin(NULL,#groupName,usage)
 
 #  define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1) \
-   returnType ctf_##name(SimObject *, S32, ConsoleValueRef *argv); \
+   returnType ctf_##name(SimObject *, S32, ConsoleValue *argv); \
    ConsoleConstructor cc_##name##_obj(NULL,#name,ctf_##name,usage1,minArgs,maxArgs, true); \
-   returnType ctf_##name(SimObject *, S32 argc, ConsoleValueRef *argv)
+   returnType ctf_##name(SimObject *, S32 argc, ConsoleValue *argv)
 
 #  define ConsoleFunctionGroupEnd(groupName) \
       static ConsoleConstructor cfg_##groupName##_GroupEnd(NULL,#groupName,NULL)
@@ -1227,23 +1354,23 @@ public:
 
 // These are identical to what's above, we just want to null out the usage strings.
 #  define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1)                   \
-      static returnType c##name(SimObject *, S32, ConsoleValueRef*);                   \
+      static returnType c##name(SimObject *, S32, ConsoleValue*);                   \
       static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs);\
-      static returnType c##name(SimObject *, S32 argc, ConsoleValueRef *argv)
+      static returnType c##name(SimObject *, S32 argc, ConsoleValue *argv)
 
 #  define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1)                   \
-   static returnType c##name(SimObject *, S32, ConsoleValueRef*);                   \
+   static returnType c##name(SimObject *, S32, ConsoleValue*);                   \
    static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs, true);\
-   static returnType c##name(SimObject *, S32 argc, ConsoleValueRef *argv)
+   static returnType c##name(SimObject *, S32 argc, ConsoleValue *argv)
 
 #  define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1)                             \
-      static inline returnType c##className##name(className *, S32, ConsoleValueRef *argv);               \
-      static returnType c##className##name##caster(SimObject *object, S32 argc, ConsoleValueRef *argv) {  \
+      static inline returnType c##className##name(className *, S32, ConsoleValue *argv);               \
+      static returnType c##className##name##caster(SimObject *object, S32 argc, ConsoleValue *argv) {  \
          conmethod_return_##returnType ) c##className##name(static_cast<className*>(object),argc,argv);              \
       };                                                                                              \
       static ConsoleConstructor                                                                       \
          className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs);        \
-      static inline returnType c##className##name(className *object, S32 argc, ConsoleValueRef *argv)
+      static inline returnType c##className##name(className *object, S32 argc, ConsoleValue *argv)
 
 #define ConsoleDoc( text )
 

+ 5 - 2
Engine/source/console/consoleFunctions.cpp

@@ -2286,7 +2286,8 @@ DefineEngineStringlyVariadicFunction( call, const char *, 2, 0, "( string functi
    "@endtsexample\n\n"
    "@ingroup Scripting" )
 {
-   return Con::execute( argc - 1, argv + 1 );
+   ConsoleValue returnValue = Con::execute(argc - 1, argv + 1);
+   return Con::getReturnBuffer(returnValue.getString());
 }
 
 //-----------------------------------------------------------------------------
@@ -2410,7 +2411,9 @@ DefineEngineFunction( exec, bool, ( const char* fileName, bool noCalls, bool jou
 
 DefineEngineFunction( eval, const char*, ( const char* consoleString ), , "eval(consoleString)" )
 {
-   return Con::evaluate(consoleString, false, NULL);
+   ConsoleValue returnValue = Con::evaluate(consoleString, false, NULL);
+
+   return Con::getReturnBuffer(returnValue.getString());
 }
 
 DefineEngineFunction( getVariable, const char*, ( const char* varName ), , "(string varName)\n" 

+ 114 - 151
Engine/source/console/consoleInternal.cpp

@@ -183,13 +183,13 @@ void Dictionary::exportVariables(const char *varString, const char *fileName, bo
 
    for (s = sortList.begin(); s != sortList.end(); s++)
    {
-      switch ((*s)->value.type)
+      switch ((*s)->type)
       {
-         case ConsoleValue::TypeInternalInt:
-            dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->value.ival, cat);
+         case Entry::TypeInternalInt:
+            dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->ival, cat);
             break;
-         case ConsoleValue::TypeInternalFloat:
-            dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->value.fval, cat);
+         case Entry::TypeInternalFloat:
+            dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->fval, cat);
             break;
          default:
             expandEscape(expandBuffer, (*s)->getStringValue());
@@ -243,13 +243,11 @@ void Dictionary::exportVariables(const char *varString, Vector<String> *names, V
 
       if (values)
       {
-         switch ((*s)->value.type)
+         switch ((*s)->type)
          {
-            case ConsoleValue::TypeInternalInt:
-               values->push_back(String::ToString((*s)->value.ival));
-               break;
-            case ConsoleValue::TypeInternalFloat:
-               values->push_back(String::ToString((*s)->value.fval));
+            case ConsoleValueType::cvInteger:
+            case ConsoleValueType::cvFloat:
+               values->push_back(String((*s)->getStringValue()));
                break;
             default:
                expandEscape(expandBuffer, (*s)->getStringValue());
@@ -470,49 +468,42 @@ char *typeValueEmpty = "";
 Dictionary::Entry::Entry(StringTableEntry in_name)
 {
    name = in_name;
-   value.type = ConsoleValue::TypeInternalString;
+   type = TypeInternalString;
    notify = NULL;
    nextEntry = NULL;
    mUsage = NULL;
    mIsConstant = false;
    mNext = NULL;
-   // NOTE: This is data inside a nameless
-   // union, so we don't need to init the rest.
-   value.init();
+
+   ival = 0;
+   fval = 0;
+   sval = typeValueEmpty;
+   bufferLen = 0;
 }
 
 Dictionary::Entry::~Entry()
 {
-   value.cleanup();
+   reset();
+}
 
+void Dictionary::Entry::reset()
+{
+   name = NULL;
+   if (type <= TypeInternalString && sval != typeValueEmpty)
+      dFree(sval);
    if (notify)
       delete notify;
 }
 
-const char *Dictionary::getVariable(StringTableEntry name, bool *entValid)
+void Dictionary::Entry::setStringValue(const char* value)
 {
-   Entry *ent = lookup(name);
-   if (ent)
+   if (mIsConstant)
    {
-      if (entValid)
-         *entValid = true;
-      return ent->getStringValue();
+      Con::errorf("Cannot assign value to constant '%s'.", name);
+      return;
    }
-   if (entValid)
-      *entValid = false;
-
-   // Warn users when they access a variable that isn't defined.
-   if (gWarnUndefinedScriptVariables)
-      Con::warnf(" *** Accessed undefined variable '%s'", name);
-
-   return "";
-}
-
-void ConsoleValue::setStringValue(const char * value)
-{
-   if (value == NULL) value = typeValueEmpty;
 
-   if (type <= ConsoleValue::TypeInternalString)
+   if (type <= TypeInternalString)
    {
       // Let's not remove empty-string-valued global vars from the dict.
       // If we remove them, then they won't be exported, and sometimes
@@ -520,26 +511,12 @@ void ConsoleValue::setStringValue(const char * value)
       // few empty-string global vars so there's no performance-related
       // need to remove them from the dict.
       /*
-      if(!value[0] && name[0] == '$')
-      {
-      gEvalState.globalVars.remove(this);
-      return;
-      }
-      */
-      if (value == typeValueEmpty)
-      {
-         if (bufferLen > 0)
-         {
-            dFree(sval);
-            bufferLen = 0;
-         }
-
-         sval = typeValueEmpty;
-         fval = 0.f;
-         ival = 0;
-         type = TypeInternalString;
-         return;
-      }
+       if(!value[0] && name[0] == '$')
+       {
+       gEvalState.globalVars.remove(this);
+       return;
+       }
+       */
 
       U32 stringLen = dStrlen(value);
 
@@ -558,98 +535,44 @@ void ConsoleValue::setStringValue(const char * value)
          ival = 0;
       }
 
+      type = TypeInternalString;
+
       // may as well pad to the next cache line
       U32 newLen = ((stringLen + 1) + 15) & ~15;
 
-      if (bufferLen == 0)
-         sval = (char *)dMalloc(newLen);
+      if (sval == typeValueEmpty)
+         sval = (char*)dMalloc(newLen);
       else if (newLen > bufferLen)
-         sval = (char *)dRealloc(sval, newLen);
-
-      type = TypeInternalString;
+         sval = (char*)dRealloc(sval, newLen);
 
       bufferLen = newLen;
       dStrcpy(sval, value, newLen);
    }
    else
       Con::setData(type, dataPtr, 0, 1, &value, enumTable);
-}
-
-
-void ConsoleValue::setStackStringValue(const char *value)
-{
-   if (value == NULL) value = typeValueEmpty;
-
-   if (type <= ConsoleValue::TypeInternalString)
-   {
-      // sval might still be temporarily present so we need to check and free it
-      if (bufferLen > 0)
-      {
-         dFree(sval);
-         bufferLen = 0;
-      }
-
-      if (value == typeValueEmpty)
-      {
-         sval = typeValueEmpty;
-         fval = 0.f;
-         ival = 0;
-         type = TypeInternalString;
-         return;
-      }
 
-      U32 stringLen = dStrlen(value);
-      if (stringLen < 256)
-      {
-         fval = dAtof(value);
-         ival = dAtoi(value);
-      }
-      else
-      {
-         fval = 0.f;
-         ival = 0;
-      }
-
-      type = TypeInternalStackString;
-      sval = (char*)value;
-      bufferLen = 0;
-   }
-   else
-      Con::setData(type, dataPtr, 0, 1, &value, enumTable);
+   // Fire off the notification if we have one.
+   if (notify)
+      notify->trigger();
 }
 
-void ConsoleValue::setStringStackPtrValue(StringStackPtr ptrValue)
+const char *Dictionary::getVariable(StringTableEntry name, bool *entValid)
 {
-   if (type <= ConsoleValue::TypeInternalString)
+   Entry *ent = lookup(name);
+   if (ent)
    {
-      const char *value = StringStackPtrRef(ptrValue).getPtr(&STR);
-      if (bufferLen > 0)
-      {
-         dFree(sval);
-         bufferLen = 0;
-      }
+      if (entValid)
+         *entValid = true;
+      return ent->getStringValue();
+   }
+   if (entValid)
+      *entValid = false;
 
-      U32 stringLen = dStrlen(value);
-      if (stringLen < 256)
-      {
-         fval = dAtof(value);
-         ival = dAtoi(value);
-      }
-      else
-      {
-         fval = 0.f;
-         ival = 0;
-      }
+   // Warn users when they access a variable that isn't defined.
+   if (gWarnUndefinedScriptVariables)
+      Con::warnf(" *** Accessed undefined variable '%s'", name);
 
-      type = TypeInternalStringStackPtr;
-      sval = (char*)(value - STR.mBuffer);
-      bufferLen = 0;
-   }
-   else
-   {
-      const char *value = StringStackPtrRef(ptrValue).getPtr(&STR);
-      Con::setData(type, dataPtr, 0, 1, &value, enumTable);
-   }
+   return "";
 }
 
 S32 Dictionary::getIntVariable(StringTableEntry name, bool *entValid)
@@ -708,19 +631,17 @@ Dictionary::Entry* Dictionary::addVariable(const char *name,
 
    Entry *ent = add(StringTable->insert(name));
 
-   if (ent->value.type <= ConsoleValue::TypeInternalString &&
-      ent->value.bufferLen > 0)
-      dFree(ent->value.sval);
+   if (ent->type <= Entry::TypeInternalString && ent->sval != typeValueEmpty)
+      dFree(ent->sval);
 
-   ent->value.type = type;
-   ent->value.dataPtr = dataPtr;
    ent->mUsage = usage;
+   ent->type = type;
+   ent->dataPtr = dataPtr;
 
    // Fetch enum table, if any.
-
    ConsoleBaseType* conType = ConsoleBaseType::getType(type);
    AssertFatal(conType, "Dictionary::addVariable - invalid console type");
-   ent->value.enumTable = conType->getEnumTable();
+   ent->enumTable = conType->getEnumTable();
 
    return ent;
 }
@@ -760,7 +681,7 @@ void Dictionary::validate()
       "Dictionary::validate() - Dictionary not owner of own hashtable!");
 }
 
-void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns)
+void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns, S32 registerCount)
 {
 #ifdef DEBUG_SPEW
    validate();
@@ -789,6 +710,12 @@ void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns)
 
    AssertFatal(!newFrame.getCount(), "ExprEvalState::pushFrame - Dictionary not empty!");
 
+   ConsoleValue* consoleValArray = new ConsoleValue[registerCount]();
+   localStack.push_back(ConsoleValueFrame(consoleValArray, false));
+   currentRegisterArray = &localStack.last();
+
+   AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
+
 #ifdef DEBUG_SPEW
    validate();
 #endif
@@ -809,6 +736,15 @@ void ExprEvalState::popFrame()
    stack[mStackDepth]->reset();
    currentVariable = NULL;
 
+   const ConsoleValueFrame& frame = localStack.last();
+   localStack.pop_back();
+   if (!frame.isReference)
+      delete[] frame.values;
+
+   currentRegisterArray = localStack.size() ? &localStack.last() : NULL;
+
+   AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
+
 #ifdef DEBUG_SPEW
    validate();
 #endif
@@ -816,7 +752,7 @@ void ExprEvalState::popFrame()
 
 void ExprEvalState::pushFrameRef(S32 stackIndex)
 {
-   AssertFatal(stackIndex >= 0 && stackIndex < stack.size(), "You must be asking for a valid frame!");
+   AssertFatal(stackIndex >= 0 && stackIndex < mStackDepth, "You must be asking for a valid frame!");
 
 #ifdef DEBUG_SPEW
    validate();
@@ -840,11 +776,30 @@ void ExprEvalState::pushFrameRef(S32 stackIndex)
    mStackDepth++;
    currentVariable = NULL;
 
+   ConsoleValue* values = localStack[stackIndex].values;
+   localStack.push_back(ConsoleValueFrame(values, true));
+   currentRegisterArray = &localStack.last();
+
+   AssertFatal(mStackDepth == localStack.size(), avar("Stack sizes do not match. mStackDepth = %d, localStack = %d", mStackDepth, localStack.size()));
+
 #ifdef DEBUG_SPEW
    validate();
 #endif
 }
 
+void ExprEvalState::pushDebugFrame(S32 stackIndex)
+{
+   pushFrameRef(stackIndex);
+
+   Dictionary& newFrame = *(stack[mStackDepth - 1]);
+
+   // debugger needs to know this info...
+   newFrame.scopeName = stack[stackIndex]->scopeName;
+   newFrame.scopeNamespace = stack[stackIndex]->scopeNamespace;
+   newFrame.code = stack[stackIndex]->code;
+   newFrame.ip = stack[stackIndex]->ip;
+}
+
 ExprEvalState::ExprEvalState()
 {
    VECTOR_SET_ASSOCIATION(stack);
@@ -1413,7 +1368,7 @@ void Namespace::markGroup(const char* name, const char* usage)
 
 extern S32 executeBlock(StmtNode *block, ExprEvalState *state);
 
-ConsoleValueRef Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprEvalState *state)
+ConsoleValue Namespace::Entry::execute(S32 argc, ConsoleValue *argv, ExprEvalState *state)
 {
    STR.clearFunctionOffset();
 
@@ -1421,11 +1376,11 @@ ConsoleValueRef Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprE
    {
       if (mFunctionOffset)
       {
-         return mCode->exec(mFunctionOffset, argv[0], mNamespace, argc, argv, false, mPackage);
+         return std::move(mCode->exec(mFunctionOffset, argv[0].getString(), mNamespace, argc, argv, false, mPackage));
       }
       else
       {
-         return ConsoleValueRef();
+         return std::move(ConsoleValue());
       }
    }
 
@@ -1435,7 +1390,7 @@ ConsoleValueRef Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprE
    if (mToolOnly && !Con::isCurrentScriptToolScript())
    {
       Con::errorf(ConsoleLogEntry::Script, "%s::%s - attempting to call tools only function from outside of tools", mNamespace->mName, mFunctionName);
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
 #endif
 
@@ -1443,25 +1398,33 @@ ConsoleValueRef Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprE
    {
       Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", mNamespace->mName, mFunctionName);
       Con::warnf(ConsoleLogEntry::Script, "usage: %s", mUsage);
-      return ConsoleValueRef();
+      return std::move(ConsoleValue());
    }
 
+   ConsoleValue result;
    switch (mType)
    {
       case StringCallbackType:
-         return ConsoleValueRef::fromValue(CSTK.pushStackString(cb.mStringCallbackFunc(state->thisObject, argc, argv)));
+      {
+         const char* str = cb.mStringCallbackFunc(state->thisObject, argc, argv);
+         result.setString(str);
+         break;
+      }
       case IntCallbackType:
-         return ConsoleValueRef::fromValue(CSTK.pushUINT((U32)cb.mIntCallbackFunc(state->thisObject, argc, argv)));
+         result.setInt(cb.mIntCallbackFunc(state->thisObject, argc, argv));
+         break;
       case FloatCallbackType:
-         return ConsoleValueRef::fromValue(CSTK.pushFLT((U32)cb.mFloatCallbackFunc(state->thisObject, argc, argv)));
+         result.setFloat(cb.mFloatCallbackFunc(state->thisObject, argc, argv));
+         break;
       case VoidCallbackType:
          cb.mVoidCallbackFunc(state->thisObject, argc, argv);
-         return ConsoleValueRef();
+         break;
       case BoolCallbackType:
-         return ConsoleValueRef::fromValue(CSTK.pushUINT((U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv)));
+         result.setBool(cb.mBoolCallbackFunc(state->thisObject, argc, argv));
+         break;
    }
 
-   return ConsoleValueRef();
+   return std::move(result);
 }
 
 //-----------------------------------------------------------------------------

+ 167 - 47
Engine/source/console/consoleInternal.h

@@ -150,7 +150,7 @@ public:
       void clear();
 
       ///
-      ConsoleValueRef execute(S32 argc, ConsoleValueRef* argv, ExprEvalState* state);
+      ConsoleValue execute(S32 argc, ConsoleValue* argv, ExprEvalState* state);
 
       /// Return a one-line documentation text string for the function.
       String getBriefDescription(String* outRemainingDocText = NULL) const;
@@ -274,17 +274,24 @@ public:
 
 typedef VectorPtr<Namespace::Entry *>::iterator NamespaceEntryListIterator;
 
-
-
 class Dictionary
 {
 public:
 
    struct Entry
    {
+      friend class Dictionary;
+
+      enum
+      {
+         TypeInternalInt = -3,
+         TypeInternalFloat = -2,
+         TypeInternalString = -1,
+      };
+
       StringTableEntry name;
-      ConsoleValue value;
       Entry *nextEntry;
+      S32 type;
 
       typedef Signal<void()> NotifySignal;
 
@@ -298,16 +305,56 @@ public:
       /// Whether this is a constant that cannot be assigned to.
       bool mIsConstant;
 
+   protected:
+
+      // NOTE: This is protected to ensure no one outside
+      // of this structure is messing with it.
+
+#pragma warning( push )
+#pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union
+
+      // An variable is either a real dynamic type or
+      // its one exposed from C++ using a data pointer.
+      //
+      // We use this nameless union and struct setup
+      // to optimize the memory usage.
+      union
+      {
+         struct
+         {
+            char* sval;
+            U32 ival;  // doubles as strlen when type is TypeInternalString
+            F32 fval;
+            U32 bufferLen;
+         };
+
+         struct
+         {
+            /// The real data pointer.
+            void* dataPtr;
+
+            /// The enum lookup table for enumerated types.
+            const EnumTable* enumTable;
+         };
+      };
+
+#pragma warning( pop ) // C4201
+
    public:
 
       Entry() {
          name = NULL;
+         type = TypeInternalString;
          notify = NULL;
          nextEntry = NULL;
          mUsage = NULL;
          mIsConstant = false;
          mNext = NULL;
-         value.init();
+
+         ival = 0;
+         fval = 0;
+         sval = typeValueEmpty;
+         bufferLen = 0;
       }
 
       Entry(StringTableEntry name);
@@ -315,26 +362,34 @@ public:
 
       Entry *mNext;
 
-      void reset() {
-         name = NULL;
-         value.cleanup();
-         if (notify)
-            delete notify;
-      }
+      void reset();
 
       inline U32 getIntValue()
       {
-         return value.getIntValue();
+         if (type <= TypeInternalString)
+            return ival;
+         else
+            return dAtoi(Con::getData(type, dataPtr, 0, enumTable));
       }
 
       inline F32 getFloatValue()
       {
-         return value.getFloatValue();
+         if (type <= TypeInternalString)
+            return fval;
+         else
+            return dAtof(Con::getData(type, dataPtr, 0, enumTable));
       }
 
       inline const char *getStringValue()
       {
-         return value.getStringValue();
+         if (type == TypeInternalString)
+            return sval;
+         if (type == TypeInternalFloat)
+            return Con::getData(TypeF32, &fval, 0);
+         else if (type == TypeInternalInt)
+            return Con::getData(TypeS32, &ival, 0);
+         else
+            return Con::getData(type, dataPtr, 0, enumTable);
       }
 
       void setIntValue(U32 val)
@@ -345,7 +400,22 @@ public:
             return;
          }
 
-         value.setIntValue(val);
+         if (type <= TypeInternalString)
+         {
+            fval = (F32)val;
+            ival = val;
+            if (sval != typeValueEmpty)
+            {
+               dFree(sval);
+               sval = typeValueEmpty;
+            }
+            type = TypeInternalInt;
+         }
+         else
+         {
+            const char* dptr = Con::getData(TypeS32, &val, 0);
+            Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
+         }
 
          // Fire off the notification if we have one.
          if (notify)
@@ -360,44 +430,29 @@ public:
             return;
          }
 
-         value.setFloatValue(val);
-
-         // Fire off the notification if we have one.
-         if (notify)
-            notify->trigger();
-      }
-
-      void setStringStackPtrValue(StringStackPtr newValue)
-      {
-         if (mIsConstant)
+         if (type <= TypeInternalString)
          {
-            Con::errorf("Cannot assign value to constant '%s'.", name);
-            return;
+            fval = val;
+            ival = static_cast<U32>(val);
+            if (sval != typeValueEmpty)
+            {
+               dFree(sval);
+               sval = typeValueEmpty;
+            }
+            type = TypeInternalFloat;
          }
-
-         value.setStringStackPtrValue(newValue);
-
-
-         // Fire off the notification if we have one.
-         if (notify)
-            notify->trigger();
-      }
-
-      void setStringValue(const char *newValue)
-      {
-         if (mIsConstant)
+         else
          {
-            Con::errorf("Cannot assign value to constant '%s'.", name);
-            return;
+            const char* dptr = Con::getData(TypeF32, &val, 0);
+            Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
          }
 
-         value.setStringValue(newValue);
-
-
          // Fire off the notification if we have one.
          if (notify)
             notify->trigger();
       }
+
+      void setStringValue(const char* value);
    };
 
    struct HashTableData
@@ -471,6 +526,21 @@ public:
    void validate();
 };
 
+struct ConsoleValueFrame
+{
+   ConsoleValue* values;
+   bool isReference;
+
+   ConsoleValueFrame() : values(NULL), isReference(false)
+   {}
+
+   ConsoleValueFrame(ConsoleValue* vals, bool isRef)
+   {
+      values = vals;
+      isReference = isRef;
+   }
+};
+
 class ExprEvalState
 {
 public:
@@ -499,6 +569,11 @@ public:
    /// an interior pointer that will become invalid when the object changes address.
    Vector< Dictionary* > stack;
 
+   S32 getTopOfStack() { return (S32)mStackDepth; }
+
+   Vector< ConsoleValueFrame > localStack;
+   ConsoleValueFrame* currentRegisterArray; // contains array at to top of localStack
+
    ///
    Dictionary globalVars;
 
@@ -511,16 +586,56 @@ public:
    void setIntVariable(S32 val);
    void setFloatVariable(F64 val);
    void setStringVariable(const char *str);
-   void setStringStackPtrVariable(StringStackPtr str);
-   void setCopyVariable();
 
-   void pushFrame(StringTableEntry frameName, Namespace *ns);
+   TORQUE_FORCEINLINE S32 getLocalIntVariable(S32 reg)
+   {
+      return currentRegisterArray->values[reg].getInt();
+   }
+
+   TORQUE_FORCEINLINE F64 getLocalFloatVariable(S32 reg)
+   {
+      return currentRegisterArray->values[reg].getFloat();
+   }
+
+   TORQUE_FORCEINLINE const char* getLocalStringVariable(S32 reg)
+   {
+      return currentRegisterArray->values[reg].getString();
+   }
+
+   TORQUE_FORCEINLINE void setLocalIntVariable(S32 reg, S64 val)
+   {
+      currentRegisterArray->values[reg].setInt(val);
+   }
+
+   TORQUE_FORCEINLINE void setLocalFloatVariable(S32 reg, F64 val)
+   {
+      currentRegisterArray->values[reg].setFloat(val);
+   }
+
+   TORQUE_FORCEINLINE void setLocalStringVariable(S32 reg, const char* val, S32 len)
+   {
+      currentRegisterArray->values[reg].setString(val, len);
+   }
+
+   TORQUE_FORCEINLINE void setLocalStringTableEntryVariable(S32 reg, StringTableEntry val)
+   {
+      currentRegisterArray->values[reg].setStringTableEntry(val);
+   }
+
+   TORQUE_FORCEINLINE void moveConsoleValue(S32 reg, ConsoleValue val)
+   {
+      currentRegisterArray->values[reg] = std::move(val);
+   }
+
+   void pushFrame(StringTableEntry frameName, Namespace *ns, S32 regCount);
    void popFrame();
 
    /// Puts a reference to an existing stack frame
    /// on the top of the stack.
    void pushFrameRef(S32 stackIndex);
 
+   void pushDebugFrame(S32 stackIndex);
+
    U32 getStackDepth() const
    {
       return mStackDepth;
@@ -531,6 +646,11 @@ public:
       return *(stack[mStackDepth - 1]);
    }
 
+   Dictionary& getFrameAt(S32 depth)
+   {
+      return *(stack[depth]);
+   }
+
    /// @}
 
    /// Run integrity checks for debugging.

+ 4 - 4
Engine/source/console/consoleLogger.cpp

@@ -79,18 +79,18 @@ void ConsoleLogger::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-bool ConsoleLogger::processArguments( S32 argc, ConsoleValueRef *argv )
+bool ConsoleLogger::processArguments( S32 argc, ConsoleValue *argv )
 {
    if( argc == 0 )
       return false;
 
    bool append = false;
 
-   if( argc == 2 )
-      append = dAtob( argv[1] );
+   if (argc == 2)
+      append = argv[1].getBool();
 
    mAppend = append;
-   mFilename = StringTable->insert( argv[0] );
+   mFilename = StringTable->insert( argv[0].getString() );
 
    if( init() )
    {

+ 1 - 1
Engine/source/console/consoleLogger.h

@@ -81,7 +81,7 @@ class ConsoleLogger : public SimObject
       /// // Example script constructor usage.
       /// %obj = new ConsoleLogger( objName, logFileName, [append = false] );
       /// @endcode
-      bool processArguments( S32 argc, ConsoleValueRef *argv );
+      bool processArguments( S32 argc, ConsoleValue *argv );
 
       /// Default constructor, make sure to initalize
       ConsoleLogger();

+ 109 - 0
Engine/source/console/consoleValueStack.h

@@ -0,0 +1,109 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+// Copyright (c) 2021 TGEMIT Authors & Contributors
+//
+// 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.
+//-----------------------------------------------------------------------------
+
+#ifndef _CONSOLE_CONSOLE_VALUE_STACK_H_
+#define _CONSOLE_CONSOLE_VALUE_STACK_H_
+
+template<S32 StackSize>
+class ConsoleValueStack
+{
+   constexpr static S32 allocatorSize = sizeof(ConsoleValue) * StackSize;
+
+   struct Frame
+   {
+      ConsoleValue* values;
+      S32 count;
+      S32 internalCounter;
+   };
+
+   Vector<Frame> stack;
+   char* memory;
+   S32 sp;
+
+   TORQUE_FORCEINLINE Frame alloc(S32 count)
+   {
+      AssertFatal(sp + count * sizeof(ConsoleValue) < allocatorSize, "ConsoleValueStack overflow");
+
+      ConsoleValue* ret = reinterpret_cast<ConsoleValue*>(memory + sp);
+      sp += count * sizeof(ConsoleValue);
+
+      return { ret, count, 1 };
+   }
+
+   TORQUE_FORCEINLINE void deAlloc(S32 count)
+   {
+      sp -= count * sizeof(ConsoleValue);
+      AssertFatal(sp >= 0, "Popped ConsoleValueStack too far, underflow");
+   }
+
+public:
+   ConsoleValueStack()
+   {
+      memory = (char*)dMalloc(allocatorSize);
+      for (S32 i = 0; i < allocatorSize; i += sizeof(ConsoleValue))
+      {
+         constructInPlace<ConsoleValue>(reinterpret_cast<ConsoleValue*>(memory + i));
+      }
+      sp = 0;
+   }
+
+   ~ConsoleValueStack()
+   {
+      dFree(memory);
+   }
+
+   TORQUE_FORCEINLINE void pushFrame(S32 count)
+   {
+      AssertISV(count >= 0, "Must be >= 0 when pushing stack frame");
+
+      // +1 for function name in argv[0]
+      const Frame& frame = alloc(count + 1);
+      stack.push_back(frame);
+   }
+
+   TORQUE_FORCEINLINE void popFrame()
+   {
+      AssertISV(stack.size() > 0, "Stack Underflow");
+
+      deAlloc(stack.last().count);
+      stack.pop_back();
+   }
+
+   TORQUE_FORCEINLINE void push(ConsoleValue&& val)
+   {
+      Frame& frame = stack.last();
+      frame.values[frame.internalCounter++] = std::move(val);
+   }
+
+   TORQUE_FORCEINLINE void argvc(StringTableEntry fn, S32& argc, ConsoleValue** argv)
+   {
+      Frame& frame = stack.last();
+      argc = frame.count;
+
+      // First param is always function name
+      frame.values[0].setStringTableEntry(fn);
+      *argv = frame.values;
+   }
+};
+
+#endif

+ 67 - 80
Engine/source/console/engineAPI.h

@@ -148,55 +148,45 @@ inline const char* EngineMarshallData( U32 value )
 /// Marshal data from native into client form stored directly in
 /// client function invocation vector.
 template< typename T >
-inline void EngineMarshallData( const T& arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( const T& arg, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = castConsoleTypeToString( arg );
-   argc ++;
+   const char* str = castConsoleTypeToString(arg);;
+   argv[ argc++ ].setString(str);
 }
-inline void EngineMarshallData( bool arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( bool arg, S32& argc, ConsoleValue *argv )
 {
-   if( arg )
-      argv[ argc ] = 1;
-   else
-      argv[ argc ] = 0;
-   argc ++;
+   argv[ argc++ ].setBool(arg);
 }
-inline void EngineMarshallData( S32 arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( S32 arg, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = arg;
-   argc ++;
+   argv[ argc++ ].setInt(arg);
 }
-inline void EngineMarshallData( U32 arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( U32 arg, S32& argc, ConsoleValue *argv )
 {
    EngineMarshallData( S32( arg ), argc, argv );
 }
-inline void EngineMarshallData( F32 arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( F32 arg, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = arg;
-   argc ++;
+   argv[ argc++ ].setFloat(arg);
 }
-inline void EngineMarshallData( const char* arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( const char* arg, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = arg;
-   argc ++;
+   argv[ argc++ ].setString(arg);
 }
-inline void EngineMarshallData( char* arg, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( char* arg, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = arg;
-   argc ++;
+   argv[ argc++ ].setString(arg);
 }
 
 template< typename T >
-inline void EngineMarshallData( T* object, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( T* object, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = object ? object->getId() : 0;
-   argc ++;
+   argv[ argc++ ].setInt(object ? object->getId() : 0);
 }
 template< typename T >
-inline void EngineMarshallData( const T* object, S32& argc, ConsoleValueRef *argv )
+inline void EngineMarshallData( const T* object, S32& argc, ConsoleValue *argv )
 {
-   argv[ argc ] = object ? object->getId() : 0;
-   argc ++;
+   argv[ argc++ ].setInt(object ? object->getId() : 0);
 }
 
 /// Unmarshal data from client form to engine form.
@@ -216,9 +206,9 @@ struct EngineUnmarshallData
 template<>
 struct EngineUnmarshallData< S32 >
 {
-   S32 operator()( ConsoleValueRef &ref ) const
+   S32 operator()( ConsoleValue &ref ) const
    {
-      return (S32)ref;
+      return (S32)ref.getInt();
    }
 
    S32 operator()( const char* str ) const
@@ -229,9 +219,9 @@ struct EngineUnmarshallData< S32 >
 template<>
 struct EngineUnmarshallData< U32 >
 {
-   U32 operator()( ConsoleValueRef &ref ) const
+   U32 operator()( ConsoleValue &ref ) const
    {
-      return (U32)((S32)ref);
+      return (U32)ref.getInt();
    }
 
    U32 operator()( const char* str ) const
@@ -242,9 +232,9 @@ struct EngineUnmarshallData< U32 >
 template<>
 struct EngineUnmarshallData< F32 >
 {
-   F32 operator()( ConsoleValueRef &ref ) const
+   F32 operator()( ConsoleValue &ref ) const
    {
-      return (F32)ref;
+      return (F32)ref.getFloat();
    }
 
    F32 operator()( const char* str ) const
@@ -255,9 +245,9 @@ struct EngineUnmarshallData< F32 >
 template<>
 struct EngineUnmarshallData< U8 >
 {
-   U8 operator()( ConsoleValueRef &ref ) const
+   U8 operator()( ConsoleValue &ref ) const
    {
-      return (U8)((S32)ref);
+      return (U8)((S32)ref.getInt());
    }
 
    U8 operator()( const char* str ) const
@@ -268,9 +258,9 @@ struct EngineUnmarshallData< U8 >
 template<>
 struct EngineUnmarshallData< const char* >
 {
-   const char* operator()( ConsoleValueRef &ref ) const
+   const char* operator()( ConsoleValue &ref ) const
    {
-      return ref.getStringValue();
+      return ref.getString();
    }
 
    const char* operator()( const char* str ) const
@@ -281,9 +271,9 @@ struct EngineUnmarshallData< const char* >
 template< typename T >
 struct EngineUnmarshallData< T* >
 {
-   T* operator()( ConsoleValueRef &ref ) const
+   T* operator()( ConsoleValue &ref ) const
    {
-      return dynamic_cast< T* >( Sim::findObject( ref.getStringValue() ) );
+      return dynamic_cast< T* >( Sim::findObject( ref ) );
    }
 
    T* operator()( const char* str ) const
@@ -294,17 +284,17 @@ struct EngineUnmarshallData< T* >
 template<>
 struct EngineUnmarshallData< void >
 {
-   void operator()( ConsoleValueRef& ) const {}
+   void operator()( ConsoleValue& ) const {}
    void operator()( const char* ) const {}
 };
 
 
 template<>
-struct EngineUnmarshallData< ConsoleValueRef >
+struct EngineUnmarshallData< ConsoleValue >
 {
-   ConsoleValueRef operator()( ConsoleValueRef ref ) const
+   ConsoleValue operator()( ConsoleValue ref ) const
    {
-      return ref;
+      return std::move(ref);
    }
 };
 
@@ -548,7 +538,7 @@ namespace engineAPI{
          static const S32 NUM_ARGS = sizeof...(ArgTs) + startArgc;
          
          template<size_t index, size_t method_offset = 0, typename ...RealArgTs>
-         static IthArgType<index> getRealArgValue(S32 argc, ConsoleValueRef *argv, const _EngineFunctionDefaultArguments< void(RealArgTs...) >& defaultArgs)
+         static IthArgType<index> getRealArgValue(S32 argc, ConsoleValue *argv, const _EngineFunctionDefaultArguments< void(RealArgTs...) >& defaultArgs)
          {
             if((startArgc + index) < argc)
             {
@@ -559,12 +549,12 @@ namespace engineAPI{
          }
          
          template<size_t ...I>
-         static R dispatchHelper(S32 argc, ConsoleValueRef *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs, Seq<I...>){
+         static R dispatchHelper(S32 argc, ConsoleValue *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs, Seq<I...>){
             return fn(SelfType::getRealArgValue<I>(argc, argv, defaultArgs) ...);
          }
          
          template<typename Frame, size_t ...I>
-         static R dispatchHelper(S32 argc, ConsoleValueRef *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs, Seq<I...>){
+         static R dispatchHelper(S32 argc, ConsoleValue *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs, Seq<I...>){
             return (frame->*fn)(SelfType::getRealArgValue<I,1>(argc, argv, defaultArgs) ...);
          }
          
@@ -579,9 +569,9 @@ namespace engineAPI{
          }
       };
       
-      template<> struct MarshallHelpers<ConsoleValueRef> {
-         template<typename ...ArgTs> static void marshallEach(S32 &argc, ConsoleValueRef *argv, const ArgTs& ...args){}
-         template<typename H, typename ...Tail> static void marshallEach(S32 &argc, ConsoleValueRef *argv, const H& head, const Tail& ...tail){
+      template<> struct MarshallHelpers<ConsoleValue> {
+         template<typename ...ArgTs> static void marshallEach(S32 &argc, ConsoleValue *argv, const ArgTs& ...args){}
+         template<typename H, typename ...Tail> static void marshallEach(S32 &argc, ConsoleValue *argv, const H& head, const Tail& ...tail){
             EngineMarshallData(head, argc, argv);
             marshallEach(argc, argv, tail...);
          }
@@ -604,12 +594,12 @@ public:
    template<typename Frame> using MethodType = typename Helper::template MethodType<Frame>;
    static const S32 NUM_ARGS = Helper::NUM_ARGS;
    
-   static ReturnType thunk( S32 argc, ConsoleValueRef *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs)
+   static ReturnType thunk( S32 argc, ConsoleValue *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs)
    {
       return _EngineConsoleThunkReturnValue( Helper::dispatchHelper(argc, argv, fn, defaultArgs, SeqType()));
    }
    template< typename Frame >
-   static ReturnType thunk( S32 argc, ConsoleValueRef *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs)
+   static ReturnType thunk( S32 argc, ConsoleValue *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs)
    {
       return _EngineConsoleThunkReturnValue( Helper::dispatchHelper(argc, argv, fn, frame, defaultArgs, SeqType()));
    }
@@ -627,12 +617,12 @@ public:
    template<typename Frame> using MethodType = typename Helper::template MethodType<Frame>;
    static const S32 NUM_ARGS = Helper::NUM_ARGS;
    
-   static void thunk( S32 argc, ConsoleValueRef *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs)
+   static void thunk( S32 argc, ConsoleValue *argv, FunctionType fn, const _EngineFunctionDefaultArguments< void(ArgTs...) >& defaultArgs)
    {
       Helper::dispatchHelper(argc, argv, fn, defaultArgs, SeqType());
    }
    template< typename Frame >
-   static void thunk( S32 argc, ConsoleValueRef *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs)
+   static void thunk( S32 argc, ConsoleValue *argv, MethodType<Frame> fn, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, ArgTs...) >& defaultArgs)
    {
       Helper::dispatchHelper(argc, argv, fn, frame, defaultArgs, SeqType());
    }
@@ -705,7 +695,7 @@ public:
       ( void* ) &fn ## name,                                                                                                     \
       0                                                                                                                          \
    );                                                                                                                            \
-   static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv )   \
+   static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, ConsoleValue *argv )      \
    {                                                                                                                             \
       return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk(                \
          argc, argv, &_fn ## name ## impl, _fn ## name ## DefaultArgs                                                            \
@@ -785,7 +775,7 @@ public:
       ( void* ) &fn ## className ## _ ## name,                                                                                                  \
       0                                                                                                                                         \
    );                                                                                                                                           \
-   static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, ConsoleValueRef *argv )  \
+   static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, ConsoleValue *argv )  \
    {                                                                                                                                            \
       _ ## className ## name ## frame frame;                                                                                                    \
       frame.object = static_cast< className* >( object );                                                                                       \
@@ -842,7 +832,7 @@ public:
       ( void* ) &fn ## className ## _ ## name,                                                                                         \
       0                                                                                                                                \
    );                                                                                                                                  \
-   static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv )\
+   static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, ConsoleValue *argv )\
    {                                                                                                                                   \
       return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk(                      \
          argc, argv, &_fn ## className ## name ## impl, _fn ## className ## name ## DefaultArgs                                        \
@@ -859,12 +849,12 @@ public:
    static inline returnType _fn ## className ## name ## impl args
 
 #  define DefineEngineStringlyVariadicFunction(name,returnType,minArgs,maxArgs,usage)                                            \
-   static inline returnType _fn ## name ## impl (SimObject *, S32 argc, ConsoleValueRef *argv);                                  \
+   static inline returnType _fn ## name ## impl (SimObject *, S32 argc, ConsoleValue *argv);                                     \
    TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## name                                                         \
       (Vector<const char*>* vec)                                                                                                 \
    {                                                                                                                             \
       _CHECK_ENGINE_INITIALIZED( name, returnType );                                                                             \
-      StringStackConsoleWrapper args(vec->size(), vec->address());                                                               \
+      StringArrayToConsoleValueWrapper args(vec->size(), vec->address());                                                               \
       return EngineTypeTraits< returnType >::ReturnValue(                                                                        \
          _fn ## name ## impl(NULL, args.count(), args)                                                                           \
       );                                                                                                                         \
@@ -882,20 +872,20 @@ public:
       0                                                                                                                          \
    );                                                                                                                            \
    ConsoleConstructor cc_##name##_obj(NULL,#name,_fn ## name ## impl,usage,minArgs,maxArgs); \
-      returnType _fn ## name ## impl(SimObject *, S32 argc, ConsoleValueRef *argv)
+      returnType _fn ## name ## impl(SimObject *, S32 argc, ConsoleValue *argv)
 
 #  define DefineEngineStringlyVariadicMethod(className, name,returnType,minArgs,maxArgs,usage)                                   \
    struct _ ## className ## name ## frame                                                                                        \
    {                                                                                                                             \
       typedef className ObjectType;                                                                                              \
       className* object;                                                                                                         \
-      inline returnType _exec (S32 argc, ConsoleValueRef* argv) const;                                                           \
+      inline returnType _exec (S32 argc, ConsoleValue* argv) const;                                                              \
    };                                                                                                                            \
    TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## className ## _ ## name                                       \
       (className* object, Vector<const char*>* vec)                                                                              \
    {                                                                                                                             \
       _CHECK_ENGINE_INITIALIZED( name, returnType );                                                                             \
-      StringStackConsoleWrapper args(vec->size(), vec->address());                                                               \
+      StringArrayToConsoleValueWrapper args(vec->size(), vec->address());                                                               \
       _ ## className ## name ## frame frame {};                                                                                  \
       frame.object = static_cast< className* >( object );                                                                        \
       return EngineTypeTraits< returnType >::ReturnValue(                                                                        \
@@ -915,14 +905,14 @@ public:
       ( void* ) &fn ## className ## _ ## name,                                                                                   \
       0                                                                                                                          \
    );                                                                                                                            \
-   returnType cm_##className##_##name##_caster(SimObject* object, S32 argc, ConsoleValueRef* argv) {                             \
+   returnType cm_##className##_##name##_caster(SimObject* object, S32 argc, ConsoleValue* argv) {                                \
       AssertFatal( dynamic_cast<className*>( object ), "Object passed to " #name " is not a " #className "!" );                  \
       _ ## className ## name ## frame frame {};                                                                                  \
       frame.object = static_cast< className* >( object );                                                                        \
       conmethod_return_##returnType ) frame._exec(argc,argv);                                                                    \
    };                                                                                                                            \
    ConsoleConstructor cc_##className##_##name##_obj(#className,#name,cm_##className##_##name##_caster,usage,minArgs,maxArgs);    \
-   inline returnType _ ## className ## name ## frame::_exec(S32 argc, ConsoleValueRef *argv) const
+   inline returnType _ ## className ## name ## frame::_exec(S32 argc, ConsoleValue *argv) const
 
 
 
@@ -1167,10 +1157,10 @@ public:
    S32 mInitialArgc;
    S32 mArgc;
    StringTableEntry mCallbackName;
-   ConsoleValueRef mArgv[ MAX_ARGUMENTS + 2 ];
+   ConsoleValue mArgv[ MAX_ARGUMENTS + 2 ];
 
-   ConsoleValueRef _exec();
-   ConsoleValueRef _execLater(SimConsoleThreadExecEvent *evt);
+   ConsoleValue _exec();
+   ConsoleValue _execLater(SimConsoleThreadExecEvent *evt);
 
    _BaseEngineConsoleCallbackHelper(): mThis(NULL), mInitialArgc(0), mArgc(0), mCallbackName(StringTable->EmptyString()){;}
 };
@@ -1181,7 +1171,7 @@ public:
 struct _EngineConsoleCallbackHelper : public _BaseEngineConsoleCallbackHelper
 {
 private:
-   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValueRef>;
+   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValue>;
 public:
 
    _EngineConsoleCallbackHelper( StringTableEntry callbackName, SimObject* pThis )
@@ -1197,8 +1187,7 @@ public:
       if (Con::isMainThread())
       {
          ConsoleStackFrameSaver sav; sav.save();
-         CSTK.reserveValues(mArgc + sizeof...(ArgTs), mArgv);
-         mArgv[ 0 ].value->setStackStringValue(mCallbackName);
+         mArgv[ 0 ].setStringTableEntry(mCallbackName);
         
         Helper::marshallEach(mArgc, mArgv, args...);
         
@@ -1209,7 +1198,7 @@ public:
          SimConsoleThreadExecCallback cb;
          SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(mArgc + sizeof...(ArgTs), NULL, false, &cb);
          evt->populateArgs(mArgv);
-         mArgv[ 0 ].value->setStackStringValue(mCallbackName);
+         mArgv[ 0 ].setStringTableEntry(mCallbackName);
         
         Helper::marshallEach(mArgc, mArgv, args...);
         
@@ -1226,7 +1215,7 @@ public:
 template<typename P1> struct _EngineConsoleExecCallbackHelper : public _BaseEngineConsoleCallbackHelper
 {
 private:
-   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValueRef>;
+   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValue>;
 public:
 
    _EngineConsoleExecCallbackHelper( SimObject* pThis )
@@ -1243,8 +1232,7 @@ public:
       if (Con::isMainThread())
       {
          ConsoleStackFrameSaver sav; sav.save();
-         CSTK.reserveValues(mArgc+sizeof...(ArgTs), mArgv);
-         mArgv[ 0 ].value->setStackStringValue(simCB);
+         mArgv[ 0 ].setString(simCB);
 
         Helper::marshallEach(mArgc, mArgv, args...);
 
@@ -1255,7 +1243,7 @@ public:
          SimConsoleThreadExecCallback cb;
          SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(mArgc+sizeof...(ArgTs), NULL, true, &cb);
          evt->populateArgs(mArgv);
-         mArgv[ 0 ].value->setStackStringValue(simCB);
+         mArgv[ 0 ].setString(simCB);
         
         Helper::marshallEach(mArgc, mArgv, args...);
 
@@ -1270,7 +1258,7 @@ public:
 template<> struct _EngineConsoleExecCallbackHelper<const char*> : public _BaseEngineConsoleCallbackHelper
 {
 private:
-   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValueRef>;
+   using Helper = engineAPI::detail::MarshallHelpers<ConsoleValue>;
 public:
    _EngineConsoleExecCallbackHelper( const char *callbackName )
    {
@@ -1285,10 +1273,9 @@ public:
       if (Con::isMainThread())
       {
          ConsoleStackFrameSaver sav; sav.save();
-         CSTK.reserveValues(mArgc+sizeof...(ArgTs), mArgv);
-         mArgv[ 0 ].value->setStackStringValue(mCallbackName);
+         mArgv[ 0 ].setStringTableEntry(mCallbackName);
         
-        Helper::marshallEach(mArgc, mArgv, args...);
+         Helper::marshallEach(mArgc, mArgv, args...);
         
          return R( EngineUnmarshallData< R >()( _exec() ) );
       }
@@ -1297,7 +1284,7 @@ public:
          SimConsoleThreadExecCallback cb;
          SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(mArgc+sizeof...(ArgTs), NULL, false, &cb);
          evt->populateArgs(mArgv);
-         mArgv[ 0 ].value->setStackStringValue(mCallbackName);
+         mArgv[ 0 ].setStringTableEntry(mCallbackName);
         
         Helper::marshallEach(mArgc, mArgv, args...);
 

+ 2 - 2
Engine/source/console/engineDoc.cpp

@@ -114,7 +114,7 @@ static void dumpVariable(  Stream& stream,
 {
    // Skip variables defined in script.
    
-   if( entry->value.type < 0 )
+   if( entry->type <= Dictionary::Entry::TypeInternalString )
       return;
          
    // Skip internals... don't export them.
@@ -149,7 +149,7 @@ static void dumpVariable(  Stream& stream,
             
    // Skip variables for which we can't decipher their type.
 
-   ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.type );
+   ConsoleBaseType* type = ConsoleBaseType::getType( entry->type );
    if( !type )
    {
       Con::errorf( "Can't find type for variable '%s'", entry->name );

+ 104 - 0
Engine/source/console/optimizer.cpp

@@ -0,0 +1,104 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+// Copyright (c) 2021 TGEMIT Authors & Contributors
+//
+// 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 "console/console.h"
+#include "console/codeBlock.h"
+
+static bool isLiteralNumber(ExprNode* node)
+{
+   ExprNodeName name = node->getExprNodeNameEnum();
+   return name == NameFloatNode || name == NameIntNode;
+}
+
+static F64 getFloatValue(ExprNode* node)
+{
+   if (node->getExprNodeNameEnum() == NameFloatNode)
+      return static_cast<FloatNode*>(node)->value;
+   return (F64)static_cast<IntNode*>(node)->value;
+}
+
+static S32 getIntValue(ExprNode* node)
+{
+   if (node->getExprNodeNameEnum() == NameFloatNode)
+      return (S32)static_cast<FloatNode*>(node)->value;
+   return static_cast<IntNode*>(node)->value;
+}
+
+bool FloatBinaryExprNode::optimize()
+{
+   // Perform constant folding
+   if (isLiteralNumber(right) && isLiteralNumber(left))
+   {
+      F64 rightValue = getFloatValue(right);
+      F64 leftValue = getFloatValue(left);
+      F64 result = 0.0;
+
+      switch (op)
+      {
+      case '+':
+         result = leftValue + rightValue;
+         break;
+      case '-':
+         result = leftValue - rightValue;
+         break;
+      case '*':
+         result = leftValue * rightValue;
+         break;
+      case '/':
+         if (rightValue != 0.0)
+            result = leftValue / rightValue;
+         break;
+      }
+
+      optimizedNode = FloatNode::alloc(dbgLineNumber, result);
+      return true;
+   }
+
+   return false;
+}
+
+bool IntBinaryExprNode::optimize()
+{
+   if (op == '%' && left->getExprNodeNameEnum() == NameVarNode && isLiteralNumber(right))
+   {
+      // %a % intconst
+      S32 val = getIntValue(right);
+      switch (val)
+      {
+      case 2: 
+         op = '&';
+         optimizedNode = IntNode::alloc(dbgLineNumber, 1);
+         return true;
+      case 4:
+         op = '&';
+         optimizedNode = IntNode::alloc(dbgLineNumber, 3);
+         return true;
+      case 8:
+         op = '&';
+         optimizedNode = IntNode::alloc(dbgLineNumber, 7);
+         return true;
+      }
+   }
+
+   return false;
+}

+ 3 - 6
Engine/source/console/sim.h

@@ -125,15 +125,12 @@ namespace Sim
    SimDataBlockGroup *getDataBlockGroup();
    SimGroup* getRootGroup();
 
-   SimObject* findObject(ConsoleValueRef&);
    SimObject* findObject(SimObjectId);
+   SimObject* findObject(const ConsoleValue&);
+   SimObject* findObject(ConsoleValue*);
    SimObject* findObject(const char* name);
    SimObject* findObject(const char* fileName, S32 declarationLine);
-   template<class T> inline bool findObject(ConsoleValueRef &ref,T*&t)
-   {
-      t = dynamic_cast<T*>(findObject(ref));
-      return t != NULL;
-   }
+
    template<class T> inline bool findObject(SimObjectId iD,T*&t)
    {
       t = dynamic_cast<T*>(findObject(iD));

+ 29 - 30
Engine/source/console/simEvents.cpp

@@ -28,30 +28,23 @@
 // Stupid globals not declared in a header
 extern ExprEvalState gEvalState;
 
-SimConsoleEvent::SimConsoleEvent(S32 argc, ConsoleValueRef *argv, bool onObject)
+SimConsoleEvent::SimConsoleEvent(S32 argc, ConsoleValue *argv, bool onObject)
 {
    mOnObject = onObject;
    mArgc = argc;
 
-   mArgv = new ConsoleValueRef[argc];
+   mArgv = new ConsoleValue[argc]();
    for (int i=0; i<argc; i++)
    {
-      mArgv[i].value = new ConsoleValue();
-      mArgv[i].value->type = ConsoleValue::TypeInternalString;
-      mArgv[i].value->init();
-     if (argv)
-     {
-      mArgv[i].value->setStringValue((const char*)argv[i]);
-     }
+      if (argv)
+      {
+         mArgv[i].setString(argv[i].getString());
+      }
    }
 }
 
 SimConsoleEvent::~SimConsoleEvent()
 {
-   for (int i=0; i<mArgc; i++)
-   {
-      delete mArgv[i].value;
-   }
    delete[] mArgv;
 }
 
@@ -67,7 +60,7 @@ void SimConsoleEvent::process(SimObject* object)
       // Grab the function name. If '::' doesn't exist, then the schedule is
       // on a global function.
       char funcName[256];
-      dStrncpy(funcName, (const char*)mArgv[0], 256);
+      dStrncpy(funcName, mArgv[0].getString(), 256);
       char* func = dStrstr( funcName, (char*)"::" );
       if( func )
       {
@@ -95,11 +88,11 @@ void SimConsoleEvent::process(SimObject* object)
    }
 }
 
-void SimConsoleEvent::populateArgs(ConsoleValueRef *argv)
+void SimConsoleEvent::populateArgs(ConsoleValue *argv)
 {
    for (U32 i=0; i<mArgc; i++)
    {
-      argv[i].value = mArgv[i].value;
+      argv[i].setString(mArgv[i].getString());
    }
 }
 
@@ -107,7 +100,6 @@ void SimConsoleEvent::populateArgs(ConsoleValueRef *argv)
 
 SimConsoleThreadExecCallback::SimConsoleThreadExecCallback()
 {
-   retVal.value = NULL;
    sem = new Semaphore(0);
 }
 
@@ -116,37 +108,44 @@ SimConsoleThreadExecCallback::~SimConsoleThreadExecCallback()
    delete sem;
 }
 
-void SimConsoleThreadExecCallback::handleCallback(ConsoleValueRef ret)
+void SimConsoleThreadExecCallback::handleCallback(ConsoleValue ret)
 {
-   retVal = ret;
+   // can we move this pls?
+   retVal.setString(ret.getString());
    sem->release();
 }
 
-ConsoleValueRef SimConsoleThreadExecCallback::waitForResult()
+ConsoleValue SimConsoleThreadExecCallback::waitForResult()
 {
    if(sem->acquire(true))
    {
-      return retVal;
+      return std::move(retVal);
    }
 
-   return ConsoleValueRef();
+   return ConsoleValue();
 }
 
 //-----------------------------------------------------------------------------
 
-SimConsoleThreadExecEvent::SimConsoleThreadExecEvent(S32 argc, ConsoleValueRef *argv, bool onObject, SimConsoleThreadExecCallback *callback) :
+SimConsoleThreadExecEvent::SimConsoleThreadExecEvent(S32 argc, ConsoleValue *argv, bool onObject, SimConsoleThreadExecCallback *callback) :
    SimConsoleEvent(argc, argv, onObject), cb(callback)
 {
 }
 
 void SimConsoleThreadExecEvent::process(SimObject* object)
 {
-   ConsoleValueRef retVal;
-   if(mOnObject)
-      retVal = Con::execute(object, mArgc, mArgv);
+   if (cb)
+   {
+      if (mOnObject)
+         cb->handleCallback(std::move(Con::execute(object, mArgc, mArgv)));
+      else
+         cb->handleCallback(std::move(Con::execute(mArgc, mArgv)));
+   }
    else
-      retVal = Con::execute(mArgc, mArgv);
-
-   if(cb)
-      cb->handleCallback(retVal);
+   {
+      if (mOnObject)
+         Con::execute(object, mArgc, mArgv);
+      else
+         Con::execute(mArgc, mArgv);
+   }
 }

+ 7 - 9
Engine/source/console/simEvents.h

@@ -83,8 +83,6 @@ public:
    virtual void process(SimObject *object)=0;
 };
 
-class ConsoleValueRef;
-
 /// Implementation of schedule() function.
 ///
 /// This allows you to set a console function to be
@@ -93,7 +91,7 @@ class SimConsoleEvent : public SimEvent
 {
 protected:
    S32 mArgc;
-   ConsoleValueRef *mArgv;
+   ConsoleValue *mArgv;
    bool mOnObject;
 public:
 
@@ -110,13 +108,13 @@ public:
    ///
    /// @see Con::execute(S32 argc, const char *argv[])
    /// @see Con::execute(SimObject *object, S32 argc, const char *argv[])
-   SimConsoleEvent(S32 argc, ConsoleValueRef *argv, bool onObject);
+   SimConsoleEvent(S32 argc, ConsoleValue *argv, bool onObject);
 
    ~SimConsoleEvent();
    virtual void process(SimObject *object);
 
    /// Creates a reference to our internal args list in argv
-   void populateArgs(ConsoleValueRef *argv);
+   void populateArgs(ConsoleValue *argv);
 };
 
 
@@ -125,13 +123,13 @@ public:
 struct SimConsoleThreadExecCallback
 {
    Semaphore   *sem;
-   ConsoleValueRef retVal;
+   ConsoleValue retVal;
 
    SimConsoleThreadExecCallback();
    ~SimConsoleThreadExecCallback();
 
-   void handleCallback(ConsoleValueRef ret);
-   ConsoleValueRef waitForResult();
+   void handleCallback(ConsoleValue ret);
+   ConsoleValue waitForResult();
 };
 
 class SimConsoleThreadExecEvent : public SimConsoleEvent
@@ -139,7 +137,7 @@ class SimConsoleThreadExecEvent : public SimConsoleEvent
    SimConsoleThreadExecCallback *cb;
 
 public:
-   SimConsoleThreadExecEvent(S32 argc, ConsoleValueRef *argv, bool onObject, SimConsoleThreadExecCallback *callback);
+   SimConsoleThreadExecEvent(S32 argc, ConsoleValue *argv, bool onObject, SimConsoleThreadExecCallback *callback);
 
    SimConsoleThreadExecCallback& getCB() { return *cb; }
    virtual void process(SimObject *object);

+ 14 - 5
Engine/source/console/simManager.cpp

@@ -328,11 +328,6 @@ SimObject* findObject(const char* fileName, S32 declarationLine)
    return gRootGroup->findObjectByLineNumber(fileName, declarationLine, true);
 }
 
-SimObject* findObject(ConsoleValueRef &ref)
-{
-   return findObject((const char*)ref);
-}
-
 SimObject* findObject(const char* name)
 {
    PROFILE_SCOPE(SimFindObject);
@@ -391,6 +386,20 @@ SimObject* findObject(const char* name)
    return obj->findObject(name + len + 1);
 }
 
+SimObject* findObject(const ConsoleValue &val)
+{
+   if (val.getType() == ConsoleValueType::cvInteger)
+      return findObject((SimObjectId)val.getFastInt());
+   return findObject(val.getString());
+}
+
+SimObject* findObject(ConsoleValue* val)
+{
+   if (val->getType() == ConsoleValueType::cvInteger)
+      return findObject((SimObjectId)val->getFastInt());
+   return findObject(val->getString());
+}
+
 SimObject* findObject(SimObjectId id)
 {
    return gIdDictionary->find(id);

+ 8 - 6
Engine/source/console/simObject.cpp

@@ -141,7 +141,7 @@ SimObject::~SimObject()
 
 //-----------------------------------------------------------------------------
 
-bool SimObject::processArguments(S32 argc, ConsoleValueRef *argv)
+bool SimObject::processArguments(S32 argc, ConsoleValue *argv)
 {
    return argc == 0;
 }
@@ -2971,8 +2971,10 @@ DefineEngineStringlyVariadicMethod( SimObject, call, const char*, 3, 0, "( strin
    "@param args Zero or more arguments for the method.\n"
    "@return The result of the method call." )
 {
-   argv[1] = argv[2];
-   return Con::execute( object, argc - 1, argv + 1 );
+   argv[1].setString(argv[2].getString());
+
+   ConsoleValue returnValue = Con::execute(object, argc - 1, argv + 1);
+   return Con::getReturnBuffer(returnValue.getString());
 }
 
 //-----------------------------------------------------------------------------
@@ -3074,9 +3076,9 @@ DefineEngineStringlyVariadicMethod( SimObject,schedule, S32, 4, 0, "( float time
    "@param args The arguments with which to call the method.\n"
    "@return The numeric ID of the created schedule.  Can be used to cancel the call.\n" )
 {
-   U32 timeDelta = U32(dAtof(argv[2]));
-   argv[2] = argv[3];
-   argv[3] = argv[1];
+   U32 timeDelta = U32(argv[2].getFloat());
+   argv[2].setString(argv[3].getString());
+   argv[3].setString(argv[1].getString());
    SimConsoleEvent *evt = new SimConsoleEvent(argc - 2, argv + 2, true);
    S32 ret = Sim::postEvent(object, evt, Sim::getCurrentTime() + timeDelta);
    // #ifdef DEBUG

+ 1 - 1
Engine/source/console/simObject.h

@@ -587,7 +587,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks
       
       virtual ~SimObject();
 
-      virtual bool processArguments(S32 argc, ConsoleValueRef *argv);  ///< Process constructor options. (ie, new SimObject(1,2,3))
+      virtual bool processArguments(S32 argc, ConsoleValue *argv);  ///< Process constructor options. (ie, new SimObject(1,2,3))
 
       /// @}
 

+ 1 - 1
Engine/source/console/simPersistSet.cpp

@@ -47,7 +47,7 @@ SimPersistSet::SimPersistSet()
 
 //-----------------------------------------------------------------------------
 
-bool SimPersistSet::processArguments( S32 argc, ConsoleValueRef *argv )
+bool SimPersistSet::processArguments( S32 argc, ConsoleValue *argv )
 {
    for( U32 i = 0; i < argc; ++ i )
    {

+ 1 - 1
Engine/source/console/simPersistSet.h

@@ -58,7 +58,7 @@ class SimPersistSet : public SimSet
       // SimSet.
       virtual void addObject( SimObject* );
       virtual void write( Stream &stream, U32 tabStop, U32 flags = 0 );
-      virtual bool processArguments( S32 argc, ConsoleValueRef *argv );
+      virtual bool processArguments( S32 argc, ConsoleValue *argv );
       
       DECLARE_CONOBJECT( SimPersistSet );
       DECLARE_CATEGORY( "Console" );

+ 5 - 7
Engine/source/console/simSet.cpp

@@ -229,17 +229,15 @@ void SimSet::scriptSort( const String &scriptCallbackFn )
 
 //-----------------------------------------------------------------------------
 
-void SimSet::callOnChildren( const String &method, S32 argc, ConsoleValueRef argv[], bool executeOnChildGroups )
+void SimSet::callOnChildren( const String &method, S32 argc, ConsoleValue argv[], bool executeOnChildGroups )
 {
    // Prep the arguments for the console exec...
    // Make sure and leave args[1] empty.
-   ConsoleValueRef args[21] = { };
-   ConsoleValue name_method;
-   name_method.setStackStringValue(method.c_str());
-   args[0] = ConsoleValueRef::fromValue(&name_method);
+   ConsoleValue args[21] = { };
+   args[0].setString(method.c_str());
 
    for (S32 i = 0; i < argc; i++)
-      args[i + 2] = argv[i];
+      args[i + 2].setString(argv[i].getString());
 
    for( iterator i = begin(); i != end(); i++ )
    {
@@ -838,7 +836,7 @@ SimGroup* SimGroup::deepClone()
 
 //-----------------------------------------------------------------------------
 
-bool SimGroup::processArguments(S32, ConsoleValueRef *argv)
+bool SimGroup::processArguments(S32, ConsoleValue *argv)
 {
    return true;
 }

+ 2 - 2
Engine/source/console/simSet.h

@@ -218,7 +218,7 @@ class SimSet : public SimObject, public TamlChildren
       
       /// @}
 
-      void callOnChildren( const String &method, S32 argc, ConsoleValueRef argv[], bool executeOnChildGroups = true );
+      void callOnChildren( const String &method, S32 argc, ConsoleValue argv[], bool executeOnChildGroups = true );
 
       /// Return the number of objects in this set as well as all sets that are contained
       /// in this set and its children.
@@ -464,7 +464,7 @@ class SimGroup: public SimSet
       virtual SimObject* findObject(const char* name);
       virtual void onRemove();
 
-      virtual bool processArguments( S32 argc, ConsoleValueRef *argv );
+      virtual bool processArguments( S32 argc, ConsoleValue *argv );
 
       virtual SimObject* getObject(const S32& index);
 

+ 1 - 254
Engine/source/console/stringStack.cpp

@@ -39,7 +39,6 @@ StringStack::StringStack()
    mStart = 0;
    mLen = 0;
    mStartStackSize = 0;
-   mFunctionOffset = 0;
    validateBufferSize(8192);
    validateArgBufferSize(2048);
    dMemset(mBuffer, '\0', mBufferSize);
@@ -82,7 +81,7 @@ void StringStack::setIntValue(U32 i)
 void StringStack::setFloatValue(F64 v)
 {
    validateBufferSize(mStart + 32);
-   dSprintf(mBuffer + mStart, 32, "%g", v);
+   dSprintf(mBuffer + mStart, 32, "%.9g", v);
    mLen = dStrlen(mBuffer + mStart);
 }
 
@@ -180,255 +179,3 @@ U32 StringStack::compare()
 
    return ret;
 }
-
-void StringStack::pushFrame()
-{
-   //Con::printf("StringStack pushFrame [frame=%i, start=%i]", mNumFrames, mStartStackSize);
-   mFrameOffsets[mNumFrames++] = mStartStackSize;
-   mStartOffsets[mStartStackSize++] = mStart;
-   mStart += ReturnBufferSpace;
-   validateBufferSize(0);
-}
-
-void StringStack::popFrame()
-{
-   //Con::printf("StringStack popFrame [frame=%i, start=%i]", mNumFrames, mStartStackSize);
-   mStartStackSize = mFrameOffsets[--mNumFrames];
-   mStart = mStartOffsets[mStartStackSize];
-   mLen = 0;
-}
-
-void StringStack::clearFrames()
-{
-   //Con::printf("StringStack clearFrames");
-   mNumFrames = 0;
-   mStart = 0;
-   mLen = 0;
-   mStartStackSize = 0;
-   mFunctionOffset = 0;
-}
-
-
-void ConsoleValueStack::getArgcArgv(StringTableEntry name, U32 *argc, ConsoleValueRef **in_argv, bool popStackFrame /* = false */)
-{
-   U32 startStack = mStackFrames[mFrame-1];
-   U32 argCount   = getMin(mStackPos - startStack, (U32)MaxArgs - 1);
-
-   *in_argv = mArgv;
-   mArgv[0].value = CSTK.pushStackString(name);
-   
-   for(U32 i = 0; i < argCount; i++) {
-      ConsoleValueRef *ref = &mArgv[i+1];
-      ref->value = &mStack[startStack + i];
-   }
-   argCount++;
-   
-   *argc = argCount;
-
-   if(popStackFrame)
-      popFrame();
-}
-
-ConsoleValueStack::ConsoleValueStack() : 
-mFrame(0),
-mStackPos(0)
-{
-   for (int i=0; i<ConsoleValueStack::MaxStackDepth; i++) {
-      mStack[i].init();
-      mStack[i].type = ConsoleValue::TypeInternalString;
-   }
-   dMemset(mStackFrames, 0, sizeof(mStackFrames));
-}
-
-ConsoleValueStack::~ConsoleValueStack()
-{
-}
-
-void ConsoleValueStack::pushVar(ConsoleValue *variable)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return;
-   }
-
-   switch (variable->type)
-   {
-   case ConsoleValue::TypeInternalInt:
-      mStack[mStackPos++].setIntValue((S32)variable->getIntValue());
-   case ConsoleValue::TypeInternalFloat:
-      mStack[mStackPos++].setFloatValue((F32)variable->getFloatValue());
-   default:
-      mStack[mStackPos++].setStackStringValue(variable->getStringValue());
-   }
-}
-
-void ConsoleValueStack::pushValue(ConsoleValue &variable)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return;
-   }
-
-   switch (variable.type)
-   {
-   case ConsoleValue::TypeInternalInt:
-      mStack[mStackPos++].setIntValue((S32)variable.getIntValue());
-   case ConsoleValue::TypeInternalFloat:
-      mStack[mStackPos++].setFloatValue((F32)variable.getFloatValue());
-   case ConsoleValue::TypeInternalStringStackPtr:
-      mStack[mStackPos++].setStringStackPtrValue(variable.getStringStackPtr());
-   default:
-      mStack[mStackPos++].setStringValue(variable.getStringValue());
-   }
-}
-
-ConsoleValue* ConsoleValueStack::reserveValues(U32 count)
-{
-   U32 startPos = mStackPos;
-   if (startPos+count >= ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK reserveValues %i", mStackPos, count);
-   mStackPos += count;
-   return &mStack[startPos];
-}
-
-bool ConsoleValueStack::reserveValues(U32 count, ConsoleValueRef *outValues)
-{
-   U32 startPos = mStackPos;
-   if (startPos+count >= ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return false;
-   }
-
-   //Con::printf("[%i]CSTK reserveValues %i", mStackPos, count);
-   for (U32 i=0; i<count; i++)
-   {
-      outValues[i].value = &mStack[mStackPos+i];
-   }
-   mStackPos += count;
-   return true;
-}
-
-ConsoleValue *ConsoleValueStack::pushString(const char *value)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK pushString %s", mStackPos, value);
-
-   mStack[mStackPos++].setStringValue(value);
-   return &mStack[mStackPos-1];
-}
-
-ConsoleValue *ConsoleValueStack::pushStackString(const char *value)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK pushString %s", mStackPos, value);
-
-   mStack[mStackPos++].setStackStringValue(value);
-   return &mStack[mStackPos-1];
-}
-
-ConsoleValue *ConsoleValueStack::pushStringStackPtr(StringStackPtr value)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK pushStringStackPtr %s", mStackPos, StringStackPtrRef(value).getPtr(&STR));
-
-   mStack[mStackPos++].setStringStackPtrValue(value);
-   return &mStack[mStackPos-1];
-}
-
-ConsoleValue *ConsoleValueStack::pushUINT(U32 value)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK pushUINT %i", mStackPos, value);
-
-   mStack[mStackPos++].setIntValue(value);
-   return &mStack[mStackPos-1];
-}
-
-ConsoleValue *ConsoleValueStack::pushFLT(float value)
-{
-   if (mStackPos == ConsoleValueStack::MaxStackDepth) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return NULL;
-   }
-
-   //Con::printf("[%i]CSTK pushFLT %f", mStackPos, value);
-
-   mStack[mStackPos++].setFloatValue(value);
-   return &mStack[mStackPos-1];
-}
-
-static ConsoleValue gNothing;
-
-ConsoleValue* ConsoleValueStack::pop()
-{
-   if (mStackPos == 0) {
-      AssertFatal(false, "Console Value Stack is empty");
-      return &gNothing;
-   }
-
-   return &mStack[--mStackPos];
-}
-
-void ConsoleValueStack::pushFrame()
-{
-   //Con::printf("CSTK pushFrame[%i] (%i)", mFrame, mStackPos);
-   mStackFrames[mFrame++] = mStackPos;
-}
-
-void ConsoleValueStack::resetFrame()
-{
-   if (mFrame == 0) {
-      mStackPos = 0;
-      return;
-   }
-
-   U32 start = mStackFrames[mFrame-1];
-   //for (U32 i=start; i<mStackPos; i++) {
-      //mStack[i].clear();
-   //}
-   mStackPos = start;
-   //Con::printf("CSTK resetFrame to %i", mStackPos);
-}
-
-void ConsoleValueStack::clearFrames()
-{
-   mStackPos = 0;
-   mFrame = 0;
-}
-
-void ConsoleValueStack::popFrame()
-{
-   //Con::printf("CSTK popFrame");
-   if (mFrame == 0) {
-      // Go back to start
-      mStackPos = 0;
-      return;
-   }
-
-   U32 start = mStackFrames[mFrame-1];
-   //for (U32 i=start; i<mStackPos; i++) {
-      //mStack[i].clear();
-   //}
-   mStackPos = start;
-   mFrame--;
-}

+ 3 - 64
Engine/source/console/stringStack.h

@@ -114,15 +114,15 @@ struct StringStack
    }
 
    /// Get an integer representation of the top of the stack.
-   inline U32 getIntValue()
+   inline S64 getIntValue()
    {
-      return dAtoi(mBuffer + mStart);
+      return dAtol(mBuffer + mStart);
    }
 
    /// Get a float representation of the top of the stack.
    inline F64 getFloatValue()
    {
-      return dAtof(mBuffer + mStart);
+      return dAtod(mBuffer + mStart);
    }
 
    /// Get a string representation of the top of the stack.
@@ -138,16 +138,6 @@ struct StringStack
       return mBuffer + mStartOffsets[mStartStackSize-1];
    }
 
-   inline StringStackPtr getStringValuePtr()
-   {
-      return (getStringValue() - mBuffer);
-   }
-
-   inline StringStackPtr getPreviousStringValuePtr()
-   {
-      return (getPreviousStringValue() - mBuffer);
-   }
-
    /// Advance the start stack, placing a zero length string on the top.
    ///
    /// @note You should use StringStack::push, not this, if you want to
@@ -179,61 +169,10 @@ struct StringStack
    /// and returning true if they matched, false if they didn't.
    U32 compare();
 
-   void pushFrame();
-
-   void popFrame();
-
    void clearFrames();
-
-   /// Get the arguments for a function call from the stack.
-   void getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv, bool popStackFrame = false);
-};
-
-
-// New console value stack
-class ConsoleValueStack
-{
-   enum {
-      MaxStackDepth = 1024,
-      MaxArgs = 20,
-      ReturnBufferSpace = 512
-   };
-
-public:
-   ConsoleValueStack();
-   ~ConsoleValueStack();
-
-   void pushVar(ConsoleValue *variable);
-   void pushValue(ConsoleValue &value);
-   ConsoleValue* reserveValues(U32 numValues);
-   bool reserveValues(U32 numValues, ConsoleValueRef *values);
-   ConsoleValue* pop();
-
-   ConsoleValue *pushString(const char *value);
-   ConsoleValue *pushStackString(const char *value);
-   ConsoleValue *pushStringStackPtr(StringStackPtr ptr);
-   ConsoleValue *pushUINT(U32 value);
-   ConsoleValue *pushFLT(float value);
-
-   void pushFrame();
-   void popFrame();
-
-   void resetFrame();
-   void clearFrames();
-
-   void getArgcArgv(StringTableEntry name, U32 *argc, ConsoleValueRef **in_argv, bool popStackFrame = false);
-
-   ConsoleValue mStack[MaxStackDepth];
-   U32 mStackFrames[MaxStackDepth];
-
-   U32 mFrame;
-   U32 mStackPos;
-
-   ConsoleValueRef mArgv[MaxArgs];
 };
 
 extern StringStack STR;
-extern ConsoleValueStack CSTK;
 
 inline char* StringStackPtrRef::getPtr(StringStack *stk) { return stk->mBuffer + mOffset; }
 

+ 46 - 3
Engine/source/console/telnetDebugger.cpp

@@ -864,6 +864,49 @@ void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char *
    if ( frame < 0 )
       frame = 0;
 
+   // Local variables use their own memory management and can't be queried by just executing
+   // TorqueScript, we have to go digging into the interpreter.
+   S32 evalBufferLen = dStrlen(evalBuffer);
+   bool isEvaluatingLocalVariable = evalBufferLen > 0 && evalBuffer[0] == '%';
+   if (isEvaluatingLocalVariable)
+   {
+      // See calculation of current frame in pushing a reference frame for console exec, we need access
+      // to the proper scope.
+      //frame = gEvalState.getTopOfStack() - frame - 1;
+      S32 stackIndex = gEvalState.getTopOfStack() - frame - 1;
+
+      const char* format = "EVALOUT %s %s\r\n";
+
+      gEvalState.pushDebugFrame(stackIndex);
+
+      Dictionary& stackFrame = gEvalState.getCurrentFrame();
+      StringTableEntry functionName = stackFrame.scopeName;
+      StringTableEntry namespaceName = stackFrame.scopeNamespace->mName;
+      StringTableEntry varToLookup = StringTable->insert(evalBuffer);
+
+      S32 registerId = stackFrame.code->variableRegisterTable.lookup(namespaceName, functionName, varToLookup);
+
+      if (registerId == -1)
+      {
+         // ERROR, can't read the variable!
+         send("EVALOUT \"\" \"\"");
+         return;
+      }
+
+      const char* varResult = gEvalState.getLocalStringVariable(registerId);
+
+      gEvalState.popFrame();
+
+      S32 len = dStrlen(format) + dStrlen(tag) + dStrlen(varResult);
+      char* buffer = new char[len];
+      dSprintf(buffer, len, format, tag, varResult[0] ? varResult : "\"\"");
+
+      send(buffer);
+      delete[] buffer;
+
+      return;
+   }
+
    // Build a buffer just big enough for this eval.
    const char* format = "return %s;";
    dsize_t len = dStrlen( format ) + dStrlen( evalBuffer );
@@ -872,14 +915,14 @@ void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char *
 
    // Execute the eval.
    CodeBlock *newCodeBlock = new CodeBlock();
-   const char* result = newCodeBlock->compileExec( NULL, buffer, false, frame );
+   ConsoleValue result = newCodeBlock->compileExec( NULL, buffer, false, frame );
    delete [] buffer;
    
    // Create a new buffer that fits the result.
    format = "EVALOUT %s %s\r\n";
-   len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result );
+   len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result.getString() );
    buffer = new char[ len ];
-   dSprintf( buffer, len, format, tag, result[0] ? result : "\"\"" );
+   dSprintf( buffer, len, format, tag, result.getString()[0] ? result.getString() : "\"\"" );
 
    send( buffer );
    delete [] buffer;

+ 730 - 113
Engine/source/console/test/ScriptTest.cpp

@@ -30,139 +30,153 @@
 #include "math/mMath.h"
 #include "console/stringStack.h"
 
-template<typename T>
-inline T Convert(ConsoleValueRef);
-
-template<>
-inline U32 Convert(ConsoleValueRef val)
+inline ConsoleValue RunScript(const char* str)
 {
-   return val.getIntValue();
+   return std::move(Con::evaluate(str, false, NULL));
 }
 
-template<>
-inline S32 Convert(ConsoleValueRef val)
+TEST(Script, Basic_Arithmetic)
 {
-   return val.getSignedIntValue();
-}
+   ConsoleValue add = RunScript(R"(
+         return 1.0 + 1;
+   )");
 
-template<>
-inline bool Convert(ConsoleValueRef val)
-{
-   return val.getBoolValue();
-}
+   ASSERT_EQ(add.getInt(), 2);
 
-template<>
-inline F32 Convert(ConsoleValueRef val)
-{
-   return val.getFloatValue();
-}
+   ConsoleValue sub = RunScript(R"(
+         return 10 - 1.0;
+   )");
 
-template<>
-inline const char* Convert(ConsoleValueRef val)
-{
-   return val.getStringValue();
-}
+   ASSERT_EQ(sub.getInt(), 9);
 
-template<>
-inline SimObject* Convert(ConsoleValueRef val)
-{
-   return Sim::findObject(val);
-}
+   ConsoleValue mult = RunScript(R"(
+         return 10 * 2.5;
+   )");
 
-template<typename T>
-inline T RunScript(const char* str)
-{
-   return Convert<T>(Con::evaluate(str, false, NULL));
-}
+   ASSERT_EQ(mult.getInt(), 25);
 
-TEST(Script, Basic_Arithmetic)
-{
-   S32 add = RunScript<S32>(R"(
-         return 1.0 + 1;
+   ConsoleValue div = RunScript(R"(
+         return 10.0 / 2;
    )");
 
-   EXPECT_EQ(add, 2);
+   ASSERT_EQ(div.getInt(), 5);
 
-   S32 sub = RunScript<S32>(R"(
-         return 10 - 1.0;
+   ConsoleValue mod = RunScript(R"(
+         return 4 % 5;
    )");
 
-   EXPECT_EQ(sub, 9);
+   ASSERT_EQ(mod.getInt(), 4);
 
-   S32 mult = RunScript<S32>(R"(
-         return 10 * 2.5;
+   ConsoleValue add2 = RunScript(R"(
+         $a = 0;
+         $a += 2;
+         return $a;
    )");
 
-   EXPECT_EQ(mult, 25);
+   ASSERT_EQ(add2.getInt(), 2);
 
-   S32 div = RunScript<S32>(R"(
-         return 10.0 / 2;
+   ConsoleValue sub2 = RunScript(R"(
+         $a = 0;
+         $a -= 2;
+         return $a;
+   )");
+
+   ASSERT_EQ(sub2.getInt(), -2);
+
+   ConsoleValue mult2 = RunScript(R"(
+         $a = 2;
+         $a *= 3;
+         return $a;
    )");
 
-   EXPECT_EQ(div, 5);
+   ASSERT_EQ(mult2.getInt(), 6);
+
+   ConsoleValue div2 = RunScript(R"(
+         $a = 10;
+         $a /= 2;
+         return $a;
+   )");
+
+   ASSERT_EQ(div2.getInt(), 5);
+
+   ConsoleValue pp = RunScript(R"(
+         $a = 0;
+         $a++;
+         return $a;
+   )");
+
+   ASSERT_EQ(pp.getInt(), 1);
+
+   ConsoleValue mm = RunScript(R"(
+         $a = 2;
+         $a--;
+         return $a;
+   )");
+
+   ASSERT_EQ(mm.getInt(), 1);
 }
 
 TEST(Script, Complex_Arithmetic)
 {
-   S32 result = RunScript<S32>(R"(
+   ConsoleValue result = RunScript(R"(
          return 1 * 2 - (0.5 * 2);
    )");
 
-   EXPECT_EQ(result, 1);
+   ASSERT_EQ(result.getInt(), 1);
 
-   S32 result2 = RunScript<S32>(R"(
+   ConsoleValue result2 = RunScript(R"(
          return 1 * 2 * 3 % 2;
    )");
 
-   EXPECT_EQ(result2, 0);
+   ASSERT_EQ(result2.getInt(), 0);
 }
 
 TEST(Script, Basic_Concatination)
 {
-   const char* result1 = RunScript<const char*>(R"(
+   ConsoleValue result1 = RunScript(R"(
          return "a" @ "b";
    )");
 
-   EXPECT_STREQ(result1, "ab");
+   ASSERT_STREQ(result1.getString(), "ab");
 
-   const char* result2 = RunScript<const char*>(R"(
+   ConsoleValue result2 = RunScript(R"(
          return "a" SPC "b";
    )");
 
-   EXPECT_STREQ(result2, "a b");
+   ASSERT_STREQ(result2.getString(), "a b");
 
-   const char* result3 = RunScript<const char*>(R"(
+   ConsoleValue result3 = RunScript(R"(
          return "a" TAB "b";
    )");
 
-   EXPECT_STREQ(result3, "a\tb");
+   ASSERT_STREQ(result3.getString(), "a\tb");
 
-   const char* result4 = RunScript<const char*>(R"(
+   ConsoleValue result4 = RunScript(R"(
          return "a" NL "b";
    )");
 
-   EXPECT_STREQ(result4, "a\nb");
+   ASSERT_STREQ(result4.getString(), "a\nb");
 
-   const char* complex = RunScript<const char*>(R"(
+   ConsoleValue complex = RunScript(R"(
          return "a" @ "b" @ "c" @ "d";
    )");
 
-   EXPECT_STREQ(complex, "abcd");
+   ASSERT_STREQ(complex.getString(), "abcd");
 }
 
 TEST(Script, Basic_Global_Variable_Tests)
 {
-   S32 value = RunScript<S32>(R"(
+   ConsoleValue value = RunScript(R"(
          $a = 1;
          return $a;
    )");
 
-   EXPECT_EQ(value, 1);
+   ASSERT_EQ(value.getInt(), 1);
 }
 
 TEST(Script, Variable_Chaining_And_Usage)
 {
-   S32 value = RunScript<S32>(R"(
+   ConsoleValue value = RunScript(R"(
          function t() 
          {
             %a = %b = 2;
@@ -171,9 +185,9 @@ TEST(Script, Variable_Chaining_And_Usage)
          return t();
    )");
 
-   EXPECT_EQ(value, 2);
+   ASSERT_EQ(value.getInt(), 2);
 
-   S32 valueGlobal = RunScript<S32>(R"(
+   ConsoleValue valueGlobal = RunScript(R"(
          function t() 
          {
             $a = %b = 2;
@@ -182,9 +196,9 @@ TEST(Script, Variable_Chaining_And_Usage)
          return $a;
    )");
 
-   EXPECT_EQ(valueGlobal, 2);
+   ASSERT_EQ(valueGlobal.getInt(), 2);
 
-   S32 value2 = RunScript<S32>(R"(
+   ConsoleValue value2 = RunScript(R"(
          function t(%a) 
          {
             if ((%b = 2 * %a) != 5)
@@ -195,26 +209,26 @@ TEST(Script, Variable_Chaining_And_Usage)
          return t(2);
    )");
 
-   EXPECT_EQ(value2, 4);
+   ASSERT_EQ(value2.getInt(), 4);
 }
 
 TEST(Script, Basic_Function_Call_And_Local_Variable_Testing)
 {
-   S32 value = RunScript<S32>(R"(
+   ConsoleValue value = RunScript(R"(
          function t() { %a = 2; return %a; }
          return t();
    )");
 
-   EXPECT_EQ(value, 2);
+   ASSERT_EQ(value.getInt(), 2);
 
-   S32 value2 = RunScript<S32>(R"(
+   ConsoleValue value2 = RunScript(R"(
          function add(%a, %b) { return %a + %b; }
          return add(2, 4);
    )");
 
-   EXPECT_EQ(value2, 6);
+   ASSERT_EQ(value2.getInt(), 6);
 
-   S32 value3 = RunScript<S32>(R"(
+   ConsoleValue value3 = RunScript(R"(
          function fib(%a) {
             if (%a == 0)
                return 0;
@@ -225,48 +239,48 @@ TEST(Script, Basic_Function_Call_And_Local_Variable_Testing)
          return fib(15);
    )");
 
-   EXPECT_EQ(value3, 610);
+   ASSERT_EQ(value3.getInt(), 610);
 
-   S32 staticCall = RunScript<S32>(R"(
+   ConsoleValue staticCall = RunScript(R"(
          function SimObject::bar(%a, %b) {
             return %a + %b;
          }
          return SimObject::bar(1, 2);
    )");
 
-   EXPECT_EQ(staticCall, 3);
+   ASSERT_EQ(staticCall.getInt(), 3);
 }
 
 TEST(Script, Basic_Conditional_Statements)
 {
-   S32 value = RunScript<S32>(R"(
+   ConsoleValue value = RunScript(R"(
          $a = "hello";
          if ($a $= "hello")
             return 1;
          return 2;
    )");
 
-   EXPECT_EQ(value, 1);
+   ASSERT_EQ(value.getInt(), 1);
 
-   const char* ternaryValue = RunScript<const char*>(R"(
+   ConsoleValue ternaryValue = RunScript(R"(
          return $a $= "hello" ? "World" : "No U";
    )");
 
-   EXPECT_STREQ(ternaryValue, "World");
+   ASSERT_STREQ(ternaryValue.getString(), "World");
 }
 
 TEST(Script, Basic_Loop_Statements)
 {
-   S32 whileValue = RunScript<S32>(R"(
+   ConsoleValue whileValue = RunScript(R"(
          $count = 0;
          while ($count < 5)
             $count++;
          return $count;
    )");
 
-   EXPECT_EQ(whileValue, 5);
+   ASSERT_EQ(whileValue.getInt(), 5);
 
-   const char* forValue = RunScript<const char*>(R"(
+   ConsoleValue forValue = RunScript(R"(
          function t(%times) 
          { 
             %result = "";
@@ -278,12 +292,28 @@ TEST(Script, Basic_Loop_Statements)
          return t(3);
    )");
 
-   EXPECT_STREQ(forValue, "aaa");
+   ASSERT_STREQ(forValue.getString(), "aaa");
+
+   ConsoleValue forReverseLoop = RunScript(R"(
+         function t(%times) 
+         { 
+            %result = "";
+            for (%i = %times - 1; %i >= 0; %i--)
+               %result = %result @ "b";
+            return %result;
+         }
+
+         return t(3);
+   )");
+
+   ASSERT_STREQ(forReverseLoop.getString(), "bbb");
 
-   const char* forIfValue = RunScript<const char*>(R"(
-         function t() {
+   ConsoleValue forIfValue = RunScript(R"(
+         function t()
+         {
             %str = "";
-            for (%i = 0; %i < 5; %i++) {
+            for (%i = 0; %i < 5; %i++)
+            {
 
                %loopValue = %i;
 
@@ -298,43 +328,238 @@ TEST(Script, Basic_Loop_Statements)
          return t();
    )");
 
-   EXPECT_STREQ(forIfValue, "0, 1, 2, 3, 4");
+   ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4");
+}
+
+TEST(Script, ForEachLoop)
+{
+   ConsoleValue forEach1 = RunScript(R"(
+         $theSimSet = new SimSet();
+         $theSimSet.add(new SimObject());
+         $theSimSet.add(new SimObject());
+
+         $counter = 0;
+         foreach ($obj in $theSimSet)
+         {
+            $counter++;
+         }
+
+         $theSimSet.delete();
+
+         return $counter;
+   )");
+
+   ASSERT_EQ(forEach1.getInt(), 2);
+
+   ConsoleValue forEach2 = RunScript(R"(
+         $counter = 0;
+         foreach$ ($word in "a b c d")
+         {
+            $counter++;
+         }
+
+         return $counter;
+   )");
+
+   ASSERT_EQ(forEach2.getInt(), 4);
+
+   ConsoleValue forEach3 = RunScript(R"(
+         function SimObject::addOne(%this)
+         {
+            return 1;
+         }
+
+         function test()
+         {
+            %set = new SimSet();
+            %set.add(new SimObject());
+            %set.add(new SimObject());
+
+            %count = 0;
+            foreach (%obj in %set)
+               %count += %obj.addOne();
+
+            %set.delete();
+
+            return %count;
+         }
+
+         return test();
+   )");
+
+   ASSERT_EQ(forEach3.getInt(), 2);
+
+   ConsoleValue forEach4 = RunScript(R"(
+         function test()
+         {
+            %string = "a b c d e";
+
+            %count = 0;
+            foreach$ (%word in %string)
+               %count++;
+
+            return %count;
+         }
+
+         return test();
+   )");
+
+   ASSERT_EQ(forEach4.getInt(), 5);
+
+   ConsoleValue forEach5 = RunScript(R"(
+      function SimObject::ret1(%this)
+      {
+         return 1;
+      }
+
+      function SimSet::doForeach5(%this)
+      {
+         %count = 0;
+         foreach (%obj in %this)
+         {
+            %count += %obj.ret1();
+         }
+         return %count;
+      }
+
+      function a()
+      {
+         %set = new SimSet();
+         %set.add(new SimObject());
+         %set.add(new SimObject());
+         %set.add(new SimObject());
+
+         return %set.doForeach5();
+      }
+
+      return a();
+   )");
+
+   ASSERT_EQ(forEach5.getInt(), 3);
+
+   ConsoleValue forEachContinue = RunScript(R"(
+      function SimSet::foreach6(%this)
+      {
+         %count = 0;
+         foreach (%obj in %this)
+         {
+            if (%obj.getName() $= "A")
+               continue;
+
+             %count++;
+         }
+         return %count;
+      }
+
+      function a()
+      {
+         %set = new SimSet();
+         %set.add(new SimObject(A));
+         %set.add(new SimObject());
+         %set.add(new SimObject());
+
+         return %set.foreach6();
+      }
+
+      return a();
+   )");
+
+   ASSERT_EQ(forEachContinue.getInt(), 2);
+
+   ConsoleValue forEachReturn = RunScript(R"(
+      function SimSet::findA(%this)
+      {
+         foreach (%obj in %this)
+         {
+            if (%obj.getName() $= "A")
+               return 76;
+         }
+         return 0;
+      }
+
+      function a()
+      {
+         %set = new SimSet();
+         %set.add(new SimObject(A));
+         %set.add(new SimObject());
+         %set.add(new SimObject());
+
+         return %set.findA();
+      }
+
+      return a();
+   )");
+
+   ASSERT_EQ(forEachReturn.getInt(), 76);
+
+   ConsoleValue forEachNestedReturn = RunScript(R"(
+      function SimSet::findA(%this)
+      {
+         foreach (%obj in %this)
+         {
+            foreach (%innerObj in %this)
+            {
+               if (%innerObj.getName() $= "A")
+                  return 42;
+            }
+         }
+         return 0;
+      }
+
+      function a()
+      {
+         %set = new SimSet();
+         %set.add(new SimObject(A));
+         %set.add(new SimObject());
+         %set.add(new SimObject());
+
+         %group = new SimGroup();
+         %group.add(%set);
+
+         return %set.findA();
+      }
+
+      return a();
+   )");
+
+   ASSERT_EQ(forEachNestedReturn.getInt(), 42);
 }
 
 TEST(Script, TorqueScript_Array_Testing)
 {
-   S32 value = RunScript<S32>(R"(
-         function t(%idx) { %a[idx] = 2; return %a[idx]; }
+   ConsoleValue value = RunScript(R"(
+         function t(%idx) { %a[%idx] = 2; return %a[%idx]; }
          return t(5);
    )");
 
-   EXPECT_EQ(value, 2);
+   ASSERT_EQ(value.getInt(), 2);
 
-   S32 value2 = RunScript<S32>(R"(
-         function t(%idx) { %a[idx, 0] = 2; return %a[idx, 0]; }
+   ConsoleValue value2 = RunScript(R"(
+         function t(%idx) { %a[%idx, 0] = 2; return %a[%idx, 0]; }
          return t(5);
    )");
 
-   EXPECT_EQ(value2, 2);
+   ASSERT_EQ(value2.getInt(), 2);
 }
 
 TEST(Script, Basic_SimObject)
 {
-   SimObject* object = RunScript<SimObject*>(R"(
-         return new SimObject(FudgeCollector) {
+   ConsoleValue object = RunScript(R"(
+         return new SimObject(FudgeCollector)
+         {
             fudge = "Chocolate"; 
          };
    )");
 
-   EXPECT_NE(object, (SimObject*)NULL);
+   ASSERT_NE(Sim::findObject(object), (SimObject*)NULL);
 
-   const char* propertyValue = RunScript<const char*>(R"(
+   ConsoleValue propertyValue = RunScript(R"(
          return FudgeCollector.fudge;
    )");
 
-   EXPECT_STREQ(propertyValue, "Chocolate");
+   ASSERT_STREQ(propertyValue.getString(), "Chocolate");
 
-   const char* funcReturn = RunScript<const char*>(R"(
+   ConsoleValue funcReturn = RunScript(R"(
          function SimObject::fooFunc(%this)
          {
             return "Bar";
@@ -343,9 +568,9 @@ TEST(Script, Basic_SimObject)
          return FudgeCollector.fooFunc();
    )");
 
-   EXPECT_STREQ(funcReturn, "Bar");
+   ASSERT_STREQ(funcReturn.getString(), "Bar");
 
-   const char* parentFn = RunScript<const char*>(R"(
+   ConsoleValue parentFn = RunScript(R"(
          new SimObject(Hello);
 
          function SimObject::fooFunc2(%this)
@@ -362,34 +587,426 @@ TEST(Script, Basic_SimObject)
          return Hello.fooFunc2();
    )");
 
-   EXPECT_STREQ(parentFn, "FooBar");
+   ASSERT_STREQ(parentFn.getString(), "FooBar");
+
+   ConsoleValue simpleFieldTest = RunScript(R"(
+         function a()
+         {
+            FudgeCollector.field = "A";
+            return FudgeCollector.field;
+         }
+         return a();
+   )");
+
+   ASSERT_STREQ(simpleFieldTest.getString(), "A");
+
+   ConsoleValue grp = RunScript(R"(
+         new SimGroup(FudgeCollectorGroup)
+         {
+            theName = "fudge";
+
+            new SimObject(ChocolateFudge)
+            {
+               type = "Chocolate";
+            };
+            new SimObject(PeanutButterFudge)
+            {
+               type = "Peanut Butter";
+
+               field["a"] = "Yes";
+            };
+         };
+
+         return FudgeCollectorGroup.getId();
+   )");
+
+   SimGroup* simGroup = dynamic_cast<SimGroup*>(Sim::findObject(grp));
+   ASSERT_NE(simGroup, (SimGroup*)NULL);
+   ASSERT_EQ(simGroup->size(), 2);
+
+   simGroup->deleteObject();
+
+   ConsoleValue fieldTest = RunScript(R"(
+         function a()
+         {
+            %obj = new SimObject();
+            %obj.field = "A";
+            %obj.val[%obj.field] = "B";
+
+            %value = %obj.val["A"];
+            %obj.delete();
+            return %value;
+         }
+         return a();
+   )");
+
+   ASSERT_STREQ(fieldTest.getString(), "B");
+
+   ConsoleValue fieldOpTest = RunScript(R"(
+         function a()
+         {
+            %obj = new SimObject();
+            %obj.field = 1;
+            %obj.field += 2;
+
+            %value = %obj.field;
+            %obj.delete();
+            return %value;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(fieldOpTest.getInt(), 3);
+}
+
+TEST(Script, Internal_Name)
+{
+   ConsoleValue value = RunScript(R"(
+         function TheFirstInner::_internalCall(%this)
+         {
+            return 5;
+         }
+
+         function a()
+         {
+            %grp = new SimGroup();
+            %obj = new SimObject(TheFirstInner)
+            {
+               internalName = "Yay";
+            };
+            %grp.add(%obj);
+
+            %val = %grp->Yay._internalCall();
+
+            %grp.delete();
+
+            return %val;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(value.getInt(), 5);
+
+   ConsoleValue recursiveValue = RunScript(R"(
+         function SimGroup::doTheInternalCall(%this)
+         {
+            return %this-->Yeah._internalCall2();
+         }
+
+         function TheAnotherObject::_internalCall2(%this)
+         {
+            return %this.property;
+         }
+
+         function a()
+         {
+            %grp = new SimGroup();
+            %obj = new SimGroup()
+            {
+               internalName = "Yay2";
+
+               new SimObject(TheAnotherObject)
+               {
+                  internalName = "Yeah";
+                  property = 12;
+               };
+            };
+            %grp.add(%obj);
+
+            %val = %grp.doTheInternalCall();
+
+            %grp.delete();
+
+            return %val;
+         }
+         return a();      
+   )");
+
+   ASSERT_EQ(recursiveValue.getInt(), 12);
 }
 
 TEST(Script, Basic_Package)
 {
-   S32 value = RunScript<S32>(R"(
+   ConsoleValue value = RunScript(R"(
          function a() { return 3; }
-         package overrides {
+         package overrides
+         {
             function a() { return 5; }
          };
          return a();
    )");
 
-   EXPECT_EQ(value, 3);
+   ASSERT_EQ(value.getInt(), 3);
 
-   S32 overrideValue = RunScript<S32>(R"(
+   ConsoleValue overrideValue = RunScript(R"(
          activatePackage(overrides);
          return a();
    )");
 
-   EXPECT_EQ(overrideValue, 5);
+   ASSERT_EQ(overrideValue.getInt(), 5);
 
-   S32 deactivatedValue = RunScript<S32>(R"(
+   ConsoleValue deactivatedValue = RunScript(R"(
          deactivatePackage(overrides);
          return a();
    )");
 
-   EXPECT_EQ(deactivatedValue, 3);
+   ASSERT_EQ(deactivatedValue.getInt(), 3);
+}
+
+TEST(Script, Sugar_Syntax)
+{
+   ConsoleValue value = RunScript(R"(
+         function a()
+         {
+            %vector = "1 2 3";
+            return %vector.y;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(value.getInt(), 2);
+
+   ConsoleValue setValue = RunScript(R"(
+         function a()
+         {
+            %vector = "1 2 3";
+            %vector.y = 4;
+            return %vector.y;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(setValue.getInt(), 4);
+
+   ConsoleValue valueArray = RunScript(R"(
+         function a()
+         {
+            %vector[0] = "1 2 3";
+            return %vector[0].y;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(valueArray.getInt(), 2);
+
+   ConsoleValue valueSetArray = RunScript(R"(
+         function a()
+         {
+            %vector[0] = "1 2 3";
+            %vector[0].z = 5;
+            return %vector[0].z;
+         }
+         return a();
+   )");
+
+   ASSERT_EQ(valueSetArray.getInt(), 5);
+
+   ConsoleValue valueStoreCalculated = RunScript(R"(
+      function a()
+      {
+         %extent = 10 SPC 20;
+         %scaling = 1;
+         %size = %extent.x * %scaling;
+         return %size;
+      }
+
+      return a();
+   )");
+
+   ASSERT_EQ(valueStoreCalculated.getInt(), 10);
+
+   ConsoleValue globalValueGet = RunScript(R"(
+      new SimObject(AAAA);
+      AAAA.doSomething = false;
+      $vec = "1 2 3";
+      return $vec.x * 4;
+   )");
+
+   ASSERT_EQ(globalValueGet.getFloat(), 4);
+
+   ConsoleValue globalValueSet = RunScript(R"(
+      new SimObject(AAAAB);
+      AAAAB.doSomething = false;
+      $vec2 = "1 2 3";
+      $vec2.x *= 4;
+      return $vec2.x;
+   )");
+
+   ASSERT_EQ(globalValueSet.getFloat(), 4);
+}
+
+TEST(Script, InnerObjectTests)
+{
+   ConsoleValue theObject = RunScript(R"(
+      function a()
+      {
+         %obj = new SimObject(TheOuterObject)
+         {
+            innerObject = new SimObject(TheInnerObject)
+            {
+               testField = 123;
+               position = "1 2 3";
+            };
+         };
+         return %obj;
+      }
+      return a();
+   )");
+
+   SimObject* outerObject = Sim::findObject("TheOuterObject");
+   ASSERT_NE(outerObject, (SimObject*)NULL);
+   if (outerObject)
+   {
+      ASSERT_EQ(theObject.getInt(), Sim::findObject("TheOuterObject")->getId());
+   }
+   ASSERT_NE(Sim::findObject("TheInnerObject"), (SimObject*)NULL);
+
+   ConsoleValue positionValue = RunScript(R"(
+      function TheOuterObject::getInnerPosition(%this)
+      {
+         return %this.innerObject.position;
+      }
+
+      function a()
+      {
+         %position = TheOuterObject.getInnerPosition();
+         return %position.y;
+      }
+      return a();
+   )");
+
+   ASSERT_EQ(positionValue.getInt(), 2);
+
+   ConsoleValue nestedFuncCall = RunScript(R"(
+      function TheInnerObject::test(%this)
+      {
+         return %this.testField;
+      }
+
+      return TheOuterObject.innerObject.test();
+   )");
+
+   ASSERT_EQ(nestedFuncCall.getInt(), 123);
+}
+
+TEST(Script, MiscRegressions)
+{
+   ConsoleValue regression1 = RunScript(R"(
+      new SimObject(TheRegressionObject);
+
+      function doTest()
+      {
+          TheRegressionObject.hidden = false;
+
+          %previewSize = 100 SPC 100;
+          %previewScaleSize = 2;
+          %size = %previewSize.x * %previewScaleSize;
+
+          return %size;
+      }
+
+      return doTest();
+   )");
+
+   ASSERT_EQ(regression1.getInt(), 200);
+
+   ConsoleValue regression2 = RunScript(R"(
+      new SimObject(TheRegressionObject2) 
+      {
+         extent = "100 200";
+      };
+
+      function doTest()
+      {
+          %scale = 2;
+          %position = TheRegressionObject2.extent.x SPC TheRegressionObject2.extent.y * %scale;
+          return %position.y;
+      }
+
+      return doTest();
+   )");
+
+   ASSERT_EQ(regression2.getInt(), 400);
+
+   ConsoleValue regression3 = RunScript(R"(
+      function doTest()
+      {
+          %button = new GuiIconButtonCtrl()
+          {
+              active = true;
+          };
+
+          %button.setExtent(120, 20);
+
+          %button.setExtent("120 20");
+
+          %button.extent = "120 20";
+
+          %button.extent.x = 120;
+          %button.extent.y = 20;
+          return %button.extent;
+      }
+      return doTest();
+   )");
+
+   ASSERT_STREQ(regression3.getString(), "120 20");
+   
+   ConsoleValue regression4 = RunScript(R"(
+    function doTest()
+    {
+        %slider = new GuiSliderCtrl()
+        {
+            range = "0 2";
+            ticks = 5;
+            active = true;
+        };
+
+        %slider.setValue(0.5);
+        return %slider.getValue();
+    }
+    return doTest();
+   )");
+   
+   ASSERT_EQ(regression4.getFloat(), 0.5);
+
+   ConsoleValue regression5 = RunScript(R"(
+      function noOpInc()
+      {
+         %count = 0;
+         %var[%count++] = 2;
+         return %var[1];
+      }
+      return noOpInc();
+   )");
+
+   ASSERT_EQ(regression5.getInt(), 2);
+
+   ConsoleValue regression6 = RunScript(R"(
+      function SimObject::crashMe(%this, %line)
+      {
+         return %line @ "1";
+      }
+
+      function doTest()
+      {
+         %obj = new SimObject();
+         for (%i = 0; %i < 99999; %i++)
+         {
+            %function = "crashMe";
+            if (%obj.isMethod(%function))
+            {
+               %line = "abcdefg";
+               %output = %obj.call(%function, %line); 
+            }
+         }
+
+         return true;
+      }
+
+      return doTest();
+   )");
+
+   ASSERT_EQ(regression6.getBool(), true);
 }
 
 #endif

+ 3 - 0
Engine/source/console/test/consoleTest.cpp

@@ -1,3 +1,5 @@
+#if 0
+
 #ifdef TORQUE_TESTS_ENABLED
 #include "testing/unitTesting.h"
 #include "platform/platform.h"
@@ -253,3 +255,4 @@ TEST(Con, execute)
 }
 
 #endif
+#endif

+ 3 - 1
Engine/source/console/test/engineAPITest.cpp

@@ -1,3 +1,4 @@
+#if 0
 #ifdef TORQUE_TESTS_ENABLED
 #include "testing/unitTesting.h"
 #include "platform/platform.h"
@@ -147,4 +148,5 @@ TEST(EngineAPI, _EngineConsoleExecCallbackHelper)
 		"All values should be printed in the correct order";
 }
 
-#endif
+#endif
+#endif

+ 1 - 1
Engine/source/core/strings/stringFunctions.cpp

@@ -428,7 +428,7 @@ S32 dStrlcpy(char *dst, const char *src, dsize_t dstSize)
    S32 copyLen = srcLen;
 
    //Check for buffer overflow and don't allow it. Warn on debug so we can fix it
-   AssertWarn(copyLen < dstSize, "Buffer too small in call to dStrlcpy!");
+   AssertFatal(copyLen < dstSize, "Buffer too small in call to dStrlcpy!");
    if (srcLen + 1 > dstSize)
    {
       copyLen = dstSize - 1;

+ 5 - 0
Engine/source/core/strings/stringFunctions.h

@@ -200,6 +200,11 @@ inline F64 dAtod(const char *str)
    return strtod(str, NULL);
 }
 
+inline S64 dAtol(const char* str)
+{
+   return strtol(str, NULL, 10);
+}
+
 inline char dToupper(const char c)
 {
    return toupper( c );

+ 10 - 0
Engine/source/core/util/tVector.h

@@ -160,6 +160,7 @@ class Vector
    void erase(U32 index, U32 count);
    void erase_fast(iterator);
    void clear();
+   void resetAndTreatAsScratchBuffer();
    void compact();
    void sort(compare_func f);
    void fill( const T& value );
@@ -529,6 +530,15 @@ template<class T> inline void Vector<T>::clear()
    mElementCount = 0;
 }
 
+/// This method sets the vector as its 0 and will overwrite memory on subsequent usage.
+/// Note that the current memory in use is never freed or deallocated, so only use this if the vector
+/// is being used as a scratch buffer only.
+template<class T> inline
+void Vector<T>::resetAndTreatAsScratchBuffer()
+{
+   mElementCount = 0;
+}
+
 template<class T> inline void Vector<T>::compact()
 {
    resize(mElementCount);

+ 1 - 1
Engine/source/environment/waterObject.cpp

@@ -418,7 +418,7 @@ void WaterObject::inspectPostApply()
    setMaskBits( UpdateMask | WaveMask | TextureMask | SoundMask );
 }
 
-bool WaterObject::processArguments( S32 argc, ConsoleValueRef *argv )
+bool WaterObject::processArguments( S32 argc, ConsoleValue *argv )
 {
    if( typeid( *this ) == typeid( WaterObject ) )
    {

+ 1 - 1
Engine/source/environment/waterObject.h

@@ -158,7 +158,7 @@ public:
    virtual bool onAdd();
    virtual void onRemove();
    virtual void inspectPostApply();
-   virtual bool processArguments(S32 argc, ConsoleValueRef *argv);
+   virtual bool processArguments(S32 argc, ConsoleValue *argv);
 
    // NetObject
    virtual U32  packUpdate( NetConnection * conn, U32 mask, BitStream *stream );

+ 2 - 4
Engine/source/gui/controls/guiGameListMenuCtrl.cpp

@@ -361,10 +361,8 @@ void GuiGameListMenuCtrl::onRenderSliderOption(Row* row, Point2I currentOffset)
    // calculate text to be at the center between the arrows
    GFont* font = profile->mFont;
 
-   ConsoleValue val;
-   val.setFloatValue(row->mValue);
-
-   const char* stringVal = val.getStringValue();
+   char stringVal[32];
+   dSprintf(stringVal, 32, "%f", row->mValue);
 
    S32 textWidth = font->getStrWidth(stringVal);
    S32 columnWidth = profile->mHitAreaLowerRight.x * xScale - profile->mRightPad - columnSplit;

+ 1 - 1
Engine/source/gui/core/guiControl.cpp

@@ -319,7 +319,7 @@ void GuiControl::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-bool GuiControl::processArguments(S32 argc, ConsoleValueRef *argv)
+bool GuiControl::processArguments(S32 argc, ConsoleValue *argv)
 {
    // argv[0] - The GuiGroup to add this control to when it's created.  
    //           this is an optional parameter that may be specified at

+ 1 - 1
Engine/source/gui/core/guiControl.h

@@ -341,7 +341,7 @@ class GuiControl : public SimGroup
       
       GuiControl();
       virtual ~GuiControl();
-      virtual bool processArguments(S32 argc, ConsoleValueRef *argv);
+      virtual bool processArguments(S32 argc, ConsoleValue *argv);
       
       static void initPersistFields();
       static void consoleInit();

+ 1 - 1
Engine/source/gui/editor/guiFilterCtrl.cpp

@@ -83,7 +83,7 @@ DefineEngineStringlyVariadicMethod( GuiFilterCtrl, setValue, void, 3, 20, "(f1,
 {
    Filter filter;
 
-   StringStackWrapper args(argc - 2, argv + 2);
+   ConsoleValueToStringArrayWrapper args(argc - 2, argv + 2);
 
    filter.set(args.count(), args);
 	object->set(filter);

+ 5 - 3
Engine/source/gui/editor/inspector/field.cpp

@@ -318,7 +318,8 @@ void GuiInspectorField::setData( const char* data, bool callbacks )
          {
             char buffer[ 2048 ];
             expandEscape( buffer, newValue );
-            newValue = (const char*)Con::evaluatef( "%%f = \"%s\"; return ( %s );", oldValue.c_str(), buffer );
+            newValue = (const char*)Con::evaluatef( "$f = \"%s\"; return ( %s );", oldValue.c_str(), buffer );
+            Con::evaluatef("$f=0;");
          }
          else if(    type == TypeS32Vector
                   || type == TypeF32Vector
@@ -353,9 +354,10 @@ void GuiInspectorField::setData( const char* data, bool callbacks )
                char buffer[ 2048 ];
                expandEscape( buffer, newComponentExpr );
 
-               const char* newComponentVal = Con::evaluatef( "%%f = \"%s\"; %%v = \"%s\"; return ( %s );",
+               const char* newComponentVal = Con::evaluatef( "$f = \"%s\"; $v = \"%s\"; return ( %s );",
                   oldComponentVal, oldValue.c_str(), buffer );
-               
+               Con::evaluatef("$f=0;$v=0;");
+
                if( !isFirst )
                   strNew.append( ' ' );
                strNew.append( newComponentVal );

+ 4 - 2
Engine/source/gui/editor/popupMenu.cpp

@@ -120,12 +120,14 @@ void PopupMenu::handleSelectEvent(U32 popID, U32 command)
 //-----------------------------------------------------------------------------
 bool PopupMenu::onMessageReceived(StringTableEntry queue, const char* event, const char* data)
 {
-   return Con::executef(this, "onMessageReceived", queue, event, data);
+   ConsoleValue returnValue = Con::executef(this, "onMessageReceived", queue, event, data);
+   return returnValue.getBool();
 }
 
 bool PopupMenu::onMessageObjectReceived(StringTableEntry queue, Message *msg )
 {
-   return Con::executef(this, "onMessageReceived", queue, Con::getIntArg(msg->getId()));
+   ConsoleValue returnValue = Con::executef(this, "onMessageReceived", queue, Con::getIntArg(msg->getId()));
+   return returnValue.getBool();
 }
 
 //////////////////////////////////////////////////////////////////////////

+ 3 - 3
Engine/source/gui/worldEditor/worldEditor.cpp

@@ -2861,17 +2861,17 @@ void WorldEditor::initPersistFields()
 //------------------------------------------------------------------------------
 // These methods are needed for the console interfaces.
 
-void WorldEditor::ignoreObjClass( U32 argc, ConsoleValueRef *argv )
+void WorldEditor::ignoreObjClass( U32 argc, ConsoleValue *argv )
 {
    for(S32 i = 2; i < argc; i++)
    {
-      ClassInfo::Entry * entry = getClassEntry(argv[i]);
+      ClassInfo::Entry * entry = getClassEntry(argv[i].getString());
       if(entry)
          entry->mIgnoreCollision = true;
       else
       {
          entry = new ClassInfo::Entry;
-         entry->mName = StringTable->insert(argv[i]);
+         entry->mName = StringTable->insert(argv[i].getString());
          entry->mIgnoreCollision = true;
          if(!addClassEntry(entry))
             delete entry;

+ 1 - 1
Engine/source/gui/worldEditor/worldEditor.h

@@ -76,7 +76,7 @@ class WorldEditor : public EditTSCtrl
          Point3F p2;
       };
 
-      void ignoreObjClass(U32 argc, ConsoleValueRef* argv);
+      void ignoreObjClass(U32 argc, ConsoleValue* argv);
       void clearIgnoreList();
 
       static bool setObjectsUseBoxCenter( void *object, const char *index, const char *data ) { static_cast<WorldEditor*>(object)->setObjectsUseBoxCenter( dAtob( data ) ); return false; };

+ 4 - 3
Engine/source/platform/types.gcc.h

@@ -43,6 +43,10 @@ typedef unsigned long  U64;
 // Compiler Version
 #define TORQUE_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
 
+#define TORQUE_FORCEINLINE __attribute__((always_inline))
+#define TORQUE_CASE_FALLTHROUGH __attribute__((fallthrough))
+#define TORQUE_NOINLINE __attribute__ ((noinline))
+#define TORQUE_UNLIKELY
 
 //--------------------------------------
 // Identify the compiler string
@@ -165,8 +169,5 @@ typedef unsigned long  U64;
 #endif
 #endif
 
-// Set GCC noinline
-#define TORQUE_NOINLINE __attribute__ ((noinline))
-
 #endif // INCLUDED_TYPES_GCC_H
 

+ 10 - 2
Engine/source/platform/types.visualc.h

@@ -102,10 +102,18 @@ typedef unsigned _int64 U64;
 
 // disable warning caused by memory layer
 // see msdn.microsoft.com "Compiler Warning (level 1) C4291" for more details
-#pragma warning(disable: 4291) 
+#pragma warning(disable: 4291)
 
-// Set MSVC noline attribute
+#define TORQUE_FORCEINLINE __forceinline
 #define TORQUE_NOINLINE __declspec(noinline)
 
+#if __cplusplus >= 201703L
+#define TORQUE_CASE_FALLTHROUGH [[fallthrough]];
+#define TORQUE_UNLIKELY [[unlikely]]
+#else
+#define TORQUE_CASE_FALLTHROUGH __fallthrough
+#define TORQUE_UNLIKELY
+#endif
+
 #endif // INCLUDED_TYPES_VISUALC_H
 

+ 12 - 7
Engine/source/platformWin32/winMath.cpp

@@ -55,31 +55,36 @@ DefineEngineStringlyVariadicFunction( mathInit, void, 1, 10, "( ... )"
    }
    for (argc--, argv++; argc; argc--, argv++)
    {
-      if (dStricmp(*argv, "DETECT") == 0) {
+      const char* str = (*argv).getString();
+      if (dStricmp(str, "DETECT") == 0) {
          Math::init(0);
          return;
       }
-      if (dStricmp(*argv, "C") == 0) {
+      if (dStricmp(str, "C") == 0) {
          properties |= CPU_PROP_C;
          continue;
       }
-      if (dStricmp(*argv, "FPU") == 0) {
+      if (dStricmp(str, "FPU") == 0) {
          properties |= CPU_PROP_FPU;
          continue;
       }
-      if (dStricmp(*argv, "MMX") == 0) {
+      if (dStricmp(str, "MMX") == 0) {
          properties |= CPU_PROP_MMX;
          continue;
       }
-      if (dStricmp(*argv, "3DNOW") == 0) {
+      if (dStricmp(str, "3DNOW") == 0) {
          properties |= CPU_PROP_3DNOW;
          continue;
       }
-      if (dStricmp(*argv, "SSE") == 0) {
+      if (dStricmp(str, "SSE") == 0) {
          properties |= CPU_PROP_SSE;
          continue;
       }
-      Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", argv->getStringValue());
+      if (dStricmp(str, "SSE2") == 0) {
+         properties |= CPU_PROP_SSE2;
+         continue;
+      }
+      Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", str);
    }
    Math::init(properties);
 }

+ 1 - 1
Engine/source/sfx/sfxSource.cpp

@@ -316,7 +316,7 @@ void SFXSource::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-bool SFXSource::processArguments( S32 argc, ConsoleValueRef *argv )
+bool SFXSource::processArguments( S32 argc, ConsoleValue *argv )
 {
    // Don't allow subclasses of this to be created via script.  Force
    // usage of the SFXSystem functions.

+ 1 - 1
Engine/source/sfx/sfxSource.h

@@ -382,7 +382,7 @@ class SFXSource : public SimGroup
 
       /// We overload this to disable creation of 
       /// a source via script 'new'.
-      virtual bool processArguments( S32 argc, ConsoleValueRef *argv );
+      virtual bool processArguments( S32 argc, ConsoleValue *argv );
       
       // Console getters/setters.
       static bool _setDescription( void *obj, const char *index, const char *data );

+ 1 - 1
Engine/source/sfx/sfxTrack.cpp

@@ -95,7 +95,7 @@ void SFXTrack::initPersistFields()
 
 //-----------------------------------------------------------------------------
 
-bool SFXTrack::processArguments( S32 argc, ConsoleValueRef *argv )
+bool SFXTrack::processArguments( S32 argc, ConsoleValue *argv )
 {
    if( typeid( *this ) == typeid( SFXTrack ) )
    {

+ 1 - 1
Engine/source/sfx/sfxTrack.h

@@ -61,7 +61,7 @@ class SFXTrack : public SimDataBlock
       StringTableEntry mParameters[ MaxNumParameters ];
    
       /// Overload this to disable direct instantiation of this class via script 'new'.
-      virtual bool processArguments( S32 argc, ConsoleValueRef *argv );
+      virtual bool processArguments( S32 argc, ConsoleValue *argv );
 
    public:
          

+ 3 - 3
Engine/source/shaderGen/GLSL/customFeatureGLSL.cpp

@@ -238,7 +238,7 @@ void CustomFeatureGLSL::addVertTexCoord(String name)
    mVars.push_back(newVarHolder);
 }
 
-void CustomFeatureGLSL::writeLine(String format, S32 argc, ConsoleValueRef * argv)
+void CustomFeatureGLSL::writeLine(String format, S32 argc, ConsoleValue * argv)
 {
    //do the var/arg fetching here
    Vector<Var*> varList;
@@ -246,7 +246,7 @@ void CustomFeatureGLSL::writeLine(String format, S32 argc, ConsoleValueRef * arg
 
    for (U32 i = 0; i < argc; i++)
    {
-      String varName = argv[i].getStringValue();
+      String varName = argv[i].getString();
       Var* newVar = (Var*)LangElement::find(varName.c_str());
       if (!newVar)
       {
@@ -304,7 +304,7 @@ void CustomFeatureGLSL::writeLine(String format, S32 argc, ConsoleValueRef * arg
          if (!newVar)
          {
             //couldn't find that variable, bail out
-            Con::errorf("CustomShaderFeature::writeLine: unable to find variable %s, meaning it was not declared before being used!", argv[i].getStringValue());
+            Con::errorf("CustomShaderFeature::writeLine: unable to find variable %s, meaning it was not declared before being used!", argv[i].getString());
             return;
          }
       }

+ 1 - 1
Engine/source/shaderGen/GLSL/customFeatureGLSL.h

@@ -128,5 +128,5 @@ public:
    void addTexture(String name, String type, String samplerState, U32 arraySize);
    void addConnector(String name, String type, String elementName);
    void addVertTexCoord(String name);
-   void writeLine(String format, S32 argc, ConsoleValueRef* argv);
+   void writeLine(String format, S32 argc, ConsoleValue* argv);
 };

+ 3 - 3
Engine/source/shaderGen/HLSL/customFeatureHLSL.cpp

@@ -255,7 +255,7 @@ void CustomFeatureHLSL::addVertTexCoord(String name)
    mVars.push_back(newVarHolder);
 }
 
-void CustomFeatureHLSL::writeLine(String format, S32 argc, ConsoleValueRef * argv)
+void CustomFeatureHLSL::writeLine(String format, S32 argc, ConsoleValue *argv)
 {
    //do the var/arg fetching here
    Vector<Var*> varList;
@@ -263,7 +263,7 @@ void CustomFeatureHLSL::writeLine(String format, S32 argc, ConsoleValueRef * arg
 
    for (U32 i = 0; i < argc; i++)
    {
-      String varName = argv[i].getStringValue();
+      String varName = argv[i].getString();
       Var* newVar = (Var*)LangElement::find(varName.c_str());
       if (!newVar)
       {
@@ -321,7 +321,7 @@ void CustomFeatureHLSL::writeLine(String format, S32 argc, ConsoleValueRef * arg
          if (!newVar)
          {
             //couldn't find that variable, bail out
-            Con::errorf("CustomShaderFeature::writeLine: unable to find variable %s, meaning it was not declared before being used!", argv[i].getStringValue());
+            Con::errorf("CustomShaderFeature::writeLine: unable to find variable %s, meaning it was not declared before being used!", argv[i].getString());
             return;
          }
       }

+ 1 - 1
Engine/source/shaderGen/HLSL/customFeatureHLSL.h

@@ -128,5 +128,5 @@ public:
    void addTexture(String name, String type, String samplerState, U32 arraySize);
    void addConnector(String name, String type, String elementName);
    void addVertTexCoord(String name);
-   void writeLine(String format, S32 argc, ConsoleValueRef* argv);
+   void writeLine(String format, S32 argc, ConsoleValue* argv);
 };

+ 2 - 2
Engine/source/shaderGen/customShaderFeature.cpp

@@ -189,7 +189,7 @@ bool CustomShaderFeatureData::hasFeature(String name)
    return false;
 }
 
-void CustomShaderFeatureData::writeLine(String format, S32 argc, ConsoleValueRef* argv)
+void CustomShaderFeatureData::writeLine(String format, S32 argc, ConsoleValue* argv)
 {
 #ifdef TORQUE_D3D11
    if (GFX->getAdapterType() == GFXAdapterType::Direct3D11)
@@ -236,7 +236,7 @@ DefineEngineStringlyVariadicMethod(CustomShaderFeatureData, writeLine, void, 3,
    "@param args Zero or more arguments for the method.\n"
    "@return The result of the method call.")
 {
-   object->writeLine(argv[2], argc - 3, argv + 3);
+   object->writeLine(argv[2].getString(), argc - 3, argv + 3);
 }
 
 DefineEngineMethod(CustomShaderFeatureData, hasFeature, bool, (String name), (""), "")

+ 1 - 1
Engine/source/shaderGen/customShaderFeature.h

@@ -77,7 +77,7 @@ public:
    void addVertTexCoord(String name);
 	bool hasFeature(String name);
 
-	void writeLine(String format, S32 argc, ConsoleValueRef *argv);
+	void writeLine(String format, S32 argc, ConsoleValue *argv);
 };
 
 #endif

+ 2 - 2
Engine/source/sim/actionMap.cpp

@@ -2080,7 +2080,7 @@ static ConsoleDocFragment _ActionMapbind2(
 DefineEngineStringlyVariadicMethod( ActionMap, bind, bool, 5, 10, "actionMap.bind( device, action, [modifier, spec, mod...], command )"
            "@hide")
 {
-   StringStackWrapper args(argc - 2, argv + 2);
+   ConsoleValueToStringArrayWrapper args(argc - 2, argv + 2);
    return object->processBind( args.count(), args, NULL );
 }
 
@@ -2136,7 +2136,7 @@ DefineEngineStringlyVariadicMethod( ActionMap, bindObj, bool, 6, 11, "(device, a
       return false;
    }
 
-   StringStackWrapper args(argc - 3, argv + 2);
+   ConsoleValueToStringArrayWrapper args(argc - 3, argv + 2);
    return object->processBind( args.count(), args, simObject );
 }
 

+ 1 - 0
Templates/BaseGame/game/core/clientServer/scripts/server/levelInfo.tscript

@@ -143,6 +143,7 @@ function parseMissionGroupForIds( %className, %childGroup )
    if(!isObject(%currentGroup))
       return "";
       
+   %classIds = "";	  
    for(%i = 0; %i < (%currentGroup).getCount(); %i++)
    {      
       if( (%currentGroup).getObject(%i).getClassName() $= %className )

+ 3 - 0
Templates/BaseGame/game/core/clientServer/scripts/server/levelLoad.tscript

@@ -193,6 +193,9 @@ function resetMission()
    
    clearServerPaths();
    
+   // TODO: Is this right?
+   %client = ClientGroup.getObject(0);
+
    // Inform the game code we're resetting.
    %hasGameMode = callGamemodeFunction("onMissionReset", %client);
 }

+ 1 - 1
Templates/BaseGame/game/core/console/guis/console.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(ConsoleDlg) {
+$guiContent = new GuiControl(ConsoleDlg) {
    position = "0 0";
    extent = "1024 768";
    minExtent = "8 8";

+ 1 - 1
Templates/BaseGame/game/core/console/scripts/console.tscript

@@ -62,7 +62,7 @@ function ConsoleEntry::eval()
        && !startsWith(%text, "for(")
        && !startsWith(%text, "switch(")
        && !startsWith(%text, "switch$("))
-      eval("%result = " @ %text);
+      %result = eval("return" SPC %text);
    else
       eval(%text);
    $Con::warnVoidAssignment = %oldWarnVoidAssignment;

+ 1 - 1
Templates/BaseGame/game/core/postFX/scripts/postFxManager.tscript

@@ -59,7 +59,7 @@ function PostFXManager::loadPresetFile()
 {
    //Show the dialog and set the flag
    getLoadFilename($PostFXManager::fileFilter, "PostFXManager::loadPresetHandler");
-   $PostFXManager::currentPreset = $Tools::FileDialogs::LastFilePath();
+   $PostFXManager::currentPreset = $Tools::FileDialogs::LastFilePath;
 }
 
 function PostFXManager::loadPresetHandler( %filename )

+ 2 - 2
Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript

@@ -15,7 +15,7 @@ function findGameObject(%name)
 	   
 	   //%assetName = AssetDatabase.getAssetName(%assetId);
       
-        if(%assetId $= %name)
+      if(%assetId $= %name)
 		{
 		   %gameObjectAsset = AssetDatabase.acquireAsset(%assetId);
 
@@ -47,7 +47,7 @@ function spawnGameObject(%name, %addToScene)
          %go.setHidden(false);
          %go.setScopeAlways();
          
-         if(%addToMissionGroup == true) //save instance when saving level
+         if(%addToScene == true) //save instance when saving level
             getScene(0).add(%go);
          else // clear instance on level exit
             MissionCleanup.add(%go);

+ 3 - 1
Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript

@@ -524,7 +524,9 @@ function loadDir(%dir)
 
 function loadDirs(%dirPath)
 {
-   %dirPath = nextToken(%dirPath, token, ";");
+   %dirPath = nextToken(%dirPath, "$token", ";");
+   %token = $token;
+
    if (%dirPath !$= "")
       loadDirs(%dirPath);
 

+ 83 - 83
Templates/BaseGame/game/core/utility/scripts/parseArgs.tscript

@@ -43,11 +43,11 @@ function popFront(%list, %delim)
 
 function parseArgs()
 {
-   for ($i = 1; $i < $Game::argc ; $i++)
+   for (%i = 1; %i < $Game::argc ; %i++)
    {
-      $arg = $Game::argv[$i];
-      $nextArg = $Game::argv[$i+1];
-      $hasNextArg = $Game::argc - $i > 1;
+      $arg = $Game::argv[%i];
+      $nextArg = $Game::argv[%i+1];
+      $hasNextArg = $Game::argc - %i > 1;
       $logModeSpecified = false;
 
       // Check for dedicated run
@@ -55,7 +55,7 @@ function parseArgs()
       {
          $userDirs = $defaultGame;
          $dirCount = 1;
-         $isDedicated = true;
+         %isDedicated = true;
       }*/
 
       switch$ ($arg)
@@ -64,15 +64,15 @@ function parseArgs()
          case "-dedicated":
             $userDirs = $defaultGame;
             $dirCount = 1;
-            $isDedicated = true;
+            %isDedicated = true;
             $Server::Dedicated = true;
             enableWinConsole(true);
             $argUsed[%i]++;
-            
+
                   //--------------------
          case "-mission":
             $argUsed[%i]++;
-            if ($hasNextArg) 
+            if ($hasNextArg)
             {
                $missionArg = $nextArg;
                $argUsed[%i+1]++;
@@ -84,7 +84,7 @@ function parseArgs()
          //--------------------
          case "-connect":
             $argUsed[%i]++;
-            if ($hasNextArg) 
+            if ($hasNextArg)
             {
                $JoinGameAddress = $nextArg;
                $argUsed[%i+1]++;
@@ -93,10 +93,10 @@ function parseArgs()
             else
                error("Error: Missing Command Line argument. Usage: -connect <ip_address>");
 
-            
+
          //--------------------
          case "-log":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                // Turn on console logging
@@ -107,22 +107,22 @@ function parseArgs()
                }
                setLogMode($nextArg);
                $logModeSpecified = true;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -log <Mode: 0,1,2>");
 
          //--------------------
          case "-dir":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                // Append the mod to the end of the current list
                $userDirs = strreplace($userDirs, $nextArg, "");
                $userDirs = pushFront($userDirs, $nextArg, ";");
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
                $dirCount++;
             }
             else
@@ -130,15 +130,15 @@ function parseArgs()
 
          //--------------------
          // changed the default behavior of this command line arg. It now
-         // defaults to ONLY loading the game, not tools 
+         // defaults to ONLY loading the game, not tools
          // default auto-run already loads in tools --SRZ 11/29/07
          case "-game":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                // Set the selected dir --NOTE: we no longer allow tools with this argument
-               /* 
-               if( $isDedicated )
+               /*
+               if( %isDedicated )
                {
                   $userDirs = $nextArg;
                   $dirCount = 1;
@@ -151,8 +151,8 @@ function parseArgs()
                */
                $userDirs = $nextArg;
                $dirCount = 1;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
                error($userDirs);
             }
             else
@@ -161,121 +161,121 @@ function parseArgs()
          //--------------------
          case "-console":
             enableWinConsole(true);
-            $argUsed[$i]++;
+            $argUsed[%i]++;
 
          //--------------------
          case "-jSave":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                echo("Saving event log to journal: " @ $nextArg);
                saveJournal($nextArg);
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -jSave <journal_name>");
 
          //--------------------
          case "-jPlay":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                playJournal($nextArg);
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -jPlay <journal_name>");
-               
+
          //--------------------
          case "-jPlayToVideo":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $VideoCapture::journalName = $nextArg;
                $VideoCapture::captureFromJournal = true;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -jPlayToVideo <journal_name>");
-               
+
          //--------------------
          case "-vidCapFile":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $VideoCapture::fileName = $nextArg;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -vidCapFile <ouput_video_name>");
-               
+
          //--------------------
          case "-vidCapFPS":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $VideoCapture::fps = $nextArg;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -vidCapFPS <ouput_video_framerate>");
-               
+
          //--------------------
          case "-vidCapEncoder":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $VideoCapture::encoder = $nextArg;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -vidCapEncoder <ouput_video_encoder>");
-               
+
          //--------------------
          case "-vidCapWidth":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $videoCapture::width = $nextArg;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -vidCapWidth <ouput_video_width>");
-               
+
          //--------------------
          case "-vidCapHeight":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                $videoCapture::height = $nextArg;
-               $argUsed[$i+1]++;
-               $i++;
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -vidCapHeight <ouput_video_height>");
 
          //--------------------
          case "-level":
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if ($hasNextArg)
             {
                %hasExt = strpos($nextArg, ".mis");
                if(%hasExt == -1)
                {
                   $levelToLoad = $nextArg @ " ";
-                  
-                  for(%i = $i + 2; %i < $Game::argc; %i++)
+
+                  for(%j = %i + 2; %j < $Game::argc; %j++)
                   {
-                     $arg = $Game::argv[%i];
+                     $arg = $Game::argv[%j];
                      %hasExt = strpos($arg, ".mis");
-                     
+
                      if(%hasExt == -1)
                      {
                         $levelToLoad = $levelToLoad @ $arg @ " ";
@@ -285,14 +285,14 @@ function parseArgs()
                         break;
                      }
                   }
-               } 
+               }
                else
                {
                   $levelToLoad = $nextArg;
                }
-               
-               $argUsed[$i+1]++;
-               $i++;
+
+               $argUsed[%i+1]++;
+               %i++;
             }
             else
                error("Error: Missing Command Line argument. Usage: -level <level file name (no path), with or without extension>");
@@ -300,33 +300,33 @@ function parseArgs()
          //-------------------
          case "-worldeditor":
             $startWorldEditor = true;
-            $argUsed[$i]++;
+            $argUsed[%i]++;
 
          //-------------------
          case "-guieditor":
             $startGUIEditor = true;
-            $argUsed[$i]++;
+            $argUsed[%i]++;
 
          //-------------------
          case "-help":
             $displayHelp = true;
-            $argUsed[$i]++;
+            $argUsed[%i]++;
 
          //-------------------
          case "-compileAll":
             $compileAll = true;
-            $argUsed[$i]++;
-            
+            $argUsed[%i]++;
+
          //-------------------
          case "-compileTools":
             $compileTools = true;
-            $argUsed[$i]++;
+            $argUsed[%i]++;
 
          //-------------------
          case "-genScript":
             $genScript = true;
-            $argUsed[$i]++;
-            
+            $argUsed[%i]++;
+
          case "-fullscreen":
             $cliFullscreen = true;
             $argUsed[%i]++;
@@ -357,36 +357,36 @@ function parseArgs()
             else
                error("Error: Missing Command Line argument. Usage: -prefs <path/script." @ $TorqueScriptFileExtension @ ">");
 
-            
+
          //-------------------
          default:
-            $argUsed[$i]++;
+            $argUsed[%i]++;
             if($userDirs $= "")
                $userDirs = $arg;
       }
    }
-   
+
    //-----------------------------------------------
    // Play journal to video file?
    if ($VideoCapture::captureFromJournal && $VideoCapture::journalName !$= "")
-   {         
+   {
       if ($VideoCapture::fileName $= "")
-         $VideoCapture::fileName = $VideoCapture::journalName;     
-      
+         $VideoCapture::fileName = $VideoCapture::journalName;
+
       if ($VideoCapture::encoder $= "")
          $VideoCapture::encoder = "THEORA";
-            
+
       if ($VideoCapture::fps $= "")
          $VideoCapture::fps = 30;
-               
+
       if ($videoCapture::width $= "")
          $videoCapture::width = 0;
-         
+
       if ($videoCapture::height $= "")
          $videoCapture::height = 0;
-         
-      playJournalToVideo(  $VideoCapture::journalName, $VideoCapture::fileName, 
-                           $VideoCapture::encoder, $VideoCapture::fps, 
+
+      playJournalToVideo(  $VideoCapture::journalName, $VideoCapture::fileName,
+                           $VideoCapture::encoder, $VideoCapture::fps,
                            $videoCapture::width SPC $videoCapture::height );
    }
-}
+}

+ 0 - 3
Templates/BaseGame/game/core/utility/scripts/scene.tscript

@@ -1,8 +1,5 @@
 function callGamemodeFunction(%gameModeFuncName, %arg0, %arg1, %arg2, %arg3, %arg4, %arg5, %arg6)
 {
-   if(%data !$= "")
-      %data = "\""@%data@"\"";
-      
    %activeSceneCount = getSceneCount();
       
    %hasGameMode = 0;

+ 1 - 1
Templates/BaseGame/game/data/ExampleModule/GUIs/ExampleGUI.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(ExampleGUI) 
+$guiContent = new GuiControl(ExampleGUI) 
 {
     position = "0 0";
 	extent = "100 100";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/IODropdownDlg.ed.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(IODropdownDlg) {
+$guiContent = new GuiControl(IODropdownDlg) {
 	profile = "GuiDefaultProfile";
 	horizSizing = "width";
 	vertSizing = "height";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/RecordingsDlg.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(recordingsDlg) {
+$guiContent = new GuiControl(recordingsDlg) {
    position = "0 0";
    extent = "1024 768";
    minExtent = "8 8";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/chooseLevelDlg.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(ChooseLevelDlg) {
+$guiContent = new GuiControl(ChooseLevelDlg) {
    position = "0 0";
    extent = "1024 768";
    minExtent = "8 8";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/guiMusicPlayer.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(GuiMusicPlayer) {
+$guiContent = new GuiControl(GuiMusicPlayer) {
    isContainer = "1";
    Profile = "GuiWindowProfile";
    HorizSizing = "right";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/joinServerMenu.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(JoinServerMenu) {
+$guiContent = new GuiControl(JoinServerMenu) {
    position = "0 0";
    extent = "1024 768";
    minExtent = "8 2";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/loadingGui.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiChunkedBitmapCtrl(LoadingGui) {
+$guiContent = new GuiChunkedBitmapCtrl(LoadingGui) {
    bitmapAsset = "UI:background_dark_image";
    useVariable = "0";
    tile = "0";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/mainMenu.gui

@@ -1,7 +1,7 @@
 exec( "tools/gui/profiles.ed.tscript" );
 
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiChunkedBitmapCtrl(MainMenuGui) {
+$guiContent = new GuiChunkedBitmapCtrl(MainMenuGui) {
    bitmapAsset = "UI:background_dark_image";
    useVariable = "0";
    tile = "0";

+ 1 - 1
Templates/BaseGame/game/data/UI/guis/messageBoxDlg.gui

@@ -1,5 +1,5 @@
 //--- OBJECT WRITE BEGIN ---
-%guiContent = new GuiControl(MessageBoxDlg) {
+$guiContent = new GuiControl(MessageBoxDlg) {
    position = "0 0";
    extent = "1024 768";
    minExtent = "8 8";

Some files were not shown because too many files changed in this diff