codeBlock.cpp

Engine/source/console/codeBlock.cpp

More...

Classes:

Detailed Description

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "console/console.h"
  25#include "console/compiler.h"
  26#include "console/codeBlock.h"
  27#include "console/telnetDebugger.h"
  28#include "console/ast.h"
  29#include "core/strings/unicode.h"
  30#include "core/strings/stringFunctions.h"
  31#include "core/stringTable.h"
  32#include "core/stream/fileStream.h"
  33
  34using namespace Compiler;
  35
  36bool           CodeBlock::smInFunction = false;
  37CodeBlock *    CodeBlock::smCodeBlockList = NULL;
  38CodeBlock *    CodeBlock::smCurrentCodeBlock = NULL;
  39ConsoleParser *CodeBlock::smCurrentParser = NULL;
  40
  41//-------------------------------------------------------------------------
  42
  43CodeBlock::CodeBlock()
  44{
  45   globalStrings = NULL;
  46   functionStrings = NULL;
  47   functionStringsMaxLen = 0;
  48   globalStringsMaxLen = 0;
  49   globalFloats = NULL;
  50   functionFloats = NULL;
  51   lineBreakPairs = NULL;
  52   breakList = NULL;
  53   breakListSize = 0;
  54
  55   refCount = 0;
  56   code = NULL;
  57   name = NULL;
  58   fullPath = NULL;
  59   modPath = NULL;
  60}
  61
  62CodeBlock::~CodeBlock()
  63{
  64   // Make sure we aren't lingering in the current code block...
  65   AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!");
  66
  67   if(name)
  68      removeFromCodeList();
  69   delete[] const_cast<char*>(globalStrings);
  70   delete[] const_cast<char*>(functionStrings);
  71   
  72   functionStringsMaxLen = 0;
  73   globalStringsMaxLen = 0;
  74
  75   delete[] globalFloats;
  76   delete[] functionFloats;
  77   delete[] code;
  78   delete[] breakList;
  79}
  80
  81//-------------------------------------------------------------------------
  82
  83StringTableEntry CodeBlock::getCurrentCodeBlockName()
  84{
  85   if (CodeBlock::getCurrentBlock())
  86      return CodeBlock::getCurrentBlock()->name;
  87   else
  88      return NULL;
  89}   
  90
  91StringTableEntry CodeBlock::getCurrentCodeBlockFullPath()
  92{
  93   if (CodeBlock::getCurrentBlock())
  94      return CodeBlock::getCurrentBlock()->fullPath;
  95   else
  96      return NULL;
  97}
  98
  99StringTableEntry CodeBlock::getCurrentCodeBlockModName()
 100{
 101   if (CodeBlock::getCurrentBlock())
 102      return CodeBlock::getCurrentBlock()->modPath;
 103   else
 104      return NULL;
 105}
 106
 107CodeBlock *CodeBlock::find(StringTableEntry name)
 108{
 109   for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
 110      if(walk->name == name)
 111         return walk;
 112   return NULL;
 113}
 114
 115//-------------------------------------------------------------------------
 116
 117void CodeBlock::addToCodeList()
 118{
 119   // remove any code blocks with my name
 120   for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile))
 121   {
 122      if((*walk)->name == name)
 123      {
 124         *walk = (*walk)->nextFile;
 125         break;
 126      }
 127   }
 128   nextFile = smCodeBlockList;
 129   smCodeBlockList = this;
 130}
 131
 132void CodeBlock::clearAllBreaks()
 133{
 134   if(!lineBreakPairs)
 135      return;
 136   for(U32 i = 0; i < lineBreakPairCount; i++)
 137   {
 138      U32 *p = lineBreakPairs + i * 2;
 139      code[p[1]] = p[0] & 0xFF;
 140   }
 141}
 142
 143void CodeBlock::clearBreakpoint(U32 lineNumber)
 144{
 145   if(!lineBreakPairs)
 146      return;
 147   for(U32 i = 0; i < lineBreakPairCount; i++)
 148   {
 149      U32 *p = lineBreakPairs + i * 2;
 150      if((p[0] >> 8) == lineNumber)
 151      {
 152         code[p[1]] = p[0] & 0xFF;
 153         return;
 154      }
 155   }
 156}
 157
 158void CodeBlock::setAllBreaks()
 159{
 160   if(!lineBreakPairs)
 161      return;
 162   for(U32 i = 0; i < lineBreakPairCount; i++)
 163   {
 164      U32 *p = lineBreakPairs + i * 2;
 165      code[p[1]] = OP_BREAK;
 166   }
 167}
 168
 169bool CodeBlock::setBreakpoint(U32 lineNumber)
 170{
 171   if(!lineBreakPairs)
 172      return false;
 173
 174   for(U32 i = 0; i < lineBreakPairCount; i++)
 175   {
 176      U32 *p = lineBreakPairs + i * 2;
 177      if((p[0] >> 8) == lineNumber)
 178      {
 179         code[p[1]] = OP_BREAK;
 180         return true;
 181      }
 182   }
 183
 184   return false;
 185}
 186
 187U32 CodeBlock::findFirstBreakLine(U32 lineNumber)
 188{
 189   if(!lineBreakPairs)
 190      return 0;
 191
 192   for(U32 i = 0; i < lineBreakPairCount; i++)
 193   {
 194      U32 *p = lineBreakPairs + i * 2;
 195      U32 line = (p[0] >> 8);
 196
 197      if( lineNumber <= line )
 198         return line;
 199   }
 200
 201   return 0;
 202}
 203
 204struct LinePair
 205{
 206   U32 instLine;
 207   U32 ip;
 208};
 209
 210void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction)
 211{
 212   U32 min = 0;
 213   U32 max = lineBreakPairCount - 1;
 214   LinePair *p = (LinePair *) lineBreakPairs;
 215
 216   U32 found;
 217   if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip)
 218   {
 219      line = 0;
 220      instruction = OP_INVALID;
 221      return;
 222   }
 223   else if(p[min].ip == ip)
 224      found = min;
 225   else if(p[max].ip == ip)
 226      found = max;
 227   else
 228   {
 229      for(;;)
 230      {
 231         if(min == max - 1)
 232         {
 233            found = min;
 234            break;
 235         }
 236         U32 mid = (min + max) >> 1;
 237         if(p[mid].ip == ip)
 238         {
 239            found = mid;
 240            break;
 241         }
 242         else if(p[mid].ip > ip)
 243            max = mid;
 244         else
 245            min = mid;
 246      }
 247   }
 248   instruction = p[found].instLine & 0xFF;
 249   line = p[found].instLine >> 8;
 250}
 251
 252const char *CodeBlock::getFileLine(U32 ip)
 253{
 254   static char nameBuffer[256];
 255   U32 line, inst;
 256   findBreakLine(ip, line, inst);
 257
 258   dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "<input>", line);
 259   return nameBuffer;
 260}
 261
 262void CodeBlock::removeFromCodeList()
 263{
 264   for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile))
 265   {
 266      if(*walk == this)
 267      {
 268         *walk = nextFile;
 269
 270         // clear out all breakpoints
 271         clearAllBreaks();
 272         break;
 273      }
 274   }
 275
 276   // Let the telnet debugger know that this code
 277   // block has been unloaded and that it needs to
 278   // remove references to it.
 279   if ( TelDebugger )
 280      TelDebugger->clearCodeBlockPointers( this );
 281}
 282
 283void CodeBlock::calcBreakList()
 284{
 285   U32 size = 0;
 286   S32 line = -1;
 287   U32 seqCount = 0;
 288   U32 i;
 289   for(i = 0; i < lineBreakPairCount; i++)
 290   {
 291      U32 lineNumber = lineBreakPairs[i * 2];
 292      if(lineNumber == U32(line + 1))
 293         seqCount++;
 294      else
 295      {
 296         if(seqCount)
 297            size++;
 298         size++;
 299         seqCount = 1;
 300      }
 301      line = lineNumber;
 302   }
 303   if(seqCount)
 304      size++;
 305
 306   breakList = new U32[size];
 307   breakListSize = size;
 308   line = -1;
 309   seqCount = 0;
 310   size = 0;
 311
 312   for(i = 0; i < lineBreakPairCount; i++)
 313   {
 314      U32 lineNumber = lineBreakPairs[i * 2];
 315
 316      if(lineNumber == U32(line + 1))
 317         seqCount++;
 318      else
 319      {
 320         if(seqCount)
 321            breakList[size++] = seqCount;
 322         breakList[size++] = lineNumber - getMax(0, line) - 1;
 323         seqCount = 1;
 324      }
 325
 326      line = lineNumber;
 327   }
 328
 329   if(seqCount)
 330      breakList[size++] = seqCount;
 331
 332   for(i = 0; i < lineBreakPairCount; i++)
 333   {
 334      U32 *p = lineBreakPairs + i * 2;
 335      p[0] = (p[0] << 8) | code[p[1]];
 336   }
 337
 338   // Let the telnet debugger know that this code
 339   // block has been loaded and that it can add break
 340   // points it has for it.
 341   if ( TelDebugger )
 342      TelDebugger->addAllBreakpoints( this );
 343}
 344
 345bool CodeBlock::read(StringTableEntry fileName, Stream &st)
 346{
 347   const StringTableEntry exePath = Platform::getMainDotCsDir();
 348   const StringTableEntry cwd = Platform::getCurrentDirectory();
 349
 350   name = fileName;
 351
 352   if(fileName)
 353   {
 354      fullPath = NULL;
 355
 356      if(Platform::isFullPath(fileName))
 357         fullPath = fileName;
 358
 359      if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0)
 360         name = StringTable->insert(fileName + dStrlen(exePath) + 1, true);
 361      else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0)
 362         name = StringTable->insert(fileName + dStrlen(cwd) + 1, true);
 363
 364      if(fullPath == NULL)
 365      {
 366         char buf[1024];
 367         fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true);
 368      }
 369
 370      modPath = Con::getModNameFromPath(fileName);
 371   }
 372   
 373   //
 374   addToCodeList();
 375
 376   U32 globalSize,size,i;
 377   st.read(&size);
 378   if(size)
 379   {
 380      globalStrings = new char[size];
 381      globalStringsMaxLen = size;
 382      st.read(size, globalStrings);
 383   }
 384   globalSize = size;
 385   st.read(&size);
 386   if(size)
 387   {
 388      functionStrings = new char[size];
 389      functionStringsMaxLen = size;
 390      st.read(size, functionStrings);
 391   }
 392   st.read(&size);
 393   if(size)
 394   {
 395      globalFloats = new F64[size];
 396      for(U32 i = 0; i < size; i++)
 397         st.read(&globalFloats[i]);
 398   }
 399   st.read(&size);
 400   if(size)
 401   {
 402      functionFloats = new F64[size];
 403      for(U32 i = 0; i < size; i++)
 404         st.read(&functionFloats[i]);
 405   }
 406   U32 codeLength;
 407   st.read(&codeLength);
 408   st.read(&lineBreakPairCount);
 409
 410   U32 totSize = codeLength + lineBreakPairCount * 2;
 411   code = new U32[totSize];
 412
 413   for(i = 0; i < codeLength; i++)
 414   {
 415      U8 b;
 416      st.read(&b);
 417      if(b == 0xFF)
 418         st.read(&code[i]);
 419      else
 420         code[i] = b;
 421   }
 422
 423   for(i = codeLength; i < totSize; i++)
 424      st.read(&code[i]);
 425
 426   lineBreakPairs = code + codeLength;
 427
 428   // StringTable-ize our identifiers.
 429   U32 identCount;
 430   st.read(&identCount);
 431   while(identCount--)
 432   {
 433      U32 offset;
 434      st.read(&offset);
 435      StringTableEntry ste;
 436      if(offset < globalSize)
 437         ste = StringTable->insert(globalStrings + offset);
 438      else
 439         ste = StringTable->insert("");
 440      U32 count;
 441      st.read(&count);
 442      while(count--)
 443      {
 444         U32 ip;
 445         st.read(&ip);
 446         code[ip] = *((U32 *) &ste);
 447      }
 448   }
 449
 450   if(lineBreakPairCount)
 451      calcBreakList();
 452
 453   return true;
 454}
 455
 456
 457bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *inScript, bool overrideNoDso)
 458{
 459   AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread");
 460   
 461   // This will return true, but return value is ignored
 462   char *script;
 463   chompUTF8BOM( inScript, &script );
 464   
 465   gSyntaxError = false;
 466
 467   consoleAllocReset();
 468
 469   STEtoCode = compileSTEtoCode;
 470
 471   gStatementList = NULL;
 472   gAnonFunctionList = NULL;
 473
 474   // Set up the parser.
 475   smCurrentParser = getParserForFile(fileName);
 476   AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
 477
 478   // Now do some parsing.
 479   smCurrentParser->setScanBuffer(script, fileName);
 480   smCurrentParser->restart(NULL);
 481   smCurrentParser->parse();
 482   if (gStatementList)
 483   {
 484      if (gAnonFunctionList)
 485      {
 486         // Prepend anonymous functions to statement list, so they're defined already when
 487         // the statements run.
 488         gAnonFunctionList->append(gStatementList);
 489         gStatementList = gAnonFunctionList;
 490      }
 491   }
 492
 493
 494   if(gSyntaxError)
 495   {
 496      consoleAllocReset();
 497      return false;
 498   }   
 499
 500#ifdef TORQUE_NO_DSO_GENERATION
 501   if(!overrideNoDso)
 502      return false;
 503#endif // !TORQUE_NO_DSO_GENERATION
 504
 505   FileStream st;
 506   if(!st.open(codeFileName, Torque::FS::File::Write)) 
 507      return false;
 508   st.write(U32(Con::DSOVersion));
 509
 510   // Reset all our value tables...
 511   resetTables();
 512
 513   smInFunction = false;
 514
 515   CodeStream codeStream;
 516   U32 lastIp;
 517   if(gStatementList)
 518   {
 519      lastIp = compileBlock(gStatementList, codeStream, 0) + 1;
 520   }
 521   else
 522   {
 523      codeSize = 1;
 524      lastIp = 0;
 525   }
 526   
 527   codeStream.emit(OP_RETURN);
 528   codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
 529   
 530   lineBreakPairCount = codeStream.getNumLineBreaks();
 531
 532   // Write string table data...
 533   getGlobalStringTable().write(st);
 534   getFunctionStringTable().write(st);
 535
 536   // Write float table data...
 537   getGlobalFloatTable().write(st);
 538   getFunctionFloatTable().write(st);
 539
 540   if(lastIp != codeSize)
 541      Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
 542   
 543   U32 totSize = codeSize + codeStream.getNumLineBreaks() * 2;
 544   st.write(codeSize);
 545   st.write(lineBreakPairCount);
 546
 547   // Write out our bytecode, doing a bit of compression for low numbers.
 548   U32 i;   
 549   for(i = 0; i < codeSize; i++)
 550   {
 551      if(code[i] < 0xFF)
 552         st.write(U8(code[i]));
 553      else
 554      {
 555         st.write(U8(0xFF));
 556         st.write(code[i]);
 557      }
 558   }
 559
 560   // Write the break info...
 561   for(i = codeSize; i < totSize; i++)
 562      st.write(code[i]);
 563
 564   getIdentTable().write(st);
 565
 566   consoleAllocReset();
 567   st.close();
 568
 569   return true;
 570
 571
 572}
 573
 574ConsoleValueRef CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, S32 setFrame)
 575{
 576   AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread");
 577   
 578   // Check for a UTF8 script file
 579   char *string;
 580   chompUTF8BOM( inString, &string );
 581
 582   STEtoCode = evalSTEtoCode;
 583   consoleAllocReset();
 584
 585   name = fileName;
 586
 587   if(fileName)
 588   {
 589      const StringTableEntry exePath = Platform::getMainDotCsDir();
 590      const StringTableEntry cwd = Platform::getCurrentDirectory();
 591
 592      fullPath = NULL;
 593      
 594      if(Platform::isFullPath(fileName))
 595         fullPath = fileName;
 596
 597      if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0)
 598         name = StringTable->insert(fileName + dStrlen(exePath) + 1, true);
 599      else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0)
 600         name = StringTable->insert(fileName + dStrlen(cwd) + 1, true);
 601
 602      if(fullPath == NULL)
 603      {
 604         char buf[1024];
 605         fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true);
 606      }
 607
 608      modPath = Con::getModNameFromPath(fileName);
 609   }
 610
 611   if(name)
 612      addToCodeList();
 613   
 614   gStatementList = NULL;
 615   gAnonFunctionList = NULL;
 616
 617   // Set up the parser.
 618   smCurrentParser = getParserForFile(fileName);
 619   AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
 620
 621   // Now do some parsing.
 622   smCurrentParser->setScanBuffer(string, fileName);
 623   smCurrentParser->restart(NULL);
 624   smCurrentParser->parse();
 625   if (gStatementList)
 626   {
 627      if (gAnonFunctionList)
 628      {
 629         // Prepend anonymous functions to statement list, so they're defined already when
 630         // the statements run.
 631         gAnonFunctionList->append(gStatementList);
 632         gStatementList = gAnonFunctionList;
 633      }
 634   }
 635
 636   if(!gStatementList)
 637   {
 638      delete this;
 639      return ConsoleValueRef();
 640   }
 641
 642   resetTables();
 643
 644   smInFunction = false;
 645   
 646   CodeStream codeStream;
 647   U32 lastIp = compileBlock(gStatementList, codeStream, 0);
 648
 649   lineBreakPairCount = codeStream.getNumLineBreaks();
 650
 651   globalStrings   = getGlobalStringTable().build();
 652   globalStringsMaxLen = getGlobalStringTable().totalLen;
 653
 654   functionStrings = getFunctionStringTable().build();
 655   functionStringsMaxLen = getFunctionStringTable().totalLen;
 656
 657   globalFloats    = getGlobalFloatTable().build();
 658   functionFloats  = getFunctionFloatTable().build();
 659   
 660   codeStream.emit(OP_RETURN);
 661   codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
 662   
 663   //dumpInstructions(0, false);
 664   
 665   consoleAllocReset();
 666
 667   if(lineBreakPairCount && fileName)
 668      calcBreakList();
 669
 670   if(lastIp+1 != codeSize)
 671      Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp);
 672
 673   return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
 674}
 675
 676//-------------------------------------------------------------------------
 677
 678void CodeBlock::incRefCount()
 679{
 680   refCount++;
 681}
 682
 683void CodeBlock::decRefCount()
 684{
 685   refCount--;
 686   if(!refCount)
 687      delete this;
 688}
 689
 690//-------------------------------------------------------------------------
 691
 692String CodeBlock::getFunctionArgs( U32 ip )
 693{
 694   StringBuilder str;
 695   
 696   U32 fnArgc = code[ ip + 5 ];
 697   for( U32 i = 0; i < fnArgc; ++ i )
 698   {
 699      StringTableEntry var = CodeToSTE(code, ip + (i*2) + 6);
 700      
 701      if( i != 0 )
 702         str.append( ", " );
 703
 704      str.append( "string " );
 705
 706      // Try to capture junked parameters
 707      if( var[ 0 ] )
 708         str.append( var + 1 );
 709      else
 710         str.append( "JUNK" );
 711   }
 712   
 713   return str.end();
 714}
 715
 716//-------------------------------------------------------------------------
 717
 718void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn )
 719{
 720   U32 ip = startIp;
 721   smInFunction = false;
 722   U32 endFuncIp = 0;
 723   
 724   while( ip < codeSize )
 725   {
 726      if (ip > endFuncIp)
 727      {
 728         smInFunction = false;
 729      }
 730      
 731      switch( code[ ip ++ ] )
 732      {
 733            
 734         case OP_FUNC_DECL:
 735         {
 736            StringTableEntry fnName       = CodeToSTE(code, ip);
 737            StringTableEntry fnNamespace  = CodeToSTE(code, ip+2);
 738            StringTableEntry fnPackage    = CodeToSTE(code, ip+4);
 739            bool hasBody = bool(code[ip+6]);
 740            U32 newIp = code[ ip + 7 ];
 741            U32 argc = code[ ip + 8 ];
 742            endFuncIp = newIp;
 743            
 744            Con::printf( "%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i",
 745               ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc );
 746               
 747            // Skip args.
 748                           
 749            ip += 9 + (argc * 2);
 750            smInFunction = true;
 751            break;
 752         }
 753            
 754         case OP_CREATE_OBJECT:
 755         {
 756            StringTableEntry objParent = CodeToSTE(code, ip);
 757            bool isDataBlock =          code[ip + 2];
 758            bool isInternal  =          code[ip + 3];
 759            bool isSingleton =          code[ip + 4];
 760            U32  lineNumber  =          code[ip + 5];
 761            U32 failJump     =          code[ip + 6];
 762            
 763            Con::printf( "%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i",
 764               ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump );
 765
 766            ip += 7;
 767            break;
 768         }
 769
 770         case OP_ADD_OBJECT:
 771         {
 772            bool placeAtRoot = code[ip++];
 773            Con::printf( "%i: OP_ADD_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot );
 774            break;
 775         }
 776         
 777         case OP_END_OBJECT:
 778         {
 779            bool placeAtRoot = code[ip++];
 780            Con::printf( "%i: OP_END_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot );
 781            break;
 782         }
 783         
 784         case OP_FINISH_OBJECT:
 785         {
 786            Con::printf( "%i: OP_FINISH_OBJECT", ip - 1 );
 787            break;
 788         }
 789         
 790         case OP_JMPIFFNOT:
 791         {
 792            Con::printf( "%i: OP_JMPIFFNOT ip=%i", ip - 1, code[ ip ] );
 793            ++ ip;
 794            break;
 795         }
 796         
 797         case OP_JMPIFNOT:
 798         {
 799            Con::printf( "%i: OP_JMPIFNOT ip=%i", ip - 1, code[ ip ] );
 800            ++ ip;
 801            break;
 802         }
 803         
 804         case OP_JMPIFF:
 805         {
 806            Con::printf( "%i: OP_JMPIFF ip=%i", ip - 1, code[ ip ] );
 807            ++ ip;
 808            break;
 809         }
 810
 811         case OP_JMPIF:
 812         {
 813            Con::printf( "%i: OP_JMPIF ip=%i", ip - 1, code[ ip ] );
 814            ++ ip;
 815            break;
 816         }
 817
 818         case OP_JMPIFNOT_NP:
 819         {
 820            Con::printf( "%i: OP_JMPIFNOT_NP ip=%i", ip - 1, code[ ip ] );
 821            ++ ip;
 822            break;
 823         }
 824
 825         case OP_JMPIF_NP:
 826         {
 827            Con::printf( "%i: OP_JMPIF_NP ip=%i", ip - 1, code[ ip ] );
 828            ++ ip;
 829            break;
 830         }
 831
 832         case OP_JMP:
 833         {
 834            Con::printf( "%i: OP_JMP ip=%i", ip - 1, code[ ip ] );
 835            ++ ip;
 836            break;
 837         }
 838
 839         case OP_RETURN:
 840         {
 841            Con::printf( "%i: OP_RETURN", ip - 1 );
 842            
 843            if( upToReturn )
 844               return;
 845               
 846            break;
 847         }
 848
 849         case OP_RETURN_VOID:
 850         {
 851            Con::printf( "%i: OP_RETURNVOID", ip - 1 );
 852
 853            if( upToReturn )
 854               return;
 855
 856            break;
 857         }
 858
 859         case OP_RETURN_UINT:
 860         {
 861            Con::printf( "%i: OP_RETURNUINT", ip - 1 );
 862
 863            if( upToReturn )
 864               return;
 865
 866            break;
 867         }
 868
 869         case OP_RETURN_FLT:
 870         {
 871            Con::printf( "%i: OP_RETURNFLT", ip - 1 );
 872
 873            if( upToReturn )
 874               return;
 875
 876            break;
 877         }
 878
 879         case OP_CMPEQ:
 880         {
 881            Con::printf( "%i: OP_CMPEQ", ip - 1 );
 882            break;
 883         }
 884
 885         case OP_CMPGR:
 886         {
 887            Con::printf( "%i: OP_CMPGR", ip - 1 );
 888            break;
 889         }
 890
 891         case OP_CMPGE:
 892         {
 893            Con::printf( "%i: OP_CMPGE", ip - 1 );
 894            break;
 895         }
 896
 897         case OP_CMPLT:
 898         {
 899            Con::printf( "%i: OP_CMPLT", ip - 1 );
 900            break;
 901         }
 902
 903         case OP_CMPLE:
 904         {
 905            Con::printf( "%i: OP_CMPLE", ip - 1 );
 906            break;
 907         }
 908
 909         case OP_CMPNE:
 910         {
 911            Con::printf( "%i: OP_CMPNE", ip - 1 );
 912            break;
 913         }
 914
 915         case OP_XOR:
 916         {
 917            Con::printf( "%i: OP_XOR", ip - 1 );
 918            break;
 919         }
 920
 921         case OP_MOD:
 922         {
 923            Con::printf( "%i: OP_MOD", ip - 1 );
 924            break;
 925         }
 926
 927         case OP_BITAND:
 928         {
 929            Con::printf( "%i: OP_BITAND", ip - 1 );
 930            break;
 931         }
 932
 933         case OP_BITOR:
 934         {
 935            Con::printf( "%i: OP_BITOR", ip - 1 );
 936            break;
 937         }
 938
 939         case OP_NOT:
 940         {
 941            Con::printf( "%i: OP_NOT", ip - 1 );
 942            break;
 943         }
 944
 945         case OP_NOTF:
 946         {
 947            Con::printf( "%i: OP_NOTF", ip - 1 );
 948            break;
 949         }
 950
 951         case OP_ONESCOMPLEMENT:
 952         {
 953            Con::printf( "%i: OP_ONESCOMPLEMENT", ip - 1 );
 954            break;
 955         }
 956
 957         case OP_SHR:
 958         {
 959            Con::printf( "%i: OP_SHR", ip - 1 );
 960            break;
 961         }
 962
 963         case OP_SHL:
 964         {
 965            Con::printf( "%i: OP_SHL", ip - 1 );
 966            break;
 967         }
 968
 969         case OP_AND:
 970         {
 971            Con::printf( "%i: OP_AND", ip - 1 );
 972            break;
 973         }
 974
 975         case OP_OR:
 976         {
 977            Con::printf( "%i: OP_OR", ip - 1 );
 978            break;
 979         }
 980
 981         case OP_ADD:
 982         {
 983            Con::printf( "%i: OP_ADD", ip - 1 );
 984            break;
 985         }
 986
 987         case OP_SUB:
 988         {
 989            Con::printf( "%i: OP_SUB", ip - 1 );
 990            break;
 991         }
 992
 993         case OP_MUL:
 994         {
 995            Con::printf( "%i: OP_MUL", ip - 1 );
 996            break;
 997         }
 998
 999         case OP_DIV:
1000         {
1001            Con::printf( "%i: OP_DIV", ip - 1 );
1002            break;
1003         }
1004
1005         case OP_NEG:
1006         {
1007            Con::printf( "%i: OP_NEG", ip - 1 );
1008            break;
1009         }
1010
1011         case OP_SETCURVAR:
1012         {
1013            StringTableEntry var = CodeToSTE(code, ip);
1014            
1015            Con::printf( "%i: OP_SETCURVAR var=%s", ip - 1, var );
1016            ip += 2;
1017            break;
1018         }
1019         
1020         case OP_SETCURVAR_CREATE:
1021         {
1022            StringTableEntry var = CodeToSTE(code, ip);
1023            
1024            Con::printf( "%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var );
1025            ip += 2;
1026            break;
1027         }
1028         
1029         case OP_SETCURVAR_ARRAY:
1030         {
1031            Con::printf( "%i: OP_SETCURVAR_ARRAY", ip - 1 );
1032            break;
1033         }
1034         
1035         case OP_SETCURVAR_ARRAY_CREATE:
1036         {
1037            Con::printf( "%i: OP_SETCURVAR_ARRAY_CREATE", ip - 1 );
1038            break;
1039         }
1040         
1041         case OP_LOADVAR_UINT:
1042         {
1043            Con::printf( "%i: OP_LOADVAR_UINT", ip - 1 );
1044            break;
1045         }
1046         
1047         case OP_LOADVAR_FLT:
1048         {
1049            Con::printf( "%i: OP_LOADVAR_FLT", ip - 1 );
1050            break;
1051         }
1052
1053         case OP_LOADVAR_STR:
1054         {
1055            Con::printf( "%i: OP_LOADVAR_STR", ip - 1 );
1056            break;
1057         }
1058
1059         case OP_LOADVAR_VAR:
1060         {
1061            Con::printf( "%i: OP_LOADVAR_VAR", ip - 1 );
1062            break;
1063         }
1064
1065         case OP_SAVEVAR_UINT:
1066         {
1067            Con::printf( "%i: OP_SAVEVAR_UINT", ip - 1 );
1068            break;
1069         }
1070
1071         case OP_SAVEVAR_FLT:
1072         {
1073            Con::printf( "%i: OP_SAVEVAR_FLT", ip - 1 );
1074            break;
1075         }
1076
1077         case OP_SAVEVAR_STR:
1078         {
1079            Con::printf( "%i: OP_SAVEVAR_STR", ip - 1 );
1080            break;
1081         }
1082
1083         case OP_SAVEVAR_VAR:
1084         {
1085            Con::printf( "%i: OP_SAVEVAR_VAR", ip - 1 );
1086            break;
1087         }
1088
1089         case OP_SETCUROBJECT:
1090         {
1091            Con::printf( "%i: OP_SETCUROBJECT", ip - 1 );
1092            break;
1093         }
1094
1095         case OP_SETCUROBJECT_NEW:
1096         {
1097            Con::printf( "%i: OP_SETCUROBJECT_NEW", ip - 1 );
1098            break;
1099         }
1100         
1101         case OP_SETCUROBJECT_INTERNAL:
1102         {
1103            Con::printf( "%i: OP_SETCUROBJECT_INTERNAL", ip - 1 );
1104            ++ ip;
1105            break;
1106         }
1107         
1108         case OP_SETCURFIELD:
1109         {
1110            StringTableEntry curField = CodeToSTE(code, ip);
1111            Con::printf( "%i: OP_SETCURFIELD field=%s", ip - 1, curField );
1112            ip += 2;
1113            break;
1114         }
1115         
1116         case OP_SETCURFIELD_ARRAY:
1117         {
1118            Con::printf( "%i: OP_SETCURFIELD_ARRAY", ip - 1 );
1119            break;
1120         }
1121
1122         case OP_SETCURFIELD_TYPE:
1123         {
1124            U32 type = code[ ip ];
1125            Con::printf( "%i: OP_SETCURFIELD_TYPE type=%i", ip - 1, type );
1126            ++ ip;
1127            break;
1128         }
1129
1130         case OP_LOADFIELD_UINT:
1131         {
1132            Con::printf( "%i: OP_LOADFIELD_UINT", ip - 1 );
1133            break;
1134         }
1135
1136         case OP_LOADFIELD_FLT:
1137         {
1138            Con::printf( "%i: OP_LOADFIELD_FLT", ip - 1 );
1139            break;
1140         }
1141
1142         case OP_LOADFIELD_STR:
1143         {
1144            Con::printf( "%i: OP_LOADFIELD_STR", ip - 1 );
1145            break;
1146         }
1147
1148         case OP_SAVEFIELD_UINT:
1149         {
1150            Con::printf( "%i: OP_SAVEFIELD_UINT", ip - 1 );
1151            break;
1152         }
1153
1154         case OP_SAVEFIELD_FLT:
1155         {
1156            Con::printf( "%i: OP_SAVEFIELD_FLT", ip - 1 );
1157            break;
1158         }
1159
1160         case OP_SAVEFIELD_STR:
1161         {
1162            Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 );
1163            break;
1164         }
1165
1166         case OP_STR_TO_UINT:
1167         {
1168            Con::printf( "%i: OP_STR_TO_UINT", ip - 1 );
1169            break;
1170         }
1171
1172         case OP_STR_TO_FLT:
1173         {
1174            Con::printf( "%i: OP_STR_TO_FLT", ip - 1 );
1175            break;
1176         }
1177
1178         case OP_STR_TO_NONE:
1179         {
1180            Con::printf( "%i: OP_STR_TO_NONE", ip - 1 );
1181            break;
1182         }
1183
1184         case OP_FLT_TO_UINT:
1185         {
1186            Con::printf( "%i: OP_FLT_TO_UINT", ip - 1 );
1187            break;
1188         }
1189
1190         case OP_FLT_TO_STR:
1191         {
1192            Con::printf( "%i: OP_FLT_TO_STR", ip - 1 );
1193            break;
1194         }
1195
1196         case OP_FLT_TO_NONE:
1197         {
1198            Con::printf( "%i: OP_FLT_TO_NONE", ip - 1 );
1199            break;
1200         }
1201
1202         case OP_UINT_TO_FLT:
1203         {
1204            Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 );
1205            break;
1206         }
1207
1208         case OP_UINT_TO_STR:
1209         {
1210            Con::printf( "%i: OP_UINT_TO_STR", ip - 1 );
1211            break;
1212         }
1213
1214         case OP_UINT_TO_NONE:
1215         {
1216            Con::printf( "%i: OP_UINT_TO_NONE", ip - 1 );
1217            break;
1218         }
1219
1220         case OP_COPYVAR_TO_NONE:
1221         {
1222            Con::printf( "%i: OP_COPYVAR_TO_NONE", ip - 1 );
1223            break;
1224         }
1225
1226         case OP_LOADIMMED_UINT:
1227         {
1228            U32 val = code[ ip ];
1229            Con::printf( "%i: OP_LOADIMMED_UINT val=%i", ip - 1, val );
1230            ++ ip;
1231            break;
1232         }
1233
1234         case OP_LOADIMMED_FLT:
1235         {
1236            F64 val = (smInFunction ? functionFloats : globalFloats)[ code[ ip ] ];
1237            Con::printf( "%i: OP_LOADIMMED_FLT val=%f", ip - 1, val );
1238            ++ ip;
1239            break;
1240         }
1241
1242         case OP_TAG_TO_STR:
1243         {
1244            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ];
1245            Con::printf( "%i: OP_TAG_TO_STR str=%s", ip - 1, str );
1246            ++ ip;
1247            break;
1248         }
1249         
1250         case OP_LOADIMMED_STR:
1251         {
1252            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ];
1253            Con::printf( "%i: OP_LOADIMMED_STR str=%s", ip - 1, str );
1254            ++ ip;
1255            break;
1256         }
1257
1258         case OP_DOCBLOCK_STR:
1259         {
1260            const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ];
1261            Con::printf( "%i: OP_DOCBLOCK_STR str=%s", ip - 1, str );
1262            ++ ip;
1263            break;
1264         }
1265         
1266         case OP_LOADIMMED_IDENT:
1267         {
1268            StringTableEntry str = CodeToSTE(code, ip);
1269            Con::printf( "%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str );
1270            ip += 2;
1271            break;
1272         }
1273
1274         case OP_CALLFUNC_RESOLVE:
1275         {
1276            StringTableEntry fnNamespace = CodeToSTE(code, ip+2);
1277            StringTableEntry fnName      = CodeToSTE(code, ip);
1278            U32 callType = code[ip+2];
1279
1280            Con::printf( "%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
1281               callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
1282                  : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" );
1283            
1284            ip += 5;
1285            break;
1286         }
1287         
1288         case OP_CALLFUNC:
1289         {
1290            StringTableEntry fnNamespace = CodeToSTE(code, ip+2);
1291            StringTableEntry fnName      = CodeToSTE(code, ip);
1292            U32 callType = code[ip+4];
1293
1294            Con::printf( "%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace,
1295               callType == FuncCallExprNode::FunctionCall ? "FunctionCall"
1296                  : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" );
1297            
1298            ip += 5;
1299            break;
1300         }
1301
1302         case OP_ADVANCE_STR:
1303         {
1304            Con::printf( "%i: OP_ADVANCE_STR", ip - 1 );
1305            break;
1306         }
1307
1308         case OP_ADVANCE_STR_APPENDCHAR:
1309         {
1310            char ch = code[ ip ];
1311            Con::printf( "%i: OP_ADVANCE_STR_APPENDCHAR char=%c", ip - 1, ch );
1312            ++ ip;
1313            break;
1314         }
1315
1316         case OP_ADVANCE_STR_COMMA:
1317         {
1318            Con::printf( "%i: OP_ADVANCE_STR_COMMA", ip - 1 );
1319            break;
1320         }
1321
1322         case OP_ADVANCE_STR_NUL:
1323         {
1324            Con::printf( "%i: OP_ADVANCE_STR_NUL", ip - 1 );
1325            break;
1326         }
1327
1328         case OP_REWIND_STR:
1329         {
1330            Con::printf( "%i: OP_REWIND_STR", ip - 1 );
1331            break;
1332         }
1333
1334         case OP_TERMINATE_REWIND_STR:
1335         {
1336            Con::printf( "%i: OP_TERMINATE_REWIND_STR", ip - 1 );
1337            break;
1338         }
1339
1340         case OP_COMPARE_STR:
1341         {
1342            Con::printf( "%i: OP_COMPARE_STR", ip - 1 );
1343            break;
1344         }
1345
1346         case OP_PUSH:
1347         {
1348            Con::printf( "%i: OP_PUSH", ip - 1 );
1349            break;
1350         }
1351
1352         case OP_PUSH_UINT:
1353         {
1354            Con::printf( "%i: OP_PUSH_UINT", ip - 1 );
1355            break;
1356         }
1357
1358         case OP_PUSH_FLT:
1359         {
1360            Con::printf( "%i: OP_PUSH_FLT", ip - 1 );
1361            break;
1362         }
1363
1364         case OP_PUSH_VAR:
1365         {
1366            Con::printf( "%i: OP_PUSH_VAR", ip - 1 );
1367            break;
1368         }
1369
1370         case OP_PUSH_FRAME:
1371         {
1372            Con::printf( "%i: OP_PUSH_FRAME", ip - 1 );
1373            break;
1374         }
1375
1376         case OP_ASSERT:
1377         {
1378            const char* message = (smInFunction ? functionStrings : globalStrings) + code[ ip ];
1379            Con::printf( "%i: OP_ASSERT message=%s", ip - 1, message );
1380            ++ ip;
1381            break;
1382         }
1383
1384         case OP_BREAK:
1385         {
1386            Con::printf( "%i: OP_BREAK", ip - 1 );
1387            break;
1388         }
1389         
1390         case OP_ITER_BEGIN:
1391         {
1392            StringTableEntry varName = CodeToSTE(code, ip);
1393            U32 failIp = code[ ip + 2 ];
1394            
1395            Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp );
1396
1397            ip += 3;
1398            break;
1399         }
1400
1401         case OP_ITER_BEGIN_STR:
1402         {
1403            StringTableEntry varName = CodeToSTE(code, ip);
1404            U32 failIp = code[ ip + 2 ];
1405            
1406            Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp );
1407
1408            ip += 3;
1409            break;
1410         }
1411         
1412         case OP_ITER:
1413         {
1414            U32 breakIp = code[ ip ];
1415            
1416            Con::printf( "%i: OP_ITER breakIp=%i", ip - 1, breakIp );
1417
1418            ++ ip;
1419            break;
1420         }
1421         
1422         case OP_ITER_END:
1423         {
1424            Con::printf( "%i: OP_ITER_END", ip - 1 );
1425            break;
1426         }
1427
1428         default:
1429            Con::printf( "%i: !!INVALID!!", ip - 1 );
1430            break;
1431      }
1432   }
1433   
1434   smInFunction = false;
1435}
1436