Torque3D Documentation / _generateds / codeInterpreter.cpp

codeInterpreter.cpp

Engine/source/console/codeInterpreter.cpp

More...

Namespaces:

namespace

This namespace contains the core of the console functionality.

Public Enumerations

enum
EvalConstants {
  MaxStackSize = 1024
  FieldBufferSizeString = 2048
  FieldBufferSizeNumeric = 128
  MethodOnComponent = -2
}

Public Typedefs

OPCodeReturn(CodeInterpreter::*
OpFn )(U32 &)

Public Variables

Stack pointer for floatStack.

Stack pointer for iterStack.

Stack pointer for intStack.

char
floatStack [MaxStackSize]
gOpCodeArray [MAX_OP_CODELEN]
intStack [MaxStackSize]
iterStack [MaxStackSize]
char
char
sTraceBuffer [1024]

Public Functions

getFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField, char val)
setFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField)

Detailed Description

Public Enumerations

EvalConstants

Enumerator

MaxStackSize = 1024
FieldBufferSizeString = 2048
FieldBufferSizeNumeric = 128
MethodOnComponent = -2

Public Typedefs

typedef OPCodeReturn(CodeInterpreter::* OpFn )(U32 &)

Public Variables

U32 _FLT 

Stack pointer for floatStack.

U32 _ITER 

Stack pointer for iterStack.

U32 _UINT 

Stack pointer for intStack.

ConsoleValueStack CSTK 
char curFieldArray [256]
F64 floatStack [MaxStackSize]
ExprEvalState gEvalState 
OpFn gOpCodeArray [MAX_OP_CODELEN]
S64 intStack [MaxStackSize]
IterStackRecord iterStack [MaxStackSize]
char prevFieldArray [256]
StringStack STR 
char sTraceBuffer [1024]

Public Functions

getFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField, char val)

setFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "console/codeInterpreter.h"
  30#include "console/compiler.h"
  31#include "console/simBase.h"
  32#include "console/telnetDebugger.h"
  33#include "sim/netStringTable.h"
  34#include "console/ICallMethod.h"
  35#include "console/stringStack.h"
  36#include "util/messaging/message.h"
  37#include "core/strings/findMatch.h"
  38#include "core/strings/stringUnit.h"
  39#include "console/console.h"
  40#include "console/consoleInternal.h"
  41#include "cinterface/cinterface.h"
  42
  43//#define TORQUE_VALIDATE_STACK
  44
  45using namespace Compiler;
  46
  47enum EvalConstants
  48{
  49   MaxStackSize = 1024,
  50   FieldBufferSizeString = 2048,
  51   FieldBufferSizeNumeric = 128,
  52   MethodOnComponent = -2
  53};
  54
  55namespace Con
  56{
  57   // Current script file name and root, these are registered as
  58   // console variables.
  59   extern StringTableEntry gCurrentFile;
  60   extern StringTableEntry gCurrentRoot;
  61   extern S32 gObjectCopyFailures;
  62}
  63
  64// Gets a component of an object's field value or a variable and returns it
  65// in val.
  66static void getFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[])
  67{
  68   const char* prevVal = NULL;
  69
  70   // Grab value from object.
  71   if (object && field)
  72      prevVal = object->getDataField(field, array);
  73
  74   // Otherwise, grab from the string stack. The value coming in will always
  75   // be a string because that is how multicomponent variables are handled.
  76   else
  77      prevVal = STR.getStringValue();
  78
  79   // Make sure we got a value.
  80   if (prevVal && *prevVal)
  81   {
  82      static const StringTableEntry xyzw[] =
  83      {
  84         StringTable->insert("x"),
  85         StringTable->insert("y"),
  86         StringTable->insert("z"),
  87         StringTable->insert("w")
  88      };
  89
  90      static const StringTableEntry rgba[] =
  91      {
  92         StringTable->insert("r"),
  93         StringTable->insert("g"),
  94         StringTable->insert("b"),
  95         StringTable->insert("a")
  96      };
  97
  98      // Translate xyzw and rgba into the indexed component 
  99      // of the variable or field.
 100      if (subField == xyzw[0] || subField == rgba[0])
 101         dStrcpy(val, StringUnit::getUnit(prevVal, 0, " \t\n"), 128);
 102
 103      else if (subField == xyzw[1] || subField == rgba[1])
 104         dStrcpy(val, StringUnit::getUnit(prevVal, 1, " \t\n"), 128);
 105
 106      else if (subField == xyzw[2] || subField == rgba[2])
 107         dStrcpy(val, StringUnit::getUnit(prevVal, 2, " \t\n"), 128);
 108
 109      else if (subField == xyzw[3] || subField == rgba[3])
 110         dStrcpy(val, StringUnit::getUnit(prevVal, 3, " \t\n"), 128);
 111
 112      else
 113         val[0] = 0;
 114   }
 115   else
 116      val[0] = 0;
 117}
 118
 119// Sets a component of an object's field value based on the sub field. 'x' will
 120// set the first field, 'y' the second, and 'z' the third.
 121static void setFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField)
 122{
 123   // Copy the current string value
 124   char strValue[1024];
 125   dStrncpy(strValue, STR.getStringValue(), 1024);
 126
 127   char val[1024] = "";
 128   const char* prevVal = NULL;
 129
 130   // Set the value on an object field.
 131   if (object && field)
 132      prevVal = object->getDataField(field, array);
 133
 134   // Set the value on a variable.
 135   else if (gEvalState.currentVariable)
 136      prevVal = gEvalState.getStringVariable();
 137
 138   // Ensure that the variable has a value
 139   if (!prevVal)
 140      return;
 141
 142   static const StringTableEntry xyzw[] =
 143   {
 144      StringTable->insert("x"),
 145      StringTable->insert("y"),
 146      StringTable->insert("z"),
 147      StringTable->insert("w")
 148   };
 149
 150   static const StringTableEntry rgba[] =
 151   {
 152      StringTable->insert("r"),
 153      StringTable->insert("g"),
 154      StringTable->insert("b"),
 155      StringTable->insert("a")
 156   };
 157
 158   // Insert the value into the specified 
 159   // component of the string.
 160   if (subField == xyzw[0] || subField == rgba[0])
 161      dStrcpy(val, StringUnit::setUnit(prevVal, 0, strValue, " \t\n"), 128);
 162
 163   else if (subField == xyzw[1] || subField == rgba[1])
 164      dStrcpy(val, StringUnit::setUnit(prevVal, 1, strValue, " \t\n"), 128);
 165
 166   else if (subField == xyzw[2] || subField == rgba[2])
 167      dStrcpy(val, StringUnit::setUnit(prevVal, 2, strValue, " \t\n"), 128);
 168
 169   else if (subField == xyzw[3] || subField == rgba[3])
 170      dStrcpy(val, StringUnit::setUnit(prevVal, 3, strValue, " \t\n"), 128);
 171
 172   if (val[0] != 0)
 173   {
 174      // Update the field or variable.
 175      if (object && field)
 176         object->setDataField(field, 0, val);
 177      else if (gEvalState.currentVariable)
 178         gEvalState.setStringVariable(val);
 179   }
 180}
 181extern ExprEvalState gEvalState;
 182
 183char sTraceBuffer[1024];
 184
 185StringStack STR;
 186ConsoleValueStack CSTK;
 187
 188U32 _FLT = 0;     ///< Stack pointer for floatStack.
 189U32 _UINT = 0;    ///< Stack pointer for intStack.
 190U32 _ITER = 0;    ///< Stack pointer for iterStack.
 191
 192IterStackRecord iterStack[MaxStackSize];
 193
 194F64 floatStack[MaxStackSize];
 195S64 intStack[MaxStackSize];
 196
 197char curFieldArray[256];
 198char prevFieldArray[256];
 199
 200typedef OPCodeReturn(CodeInterpreter::*OpFn)(U32&);
 201
 202static OpFn gOpCodeArray[MAX_OP_CODELEN];
 203
 204CodeInterpreter::CodeInterpreter(CodeBlock *cb) :
 205   mCodeBlock(cb),
 206   mIterDepth(0),
 207   mCurFloatTable(nullptr),
 208   mCurStringTable(nullptr),
 209   mThisFunctionName(nullptr),
 210   mPopFrame(false),
 211   mObjectCreationStackIndex(0),
 212   mCurrentNewObject(nullptr),
 213   mFailJump(0),
 214   mPrevField(nullptr),
 215   mCurField(nullptr),
 216   mPrevObject(nullptr),
 217   mCurObject(nullptr),
 218   mSaveObject(nullptr),
 219   mThisObject(nullptr),
 220   mNSEntry(nullptr),
 221   mCurFNDocBlock(nullptr),
 222   mCurNSDocBlock(nullptr),
 223   mCallArgc(0),
 224   mCallArgv(nullptr),
 225   mSaveCodeBlock(nullptr),
 226   mCurrentInstruction(0)
 227{
 228   dMemset(&mExec, 0, sizeof(mExec));
 229   dMemset(&mObjectCreationStack, 0, sizeof(mObjectCreationStack));
 230   dMemset(&mNSDocBlockClass, 0, sizeof(mNSDocBlockClass));
 231}
 232
 233CodeInterpreter::~CodeInterpreter()
 234{
 235}
 236
 237void CodeInterpreter::init()
 238{
 239   gOpCodeArray[OP_FUNC_DECL] = &CodeInterpreter::op_func_decl;
 240   gOpCodeArray[OP_CREATE_OBJECT] = &CodeInterpreter::op_create_object;
 241   gOpCodeArray[OP_ADD_OBJECT] = &CodeInterpreter::op_add_object;
 242   gOpCodeArray[OP_END_OBJECT] = &CodeInterpreter::op_end_object;
 243   gOpCodeArray[OP_FINISH_OBJECT] = &CodeInterpreter::op_finish_object;
 244   gOpCodeArray[OP_JMPIFFNOT] = &CodeInterpreter::op_jmpiffnot;
 245   gOpCodeArray[OP_JMPIFNOT] = &CodeInterpreter::op_jmpifnot;
 246   gOpCodeArray[OP_JMPIFF] = &CodeInterpreter::op_jmpiff;
 247   gOpCodeArray[OP_JMPIF] = &CodeInterpreter::op_jmpif;
 248   gOpCodeArray[OP_JMPIFNOT_NP] = &CodeInterpreter::op_jmpifnot_np;
 249   gOpCodeArray[OP_JMPIF_NP] = &CodeInterpreter::op_jmpif_np;
 250   gOpCodeArray[OP_JMP] = &CodeInterpreter::op_jmp;
 251   gOpCodeArray[OP_RETURN] = &CodeInterpreter::op_return;
 252   gOpCodeArray[OP_RETURN_VOID] = &CodeInterpreter::op_return_void;
 253   gOpCodeArray[OP_RETURN_FLT] = &CodeInterpreter::op_return_flt;
 254   gOpCodeArray[OP_RETURN_UINT] = &CodeInterpreter::op_return_uint;
 255   gOpCodeArray[OP_CMPEQ] = &CodeInterpreter::op_cmpeq;
 256   gOpCodeArray[OP_CMPGR] = &CodeInterpreter::op_cmpgr;
 257   gOpCodeArray[OP_CMPGE] = &CodeInterpreter::op_cmpge;
 258   gOpCodeArray[OP_CMPLT] = &CodeInterpreter::op_cmplt;
 259   gOpCodeArray[OP_CMPLE] = &CodeInterpreter::op_cmple;
 260   gOpCodeArray[OP_CMPNE] = &CodeInterpreter::op_cmpne;
 261   gOpCodeArray[OP_XOR] = &CodeInterpreter::op_xor;
 262   gOpCodeArray[OP_MOD] = &CodeInterpreter::op_mod;
 263   gOpCodeArray[OP_BITAND] = &CodeInterpreter::op_bitand;
 264   gOpCodeArray[OP_BITOR] = &CodeInterpreter::op_bitor;
 265   gOpCodeArray[OP_NOT] = &CodeInterpreter::op_not;
 266   gOpCodeArray[OP_NOTF] = &CodeInterpreter::op_notf;
 267   gOpCodeArray[OP_ONESCOMPLEMENT] = &CodeInterpreter::op_onescomplement;
 268   gOpCodeArray[OP_SHR] = &CodeInterpreter::op_shr;
 269   gOpCodeArray[OP_SHL] = &CodeInterpreter::op_shl;
 270   gOpCodeArray[OP_AND] = &CodeInterpreter::op_and;
 271   gOpCodeArray[OP_OR] = &CodeInterpreter::op_or;
 272   gOpCodeArray[OP_ADD] = &CodeInterpreter::op_add;
 273   gOpCodeArray[OP_SUB] = &CodeInterpreter::op_sub;
 274   gOpCodeArray[OP_MUL] = &CodeInterpreter::op_mul;
 275   gOpCodeArray[OP_DIV] = &CodeInterpreter::op_div;
 276   gOpCodeArray[OP_NEG] = &CodeInterpreter::op_neg;
 277   gOpCodeArray[OP_INC] = &CodeInterpreter::op_inc;
 278   gOpCodeArray[OP_DEC] = &CodeInterpreter::op_dec;
 279   gOpCodeArray[OP_SETCURVAR] = &CodeInterpreter::op_setcurvar;
 280   gOpCodeArray[OP_SETCURVAR_CREATE] = &CodeInterpreter::op_setcurvar_create;
 281   gOpCodeArray[OP_SETCURVAR_ARRAY] = &CodeInterpreter::op_setcurvar_array;
 282   gOpCodeArray[OP_SETCURVAR_ARRAY_VARLOOKUP] = &CodeInterpreter::op_setcurvar_array_varlookup;
 283   gOpCodeArray[OP_SETCURVAR_ARRAY_CREATE] = &CodeInterpreter::op_setcurvar_array_create;
 284   gOpCodeArray[OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP] = &CodeInterpreter::op_setcurvar_array_create_varlookup;
 285   gOpCodeArray[OP_LOADVAR_UINT] = &CodeInterpreter::op_loadvar_uint;
 286   gOpCodeArray[OP_LOADVAR_FLT] = &CodeInterpreter::op_loadvar_flt;
 287   gOpCodeArray[OP_LOADVAR_STR] = &CodeInterpreter::op_loadvar_str;
 288   gOpCodeArray[OP_LOADVAR_VAR] = &CodeInterpreter::op_loadvar_var;
 289   gOpCodeArray[OP_SAVEVAR_UINT] = &CodeInterpreter::op_savevar_uint;
 290   gOpCodeArray[OP_SAVEVAR_FLT] = &CodeInterpreter::op_savevar_flt;
 291   gOpCodeArray[OP_SAVEVAR_STR] = &CodeInterpreter::op_savevar_str;
 292   gOpCodeArray[OP_SAVEVAR_VAR] = &CodeInterpreter::op_savevar_var;
 293   gOpCodeArray[OP_SETCUROBJECT] = &CodeInterpreter::op_setcurobject;
 294   gOpCodeArray[OP_SETCUROBJECT_INTERNAL] = &CodeInterpreter::op_setcurobject_internal;
 295   gOpCodeArray[OP_SETCUROBJECT_NEW] = &CodeInterpreter::op_setcurobject_new;
 296   gOpCodeArray[OP_SETCURFIELD] = &CodeInterpreter::op_setcurfield;
 297   gOpCodeArray[OP_SETCURFIELD_ARRAY] = &CodeInterpreter::op_setcurfield_array;
 298   gOpCodeArray[OP_SETCURFIELD_TYPE] = &CodeInterpreter::op_setcurfield_type;
 299   gOpCodeArray[OP_SETCURFIELD_ARRAY_VAR] = &CodeInterpreter::op_setcurfield_array_var;
 300   gOpCodeArray[OP_SETCURFIELD_THIS] = &CodeInterpreter::op_setcurfield_this;
 301   gOpCodeArray[OP_LOADFIELD_UINT] = &CodeInterpreter::op_loadfield_uint;
 302   gOpCodeArray[OP_LOADFIELD_FLT] = &CodeInterpreter::op_loadfield_flt;
 303   gOpCodeArray[OP_LOADFIELD_STR] = &CodeInterpreter::op_loadfield_str;
 304   gOpCodeArray[OP_SAVEFIELD_UINT] = &CodeInterpreter::op_savefield_uint;
 305   gOpCodeArray[OP_SAVEFIELD_FLT] = &CodeInterpreter::op_savefield_flt;
 306   gOpCodeArray[OP_SAVEFIELD_STR] = &CodeInterpreter::op_savefield_str;
 307   gOpCodeArray[OP_STR_TO_UINT] = &CodeInterpreter::op_str_to_uint;
 308   gOpCodeArray[OP_STR_TO_FLT] = &CodeInterpreter::op_str_to_flt;
 309   gOpCodeArray[OP_STR_TO_NONE] = &CodeInterpreter::op_str_to_none;
 310   gOpCodeArray[OP_FLT_TO_UINT] = &CodeInterpreter::op_flt_to_uint;
 311   gOpCodeArray[OP_FLT_TO_STR] = &CodeInterpreter::op_flt_to_str;
 312   gOpCodeArray[OP_FLT_TO_NONE] = &CodeInterpreter::op_flt_to_none;
 313   gOpCodeArray[OP_UINT_TO_FLT] = &CodeInterpreter::op_uint_to_flt;
 314   gOpCodeArray[OP_UINT_TO_STR] = &CodeInterpreter::op_uint_to_str;
 315   gOpCodeArray[OP_UINT_TO_NONE] = &CodeInterpreter::op_uint_to_none;
 316   gOpCodeArray[OP_COPYVAR_TO_NONE] = &CodeInterpreter::op_copyvar_to_none;
 317   gOpCodeArray[OP_LOADIMMED_UINT] = &CodeInterpreter::op_loadimmed_uint;
 318   gOpCodeArray[OP_LOADIMMED_FLT] = &CodeInterpreter::op_loadimmed_flt;
 319   gOpCodeArray[OP_TAG_TO_STR] = &CodeInterpreter::op_tag_to_str;
 320   gOpCodeArray[OP_LOADIMMED_STR] = &CodeInterpreter::op_loadimmed_str;
 321   gOpCodeArray[OP_DOCBLOCK_STR] = &CodeInterpreter::op_docblock_str;
 322   gOpCodeArray[OP_LOADIMMED_IDENT] = &CodeInterpreter::op_loadimmed_ident;
 323   gOpCodeArray[OP_CALLFUNC_RESOLVE] = &CodeInterpreter::op_callfunc_resolve;
 324   gOpCodeArray[OP_CALLFUNC] = &CodeInterpreter::op_callfunc;
 325   gOpCodeArray[OP_CALLFUNC_POINTER] = &CodeInterpreter::op_callfunc_pointer;
 326   gOpCodeArray[OP_CALLFUNC_THIS] = &CodeInterpreter::op_callfunc_this;
 327   gOpCodeArray[OP_ADVANCE_STR] = &CodeInterpreter::op_advance_str;
 328   gOpCodeArray[OP_ADVANCE_STR_APPENDCHAR] = &CodeInterpreter::op_advance_str_appendchar;
 329   gOpCodeArray[OP_ADVANCE_STR_COMMA] = &CodeInterpreter::op_advance_str_comma;
 330   gOpCodeArray[OP_ADVANCE_STR_NUL] = &CodeInterpreter::op_advance_str_nul;
 331   gOpCodeArray[OP_REWIND_STR] = &CodeInterpreter::op_rewind_str;
 332   gOpCodeArray[OP_TERMINATE_REWIND_STR] = &CodeInterpreter::op_terminate_rewind_str;
 333   gOpCodeArray[OP_COMPARE_STR] = &CodeInterpreter::op_compare_str;
 334   gOpCodeArray[OP_PUSH] = &CodeInterpreter::op_push;
 335   gOpCodeArray[OP_PUSH_UINT] = &CodeInterpreter::op_push_uint;
 336   gOpCodeArray[OP_PUSH_FLT] = &CodeInterpreter::op_push_flt;
 337   gOpCodeArray[OP_PUSH_VAR] = &CodeInterpreter::op_push_var;
 338   gOpCodeArray[OP_PUSH_THIS] = &CodeInterpreter::op_push_this;
 339   gOpCodeArray[OP_PUSH_FRAME] = &CodeInterpreter::op_push_frame;
 340   gOpCodeArray[OP_ASSERT] = &CodeInterpreter::op_assert;
 341   gOpCodeArray[OP_BREAK] = &CodeInterpreter::op_break;
 342   gOpCodeArray[OP_ITER_BEGIN_STR] = &CodeInterpreter::op_iter_begin_str;
 343   gOpCodeArray[OP_ITER_BEGIN] = &CodeInterpreter::op_iter_begin;
 344   gOpCodeArray[OP_ITER] = &CodeInterpreter::op_iter;
 345   gOpCodeArray[OP_ITER_END] = &CodeInterpreter::op_iter_end;
 346   gOpCodeArray[OP_INVALID] = &CodeInterpreter::op_invalid;
 347}
 348
 349ConsoleValueRef CodeInterpreter::exec(U32 ip,
 350                                      StringTableEntry functionName,
 351                                      Namespace *thisNamespace,
 352                                      U32 argc, 
 353                                      ConsoleValueRef *argv,
 354                                      bool noCalls,
 355                                      StringTableEntry packageName,
 356                                      S32 setFrame) 
 357{
 358   mExec.functionName = functionName;
 359   mExec.thisNamespace = thisNamespace;
 360   mExec.argc = argc;
 361   mExec.argv = argv;
 362   mExec.noCalls = noCalls;
 363   mExec.packageName = packageName;
 364   mExec.setFrame = setFrame;
 365
 366   mCodeBlock->incRefCount();
 367
 368   mPopFrame = false;
 369
 370#ifdef TORQUE_VALIDATE_STACK
 371   U32 stackStart = STR.mStartStackSize;
 372#endif
 373
 374   STR.clearFunctionOffset(); // ensures arg buffer offset is back to 0
 375
 376   // Lets load up our function arguments.
 377   parseArgs(ip);
 378
 379   // Grab the state of the telenet debugger here once
 380   // so that the push and pop frames are always balanced.
 381   const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected();
 382   if (telDebuggerOn && setFrame < 0)
 383      TelDebugger->pushStackFrame();
 384
 385   mSaveCodeBlock = CodeBlock::smCurrentCodeBlock;
 386   CodeBlock::smCurrentCodeBlock = mCodeBlock;
 387   if (mCodeBlock->name)
 388   {
 389      Con::gCurrentFile = mCodeBlock->name;
 390      Con::gCurrentRoot = mCodeBlock->modPath;
 391   }
 392
 393   U32 *code = mCodeBlock->code;
 394
 395   while (true)
 396   {
 397      mCurrentInstruction = code[ip++];
 398      mNSEntry = nullptr;
 399
 400#ifdef TORQUE_VALIDATE_STACK
 401      // OP Code check.
 402      AssertFatal(mCurrentInstruction < MAX_OP_CODELEN, "Invalid OP code in script interpreter");
 403#endif
 404
 405   breakContinueLabel:
 406      OPCodeReturn ret = (this->*gOpCodeArray[mCurrentInstruction])(ip);
 407      if (ret == OPCodeReturn::exitCode)
 408         break;
 409      else if (ret == OPCodeReturn::breakContinue)
 410         goto breakContinueLabel;
 411   }
 412
 413   if (telDebuggerOn && setFrame < 0)
 414      TelDebugger->popStackFrame();
 415
 416   if (mPopFrame)
 417      gEvalState.popFrame();
 418
 419   if (argv)
 420   {
 421      if (gEvalState.traceOn)
 422      {
 423         sTraceBuffer[0] = 0;
 424         dStrcat(sTraceBuffer, "Leaving ", 1024);
 425
 426         if (packageName)
 427         {
 428            dStrcat(sTraceBuffer, "[", 1024);
 429            dStrcat(sTraceBuffer, packageName, 1024);
 430            dStrcat(sTraceBuffer, "]", 1024);
 431         }
 432         if (thisNamespace && thisNamespace->mName)
 433         {
 434            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
 435               "%s::%s() - return %s", thisNamespace->mName, mThisFunctionName, STR.getStringValue());
 436         }
 437         else
 438         {
 439            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
 440               "%s() - return %s", mThisFunctionName, STR.getStringValue());
 441         }
 442         Con::printf("%s", sTraceBuffer);
 443      }
 444   }
 445
 446   CodeBlock::smCurrentCodeBlock = mSaveCodeBlock;
 447   if (mSaveCodeBlock && mSaveCodeBlock->name)
 448   {
 449      Con::gCurrentFile = mSaveCodeBlock->name;
 450      Con::gCurrentRoot = mSaveCodeBlock->modPath;
 451   }
 452
 453   mCodeBlock->decRefCount();
 454
 455#ifdef TORQUE_VALIDATE_STACK
 456   AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec");
 457   AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec");
 458#endif
 459
 460   return mReturnValue;
 461}
 462
 463void CodeInterpreter::parseArgs(U32 &ip)
 464{
 465   U32 *code = mCodeBlock->code;
 466
 467   if (mExec.argv)
 468   {
 469      U32 fnArgc = code[ip + 2 + 6];
 470      mThisFunctionName = Compiler::CodeToSTE(code, ip);
 471      S32 wantedArgc = getMin(mExec.argc - 1, fnArgc); // argv[0] is func name
 472      if (gEvalState.traceOn)
 473      {
 474         sTraceBuffer[0] = 0;
 475         dStrcat(sTraceBuffer, "Entering ", 1024);
 476
 477         if (mExec.packageName)
 478         {
 479            dStrcat(sTraceBuffer, "[", 1024);
 480            dStrcat(sTraceBuffer, mExec.packageName, 1024);
 481            dStrcat(sTraceBuffer, "]", 1024);
 482         }
 483         if (mExec.thisNamespace && mExec.thisNamespace->mName)
 484         {
 485            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
 486               "%s::%s(", mExec.thisNamespace->mName, mThisFunctionName);
 487         }
 488         else
 489         {
 490            dSprintf(sTraceBuffer + dStrlen(sTraceBuffer), (U32)(sizeof(sTraceBuffer) - dStrlen(sTraceBuffer)),
 491               "%s(", mThisFunctionName);
 492         }
 493         for (S32 i = 0; i < wantedArgc; i++)
 494         {
 495            dStrcat(sTraceBuffer, mExec.argv[i + 1], 1024);
 496            if (i != wantedArgc - 1)
 497               dStrcat(sTraceBuffer, ", ", 1024);
 498         }
 499         dStrcat(sTraceBuffer, ")", 1024);
 500         Con::printf("%s", sTraceBuffer);
 501      }
 502
 503      gEvalState.pushFrame(mThisFunctionName, mExec.thisNamespace);
 504      mPopFrame = true;
 505
 506      StringTableEntry thisPointer = StringTable->insert("%this");
 507
 508      for (S32 i = 0; i < wantedArgc; i++)
 509      {
 510         StringTableEntry var = Compiler::CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2));
 511         gEvalState.setCurVarNameCreate(var);
 512
 513         ConsoleValueRef ref = mExec.argv[i + 1];
 514
 515         switch (ref.getType())
 516         {
 517         case ConsoleValue::TypeInternalInt:
 518            gEvalState.setIntVariable(ref);
 519            break;
 520         case ConsoleValue::TypeInternalFloat:
 521            gEvalState.setFloatVariable(ref);
 522            break;
 523         case ConsoleValue::TypeInternalStringStackPtr:
 524            gEvalState.setStringStackPtrVariable(ref.getStringStackPtrValue());
 525            break;
 526         case ConsoleValue::TypeInternalStackString:
 527         case ConsoleValue::TypeInternalString:
 528         default:
 529            gEvalState.setStringVariable(ref);
 530            break;
 531         }
 532
 533         if (var == thisPointer)
 534         {
 535            // %this gets optimized as it is flagged as a constant.
 536            // Since it is guarenteed to be constant, we can then perform optimizations.
 537            gEvalState.currentVariable->mIsConstant = true;
 538
 539            // Store a reference to the this pointer object.
 540            mThisObject = Sim::findObject(gEvalState.getStringVariable());
 541         }
 542      }
 543
 544      ip = ip + (fnArgc * 2) + (2 + 6 + 1);
 545      mCurFloatTable = mCodeBlock->functionFloats;
 546      mCurStringTable = mCodeBlock->functionStrings;
 547   }
 548   else
 549   {
 550      mCurFloatTable = mCodeBlock->globalFloats;
 551      mCurStringTable = mCodeBlock->globalStrings;
 552
 553      // If requested stack frame isn't available, request a new one
 554      // (this prevents assert failures when creating local
 555      //  variables without a stack frame)
 556      if (gEvalState.getStackDepth() <= mExec.setFrame)
 557         mExec.setFrame = -1;
 558
 559      // Do we want this code to execute using a new stack frame?
 560      if (mExec.setFrame < 0)
 561      {
 562         gEvalState.pushFrame(NULL, NULL);
 563         mPopFrame = true;
 564      }
 565      else
 566      {
 567         // We want to copy a reference to an existing stack frame
 568         // on to the top of the stack.  Any change that occurs to 
 569         // the locals during this new frame will also occur in the 
 570         // original frame.
 571         S32 stackIndex = gEvalState.getStackDepth() - mExec.setFrame - 1;
 572         gEvalState.pushFrameRef(stackIndex);
 573         mPopFrame = true;
 574      }
 575   }
 576}
 577
 578OPCodeReturn CodeInterpreter::op_func_decl(U32 &ip)
 579{
 580   U32 *code = mCodeBlock->code;
 581
 582   if (!mExec.noCalls)
 583   {
 584      StringTableEntry fnName = CodeToSTE(code, ip);
 585      StringTableEntry fnNamespace = CodeToSTE(code, ip + 2);
 586      StringTableEntry fnPackage = CodeToSTE(code, ip + 4);
 587      bool hasBody = (code[ip + 6] & 0x01) != 0;
 588      U32 lineNumber = code[ip + 6] >> 1;
 589
 590      Namespace::unlinkPackages();
 591      Namespace *ns = Namespace::find(fnNamespace, fnPackage);
 592      ns->addFunction(fnName, mCodeBlock, hasBody ? ip : 0, mCurFNDocBlock ? dStrdup(mCurFNDocBlock) : NULL, lineNumber);// if no body, set the IP to 0
 593      if (mCurNSDocBlock)
 594      {
 595         // If we have a docblock before we declare the function in the script file,
 596         // this will attempt to set the doc block to the function.
 597         // See OP_DOCBLOCK_STR
 598         if (fnNamespace == StringTable->lookup(mNSDocBlockClass))
 599         {
 600            char *usageStr = dStrdup(mCurNSDocBlock);
 601            usageStr[dStrlen(usageStr)] = '\0';
 602            ns->mUsage = usageStr;
 603            ns->mCleanUpUsage = true;
 604            mCurNSDocBlock = NULL;
 605         }
 606      }
 607      Namespace::relinkPackages();
 608
 609      // If we had a docblock, it's definitely not valid anymore, so clear it out.
 610      mCurFNDocBlock = NULL;
 611
 612      //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip);
 613   }
 614   ip = code[ip + 7];
 615   return OPCodeReturn::success;
 616}
 617
 618OPCodeReturn CodeInterpreter::op_create_object(U32 &ip)
 619{
 620   U32 *code = mCodeBlock->code;
 621
 622   // Read some useful info.
 623   StringTableEntry objParent = CodeToSTE(code, ip);
 624   bool isDataBlock = code[ip + 2];
 625   bool isInternal = code[ip + 3];
 626   bool isSingleton = code[ip + 4];
 627   U32  lineNumber = code[ip + 5];
 628   mFailJump = code[ip + 6];
 629
 630   // If we don't allow calls, we certainly don't allow creating objects!
 631   // Moved this to after failJump is set. Engine was crashing when
 632   // noCalls = true and an object was being created at the beginning of
 633   // a file. ADL.
 634   if (mExec.noCalls)
 635   {
 636      ip = mFailJump;
 637      return OPCodeReturn::success;
 638   }
 639
 640   // Push the old info to the stack
 641   //Assert( objectCreationStackIndex < objectCreationStackSize );
 642   mObjectCreationStack[mObjectCreationStackIndex].newObject = mCurrentNewObject;
 643   mObjectCreationStack[mObjectCreationStackIndex++].failJump = mFailJump;
 644
 645   // Get the constructor information off the stack.
 646   CSTK.getArgcArgv(NULL, &mCallArgc, &mCallArgv);
 647   const char *objectName = mCallArgv[2];
 648
 649   // Con::printf("Creating object...");
 650
 651   // objectName = argv[1]...
 652   mCurrentNewObject = NULL;
 653
 654   // Are we creating a datablock? If so, deal with case where we override
 655   // an old one.
 656   if (isDataBlock)
 657   {
 658      // Con::printf("  - is a datablock");
 659
 660      // Find the old one if any.
 661      SimObject *db = Sim::getDataBlockGroup()->findObject(objectName);
 662
 663      // Make sure we're not changing types on ourselves...
 664      if (db && dStricmp(db->getClassName(), mCallArgv[1]))
 665      {
 666         Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", mCodeBlock->getFileLine(ip), objectName);
 667         ip = mFailJump;
 668         STR.popFrame();
 669         CSTK.popFrame();
 670         return OPCodeReturn::success;
 671      }
 672
 673      // If there was one, set the currentNewObject and move on.
 674      if (db)
 675         mCurrentNewObject = db;
 676   }
 677   else if (!isInternal)
 678   {
 679      // IF we aren't looking at a local/internal object, then check if 
 680      // this object already exists in the global space
 681
 682      AbstractClassRep* rep = AbstractClassRep::findClassRep(objectName);
 683      if (rep != NULL) {
 684         Con::errorf(ConsoleLogEntry::General, "%s: Cannot name object [%s] the same name as a script class.",
 685            mCodeBlock->getFileLine(ip), objectName);
 686         ip = mFailJump;
 687         STR.popFrame();
 688         CSTK.popFrame();
 689         return OPCodeReturn::success;
 690      }
 691
 692      SimObject *obj = Sim::findObject((const char*)objectName);
 693      if (obj /*&& !obj->isLocalName()*/)
 694      {
 695         if (isSingleton)
 696         {
 697            // Make sure we're not trying to change types
 698            if (dStricmp(obj->getClassName(), (const char*)mCallArgv[1]) != 0)
 699            {
 700               Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].",
 701                  mCodeBlock->getFileLine(ip), objectName, (const char*)mCallArgv[1], obj->getClassName());
 702               ip = mFailJump;
 703               STR.popFrame();
 704               CSTK.popFrame();
 705               return OPCodeReturn::success;
 706            }
 707
 708            // We're creating a singleton, so use the found object
 709            // instead of creating a new object.
 710            mCurrentNewObject = obj;
 711         }
 712         else
 713         {
 714            const char* redefineBehavior = Con::getVariable("$Con::redefineBehavior");
 715
 716            if (dStricmp(redefineBehavior, "replaceExisting") == 0)
 717            {
 718               // Save our constructor args as the argv vector is stored on the
 719               // string stack and may get stomped if deleteObject triggers
 720               // script execution.
 721
 722               ConsoleValueRef savedArgv[StringStack::MaxArgs];
 723               for (int i = 0; i< mCallArgc; i++) {
 724                  savedArgv[i] = mCallArgv[i];
 725               }
 726               //dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc );
 727
 728               // Prevent stack value corruption
 729               CSTK.pushFrame();
 730               STR.pushFrame();
 731               // --
 732
 733               obj->deleteObject();
 734               obj = NULL;
 735
 736               // Prevent stack value corruption
 737               CSTK.popFrame();
 738               STR.popFrame();
 739               // --
 740
 741               //dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc );
 742               for (int i = 0; i<mCallArgc; i++) {
 743                  mCallArgv[i] = savedArgv[i];
 744               }
 745            }
 746            else if (dStricmp(redefineBehavior, "renameNew") == 0)
 747            {
 748               for (U32 i = 1;; ++i)
 749               {
 750                  String newName = String::ToString("%s%i", objectName, i);
 751                  if (!Sim::findObject(newName))
 752                  {
 753                     objectName = StringTable->insert(newName);
 754                     break;
 755                  }
 756               }
 757            }
 758            else if (dStricmp(redefineBehavior, "unnameNew") == 0)
 759            {
 760               objectName = StringTable->insert("");
 761            }
 762            else if (dStricmp(redefineBehavior, "postfixNew") == 0)
 763            {
 764               const char* postfix = Con::getVariable("$Con::redefineBehaviorPostfix");
 765               String newName = String::ToString("%s%s", objectName, postfix);
 766
 767               if (Sim::findObject(newName))
 768               {
 769                  Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].",
 770                     mCodeBlock->getFileLine(ip), newName.c_str());
 771                  ip = mFailJump;
 772                  STR.popFrame();
 773                  CSTK.popFrame();
 774                  return OPCodeReturn::success;
 775               }
 776               else
 777                  objectName = StringTable->insert(newName);
 778            }
 779            else
 780            {
 781               Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].",
 782                  mCodeBlock->getFileLine(ip), objectName);
 783               ip = mFailJump;
 784               STR.popFrame();
 785               CSTK.popFrame();
 786               return OPCodeReturn::success;
 787            }
 788         }
 789      }
 790   }
 791
 792   STR.popFrame();
 793   CSTK.popFrame();
 794
 795   if (!mCurrentNewObject)
 796   {
 797      // Well, looks like we have to create a new object.
 798      ConsoleObject *object = ConsoleObject::create((const char*)mCallArgv[1]);
 799
 800      // Deal with failure!
 801      if (!object)
 802      {
 803         Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
 804         ip = mFailJump;
 805         return OPCodeReturn::success;
 806      }
 807
 808      // Do special datablock init if appropros
 809      if (isDataBlock)
 810      {
 811         SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(object);
 812         if (dataBlock)
 813         {
 814            dataBlock->assignId();
 815         }
 816         else
 817         {
 818            // They tried to make a non-datablock with a datablock keyword!
 819            Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
 820            // Clean up...
 821            delete object;
 822            mCurrentNewObject = NULL;
 823            ip = mFailJump;
 824            return OPCodeReturn::success;
 825         }
 826      }
 827
 828      // Finally, set currentNewObject to point to the new one.
 829      mCurrentNewObject = dynamic_cast<SimObject *>(object);
 830
 831      // Deal with the case of a non-SimObject.
 832      if (!mCurrentNewObject)
 833      {
 834         Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", mCodeBlock->getFileLine(ip), (const char*)mCallArgv[1]);
 835         delete object;
 836         mCurrentNewObject = NULL;
 837         ip = mFailJump;
 838         return OPCodeReturn::success;
 839      }
 840
 841      // Set the declaration line
 842      mCurrentNewObject->setDeclarationLine(lineNumber);
 843
 844      // Set the file that this object was created in
 845      mCurrentNewObject->setFilename(mCodeBlock->name);
 846
 847      // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance)
 848      if (*objParent)
 849      {
 850         // Find it!
 851         SimObject *parent;
 852         if (Sim::findObject(objParent, parent))
 853         {
 854            // Con::printf(" - Parent object found: %s", parent->getClassName());
 855
 856            mCurrentNewObject->setCopySource(parent);
 857            mCurrentNewObject->assignFieldsFrom(parent);
 858
 859            // copy any substitution statements
 860            SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
 861            if (parent_db)
 862            {
 863               SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(mCurrentNewObject);
 864               if (currentNewObject_db)
 865                  currentNewObject_db->copySubstitutionsFrom(parent_db);
 866            }
 867         }
 868         else
 869         {
 870            if (Con::gObjectCopyFailures == -1)
 871               Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", mCodeBlock->getFileLine(ip), objParent, (const char*)mCallArgv[1]);
 872            else
 873               ++Con::gObjectCopyFailures;
 874
 875            // Fail to create the object.
 876            delete object;
 877            mCurrentNewObject = NULL;
 878            ip = mFailJump;
 879            return OPCodeReturn::success;
 880         }
 881      }
 882
 883      // If a name was passed, assign it.
 884      if (objectName[0])
 885      {
 886         if (!isInternal)
 887            mCurrentNewObject->assignName(objectName);
 888         else
 889            mCurrentNewObject->setInternalName(objectName);
 890
 891         // Set the original name
 892         mCurrentNewObject->setOriginalName(objectName);
 893      }
 894
 895      // Prevent stack value corruption
 896      CSTK.pushFrame();
 897      STR.pushFrame();
 898      // --
 899
 900      // Do the constructor parameters.
 901      if (!mCurrentNewObject->processArguments(mCallArgc - 3, mCallArgv + 3))
 902      {
 903         delete mCurrentNewObject;
 904         mCurrentNewObject = NULL;
 905         ip = mFailJump;
 906
 907         // Prevent stack value corruption
 908         CSTK.popFrame();
 909         STR.popFrame();
 910         // --
 911         return OPCodeReturn::success;
 912      }
 913
 914      // Prevent stack value corruption
 915      CSTK.popFrame();
 916      STR.popFrame();
 917      // --
 918
 919      // If it's not a datablock, allow people to modify bits of it.
 920      if (!isDataBlock)
 921      {
 922         mCurrentNewObject->setModStaticFields(true);
 923         mCurrentNewObject->setModDynamicFields(true);
 924      }
 925   }
 926   else
 927   {
 928      mCurrentNewObject->reloadReset(); // AFX (reload-reset)
 929                                       // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance)
 930      if (*objParent)
 931      {
 932         // Find it!
 933         SimObject *parent;
 934         if (Sim::findObject(objParent, parent))
 935         {
 936            // Con::printf(" - Parent object found: %s", parent->getClassName());
 937
 938            // temporarily block name change
 939            SimObject::preventNameChanging = true;
 940            mCurrentNewObject->setCopySource(parent);
 941            mCurrentNewObject->assignFieldsFrom(parent);
 942            // restore name changing
 943            SimObject::preventNameChanging = false;
 944
 945            // copy any substitution statements
 946            SimDataBlock* parent_db = dynamic_cast<SimDataBlock*>(parent);
 947            if (parent_db)
 948            {
 949               SimDataBlock* currentNewObject_db = dynamic_cast<SimDataBlock*>(mCurrentNewObject);
 950               if (currentNewObject_db)
 951                  currentNewObject_db->copySubstitutionsFrom(parent_db);
 952            }
 953         }
 954         else
 955            Con::errorf(ConsoleLogEntry::General, "%d: Unable to find parent object %s for %s.", lineNumber, objParent, (const char*)mCallArgv[1]);
 956      }
 957   }
 958
 959   // Advance the IP past the create info...
 960   ip += 7;
 961   return OPCodeReturn::success;
 962}
 963
 964OPCodeReturn CodeInterpreter::op_add_object(U32 &ip)
 965{
 966   // See OP_SETCURVAR for why we do this.
 967   mCurFNDocBlock = NULL;
 968   mCurNSDocBlock = NULL;
 969
 970   // Do we place this object at the root?
 971   bool placeAtRoot = mCodeBlock->code[ip++];
 972
 973   // Con::printf("Adding object %s", currentNewObject->getName());
 974
 975   // Prevent stack value corruption
 976   CSTK.pushFrame();
 977   STR.pushFrame();
 978   // --
 979
 980   // Make sure it wasn't already added, then add it.
 981   if (mCurrentNewObject->isProperlyAdded() == false)
 982   {
 983      bool ret = false;
 984
 985      Message *msg = dynamic_cast<Message *>(mCurrentNewObject);
 986      if (msg)
 987      {
 988         SimObjectId id = Message::getNextMessageID();
 989         if (id != 0xffffffff)
 990            ret = mCurrentNewObject->registerObject(id);
 991         else
 992            Con::errorf("%s: No more object IDs available for messages", mCodeBlock->getFileLine(ip));
 993      }
 994      else
 995         ret = mCurrentNewObject->registerObject();
 996
 997      if (!ret)
 998      {
 999         // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields().
1000         Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", mCodeBlock->getFileLine(ip), mCurrentNewObject->getName(), mCurrentNewObject->getClassName());
1001         delete mCurrentNewObject;
1002         mCurrentNewObject = NULL;
1003         ip = mFailJump;
1004         // Prevent stack value corruption
1005         CSTK.popFrame();
1006         STR.popFrame();
1007         // --
1008         return OPCodeReturn::success;
1009      }
1010   }
1011
1012   // Are we dealing with a datablock?
1013   SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(mCurrentNewObject);
1014   static String errorStr;
1015
1016   // If so, preload it.
1017   if (dataBlock && !dataBlock->preload(true, errorStr))
1018   {
1019      Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", mCodeBlock->getFileLine(ip),
1020         mCurrentNewObject->getName(), errorStr.c_str());
1021      dataBlock->deleteObject();
1022      mCurrentNewObject = NULL;
1023      ip = mFailJump;
1024
1025      // Prevent stack value corruption
1026      CSTK.popFrame();
1027      STR.popFrame();
1028      // --
1029      return OPCodeReturn::success;
1030   }
1031
1032   // What group will we be added to, if any?
1033   U32 groupAddId = intStack[_UINT];
1034   SimGroup *grp = NULL;
1035   SimSet   *set = NULL;
1036   bool isMessage = dynamic_cast<Message *>(mCurrentNewObject) != NULL;
1037
1038   if (!placeAtRoot || !mCurrentNewObject->getGroup())
1039   {
1040      if (!isMessage)
1041      {
1042         if (!placeAtRoot)
1043         {
1044            // Otherwise just add to the requested group or set.
1045            if (!Sim::findObject(groupAddId, grp))
1046               Sim::findObject(groupAddId, set);
1047         }
1048
1049         if (placeAtRoot)
1050         {
1051            // Deal with the instantGroup if we're being put at the root or we're adding to a component.
1052            if (Con::gInstantGroup.isEmpty()
1053               || !Sim::findObject(Con::gInstantGroup, grp))
1054               grp = Sim::getRootGroup();
1055         }
1056      }
1057
1058      // If we didn't get a group, then make sure we have a pointer to
1059      // the rootgroup.
1060      if (!grp)
1061         grp = Sim::getRootGroup();
1062
1063      // add to the parent group
1064      grp->addObject(mCurrentNewObject);
1065
1066      // If for some reason the add failed, add the object to the
1067      // root group so it won't leak.
1068      if (!mCurrentNewObject->getGroup())
1069         Sim::getRootGroup()->addObject(mCurrentNewObject);
1070
1071      // add to any set we might be in
1072      if (set)
1073         set->addObject(mCurrentNewObject);
1074   }
1075
1076   // store the new object's ID on the stack (overwriting the group/set
1077   // id, if one was given, otherwise getting pushed)
1078   if (placeAtRoot)
1079      intStack[_UINT] = mCurrentNewObject->getId();
1080   else
1081      intStack[++_UINT] = mCurrentNewObject->getId();
1082
1083   // Prevent stack value corruption
1084   CSTK.popFrame();
1085   STR.popFrame();
1086   // --
1087   return OPCodeReturn::success;
1088}
1089
1090OPCodeReturn CodeInterpreter::op_end_object(U32 &ip)
1091{
1092   // If we're not to be placed at the root, make sure we clean up
1093   // our group reference.
1094   bool placeAtRoot = mCodeBlock->code[ip++];
1095   if (!placeAtRoot)
1096      _UINT--;
1097   return OPCodeReturn::success;
1098}
1099
1100OPCodeReturn CodeInterpreter::op_finish_object(U32 &ip)
1101{
1102   if (mCurrentNewObject)
1103      mCurrentNewObject->onPostAdd();
1104
1105   //Assert( objectCreationStackIndex >= 0 );
1106   // Restore the object info from the stack [7/9/2007 Black]
1107   mCurrentNewObject = mObjectCreationStack[--mObjectCreationStackIndex].newObject;
1108   mFailJump = mObjectCreationStack[mObjectCreationStackIndex].failJump;
1109   return OPCodeReturn::success;
1110}
1111
1112OPCodeReturn CodeInterpreter::op_jmpiffnot(U32 &ip)
1113{
1114   if (floatStack[_FLT--])
1115   {
1116      ip++;
1117      return OPCodeReturn::success;
1118   }
1119   ip = mCodeBlock->code[ip];
1120   return OPCodeReturn::success;
1121}
1122
1123
1124OPCodeReturn CodeInterpreter::op_jmpifnot(U32 &ip)
1125{
1126   if (intStack[_UINT--])
1127   {
1128      ip++;
1129      return OPCodeReturn::success;
1130   }
1131   ip = mCodeBlock->code[ip];
1132   return OPCodeReturn::success;
1133}
1134
1135OPCodeReturn CodeInterpreter::op_jmpiff(U32 &ip)
1136{
1137   if (!floatStack[_FLT--])
1138   {
1139      ip++;
1140      return OPCodeReturn::success;
1141   }
1142   ip = mCodeBlock->code[ip];
1143   return OPCodeReturn::success;
1144}
1145
1146OPCodeReturn CodeInterpreter::op_jmpif(U32 &ip)
1147{
1148   if (!intStack[_UINT--])
1149   {
1150      ip++;
1151      return OPCodeReturn::success;
1152   }
1153   ip = mCodeBlock->code[ip];
1154   return OPCodeReturn::success;
1155}
1156
1157OPCodeReturn CodeInterpreter::op_jmpifnot_np(U32 &ip)
1158{
1159   if (intStack[_UINT])
1160   {
1161      _UINT--;
1162      ip++;
1163      return OPCodeReturn::success;
1164   }
1165   ip = mCodeBlock->code[ip];
1166   return OPCodeReturn::success;
1167}
1168
1169OPCodeReturn CodeInterpreter::op_jmpif_np(U32 &ip)
1170{
1171   if (!intStack[_UINT])
1172   {
1173      _UINT--;
1174      ip++;
1175      return OPCodeReturn::success;
1176   }
1177   ip = mCodeBlock->code[ip];
1178   return OPCodeReturn::success;
1179}
1180
1181OPCodeReturn CodeInterpreter::op_jmp(U32 &ip)
1182{
1183   ip = mCodeBlock->code[ip];
1184   return OPCodeReturn::success;
1185}
1186
1187OPCodeReturn CodeInterpreter::op_return_void(U32 &ip)
1188{
1189   STR.setStringValue("");
1190   // We're falling thru here on purpose.
1191
1192   OPCodeReturn ret = op_return(ip);
1193
1194   return ret;
1195}
1196
1197OPCodeReturn CodeInterpreter::op_return(U32 &ip)
1198{
1199   StringStackPtr retValue = STR.getStringValuePtr();
1200
1201   if (mIterDepth > 0)
1202   {
1203      // Clear iterator state.
1204      while (mIterDepth > 0)
1205      {
1206         iterStack[--_ITER].mIsStringIter = false;
1207         --mIterDepth;
1208      }
1209
1210      STR.rewind();
1211      STR.setStringValue(StringStackPtrRef(retValue).getPtr(&STR)); // Not nice but works.
1212      retValue = STR.getStringValuePtr();
1213   }
1214
1215   // Previously the return value was on the stack and would be returned using STR.getStringValue().
1216   // Now though we need to wrap it in a ConsoleValueRef 
1217   mReturnValue.value = CSTK.pushStringStackPtr(retValue);
1218
1219   return OPCodeReturn::exitCode;
1220}
1221
1222OPCodeReturn CodeInterpreter::op_return_flt(U32 &ip)
1223{
1224   if (mIterDepth > 0)
1225   {
1226      // Clear iterator state.
1227      while (mIterDepth > 0)
1228      {
1229         iterStack[--_ITER].mIsStringIter = false;
1230         --mIterDepth;
1231      }
1232
1233   }
1234
1235   mReturnValue.value = CSTK.pushFLT(floatStack[_FLT]);
1236   _FLT--;
1237
1238   return OPCodeReturn::exitCode;
1239}
1240
1241OPCodeReturn CodeInterpreter::op_return_uint(U32 &ip)
1242{
1243   if (mIterDepth > 0)
1244   {
1245      // Clear iterator state.
1246      while (mIterDepth > 0)
1247      {
1248         iterStack[--_ITER].mIsStringIter = false;
1249         --mIterDepth;
1250      }
1251   }
1252
1253   mReturnValue.value = CSTK.pushUINT(intStack[_UINT]);
1254   _UINT--;
1255
1256   return OPCodeReturn::exitCode;
1257}
1258
1259OPCodeReturn CodeInterpreter::op_cmpeq(U32 &ip)
1260{
1261   intStack[_UINT + 1] = bool(floatStack[_FLT] == floatStack[_FLT - 1]);
1262   _UINT++;
1263   _FLT -= 2;
1264   return OPCodeReturn::success;
1265}
1266
1267OPCodeReturn CodeInterpreter::op_cmpgr(U32 &ip)
1268{
1269   intStack[_UINT + 1] = bool(floatStack[_FLT] > floatStack[_FLT - 1]);
1270   _UINT++;
1271   _FLT -= 2;
1272   return OPCodeReturn::success;
1273}
1274
1275OPCodeReturn CodeInterpreter::op_cmpge(U32 &ip)
1276{
1277   intStack[_UINT + 1] = bool(floatStack[_FLT] >= floatStack[_FLT - 1]);
1278   _UINT++;
1279   _FLT -= 2;
1280   return OPCodeReturn::success;
1281}
1282
1283OPCodeReturn CodeInterpreter::op_cmplt(U32 &ip)
1284{
1285   intStack[_UINT + 1] = bool(floatStack[_FLT] < floatStack[_FLT - 1]);
1286   _UINT++;
1287   _FLT -= 2;
1288   return OPCodeReturn::success;
1289}
1290
1291OPCodeReturn CodeInterpreter::op_cmple(U32 &ip)
1292{
1293   intStack[_UINT + 1] = bool(floatStack[_FLT] <= floatStack[_FLT - 1]);
1294   _UINT++;
1295   _FLT -= 2;
1296   return OPCodeReturn::success;
1297}
1298
1299OPCodeReturn CodeInterpreter::op_cmpne(U32 &ip)
1300{
1301   intStack[_UINT + 1] = bool(floatStack[_FLT] != floatStack[_FLT - 1]);
1302   _UINT++;
1303   _FLT -= 2;
1304   return OPCodeReturn::success;
1305}
1306
1307OPCodeReturn CodeInterpreter::op_xor(U32 &ip)
1308{
1309   intStack[_UINT - 1] = intStack[_UINT] ^ intStack[_UINT - 1];
1310   _UINT--;
1311   return OPCodeReturn::success;
1312}
1313
1314OPCodeReturn CodeInterpreter::op_mod(U32 &ip)
1315{
1316   if (intStack[_UINT - 1] != 0)
1317      intStack[_UINT - 1] = intStack[_UINT] % intStack[_UINT - 1];
1318   else
1319      intStack[_UINT - 1] = 0;
1320   _UINT--;
1321   return OPCodeReturn::success;
1322}
1323
1324OPCodeReturn CodeInterpreter::op_bitand(U32 &ip)
1325{
1326   intStack[_UINT - 1] = intStack[_UINT] & intStack[_UINT - 1];
1327   _UINT--;
1328   return OPCodeReturn::success;
1329}
1330
1331OPCodeReturn CodeInterpreter::op_bitor(U32 &ip)
1332{
1333   intStack[_UINT - 1] = intStack[_UINT] | intStack[_UINT - 1];
1334   _UINT--;
1335   return OPCodeReturn::success;
1336}
1337
1338OPCodeReturn CodeInterpreter::op_not(U32 &ip)
1339{
1340   intStack[_UINT] = !intStack[_UINT];
1341   return OPCodeReturn::success;
1342}
1343
1344OPCodeReturn CodeInterpreter::op_notf(U32 &ip)
1345{
1346   intStack[_UINT + 1] = !floatStack[_FLT];
1347   _FLT--;
1348   _UINT++;
1349   return OPCodeReturn::success;
1350}
1351
1352OPCodeReturn CodeInterpreter::op_onescomplement(U32 &ip)
1353{
1354   intStack[_UINT] = ~<a href="/coding/file/codeinterpreter_8cpp/#codeinterpreter_8cpp_1a585e657b7dadf3b57573aac845ef36e4">intStack</a>[_UINT];
1355   return OPCodeReturn::success;
1356}
1357
1358OPCodeReturn CodeInterpreter::op_shr(U32 &ip)
1359{
1360   intStack[_UINT - 1] = intStack[_UINT] >> intStack[_UINT - 1];
1361   _UINT--;
1362   return OPCodeReturn::success;
1363}
1364
1365OPCodeReturn CodeInterpreter::op_shl(U32 &ip)
1366{
1367   intStack[_UINT - 1] = intStack[_UINT] << intStack[_UINT - 1];
1368   _UINT--;
1369   return OPCodeReturn::success;
1370}
1371
1372OPCodeReturn CodeInterpreter::op_and(U32 &ip)
1373{
1374   intStack[_UINT - 1] = intStack[_UINT] && intStack[_UINT - 1];
1375   _UINT--;
1376   return OPCodeReturn::success;
1377}
1378
1379OPCodeReturn CodeInterpreter::op_or(U32 &ip)
1380{
1381   intStack[_UINT - 1] = intStack[_UINT] || intStack[_UINT - 1];
1382   _UINT--;
1383   return OPCodeReturn::success;
1384}
1385
1386OPCodeReturn CodeInterpreter::op_add(U32 &ip)
1387{
1388   floatStack[_FLT - 1] = floatStack[_FLT] + floatStack[_FLT - 1];
1389   _FLT--;
1390   return OPCodeReturn::success;
1391}
1392
1393OPCodeReturn CodeInterpreter::op_sub(U32 &ip)
1394{
1395   floatStack[_FLT - 1] = floatStack[_FLT] - floatStack[_FLT - 1];
1396   _FLT--;
1397   return OPCodeReturn::success;
1398}
1399
1400OPCodeReturn CodeInterpreter::op_mul(U32 &ip)
1401{
1402   floatStack[_FLT - 1] = floatStack[_FLT] * floatStack[_FLT - 1];
1403   _FLT--;
1404   return OPCodeReturn::success;
1405}
1406
1407OPCodeReturn CodeInterpreter::op_div(U32 &ip)
1408{
1409   floatStack[_FLT - 1] = floatStack[_FLT] / floatStack[_FLT - 1];
1410   _FLT--;
1411   return OPCodeReturn::success;
1412}
1413
1414OPCodeReturn CodeInterpreter::op_neg(U32 &ip)
1415{
1416   floatStack[_FLT] = -floatStack[_FLT];
1417   return OPCodeReturn::success;
1418}
1419
1420OPCodeReturn CodeInterpreter::op_inc(U32 &ip)
1421{
1422   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
1423   ip += 2;
1424
1425   // If a variable is set, then these must be NULL. It is necessary
1426   // to set this here so that the vector parser can appropriately
1427   // identify whether it's dealing with a vector.
1428   mPrevField = NULL;
1429   mPrevObject = NULL;
1430   mCurObject = NULL;
1431
1432   gEvalState.setCurVarNameCreate(var);
1433
1434   // In order to let docblocks work properly with variables, we have
1435   // clear the current docblock when we do an assign. This way it 
1436   // won't inappropriately carry forward to following function decls.
1437   mCurFNDocBlock = NULL;
1438   mCurNSDocBlock = NULL;
1439
1440   F64 val = gEvalState.getFloatVariable() + 1.0;
1441   gEvalState.setFloatVariable(val);
1442
1443   // We gotta push val onto the stack. What if we have
1444   // more expressions that have to use this.
1445   // If we don't, we send out an op code to pop it.
1446   floatStack[_FLT + 1] = val;
1447   _FLT++;
1448
1449   return OPCodeReturn::success;
1450}
1451
1452OPCodeReturn CodeInterpreter::op_dec(U32 &ip)
1453{
1454   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
1455   ip += 2;
1456
1457   // If a variable is set, then these must be NULL. It is necessary
1458   // to set this here so that the vector parser can appropriately
1459   // identify whether it's dealing with a vector.
1460   mPrevField = NULL;
1461   mPrevObject = NULL;
1462   mCurObject = NULL;
1463
1464   gEvalState.setCurVarNameCreate(var);
1465
1466   // In order to let docblocks work properly with variables, we have
1467   // clear the current docblock when we do an assign. This way it 
1468   // won't inappropriately carry forward to following function decls.
1469   mCurFNDocBlock = NULL;
1470   mCurNSDocBlock = NULL;
1471
1472   F64 val = gEvalState.getFloatVariable() - 1.0;
1473   gEvalState.setFloatVariable(val);
1474
1475   // We gotta push val onto the stack. What if we have
1476   // more expressions that have to use this.
1477   // If we don't, we send out an op code to pop it.
1478   floatStack[_FLT + 1] = val;
1479   _FLT++;
1480
1481   return OPCodeReturn::success;
1482}
1483
1484OPCodeReturn CodeInterpreter::op_setcurvar(U32 &ip)
1485{
1486   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
1487   ip += 2;
1488
1489   // If a variable is set, then these must be NULL. It is necessary
1490   // to set this here so that the vector parser can appropriately
1491   // identify whether it's dealing with a vector.
1492   mPrevField = NULL;
1493   mPrevObject = NULL;
1494   mCurObject = NULL;
1495
1496   gEvalState.setCurVarName(var);
1497
1498   // In order to let docblocks work properly with variables, we have
1499   // clear the current docblock when we do an assign. This way it 
1500   // won't inappropriately carry forward to following function decls.
1501   mCurFNDocBlock = NULL;
1502   mCurNSDocBlock = NULL;
1503   return OPCodeReturn::success;
1504}
1505
1506OPCodeReturn CodeInterpreter::op_setcurvar_create(U32 &ip)
1507{
1508   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
1509   ip += 2;
1510
1511   // See OP_SETCURVAR
1512   mPrevField = NULL;
1513   mPrevObject = NULL;
1514   mCurObject = NULL;
1515
1516   gEvalState.setCurVarNameCreate(var);
1517
1518   // See OP_SETCURVAR for why we do this.
1519   mCurFNDocBlock = NULL;
1520   mCurNSDocBlock = NULL;
1521   return OPCodeReturn::success;
1522}
1523
1524OPCodeReturn CodeInterpreter::op_setcurvar_array(U32 &ip)
1525{
1526   StringTableEntry var = STR.getSTValue();
1527
1528   // See OP_SETCURVAR
1529   mPrevField = NULL;
1530   mPrevObject = NULL;
1531   mCurObject = NULL;
1532
1533   gEvalState.setCurVarName(var);
1534
1535   // See OP_SETCURVAR for why we do this.
1536   mCurFNDocBlock = NULL;
1537   mCurNSDocBlock = NULL;
1538   return OPCodeReturn::success;
1539}
1540
1541OPCodeReturn CodeInterpreter::op_setcurvar_array_varlookup(U32 &ip)
1542{
1543   StringTableEntry arrayName = CodeToSTE(mCodeBlock->code, ip);
1544   StringTableEntry arrayLookup = CodeToSTE(mCodeBlock->code, ip + 2);
1545   ip += 4;
1546
1547   STR.setStringValue(arrayName);
1548   STR.advance();
1549
1550   // See OP_SETCURVAR
1551   mPrevField = NULL;
1552   mPrevObject = NULL;
1553   mCurObject = NULL;
1554
1555   // resolve arrayLookup to get the 'value'
1556   // Note: we have to setCurVarNameCreate in case the var doesn't exist.
1557   // this won't cause much of a performance hit since vars are hashed.
1558   gEvalState.setCurVarNameCreate(arrayLookup);
1559   StringTableEntry hash = gEvalState.getStringVariable();
1560
1561   STR.setStringValue(hash);
1562   STR.rewind();
1563
1564   // Generate new array name.
1565   StringTableEntry var = STR.getSTValue();
1566   gEvalState.setCurVarName(var);
1567
1568   // See OP_SETCURVAR for why we do this.
1569   mCurFNDocBlock = NULL;
1570   mCurNSDocBlock = NULL;
1571
1572   return OPCodeReturn::success;
1573}
1574
1575OPCodeReturn CodeInterpreter::op_setcurvar_array_create(U32 &ip)
1576{
1577   StringTableEntry var = STR.getSTValue();
1578
1579   // See OP_SETCURVAR
1580   mPrevField = NULL;
1581   mPrevObject = NULL;
1582   mCurObject = NULL;
1583
1584   gEvalState.setCurVarNameCreate(var);
1585
1586   // See OP_SETCURVAR for why we do this.
1587   mCurFNDocBlock = NULL;
1588   mCurNSDocBlock = NULL;
1589   return OPCodeReturn::success;
1590}
1591
1592OPCodeReturn CodeInterpreter::op_setcurvar_array_create_varlookup(U32 &ip)
1593{
1594   StringTableEntry arrayName = CodeToSTE(mCodeBlock->code, ip);
1595   StringTableEntry arrayLookup = CodeToSTE(mCodeBlock->code, ip + 2);
1596   ip += 4;
1597
1598   // See OP_SETCURVAR
1599   mPrevField = NULL;
1600   mPrevObject = NULL;
1601   mCurObject = NULL;
1602
1603   STR.setStringValue(arrayName);
1604   STR.advance();
1605
1606   // resolve arrayLookup to get the 'value'
1607   // Note: we have to setCurVarNameCreate in case the var doesn't exist.
1608   // this won't cause much of a performance hit since vars are hashed.
1609   gEvalState.setCurVarNameCreate(arrayLookup);
1610   StringTableEntry hash = gEvalState.getStringVariable();
1611
1612   STR.setStringValue(hash);
1613   STR.rewind();
1614
1615   // Generate new array name.
1616   StringTableEntry var = STR.getSTValue();
1617   gEvalState.setCurVarNameCreate(var);
1618
1619   // See OP_SETCURVAR for why we do this.
1620   mCurFNDocBlock = NULL;
1621   mCurNSDocBlock = NULL;
1622
1623   return OPCodeReturn::success;
1624}
1625
1626OPCodeReturn CodeInterpreter::op_loadvar_uint(U32 &ip)
1627{
1628   intStack[_UINT + 1] = gEvalState.getIntVariable();
1629   _UINT++;
1630   return OPCodeReturn::success;
1631}
1632
1633OPCodeReturn CodeInterpreter::op_loadvar_flt(U32 &ip)
1634{
1635   floatStack[_FLT + 1] = gEvalState.getFloatVariable();
1636   _FLT++;
1637   return OPCodeReturn::success;
1638}
1639
1640OPCodeReturn CodeInterpreter::op_loadvar_str(U32 &ip)
1641{
1642   StringTableEntry val = gEvalState.getStringVariable();
1643   STR.setStringValue(val);
1644   return OPCodeReturn::success;
1645}
1646
1647OPCodeReturn CodeInterpreter::op_loadvar_var(U32 &ip)
1648{
1649   // Sets current source of OP_SAVEVAR_VAR
1650   gEvalState.copyVariable = gEvalState.currentVariable;
1651   return OPCodeReturn::success;
1652}
1653
1654OPCodeReturn CodeInterpreter::op_savevar_uint(U32 &ip)
1655{
1656   gEvalState.setIntVariable(intStack[_UINT]);
1657   return OPCodeReturn::success;
1658}
1659
1660OPCodeReturn CodeInterpreter::op_savevar_flt(U32 &ip)
1661{
1662   gEvalState.setFloatVariable(floatStack[_FLT]);
1663   return OPCodeReturn::success;
1664}
1665
1666OPCodeReturn CodeInterpreter::op_savevar_str(U32 &ip)
1667{
1668   gEvalState.setStringVariable(STR.getStringValue());
1669   return OPCodeReturn::success;
1670}
1671
1672OPCodeReturn CodeInterpreter::op_savevar_var(U32 &ip)
1673{
1674   // this basically handles %var1 = %var2
1675   gEvalState.setCopyVariable();
1676   return OPCodeReturn::success;
1677}
1678
1679OPCodeReturn CodeInterpreter::op_setcurobject(U32 &ip)
1680{
1681   // Save the previous object for parsing vector fields.
1682   mPrevObject = mCurObject;
1683   StringTableEntry val = STR.getStringValue();
1684
1685   // Sim::findObject will sometimes find valid objects from
1686   // multi-component strings. This makes sure that doesn't
1687   // happen.
1688   for (const char* check = val; *check; check++)
1689   {
1690      if (*check == ' ')
1691      {
1692         val = "";
1693         break;
1694      }
1695   }
1696   mCurObject = Sim::findObject(val);
1697   return OPCodeReturn::success;
1698}
1699
1700OPCodeReturn CodeInterpreter::op_setcurobject_internal(U32 &ip)
1701{
1702   ++ip; // To skip the recurse flag if the object wasn't found
1703   if (mCurObject)
1704   {
1705      SimSet *set = dynamic_cast<SimSet *>(mCurObject);
1706      if (set)
1707      {
1708         StringTableEntry intName = StringTable->insert(STR.getStringValue());
1709         bool recurse = mCodeBlock->code[ip - 1];
1710         SimObject *obj = set->findObjectByInternalName(intName, recurse);
1711         intStack[_UINT + 1] = obj ? obj->getId() : 0;
1712         _UINT++;
1713      }
1714      else
1715      {
1716         Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", mCodeBlock->getFileLine(ip - 2), mCurObject->getName(), mCurObject->getClassName());
1717         intStack[_UINT] = 0;
1718      }
1719   }
1720   return OPCodeReturn::success;
1721}
1722
1723OPCodeReturn CodeInterpreter::op_setcurobject_new(U32 &ip)
1724{
1725   mCurObject = mCurrentNewObject;
1726   return OPCodeReturn::success;
1727}
1728
1729OPCodeReturn CodeInterpreter::op_setcurfield(U32 &ip)
1730{
1731   // Save the previous field for parsing vector fields.
1732   mPrevField = mCurField;
1733   dStrcpy(prevFieldArray, curFieldArray, 256);
1734   mCurField = CodeToSTE(mCodeBlock->code, ip);
1735   curFieldArray[0] = 0;
1736   ip += 2;
1737   return OPCodeReturn::success;
1738}
1739
1740OPCodeReturn CodeInterpreter::op_setcurfield_array(U32 &ip)
1741{
1742   dStrcpy(curFieldArray, STR.getStringValue(), 256);
1743   return OPCodeReturn::success;
1744}
1745
1746OPCodeReturn CodeInterpreter::op_setcurfield_type(U32 &ip)
1747{
1748   if (mCurObject)
1749      mCurObject->setDataFieldType(mCodeBlock->code[ip], mCurField, curFieldArray);
1750   ip++;
1751   return OPCodeReturn::success;
1752}
1753
1754OPCodeReturn CodeInterpreter::op_setcurfield_array_var(U32 &ip)
1755{
1756   StringTableEntry var = CodeToSTE(mCodeBlock->code, ip);
1757   ip += 2;
1758
1759   // We set the current var name (create it as well in case if it doesn't exist,
1760   // otherwise we will crash).
1761   gEvalState.setCurVarNameCreate(var);
1762
1763   // Then load the var and copy the contents to the current field array
1764   dStrncpy(curFieldArray, gEvalState.currentVariable->getStringValue(), sizeof(curFieldArray));
1765
1766   return OPCodeReturn::success;
1767}
1768
1769OPCodeReturn CodeInterpreter::op_setcurfield_this(U32 &ip)
1770{
1771   // set the 'this pointer' as the current object.
1772   mCurObject = mThisObject;
1773
1774   mPrevField = mCurField;
1775   dStrcpy(prevFieldArray, curFieldArray, 256);
1776   mCurField = CodeToSTE(mCodeBlock->code, ip);
1777   curFieldArray[0] = 0;
1778   ip += 2;
1779   return OPCodeReturn::success;
1780}
1781
1782OPCodeReturn CodeInterpreter::op_loadfield_uint(U32 &ip)
1783{
1784   if (mCurObject)
1785      intStack[_UINT + 1] = U32(dAtoi(mCurObject->getDataField(mCurField, curFieldArray)));
1786   else
1787   {
1788      // The field is not being retrieved from an object. Maybe it's
1789      // a special accessor?
1790      char buff[FieldBufferSizeNumeric];
1791      memset(buff, 0, sizeof(buff));
1792      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
1793      intStack[_UINT + 1] = dAtoi(buff);
1794   }
1795   _UINT++;
1796   return OPCodeReturn::success;
1797}
1798
1799OPCodeReturn CodeInterpreter::op_loadfield_flt(U32 &ip)
1800{
1801   if (mCurObject)
1802      floatStack[_FLT + 1] = dAtof(mCurObject->getDataField(mCurField, curFieldArray));
1803   else
1804   {
1805      // The field is not being retrieved from an object. Maybe it's
1806      // a special accessor?
1807      char buff[FieldBufferSizeNumeric];
1808      memset(buff, 0, sizeof(buff));
1809      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
1810      floatStack[_FLT + 1] = dAtof(buff);
1811   }
1812   _FLT++;
1813   return OPCodeReturn::success;
1814}
1815
1816OPCodeReturn CodeInterpreter::op_loadfield_str(U32 &ip)
1817{
1818   if (mCurObject)
1819   {
1820      StringTableEntry val = mCurObject->getDataField(mCurField, curFieldArray);
1821      STR.setStringValue(val);
1822   }
1823   else
1824   {
1825      // The field is not being retrieved from an object. Maybe it's
1826      // a special accessor?
1827      char buff[FieldBufferSizeString];
1828      memset(buff, 0, sizeof(buff));
1829      getFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField, buff);
1830      STR.setStringValue(buff);
1831   }
1832   return OPCodeReturn::success;
1833}
1834
1835OPCodeReturn CodeInterpreter::op_savefield_uint(U32 &ip)
1836{
1837   STR.setIntValue(intStack[_UINT]);
1838   if (mCurObject)
1839      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
1840   else
1841   {
1842      // The field is not being set on an object. Maybe it's
1843      // a special accessor?
1844      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
1845      mPrevObject = NULL;
1846   }
1847   return OPCodeReturn::success;
1848}
1849
1850OPCodeReturn CodeInterpreter::op_savefield_flt(U32 &ip)
1851{
1852   STR.setFloatValue(floatStack[_FLT]);
1853   if (mCurObject)
1854      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
1855   else
1856   {
1857      // The field is not being set on an object. Maybe it's
1858      // a special accessor?
1859      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
1860      mPrevObject = NULL;
1861   }
1862   return OPCodeReturn::success;
1863}
1864
1865OPCodeReturn CodeInterpreter::op_savefield_str(U32 &ip)
1866{
1867   if (mCurObject)
1868      mCurObject->setDataField(mCurField, curFieldArray, STR.getStringValue());
1869   else
1870   {
1871      // The field is not being set on an object. Maybe it's
1872      // a special accessor?
1873      setFieldComponent(mPrevObject, mPrevField, prevFieldArray, mCurField);
1874      mPrevObject = NULL;
1875   }
1876   return OPCodeReturn::success;
1877}
1878
1879OPCodeReturn CodeInterpreter::op_str_to_uint(U32 &ip)
1880{
1881   intStack[_UINT + 1] = STR.getIntValue();
1882   _UINT++;
1883   return OPCodeReturn::success;
1884}
1885
1886OPCodeReturn CodeInterpreter::op_str_to_flt(U32 &ip)
1887{
1888   floatStack[_FLT + 1] = STR.getFloatValue();
1889   _FLT++;
1890   return OPCodeReturn::success;
1891}
1892
1893OPCodeReturn CodeInterpreter::op_str_to_none(U32 &ip)
1894{
1895   // This exists simply to deal with certain typecast situations.
1896   return OPCodeReturn::success;
1897}
1898
1899OPCodeReturn CodeInterpreter::op_flt_to_uint(U32 &ip)
1900{
1901   intStack[_UINT + 1] = (S64)floatStack[_FLT];
1902   _FLT--;
1903   _UINT++;
1904   return OPCodeReturn::success;
1905}
1906
1907OPCodeReturn CodeInterpreter::op_flt_to_str(U32 &ip)
1908{
1909   STR.setFloatValue(floatStack[_FLT]);
1910   _FLT--;
1911   return OPCodeReturn::success;
1912}
1913
1914OPCodeReturn CodeInterpreter::op_flt_to_none(U32 &ip)
1915{
1916   _FLT--;
1917   return OPCodeReturn::success;
1918}
1919
1920OPCodeReturn CodeInterpreter::op_uint_to_flt(U32 &ip)
1921{
1922   floatStack[_FLT + 1] = (F32)intStack[_UINT];
1923   _UINT--;
1924   _FLT++;
1925   return OPCodeReturn::success;
1926}
1927
1928OPCodeReturn CodeInterpreter::op_uint_to_str(U32 &ip)
1929{
1930   STR.setIntValue(intStack[_UINT]);
1931   _UINT--;
1932   return OPCodeReturn::success;
1933}
1934
1935OPCodeReturn CodeInterpreter::op_uint_to_none(U32 &ip)
1936{
1937   _UINT--;
1938   return OPCodeReturn::success;
1939}
1940
1941OPCodeReturn CodeInterpreter::op_copyvar_to_none(U32 &ip)
1942{
1943   gEvalState.copyVariable = NULL;
1944   return OPCodeReturn::success;
1945}
1946
1947OPCodeReturn CodeInterpreter::op_loadimmed_uint(U32 &ip)
1948{
1949   intStack[_UINT + 1] = mCodeBlock->code[ip++];
1950   _UINT++;
1951   return OPCodeReturn::success;
1952}
1953
1954OPCodeReturn CodeInterpreter::op_loadimmed_flt(U32 &ip)
1955{
1956   floatStack[_FLT + 1] = mCurFloatTable[mCodeBlock->code[ip]];
1957   ip++;
1958   _FLT++;
1959   return OPCodeReturn::success;
1960}
1961
1962OPCodeReturn CodeInterpreter::op_tag_to_str(U32 &ip)
1963{
1964   mCodeBlock->code[ip - 1] = OP_LOADIMMED_STR;
1965   // it's possible the string has already been converted
1966   if (U8(mCurStringTable[mCodeBlock->code[ip]]) != StringTagPrefixByte)
1967   {
1968      U32 id = GameAddTaggedString(mCurStringTable + mCodeBlock->code[ip]);
1969      dSprintf(mCurStringTable + mCodeBlock->code[ip] + 1, 7, "%d", id);
1970      *(mCurStringTable + mCodeBlock->code[ip]) = StringTagPrefixByte;
1971   }
1972
1973   // Fallthrough
1974   OPCodeReturn ret = op_loadimmed_str(ip);
1975
1976   return ret;
1977}
1978
1979OPCodeReturn CodeInterpreter::op_loadimmed_str(U32 &ip)
1980{
1981   STR.setStringValue(mCurStringTable + mCodeBlock->code[ip++]);
1982   return OPCodeReturn::success;
1983}
1984
1985OPCodeReturn CodeInterpreter::op_docblock_str(U32 &ip)
1986{
1987   // If the first word of the doc is '\class' or '@class', then this
1988   // is a namespace doc block, otherwise it is a function doc block.
1989   const char* docblock = mCurStringTable + mCodeBlock->code[ip++];
1990
1991   const char* sansClass = dStrstr(docblock, "@class");
1992   if (!sansClass)
1993      sansClass = dStrstr(docblock, "\\class");
1994
1995   if (sansClass)
1996   {
1997      // Don't save the class declaration. Scan past the 'class'
1998      // keyword and up to the first whitespace.
1999      sansClass += 7;
2000      S32 index = 0;
2001      while ((*sansClass != ' ') && (*sansClass != '\n') && *sansClass && (index < (nsDocLength - 1)))
2002      {
2003         mNSDocBlockClass[index++] = *sansClass;
2004         sansClass++;
2005      }
2006      mNSDocBlockClass[index] = '\0';
2007
2008      mCurNSDocBlock = sansClass + 1;
2009   }
2010   else
2011      mCurFNDocBlock = docblock;
2012
2013   return OPCodeReturn::success;
2014}
2015
2016OPCodeReturn CodeInterpreter::op_loadimmed_ident(U32 &ip)
2017{
2018   STR.setStringValue(CodeToSTE(mCodeBlock->code, ip));
2019   ip += 2;
2020   return OPCodeReturn::success;
2021}
2022
2023OPCodeReturn CodeInterpreter::op_callfunc_resolve(U32 &ip)
2024{
2025   // This deals with a function that is potentially living in a namespace.
2026   StringTableEntry fnNamespace = CodeToSTE(mCodeBlock->code, ip + 2);
2027   StringTableEntry fnName = CodeToSTE(mCodeBlock->code, ip);
2028
2029   // Try to look it up.
2030   mNSEntry = Namespace::find(fnNamespace)->lookup(fnName);
2031   if (!CInterface::GetCInterface().isMethod(fnNamespace, fnName) && !mNSEntry)
2032   {
2033      ip += 5;
2034      Con::warnf(ConsoleLogEntry::General,
2035         "%s: Unable to find function %s%s%s",
2036         mCodeBlock->getFileLine(ip - 7), fnNamespace ? fnNamespace : "",
2037         fnNamespace ? "::" : "", fnName);
2038      STR.popFrame();
2039      CSTK.popFrame();
2040      return OPCodeReturn::success;
2041   }
2042
2043   // Fallthrough to op_callfunc_resolve
2044   OPCodeReturn ret = op_callfunc(ip);
2045
2046   return ret;
2047}
2048
2049OPCodeReturn CodeInterpreter::op_callfunc(U32 &ip)
2050{
2051   // This routingId is set when we query the object as to whether
2052   // it handles this method.  It is set to an enum from the table
2053   // above indicating whether it handles it on a component it owns
2054   // or just on the object.
2055   S32 routingId = 0;
2056
2057   U32 *code = mCodeBlock->code;
2058
2059   StringTableEntry fnNamespace = CodeToSTE(mCodeBlock->code, ip + 2);
2060   StringTableEntry fnName = CodeToSTE(code, ip);
2061
2062   //if this is called from inside a function, append the ip and codeptr
2063   if (gEvalState.getStackDepth() > 0)
2064   {
2065      gEvalState.getCurrentFrame().code = mCodeBlock;
2066      gEvalState.getCurrentFrame().ip = ip - 1;
2067   }
2068
2069   U32 callType = code[ip + 4];
2070
2071   ip += 5;
2072   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
2073
2074   const char *componentReturnValue = "";
2075   Namespace *ns = NULL;
2076
2077   bool cFunctionRes = false;
2078   const char* cRetRes = NULL;
2079
2080   if (callType == FuncCallExprNode::FunctionCall)
2081   {
2082      if (!mNSEntry)
2083         mNSEntry = Namespace::global()->lookup(fnName);
2084
2085      StringStackWrapper args(mCallArgc, mCallArgv);
2086      cRetRes = CInterface::CallFunction(fnNamespace, fnName, args.argv + 1, args.argc - 1, &cFunctionRes);
2087   }
2088   else if (callType == FuncCallExprNode::MethodCall)
2089   {
2090      mSaveObject = gEvalState.thisObject;
2091      gEvalState.thisObject = Sim::findObject((const char*)mCallArgv[1]);
2092      if (!gEvalState.thisObject)
2093      {
2094         // Go back to the previous saved object.
2095         gEvalState.thisObject = mSaveObject;
2096
2097         Con::warnf(ConsoleLogEntry::General, "%s: Unable to find object: '%s' attempting to call function '%s'", mCodeBlock->getFileLine(ip - 4), (const char*)mCallArgv[1], fnName);
2098         STR.popFrame();
2099         CSTK.popFrame();
2100         STR.setStringValue("");
2101         return OPCodeReturn::success;
2102      }
2103
2104      bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName, &routingId);
2105      if (handlesMethod && routingId == MethodOnComponent)
2106      {
2107         ICallMethod *pComponent = dynamic_cast<ICallMethod *>(gEvalState.thisObject);
2108         if (pComponent)
2109            componentReturnValue = pComponent->callMethodArgList(mCallArgc, mCallArgv, false);
2110      }
2111
2112      ns = gEvalState.thisObject->getNamespace();
2113      if (ns)
2114         mNSEntry = ns->lookup(fnName);
2115      else
2116         mNSEntry = NULL;
2117
2118      StringStackWrapper args(mCallArgc, mCallArgv);
2119      cRetRes = CInterface::CallMethod(gEvalState.thisObject, fnName, args.argv + 2, args.argc - 2, &cFunctionRes);
2120   }
2121   else // it's a ParentCall
2122   {
2123      if (mExec.thisNamespace)
2124      {
2125         ns = mExec.thisNamespace->mParent;
2126         if (ns)
2127            mNSEntry = ns->lookup(fnName);
2128         else
2129            mNSEntry = NULL;
2130      }
2131      else
2132      {
2133         ns = NULL;
2134         mNSEntry = NULL;
2135      }
2136   }
2137
2138   Namespace::Entry::CallbackUnion * nsCb = NULL;
2139   const char * nsUsage = NULL;
2140   if (mNSEntry)
2141   {
2142      nsCb = &mNSEntry->cb;
2143      nsUsage = mNSEntry->mUsage;
2144      routingId = 0;
2145   }
2146   if (!cFunctionRes && (!mNSEntry || mExec.noCalls))
2147   {
2148      if (!mExec.noCalls && !(routingId == MethodOnComponent))
2149      {
2150         if (callType == FuncCallExprNode::MethodCall)
2151         {
2152            if (gEvalState.thisObject != NULL)
2153            {
2154               // Try to use the name instead of the id
2155               StringTableEntry name = gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : gEvalState.thisObject->getIdString();
2156               Con::warnf(ConsoleLogEntry::General, "%s: Unknown method %s.%s Namespace List: %s", mCodeBlock->getFileLine(ip - 6), name, fnName, Con::getNamespaceList(ns));
2157            }
2158            else
2159            {
2160               // NULL.
2161               Con::warnf(ConsoleLogEntry::General, "%s: Unknown method NULL.%s", mCodeBlock->getFileLine(ip - 6), fnName);
2162            }
2163         }
2164         else if (callType == FuncCallExprNode::ParentCall)
2165         {
2166            Con::warnf(ConsoleLogEntry::General, "%s: Unknown parent call %s.", mCodeBlock->getFileLine(ip - 6), fnName);
2167         }
2168         else 
2169         {
2170            Con::warnf(ConsoleLogEntry::General, "%s: Unknown function %s.", mCodeBlock->getFileLine(ip - 6), fnName);
2171         }
2172      }
2173      STR.popFrame();
2174      CSTK.popFrame();
2175
2176      if (routingId == MethodOnComponent)
2177         STR.setStringValue(componentReturnValue);
2178      else
2179         STR.setStringValue("");
2180      return OPCodeReturn::success;
2181   }
2182
2183   // ConsoleFunctionType is for any function defined by script.
2184   // Any 'callback' type is an engine function that is exposed to script.
2185   if (cFunctionRes || mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
2186   {
2187      ConsoleValue retVal;
2188      ConsoleValueRef ret;
2189      if (cFunctionRes)
2190      {
2191         retVal.init();
2192         ret.value = &retVal;
2193         retVal.setStackStringValue(cRetRes);
2194      }
2195      else if (mNSEntry->mFunctionOffset)
2196      {
2197         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->mPackage);
2198      }
2199
2200      STR.popFrame();
2201      // Functions are assumed to return strings, so look ahead to see if we can skip the conversion
2202      if (code[ip] == OP_STR_TO_UINT)
2203      {
2204         ip++;
2205         intStack[++_UINT] = (U32)((S32)ret);
2206      }
2207      else if (code[ip] == OP_STR_TO_FLT)
2208      {
2209         ip++;
2210         floatStack[++_FLT] = (F32)ret;
2211      }
2212      else if (code[ip] == OP_STR_TO_NONE)
2213      {
2214         STR.setStringValue(ret.getStringValue());
2215         ip++;
2216      }
2217      else
2218         STR.setStringValue((const char*)ret);
2219
2220      // This will clear everything including returnValue
2221      CSTK.popFrame();
2222      //STR.clearFunctionOffset();
2223   }
2224   else
2225   {
2226      const char* nsName = ns ? ns->mName : "";
2227#ifndef TORQUE_DEBUG
2228      // [tom, 12/13/2006] This stops tools functions from working in the console,
2229      // which is useful behavior when debugging so I'm ifdefing this out for debug builds.
2230      if (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
2231      {
2232         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
2233      }
2234      else
2235#endif
2236      if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
2237      {
2238         Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
2239            mCodeBlock->getFileLine(ip - 6), nsName, fnName,
2240            mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
2241         Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
2242         STR.popFrame();
2243         CSTK.popFrame();
2244      }
2245      else
2246      {
2247         switch (mNSEntry->mType)
2248         {
2249         case Namespace::Entry::StringCallbackType:
2250         {
2251            const char *ret = mNSEntry->cb.mStringCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2252            STR.popFrame();
2253            CSTK.popFrame();
2254            if (ret != STR.getStringValue())
2255               STR.setStringValue(ret);
2256            //else
2257            //   sSTR.setLen(dStrlen(ret));
2258            break;
2259         }
2260         case Namespace::Entry::IntCallbackType:
2261         {
2262            S32 result = mNSEntry->cb.mIntCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2263            STR.popFrame();
2264            CSTK.popFrame();
2265            if (code[ip] == OP_STR_TO_UINT)
2266            {
2267               ip++;
2268               intStack[++_UINT] = result;
2269               break;
2270            }
2271            else if (code[ip] == OP_STR_TO_FLT)
2272            {
2273               ip++;
2274               floatStack[++_FLT] = result;
2275               break;
2276            }
2277            else if (code[ip] == OP_STR_TO_NONE)
2278               ip++;
2279            else
2280               STR.setIntValue(result);
2281            break;
2282         }
2283         case Namespace::Entry::FloatCallbackType:
2284         {
2285            F64 result = mNSEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2286            STR.popFrame();
2287            CSTK.popFrame();
2288            if (code[ip] == OP_STR_TO_UINT)
2289            {
2290               ip++;
2291               intStack[++_UINT] = (S64)result;
2292               break;
2293            }
2294            else if (code[ip] == OP_STR_TO_FLT)
2295            {
2296               ip++;
2297               floatStack[++_FLT] = result;
2298               break;
2299            }
2300            else if (code[ip] == OP_STR_TO_NONE)
2301               ip++;
2302            else
2303               STR.setFloatValue(result);
2304            break;
2305         }
2306         case Namespace::Entry::VoidCallbackType:
2307            mNSEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2308            if (code[ip] != OP_STR_TO_NONE && Con::getBoolVariable("$Con::warnVoidAssignment", true))
2309               Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
2310
2311            STR.popFrame();
2312            CSTK.popFrame();
2313            STR.setStringValue("");
2314            break;
2315         case Namespace::Entry::BoolCallbackType:
2316         {
2317            bool result = mNSEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2318            STR.popFrame();
2319            CSTK.popFrame();
2320            if (code[ip] == OP_STR_TO_UINT)
2321            {
2322               ip++;
2323               intStack[++_UINT] = result;
2324               break;
2325            }
2326            else if (code[ip] == OP_STR_TO_FLT)
2327            {
2328               ip++;
2329               floatStack[++_FLT] = result;
2330               break;
2331            }
2332            else if (code[ip] == OP_STR_TO_NONE)
2333               ip++;
2334            else
2335               STR.setIntValue(result);
2336            break;
2337         }
2338         }
2339      }
2340   }
2341
2342   if (callType == FuncCallExprNode::MethodCall)
2343      gEvalState.thisObject = mSaveObject;
2344   return OPCodeReturn::success;
2345}
2346
2347OPCodeReturn CodeInterpreter::op_callfunc_pointer(U32 &ip)
2348{
2349   // get function name. This is the 'function pointer'.
2350   StringTableEntry fnName = StringTable->insert(STR.getStringValue());
2351
2352   U32 *code = mCodeBlock->code;
2353
2354   mNSEntry = Namespace::global()->lookup(fnName);
2355
2356   //if this is called from inside a function, append the ip and codeptr
2357   if (gEvalState.getStackDepth() > 0)
2358   {
2359      gEvalState.getCurrentFrame().code = mCodeBlock;
2360      gEvalState.getCurrentFrame().ip = ip - 1;
2361   }
2362
2363   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
2364
2365
2366   if (!mNSEntry || mExec.noCalls)
2367   {
2368      if (!mExec.noCalls)
2369      {
2370         Con::warnf(ConsoleLogEntry::General, "%s: Unknown function %s.", mCodeBlock->getFileLine(ip - 6), fnName);
2371      }
2372      STR.popFrame();
2373      CSTK.popFrame();
2374
2375      STR.setStringValue("");
2376      return OPCodeReturn::success;
2377   }
2378
2379   // ConsoleFunctionType is for any function defined by script.
2380   // Any 'callback' type is an engine function that is exposed to script.
2381   if (mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
2382   {
2383      ConsoleValueRef ret;
2384      if (mNSEntry->mFunctionOffset)
2385         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->mPackage);
2386
2387      STR.popFrame();
2388      // Functions are assumed to return strings, so look ahead to see if we can skip the conversion
2389      if (code[ip] == OP_STR_TO_UINT)
2390      {
2391         ip++;
2392         intStack[++_UINT] = (U32)((S32)ret);
2393      }
2394      else if (code[ip] == OP_STR_TO_FLT)
2395      {
2396         ip++;
2397         floatStack[++_FLT] = (F32)ret;
2398      }
2399      else if (code[ip] == OP_STR_TO_NONE)
2400      {
2401         STR.setStringValue(ret.getStringValue());
2402         ip++;
2403      }
2404      else
2405         STR.setStringValue((const char*)ret);
2406
2407      // This will clear everything including returnValue
2408      CSTK.popFrame();
2409      //STR.clearFunctionOffset();
2410   }
2411   else
2412   {
2413      const char* nsName = "";
2414
2415#ifndef TORQUE_DEBUG
2416      // [tom, 12/13/2006] This stops tools functions from working in the console,
2417      // which is useful behavior when debugging so I'm ifdefing this out for debug builds.
2418      if (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
2419      {
2420         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
2421      }
2422      else
2423#endif
2424         if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
2425         {
2426            Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
2427               mCodeBlock->getFileLine(ip - 6), nsName, fnName,
2428               mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
2429            Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
2430            STR.popFrame();
2431            CSTK.popFrame();
2432         }
2433         else
2434         {
2435            switch (mNSEntry->mType)
2436            {
2437            case Namespace::Entry::StringCallbackType:
2438            {
2439               const char *ret = mNSEntry->cb.mStringCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2440               STR.popFrame();
2441               CSTK.popFrame();
2442               if (ret != STR.getStringValue())
2443                  STR.setStringValue(ret);
2444               //else
2445               //   sSTR.setLen(dStrlen(ret));
2446               break;
2447            }
2448            case Namespace::Entry::IntCallbackType:
2449            {
2450               S32 result = mNSEntry->cb.mIntCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2451               STR.popFrame();
2452               CSTK.popFrame();
2453               if (code[ip] == OP_STR_TO_UINT)
2454               {
2455                  ip++;
2456                  intStack[++_UINT] = result;
2457                  break;
2458               }
2459               else if (code[ip] == OP_STR_TO_FLT)
2460               {
2461                  ip++;
2462                  floatStack[++_FLT] = result;
2463                  break;
2464               }
2465               else if (code[ip] == OP_STR_TO_NONE)
2466                  ip++;
2467               else
2468                  STR.setIntValue(result);
2469               break;
2470            }
2471            case Namespace::Entry::FloatCallbackType:
2472            {
2473               F64 result = mNSEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2474               STR.popFrame();
2475               CSTK.popFrame();
2476               if (code[ip] == OP_STR_TO_UINT)
2477               {
2478                  ip++;
2479                  intStack[++_UINT] = (S64)result;
2480                  break;
2481               }
2482               else if (code[ip] == OP_STR_TO_FLT)
2483               {
2484                  ip++;
2485                  floatStack[++_FLT] = result;
2486                  break;
2487               }
2488               else if (code[ip] == OP_STR_TO_NONE)
2489                  ip++;
2490               else
2491                  STR.setFloatValue(result);
2492               break;
2493            }
2494            case Namespace::Entry::VoidCallbackType:
2495               mNSEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2496               if (code[ip] != OP_STR_TO_NONE && Con::getBoolVariable("$Con::warnVoidAssignment", true))
2497                  Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
2498
2499               STR.popFrame();
2500               CSTK.popFrame();
2501               STR.setStringValue("");
2502               break;
2503            case Namespace::Entry::BoolCallbackType:
2504            {
2505               bool result = mNSEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, mCallArgc, mCallArgv);
2506               STR.popFrame();
2507               CSTK.popFrame();
2508               if (code[ip] == OP_STR_TO_UINT)
2509               {
2510                  ip++;
2511                  intStack[++_UINT] = result;
2512                  break;
2513               }
2514               else if (code[ip] == OP_STR_TO_FLT)
2515               {
2516                  ip++;
2517                  floatStack[++_FLT] = result;
2518                  break;
2519               }
2520               else if (code[ip] == OP_STR_TO_NONE)
2521                  ip++;
2522               else
2523                  STR.setIntValue(result);
2524               break;
2525            }
2526            }
2527         }
2528   }
2529
2530
2531   return OPCodeReturn::success;
2532}
2533
2534OPCodeReturn CodeInterpreter::op_callfunc_this(U32 &ip)
2535{
2536   U32 *code = mCodeBlock->code;
2537
2538   StringTableEntry fnName = CodeToSTE(code, ip);
2539
2540   //if this is called from inside a function, append the ip and codeptr
2541   if (gEvalState.getStackDepth() > 0)
2542   {
2543      gEvalState.getCurrentFrame().code = mCodeBlock;
2544      gEvalState.getCurrentFrame().ip = ip - 1;
2545   }
2546
2547   ip += 2;
2548   CSTK.getArgcArgv(fnName, &mCallArgc, &mCallArgv);
2549
2550   Namespace *ns = mThisObject ? mThisObject->getNamespace() : NULL;
2551   if (ns)
2552      mNSEntry = ns->lookup(fnName);
2553   else
2554      mNSEntry = NULL;
2555
2556   if (!mNSEntry || mExec.noCalls)
2557   {
2558      if (!mExec.noCalls)
2559      {
2560         if (mThisObject)
2561         {
2562            // Try to use the name instead of the id
2563            StringTableEntry name = mThisObject->getName() ? mThisObject->getName() : mThisObject->getIdString();
2564            Con::warnf(ConsoleLogEntry::General, "%s: Unknown method %s.%s Namespace List: %s", mCodeBlock->getFileLine(ip - 6), name, fnName, Con::getNamespaceList(ns));
2565         }
2566         else
2567         {
2568            // At least let the scripter know that they access the object.
2569            Con::warnf(ConsoleLogEntry::General, "%s: Unknown method NULL.%s", mCodeBlock->getFileLine(ip - 6), fnName);
2570         }
2571      }
2572      STR.popFrame();
2573      CSTK.popFrame();
2574
2575      STR.setStringValue("");
2576      return OPCodeReturn::success;
2577   }
2578
2579   // ConsoleFunctionType is for any function defined by script.
2580   // Any 'callback' type is an engine function that is exposed to script.
2581   if (mNSEntry->mType == Namespace::Entry::ConsoleFunctionType)
2582   {
2583      ConsoleValueRef ret;
2584      if (mNSEntry->mFunctionOffset)
2585         ret = mNSEntry->mCode->exec(mNSEntry->mFunctionOffset, fnName, mNSEntry->mNamespace, mCallArgc, mCallArgv, false, mNSEntry->mPackage);
2586
2587      STR.popFrame();
2588      // Functions are assumed to return strings, so look ahead to see if we can skip the conversion
2589      if (code[ip] == OP_STR_TO_UINT)
2590      {
2591         ip++;
2592         intStack[++_UINT] = (U32)((S32)ret);
2593      }
2594      else if (code[ip] == OP_STR_TO_FLT)
2595      {
2596         ip++;
2597         floatStack[++_FLT] = (F32)ret;
2598      }
2599      else if (code[ip] == OP_STR_TO_NONE)
2600      {
2601         STR.setStringValue(ret.getStringValue());
2602         ip++;
2603      }
2604      else
2605         STR.setStringValue((const char*)ret);
2606
2607      // This will clear everything including returnValue
2608      CSTK.popFrame();
2609      //STR.clearFunctionOffset();
2610   }
2611   else
2612   {
2613      const char* nsName = ns ? ns->mName : "";
2614#ifndef TORQUE_DEBUG
2615      // [tom, 12/13/2006] This stops tools functions from working in the console,
2616      // which is useful behavior when debugging so I'm ifdefing this out for debug builds.
2617      if (mNSEntry->mToolOnly && !Con::isCurrentScriptToolScript())
2618      {
2619         Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", mCodeBlock->getFileLine(ip - 6), nsName, fnName);
2620      }
2621      else
2622#endif
2623         if ((mNSEntry->mMinArgs && S32(mCallArgc) < mNSEntry->mMinArgs) || (mNSEntry->mMaxArgs && S32(mCallArgc) > mNSEntry->mMaxArgs))
2624         {
2625            Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).",
2626               mCodeBlock->getFileLine(ip - 6), nsName, fnName,
2627               mCallArgc, mNSEntry->mMinArgs, mNSEntry->mMaxArgs);
2628            Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", mCodeBlock->getFileLine(ip - 6), mNSEntry->mUsage);
2629            STR.popFrame();
2630            CSTK.popFrame();
2631         }
2632         else
2633         {
2634            switch (mNSEntry->mType)
2635            {
2636            case Namespace::Entry::StringCallbackType:
2637            {
2638               const char *ret = mNSEntry->cb.mStringCallbackFunc(mThisObject, mCallArgc, mCallArgv);
2639               STR.popFrame();
2640               CSTK.popFrame();
2641               if (ret != STR.getStringValue())
2642                  STR.setStringValue(ret);
2643               //else
2644               //   sSTR.setLen(dStrlen(ret));
2645               break;
2646            }
2647            case Namespace::Entry::IntCallbackType:
2648            {
2649               S32 result = mNSEntry->cb.mIntCallbackFunc(mThisObject, mCallArgc, mCallArgv);
2650               STR.popFrame();
2651               CSTK.popFrame();
2652               if (code[ip] == OP_STR_TO_UINT)
2653               {
2654                  ip++;
2655                  intStack[++_UINT] = result;
2656                  break;
2657               }
2658               else if (code[ip] == OP_STR_TO_FLT)
2659               {
2660                  ip++;
2661                  floatStack[++_FLT] = result;
2662                  break;
2663               }
2664               else if (code[ip] == OP_STR_TO_NONE)
2665                  ip++;
2666               else
2667                  STR.setIntValue(result);
2668               break;
2669            }
2670            case Namespace::Entry::FloatCallbackType:
2671            {
2672               F64 result = mNSEntry->cb.mFloatCallbackFunc(mThisObject, mCallArgc, mCallArgv);
2673               STR.popFrame();
2674               CSTK.popFrame();
2675               if (code[ip] == OP_STR_TO_UINT)
2676               {
2677                  ip++;
2678                  intStack[++_UINT] = (S64)result;
2679                  break;
2680               }
2681               else if (code[ip] == OP_STR_TO_FLT)
2682               {
2683                  ip++;
2684                  floatStack[++_FLT] = result;
2685                  break;
2686               }
2687               else if (code[ip] == OP_STR_TO_NONE)
2688                  ip++;
2689               else
2690                  STR.setFloatValue(result);
2691               break;
2692            }
2693            case Namespace::Entry::VoidCallbackType:
2694               mNSEntry->cb.mVoidCallbackFunc(mThisObject, mCallArgc, mCallArgv);
2695               if (code[ip] != OP_STR_TO_NONE && Con::getBoolVariable("$Con::warnVoidAssignment", true))
2696                  Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", mCodeBlock->getFileLine(ip - 6), fnName, mExec.functionName);
2697
2698               STR.popFrame();
2699               CSTK.popFrame();
2700               STR.setStringValue("");
2701               break;
2702            case Namespace::Entry::BoolCallbackType:
2703            {
2704               bool result = mNSEntry->cb.mBoolCallbackFunc(mThisObject, mCallArgc, mCallArgv);
2705               STR.popFrame();
2706               CSTK.popFrame();
2707               if (code[ip] == OP_STR_TO_UINT)
2708               {
2709                  ip++;
2710                  intStack[++_UINT] = result;
2711                  break;
2712               }
2713               else if (code[ip] == OP_STR_TO_FLT)
2714               {
2715                  ip++;
2716                  floatStack[++_FLT] = result;
2717                  break;
2718               }
2719               else if (code[ip] == OP_STR_TO_NONE)
2720                  ip++;
2721               else
2722                  STR.setIntValue(result);
2723               break;
2724            }
2725            }
2726         }
2727   }
2728
2729   return OPCodeReturn::success;
2730}
2731
2732OPCodeReturn CodeInterpreter::op_advance_str(U32 &ip)
2733{
2734   STR.advance();
2735   return OPCodeReturn::success;
2736}
2737
2738OPCodeReturn CodeInterpreter::op_advance_str_appendchar(U32 &ip)
2739{
2740   STR.advanceChar(mCodeBlock->code[ip++]);
2741   return OPCodeReturn::success;
2742}
2743
2744OPCodeReturn CodeInterpreter::op_advance_str_comma(U32 &ip)
2745{
2746   STR.advanceChar('_');
2747   return OPCodeReturn::success;
2748}
2749
2750OPCodeReturn CodeInterpreter::op_advance_str_nul(U32 &ip)
2751{
2752   STR.advanceChar(0);
2753   return OPCodeReturn::success;
2754}
2755
2756OPCodeReturn CodeInterpreter::op_rewind_str(U32 &ip)
2757{
2758   STR.rewind();
2759   return OPCodeReturn::success;
2760}
2761
2762OPCodeReturn CodeInterpreter::op_terminate_rewind_str(U32 &ip)
2763{
2764   STR.rewindTerminate();
2765   return OPCodeReturn::success;
2766}
2767
2768OPCodeReturn CodeInterpreter::op_compare_str(U32 &ip)
2769{
2770   intStack[++_UINT] = STR.compare();
2771   return OPCodeReturn::success;
2772}
2773
2774OPCodeReturn CodeInterpreter::op_push(U32 &ip)
2775{
2776   STR.push();
2777   CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr());
2778   return OPCodeReturn::success;
2779}
2780
2781OPCodeReturn CodeInterpreter::op_push_uint(U32 &ip)
2782{
2783   CSTK.pushUINT(intStack[_UINT]);
2784   _UINT--;
2785   return OPCodeReturn::success;
2786}
2787
2788OPCodeReturn CodeInterpreter::op_push_flt(U32 &ip)
2789{
2790   CSTK.pushFLT(floatStack[_FLT]);
2791   _FLT--;
2792   return OPCodeReturn::success;
2793}
2794
2795OPCodeReturn CodeInterpreter::op_push_var(U32 &ip)
2796{
2797   if (gEvalState.currentVariable)
2798      CSTK.pushValue(gEvalState.currentVariable->value);
2799   else
2800      CSTK.pushString("");
2801   return OPCodeReturn::success;
2802}
2803
2804OPCodeReturn CodeInterpreter::op_push_this(U32 &ip)
2805{
2806   StringTableEntry varName = CodeToSTE(mCodeBlock->code, ip);
2807   ip += 2;
2808
2809   // shorthand OP_SETCURVAR
2810
2811   // If a variable is set, then these must be NULL. It is necessary
2812   // to set this here so that the vector parser can appropriately
2813   // identify whether it's dealing with a vector.
2814   mPrevField = NULL;
2815   mPrevObject = NULL;
2816   mCurObject = NULL;
2817
2818   gEvalState.setCurVarName(varName);
2819
2820   // In order to let docblocks work properly with variables, we have
2821   // clear the current docblock when we do an assign. This way it 
2822   // won't inappropriately carry forward to following function decls.
2823   mCurFNDocBlock = NULL;
2824   mCurNSDocBlock = NULL;
2825
2826   // shorthand OP_LOADVAR_STR (since objs can be by name we can't assume uint)
2827   STR.setStringValue(gEvalState.getStringVariable());
2828
2829   // shorthand OP_PUSH
2830   STR.push();
2831   CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr());
2832
2833   return OPCodeReturn::success;
2834}
2835
2836OPCodeReturn CodeInterpreter::op_push_frame(U32 &ip)
2837{
2838   STR.pushFrame();
2839   CSTK.pushFrame();
2840   return OPCodeReturn::success;
2841}
2842
2843OPCodeReturn CodeInterpreter::op_assert(U32 &ip)
2844{
2845   if (!intStack[_UINT--])
2846   {
2847      const char *message = mCurStringTable + mCodeBlock->code[ip];
2848
2849      U32 breakLine, inst;
2850      mCodeBlock->findBreakLine(ip - 1, breakLine, inst);
2851
2852      if (PlatformAssert::processAssert(PlatformAssert::Fatal,
2853         mCodeBlock->name ? mCodeBlock->name : "eval",
2854         breakLine,
2855         message))
2856      {
2857         if (TelDebugger && TelDebugger->isConnected() && breakLine > 0)
2858         {
2859            TelDebugger->breakProcess();
2860         }
2861         else
2862            Platform::debugBreak();
2863      }
2864   }
2865
2866   ip++;
2867
2868   return OPCodeReturn::success;
2869}
2870
2871OPCodeReturn CodeInterpreter::op_break(U32 &ip)
2872{
2873   //append the ip and codeptr before managing the breakpoint!
2874   AssertFatal(gEvalState.getStackDepth() > 0, "Empty eval stack on break!");
2875   gEvalState.getCurrentFrame().code = mCodeBlock;
2876   gEvalState.getCurrentFrame().ip = ip - 1;
2877
2878   U32 breakLine;
2879   mCodeBlock->findBreakLine(ip - 1, breakLine, mCurrentInstruction);
2880   if (!breakLine)
2881      return OPCodeReturn::breakContinue;
2882   TelDebugger->executionStopped(mCodeBlock, breakLine);
2883   return OPCodeReturn::breakContinue;
2884}
2885
2886OPCodeReturn CodeInterpreter::op_iter_begin_str(U32 &ip)
2887{
2888   iterStack[_ITER].mIsStringIter = true;
2889
2890   // Emulate fallthrough:
2891   OPCodeReturn fallthrough = op_iter_begin(ip);
2892
2893   return fallthrough;
2894}
2895
2896OPCodeReturn CodeInterpreter::op_iter_begin(U32 &ip)
2897{
2898   StringTableEntry varName = CodeToSTE(mCodeBlock->code, ip);
2899   U32 failIp = mCodeBlock->code[ip + 2];
2900
2901   IterStackRecord& iter = iterStack[_ITER];
2902
2903   if (varName[0] == '$')
2904      iter.mVariable = gEvalState.globalVars.add(varName);
2905   else
2906      iter.mVariable = gEvalState.getCurrentFrame().add(varName);
2907
2908   if (iter.mIsStringIter)
2909   {
2910      iter.mData.mStr.mString = STR.getStringValuePtr();
2911      iter.mData.mStr.mIndex = 0;
2912   }
2913   else
2914   {
2915      // Look up the object.
2916
2917      SimSet* set;
2918      if (!Sim::findObject(STR.getStringValue(), set))
2919      {
2920         Con::errorf(ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue());
2921         Con::errorf(ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?");
2922         ip = failIp;
2923         return OPCodeReturn::success;
2924      }
2925
2926      // Set up.
2927
2928      iter.mData.mObj.mSet = set;
2929      iter.mData.mObj.mIndex = 0;
2930   }
2931
2932   _ITER++;
2933   mIterDepth++;
2934
2935   STR.push();
2936
2937   ip += 3;
2938
2939   return OPCodeReturn::success;
2940}
2941
2942OPCodeReturn CodeInterpreter::op_iter(U32 &ip)
2943{
2944   U32 breakIp = mCodeBlock->code[ip];
2945   IterStackRecord& iter = iterStack[_ITER - 1];
2946
2947   if (iter.mIsStringIter)
2948   {
2949      const char* str = StringStackPtrRef(iter.mData.mStr.mString).getPtr(&STR);
2950
2951      U32 startIndex = iter.mData.mStr.mIndex;
2952      U32 endIndex = startIndex;
2953
2954      // Break if at end.
2955
2956      if (!str[startIndex])
2957      {
2958         ip = breakIp;
2959         return OPCodeReturn::success; // continue in old interpreter
2960      }
2961
2962      // Find right end of current component.
2963
2964      if (!dIsspace(str[endIndex]))
2965         do ++endIndex;
2966      while (str[endIndex] && !dIsspace(str[endIndex]));
2967
2968      // Extract component.
2969
2970      if (endIndex != startIndex)
2971      {
2972         char savedChar = str[endIndex];
2973         const_cast< char* >(str)[endIndex] = '\0'; // We are on the string stack so this is okay.
2974         iter.mVariable->setStringValue(&str[startIndex]);
2975         const_cast< char* >(str)[endIndex] = savedChar;
2976      }
2977      else
2978         iter.mVariable->setStringValue("");
2979
2980      // Skip separator.
2981      if (str[endIndex] != '\0')
2982         ++endIndex;
2983
2984      iter.mData.mStr.mIndex = endIndex;
2985   }
2986   else
2987   {
2988      U32 index = iter.mData.mObj.mIndex;
2989      SimSet* set = iter.mData.mObj.mSet;
2990
2991      if (index >= set->size())
2992      {
2993         ip = breakIp;
2994         return OPCodeReturn::success; // continue in old interpreter
2995      }
2996
2997      iter.mVariable->setIntValue(set->at(index)->getId());
2998      iter.mData.mObj.mIndex = index + 1;
2999   }
3000
3001   ++ip;
3002
3003   return OPCodeReturn::success;
3004}
3005
3006OPCodeReturn CodeInterpreter::op_iter_end(U32 &ip)
3007{
3008   --_ITER;
3009   --mIterDepth;
3010   STR.rewind();
3011   iterStack[_ITER].mIsStringIter = false;
3012   return OPCodeReturn::success;
3013}
3014
3015OPCodeReturn CodeInterpreter::op_invalid(U32 &ip)
3016{
3017   // Invalid does nothing.
3018   return OPCodeReturn::exitCode;
3019}
3020