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