astNodes.cpp

Engine/source/console/astNodes.cpp

More...

Classes:

class

Namespaces:

namespace

Public Functions

getAssignOpTypeOp(S32 op, TypeReq & type, U32 & operand)

Detailed Description

Public Functions

conversionOp(TypeReq src, TypeReq dst)

getAssignOpTypeOp(S32 op, TypeReq & type, U32 & operand)

   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 "platform/platform.h"
  25#include "console/console.h"
  26#include "console/telnetDebugger.h"
  27
  28#include "console/ast.h"
  29#include "core/tAlgorithm.h"
  30
  31#include "core/strings/findMatch.h"
  32#include "console/consoleInternal.h"
  33#include "core/stream/fileStream.h"
  34#include "console/compiler.h"
  35
  36#include "console/simBase.h"
  37
  38template< typename T >
  39struct Token
  40{
  41   T value;
  42   S32 lineNumber;
  43};
  44#include "console/cmdgram.h"
  45
  46namespace Compiler
  47{
  48   U32 compileBlock(StmtNode *block, CodeStream &codeStream, U32 ip)
  49   {
  50      for (StmtNode *walk = block; walk; walk = walk->getNext())
  51         ip = walk->compileStmt(codeStream, ip);
  52      return codeStream.tell();
  53   }
  54
  55   inline bool isSimpleVarLookup(ExprNode *arrayExpr, StringTableEntry &varName)
  56   {
  57      if (arrayExpr == nullptr)
  58      {
  59         varName = StringTable->insert("");
  60         return false;
  61      }
  62
  63      // No double arrays allowed for optimization.
  64      VarNode *var = dynamic_cast<VarNode*>(arrayExpr);
  65      if (var && !var->arrayIndex)
  66      {
  67         StringTableEntry arrayVar = StringTable->insert(var->varName);
  68         precompileIdent(arrayVar);
  69         varName = arrayVar;
  70         return true;
  71      }
  72      return false;
  73   }
  74
  75   // Do not allow 'recursive' %this optimizations. It can lead to weird bytecode
  76   // generation since we can only optimize one expression at a time.
  77   static bool OnlyOneThisOptimization = false;
  78
  79   inline bool isThisVar(ExprNode *objectExpr)
  80   {
  81      // If we are currently optimizing a this var, don't allow extra optimization.
  82      if (objectExpr == nullptr || OnlyOneThisOptimization)
  83         return false;
  84
  85      VarNode *thisVar = dynamic_cast<VarNode*>(objectExpr);
  86      if (thisVar && thisVar->varName == StringTable->insert("%this"))
  87         return true;
  88      return false;
  89   }
  90
  91   inline void optimizeThisPointer(CodeStream &codeStream, ExprNode *arrayExpr, U32 &ip, StringTableEntry slotName)
  92   {
  93      OnlyOneThisOptimization = true;
  94
  95      // Is the array a simple variable? If so, we can optimize that.
  96      StringTableEntry varName = nullptr;
  97      bool simple = false;
  98
  99      if (arrayExpr)
 100      {
 101         simple = isSimpleVarLookup(arrayExpr, varName);
 102         if (!simple)
 103         {
 104            // Less optimized array setting.
 105            codeStream.emit(OP_ADVANCE_STR);
 106            ip = arrayExpr->compile(codeStream, ip, TypeReqString);
 107         }
 108      }
 109
 110      codeStream.emit(OP_SETCURFIELD_THIS);
 111      codeStream.emitSTE(slotName);
 112
 113      if (arrayExpr)
 114      {
 115         if (simple)
 116         {
 117            codeStream.emit(OP_SETCURFIELD_ARRAY_VAR);
 118            codeStream.emitSTE(varName);
 119         }
 120         else
 121         {
 122            codeStream.emit(OP_SETCURFIELD_ARRAY);
 123            codeStream.emit(OP_TERMINATE_REWIND_STR);
 124         }
 125      }
 126
 127      OnlyOneThisOptimization = false;
 128   }
 129}
 130
 131using namespace Compiler;
 132
 133//-----------------------------------------------------------------------------
 134
 135void StmtNode::addBreakLine(CodeStream &code)
 136{
 137   code.addBreakLine(dbgLineNumber, code.tell());
 138}
 139
 140//------------------------------------------------------------
 141
 142StmtNode::StmtNode()
 143{
 144   mNext = NULL;
 145   dbgFileName = CodeBlock::smCurrentParser->getCurrentFile();
 146   dbgLineNumber = 0;
 147}
 148
 149void StmtNode::setPackage(StringTableEntry)
 150{
 151}
 152
 153void StmtNode::append(StmtNode *next)
 154{
 155   StmtNode *walk = this;
 156   while (walk->mNext)
 157      walk = walk->mNext;
 158   walk->mNext = next;
 159}
 160
 161
 162void FunctionDeclStmtNode::setPackage(StringTableEntry packageName)
 163{
 164   package = packageName;
 165}
 166
 167//------------------------------------------------------------
 168//
 169// Console language compilers
 170//
 171//------------------------------------------------------------
 172
 173static U32 conversionOp(TypeReq src, TypeReq dst)
 174{
 175   if (src == TypeReqString)
 176   {
 177      switch (dst)
 178      {
 179         case TypeReqUInt:
 180            return OP_STR_TO_UINT;
 181         case TypeReqFloat:
 182            return OP_STR_TO_FLT;
 183         case TypeReqNone:
 184            return OP_STR_TO_NONE;
 185         case TypeReqVar:
 186            return OP_SAVEVAR_STR;
 187         default:
 188            break;
 189      }
 190   }
 191   else if (src == TypeReqFloat)
 192   {
 193      switch (dst)
 194      {
 195         case TypeReqUInt:
 196            return OP_FLT_TO_UINT;
 197         case TypeReqString:
 198            return OP_FLT_TO_STR;
 199         case TypeReqNone:
 200            return OP_FLT_TO_NONE;
 201         case TypeReqVar:
 202            return OP_SAVEVAR_FLT;
 203         default:
 204            break;
 205      }
 206   }
 207   else if (src == TypeReqUInt)
 208   {
 209      switch (dst)
 210      {
 211         case TypeReqFloat:
 212            return OP_UINT_TO_FLT;
 213         case TypeReqString:
 214            return OP_UINT_TO_STR;
 215         case TypeReqNone:
 216            return OP_UINT_TO_NONE;
 217         case TypeReqVar:
 218            return OP_SAVEVAR_UINT;
 219         default:
 220            break;
 221      }
 222   }
 223   else if (src == TypeReqVar)
 224   {
 225      switch (dst)
 226      {
 227         case TypeReqUInt:
 228            return OP_LOADVAR_UINT;
 229         case TypeReqFloat:
 230            return OP_LOADVAR_FLT;
 231         case TypeReqString:
 232            return OP_LOADVAR_STR;
 233         case TypeReqNone:
 234            return OP_COPYVAR_TO_NONE;
 235         default:
 236            break;
 237      }
 238   }
 239   return OP_INVALID;
 240}
 241
 242//------------------------------------------------------------
 243
 244U32 BreakStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 245{
 246   if (codeStream.inLoop())
 247   {
 248      addBreakLine(codeStream);
 249      codeStream.emit(OP_JMP);
 250      codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
 251   }
 252   else
 253   {
 254      Con::warnf(ConsoleLogEntry::General, "%s (%d): break outside of loop... ignoring.", dbgFileName, dbgLineNumber);
 255   }
 256   return codeStream.tell();
 257}
 258
 259//------------------------------------------------------------
 260
 261U32 ContinueStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 262{
 263   if (codeStream.inLoop())
 264   {
 265      addBreakLine(codeStream);
 266      codeStream.emit(OP_JMP);
 267      codeStream.emitFix(CodeStream::FIXTYPE_CONTINUE);
 268   }
 269   else
 270   {
 271      Con::warnf(ConsoleLogEntry::General, "%s (%d): continue outside of loop... ignoring.", dbgFileName, dbgLineNumber);
 272   }
 273   return codeStream.tell();
 274}
 275
 276//------------------------------------------------------------
 277
 278U32 ExprNode::compileStmt(CodeStream &codeStream, U32 ip)
 279{
 280   addBreakLine(codeStream);
 281   return compile(codeStream, ip, TypeReqNone);
 282}
 283
 284//------------------------------------------------------------
 285
 286U32 ReturnStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 287{
 288   addBreakLine(codeStream);
 289   if (!expr)
 290      codeStream.emit(OP_RETURN_VOID);
 291   else
 292   {
 293      TypeReq walkType = expr->getPreferredType();
 294      if (walkType == TypeReqNone) walkType = TypeReqString;
 295      ip = expr->compile(codeStream, ip, walkType);
 296
 297      // Return the correct type
 298      switch (walkType) {
 299         case TypeReqUInt:
 300            codeStream.emit(OP_RETURN_UINT);
 301            break;
 302         case TypeReqFloat:
 303            codeStream.emit(OP_RETURN_FLT);
 304            break;
 305         default:
 306            codeStream.emit(OP_RETURN);
 307            break;
 308      }
 309   }
 310   return codeStream.tell();
 311}
 312
 313//------------------------------------------------------------
 314
 315ExprNode *IfStmtNode::getSwitchOR(ExprNode *left, ExprNode *list, bool string)
 316{
 317   ExprNode *nextExpr = (ExprNode *)list->getNext();
 318   ExprNode *test;
 319   if (string)
 320      test = StreqExprNode::alloc(left->dbgLineNumber, left, list, true);
 321   else
 322      test = IntBinaryExprNode::alloc(left->dbgLineNumber, opEQ, left, list);
 323   if (!nextExpr)
 324      return test;
 325   return IntBinaryExprNode::alloc(test->dbgLineNumber, opOR, test, getSwitchOR(left, nextExpr, string));
 326}
 327
 328void IfStmtNode::propagateSwitchExpr(ExprNode *left, bool string)
 329{
 330   testExpr = getSwitchOR(left, testExpr, string);
 331   if (propagate && elseBlock)
 332      ((IfStmtNode *)elseBlock)->propagateSwitchExpr(left, string);
 333}
 334
 335U32 IfStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 336{
 337   U32 endifIp, elseIp;
 338   addBreakLine(codeStream);
 339
 340   if (testExpr->getPreferredType() == TypeReqUInt)
 341   {
 342      integer = true;
 343   }
 344   else
 345   {
 346      integer = false;
 347   }
 348
 349   ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat);
 350   codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT);
 351
 352   if (elseBlock)
 353   {
 354      elseIp = codeStream.emit(0);
 355      elseOffset = compileBlock(ifBlock, codeStream, ip) + 2;
 356      codeStream.emit(OP_JMP);
 357      endifIp = codeStream.emit(0);
 358      endifOffset = compileBlock(elseBlock, codeStream, ip);
 359
 360      codeStream.patch(endifIp, endifOffset);
 361      codeStream.patch(elseIp, elseOffset);
 362   }
 363   else
 364   {
 365      endifIp = codeStream.emit(0);
 366      endifOffset = compileBlock(ifBlock, codeStream, ip);
 367
 368      codeStream.patch(endifIp, endifOffset);
 369   }
 370
 371   // Resolve fixes
 372   return codeStream.tell();
 373}
 374
 375//------------------------------------------------------------
 376
 377U32 LoopStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 378{
 379   if (testExpr->getPreferredType() == TypeReqUInt)
 380   {
 381      integer = true;
 382   }
 383   else
 384   {
 385      integer = false;
 386   }
 387
 388   // if it's a for loop or a while loop it goes:
 389   // initExpr
 390   // testExpr
 391   // OP_JMPIFNOT to break point
 392   // loopStartPoint:
 393   // loopBlock
 394   // continuePoint:
 395   // endLoopExpr
 396   // testExpr
 397   // OP_JMPIF loopStartPoint
 398   // breakPoint:
 399
 400   // otherwise if it's a do ... while() it goes:
 401   // initExpr
 402   // loopStartPoint:
 403   // loopBlock
 404   // continuePoint:
 405   // endLoopExpr
 406   // testExpr
 407   // OP_JMPIF loopStartPoint
 408   // breakPoint:
 409
 410   // loopBlockStart == start of loop block
 411   // continue == skip to end
 412   // break == exit loop
 413
 414
 415   addBreakLine(codeStream);
 416   codeStream.pushFixScope(true);
 417
 418   if (initExpr)
 419      ip = initExpr->compile(codeStream, ip, TypeReqNone);
 420
 421   if (!isDoLoop)
 422   {
 423      ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat);
 424      codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT);
 425      codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
 426   }
 427
 428   // Compile internals of loop.
 429   loopBlockStartOffset = codeStream.tell();
 430   continueOffset = compileBlock(loopBlock, codeStream, ip);
 431
 432   if (endLoopExpr)
 433      ip = endLoopExpr->compile(codeStream, ip, TypeReqNone);
 434
 435   ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat);
 436
 437   codeStream.emit(integer ? OP_JMPIF : OP_JMPIFF);
 438   codeStream.emitFix(CodeStream::FIXTYPE_LOOPBLOCKSTART);
 439
 440   breakOffset = codeStream.tell(); // exit loop
 441
 442   codeStream.fixLoop(loopBlockStartOffset, breakOffset, continueOffset);
 443   codeStream.popFixScope();
 444
 445   return codeStream.tell();
 446}
 447
 448//------------------------------------------------------------
 449
 450U32 IterStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
 451{
 452   // Instruction sequence:
 453   //
 454   //   containerExpr
 455   //   OP_ITER_BEGIN varName .fail
 456   // .continue:
 457   //   OP_ITER .break
 458   //   body
 459   //   OP_JMP .continue
 460   // .break:
 461   //   OP_ITER_END
 462   // .fail:
 463
 464   addBreakLine(codeStream);
 465
 466   codeStream.pushFixScope(true);
 467
 468   const U32 startIp = ip;
 469   containerExpr->compile(codeStream, startIp, TypeReqString);
 470
 471   codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN);
 472   codeStream.emitSTE(varName);
 473   const U32 finalFix = codeStream.emit(0);
 474   const U32 continueIp = codeStream.emit(OP_ITER);
 475   codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
 476   const U32 bodyIp = codeStream.tell();
 477
 478   const U32 jmpIp = compileBlock(body, codeStream, bodyIp);
 479   const U32 breakIp = jmpIp + 2;
 480   const U32 finalIp = breakIp + 1;
 481
 482   codeStream.emit(OP_JMP);
 483   codeStream.emitFix(CodeStream::FIXTYPE_CONTINUE);
 484   codeStream.emit(OP_ITER_END);
 485
 486   codeStream.patch(finalFix, finalIp);
 487   codeStream.fixLoop(bodyIp, breakIp, continueIp);
 488   codeStream.popFixScope();
 489
 490   return codeStream.tell();
 491}
 492
 493//------------------------------------------------------------
 494
 495U32 ConditionalExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 496{
 497   // code is testExpr
 498   // JMPIFNOT falseStart
 499   // trueExpr
 500   // JMP end
 501   // falseExpr
 502   if (testExpr->getPreferredType() == TypeReqUInt)
 503   {
 504      integer = true;
 505   }
 506   else
 507   {
 508      integer = false;
 509   }
 510
 511   ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat);
 512   codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT);
 513
 514   U32 jumpElseIp = codeStream.emit(0);
 515   ip = trueExpr->compile(codeStream, ip, type);
 516   codeStream.emit(OP_JMP);
 517   U32 jumpEndIp = codeStream.emit(0);
 518   codeStream.patch(jumpElseIp, codeStream.tell());
 519   ip = falseExpr->compile(codeStream, ip, type);
 520   codeStream.patch(jumpEndIp, codeStream.tell());
 521
 522   return codeStream.tell();
 523}
 524
 525TypeReq ConditionalExprNode::getPreferredType()
 526{
 527   return trueExpr->getPreferredType();
 528}
 529
 530//------------------------------------------------------------
 531
 532U32 FloatBinaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 533{
 534   ip = right->compile(codeStream, ip, TypeReqFloat);
 535   ip = left->compile(codeStream, ip, TypeReqFloat);
 536   U32 operand = OP_INVALID;
 537   switch (op)
 538   {
 539      case '+':
 540         operand = OP_ADD;
 541         break;
 542      case '-':
 543         operand = OP_SUB;
 544         break;
 545      case '/':
 546         operand = OP_DIV;
 547         break;
 548      case '*':
 549         operand = OP_MUL;
 550         break;
 551   }
 552   codeStream.emit(operand);
 553   if (type != TypeReqFloat)
 554      codeStream.emit(conversionOp(TypeReqFloat, type));
 555   return codeStream.tell();
 556}
 557
 558TypeReq FloatBinaryExprNode::getPreferredType()
 559{
 560   return TypeReqFloat;
 561}
 562
 563//------------------------------------------------------------
 564
 565void IntBinaryExprNode::getSubTypeOperand()
 566{
 567   subType = TypeReqUInt;
 568   switch (op)
 569   {
 570      case '^':
 571         operand = OP_XOR;
 572         break;
 573      case '%':
 574         operand = OP_MOD;
 575         break;
 576      case '&':
 577         operand = OP_BITAND;
 578         break;
 579      case '|':
 580         operand = OP_BITOR;
 581         break;
 582      case '<':
 583         operand = OP_CMPLT;
 584         subType = TypeReqFloat;
 585         break;
 586      case '>':
 587         operand = OP_CMPGR;
 588         subType = TypeReqFloat;
 589         break;
 590      case opGE:
 591         operand = OP_CMPGE;
 592         subType = TypeReqFloat;
 593         break;
 594      case opLE:
 595         operand = OP_CMPLE;
 596         subType = TypeReqFloat;
 597         break;
 598      case opEQ:
 599         operand = OP_CMPEQ;
 600         subType = TypeReqFloat;
 601         break;
 602      case opNE:
 603         operand = OP_CMPNE;
 604         subType = TypeReqFloat;
 605         break;
 606      case opOR:
 607         operand = OP_OR;
 608         break;
 609      case opAND:
 610         operand = OP_AND;
 611         break;
 612      case opSHR:
 613         operand = OP_SHR;
 614         break;
 615      case opSHL:
 616         operand = OP_SHL;
 617         break;
 618   }
 619}
 620
 621U32 IntBinaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 622{
 623   getSubTypeOperand();
 624
 625   if (operand == OP_OR || operand == OP_AND)
 626   {
 627      ip = left->compile(codeStream, ip, subType);
 628      codeStream.emit(operand == OP_OR ? OP_JMPIF_NP : OP_JMPIFNOT_NP);
 629      U32 jmpIp = codeStream.emit(0);
 630      ip = right->compile(codeStream, ip, subType);
 631      codeStream.patch(jmpIp, ip);
 632   }
 633   else
 634   {
 635      ip = right->compile(codeStream, ip, subType);
 636      ip = left->compile(codeStream, ip, subType);
 637      codeStream.emit(operand);
 638   }
 639   if (type != TypeReqUInt)
 640      codeStream.emit(conversionOp(TypeReqUInt, type));
 641   return codeStream.tell();
 642}
 643
 644TypeReq IntBinaryExprNode::getPreferredType()
 645{
 646   return TypeReqUInt;
 647}
 648
 649//------------------------------------------------------------
 650
 651U32 StreqExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 652{
 653   // eval str left
 654   // OP_ADVANCE_STR_NUL
 655   // eval str right
 656   // OP_COMPARE_STR
 657   // optional conversion
 658
 659   ip = left->compile(codeStream, ip, TypeReqString);
 660   codeStream.emit(OP_ADVANCE_STR_NUL);
 661   ip = right->compile(codeStream, ip, TypeReqString);
 662   codeStream.emit(OP_COMPARE_STR);
 663   if (!eq)
 664      codeStream.emit(OP_NOT);
 665   if (type != TypeReqUInt)
 666      codeStream.emit(conversionOp(TypeReqUInt, type));
 667   return codeStream.tell();
 668}
 669
 670TypeReq StreqExprNode::getPreferredType()
 671{
 672   return TypeReqUInt;
 673}
 674
 675//------------------------------------------------------------
 676
 677U32 StrcatExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 678{
 679   ip = left->compile(codeStream, ip, TypeReqString);
 680   if (!appendChar)
 681      codeStream.emit(OP_ADVANCE_STR);
 682   else
 683   {
 684      codeStream.emit(OP_ADVANCE_STR_APPENDCHAR);
 685      codeStream.emit(appendChar);
 686   }
 687   ip = right->compile(codeStream, ip, TypeReqString);
 688   codeStream.emit(OP_REWIND_STR);
 689   if (type == TypeReqUInt)
 690      codeStream.emit(OP_STR_TO_UINT);
 691   else if (type == TypeReqFloat)
 692      codeStream.emit(OP_STR_TO_FLT);
 693   return codeStream.tell();
 694}
 695
 696TypeReq StrcatExprNode::getPreferredType()
 697{
 698   return TypeReqString;
 699}
 700
 701//------------------------------------------------------------
 702
 703U32 CommaCatExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 704{
 705   ip = left->compile(codeStream, ip, TypeReqString);
 706   codeStream.emit(OP_ADVANCE_STR_COMMA);
 707   ip = right->compile(codeStream, ip, TypeReqString);
 708   codeStream.emit(OP_REWIND_STR);
 709
 710   // At this point the stack has the concatenated string.
 711
 712   // But we're paranoid, so accept (but whine) if we get an oddity...
 713   if (type == TypeReqUInt || type == TypeReqFloat)
 714      Con::warnf(ConsoleLogEntry::General, "%s (%d): converting comma string to a number... probably wrong.", dbgFileName, dbgLineNumber);
 715   if (type == TypeReqUInt)
 716      codeStream.emit(OP_STR_TO_UINT);
 717   else if (type == TypeReqFloat)
 718      codeStream.emit(OP_STR_TO_FLT);
 719   return codeStream.tell();
 720}
 721
 722TypeReq CommaCatExprNode::getPreferredType()
 723{
 724   return TypeReqString;
 725}
 726
 727//------------------------------------------------------------
 728
 729U32 IntUnaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 730{
 731   integer = true;
 732   TypeReq prefType = expr->getPreferredType();
 733   if (op == '!' && (prefType == TypeReqFloat || prefType == TypeReqString))
 734      integer = false;
 735
 736   ip = expr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat);
 737   if (op == '!')
 738      codeStream.emit(integer ? OP_NOT : OP_NOTF);
 739   else if (op == '~')
 740      codeStream.emit(OP_ONESCOMPLEMENT);
 741   if (type != TypeReqUInt)
 742      codeStream.emit(conversionOp(TypeReqUInt, type));
 743   return codeStream.tell();
 744}
 745
 746TypeReq IntUnaryExprNode::getPreferredType()
 747{
 748   return TypeReqUInt;
 749}
 750
 751//------------------------------------------------------------
 752
 753U32 FloatUnaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 754{
 755   ip = expr->compile(codeStream, ip, TypeReqFloat);
 756   codeStream.emit(OP_NEG);
 757   if (type != TypeReqFloat)
 758      codeStream.emit(conversionOp(TypeReqFloat, type));
 759   return codeStream.tell();
 760}
 761
 762TypeReq FloatUnaryExprNode::getPreferredType()
 763{
 764   return TypeReqFloat;
 765}
 766
 767//------------------------------------------------------------
 768
 769U32 VarNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 770{
 771   // if this has an arrayIndex and we are not short circuiting from a constant.
 772   //    if we are a var node
 773   //    OP_SETCURVAR_ARRAY_VARLOOKUP
 774   //    varName
 775   //    varNodeVarName
 776
 777   //    else
 778   //    OP_LOADIMMED_IDENT
 779   //    varName
 780   //    OP_ADVANCE_STR
 781   //    evaluate arrayIndex TypeReqString
 782   //    OP_REWIND_STR
 783   //    OP_SETCURVAR_ARRAY
 784   //    OP_LOADVAR (type)
 785
 786   // else
 787   // OP_SETCURVAR
 788   // varName
 789   // OP_LOADVAR (type)
 790
 791   if (type == TypeReqNone)
 792      return codeStream.tell();
 793
 794   bool shortCircuit = false;
 795   if (arrayIndex)
 796   {
 797      // If we have a constant, shortcircuit the array logic.
 798
 799      IntNode *intNode = dynamic_cast<IntNode*>(arrayIndex);
 800      StrConstNode *strNode = dynamic_cast<StrConstNode*>(arrayIndex);
 801      if (intNode)
 802      {
 803         varName = StringTable->insert(avar("%s%d", varName, intNode->value));
 804         shortCircuit = true;
 805      }
 806      else if (strNode)
 807      {
 808         varName = StringTable->insert(avar("%s%s", varName, strNode->str));
 809         shortCircuit = true;
 810      }
 811   }
 812
 813   precompileIdent(varName);
 814
 815   if (arrayIndex && !shortCircuit)
 816   {
 817      // Ok, lets try to optimize %var[%someothervar] as this is
 818      // a common case for array usage.
 819      StringTableEntry varNodeVarName;
 820      if (isSimpleVarLookup(arrayIndex, varNodeVarName))
 821      {
 822         codeStream.emit(OP_SETCURVAR_ARRAY_VARLOOKUP);
 823         codeStream.emitSTE(varName);
 824         codeStream.emitSTE(varNodeVarName);
 825      }
 826      else
 827      {
 828         codeStream.emit(OP_LOADIMMED_IDENT);
 829         codeStream.emitSTE(varName);
 830         codeStream.emit(OP_ADVANCE_STR);
 831         ip = arrayIndex->compile(codeStream, ip, TypeReqString);
 832         codeStream.emit(OP_REWIND_STR);
 833         codeStream.emit(OP_SETCURVAR_ARRAY);
 834      }
 835   }
 836   else
 837   {
 838      codeStream.emit(OP_SETCURVAR);
 839      codeStream.emitSTE(varName);
 840   }
 841
 842   switch (type)
 843   {
 844      case TypeReqUInt:
 845         codeStream.emit(OP_LOADVAR_UINT);
 846         break;
 847      case TypeReqFloat:
 848         codeStream.emit(OP_LOADVAR_FLT);
 849         break;
 850      case TypeReqString:
 851         codeStream.emit(OP_LOADVAR_STR);
 852         break;
 853      case TypeReqVar:
 854         codeStream.emit(OP_LOADVAR_VAR);
 855         break;
 856      case TypeReqNone:
 857         break;
 858      default:
 859         break;
 860   }
 861   return codeStream.tell();
 862}
 863
 864TypeReq VarNode::getPreferredType()
 865{
 866   return TypeReqNone; // no preferred type
 867}
 868
 869//------------------------------------------------------------
 870
 871U32 IntNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 872{
 873   if (type == TypeReqString)
 874      index = getCurrentStringTable()->addIntString(value);
 875   else if (type == TypeReqFloat)
 876      index = getCurrentFloatTable()->add(value);
 877
 878   switch (type)
 879   {
 880      case TypeReqUInt:
 881         codeStream.emit(OP_LOADIMMED_UINT);
 882         codeStream.emit(value);
 883         break;
 884      case TypeReqString:
 885         codeStream.emit(OP_LOADIMMED_STR);
 886         codeStream.emit(index);
 887         break;
 888      case TypeReqFloat:
 889         codeStream.emit(OP_LOADIMMED_FLT);
 890         codeStream.emit(index);
 891         break;
 892      case TypeReqNone:
 893         break;
 894   }
 895   return codeStream.tell();
 896}
 897
 898TypeReq IntNode::getPreferredType()
 899{
 900   return TypeReqUInt;
 901}
 902
 903//------------------------------------------------------------
 904
 905U32 FloatNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 906{
 907   if (type == TypeReqString)
 908      index = getCurrentStringTable()->addFloatString(value);
 909   else if (type == TypeReqFloat)
 910      index = getCurrentFloatTable()->add(value);
 911
 912   switch (type)
 913   {
 914      case TypeReqUInt:
 915         codeStream.emit(OP_LOADIMMED_UINT);
 916         codeStream.emit(U32(value));
 917         break;
 918      case TypeReqString:
 919         codeStream.emit(OP_LOADIMMED_STR);
 920         codeStream.emit(index);
 921         break;
 922      case TypeReqFloat:
 923         codeStream.emit(OP_LOADIMMED_FLT);
 924         codeStream.emit(index);
 925         break;
 926      case TypeReqNone:
 927         break;
 928   }
 929   return codeStream.tell();
 930}
 931
 932TypeReq FloatNode::getPreferredType()
 933{
 934   return TypeReqFloat;
 935}
 936
 937//------------------------------------------------------------
 938
 939U32 StrConstNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 940{
 941   // Early out for documentation block.
 942   if (doc)
 943   {
 944      index = getCurrentStringTable()->add(str, true, tag);
 945   }
 946   else if (type == TypeReqString)
 947   {
 948      index = getCurrentStringTable()->add(str, true, tag);
 949   }
 950   else if (type != TypeReqNone)
 951   {
 952      fVal = consoleStringToNumber(str, dbgFileName, dbgLineNumber);
 953      if (type == TypeReqFloat)
 954      {
 955         index = getCurrentFloatTable()->add(fVal);
 956      }
 957   }
 958
 959   // If this is a DOCBLOCK, then process w/ appropriate op...
 960   if (doc)
 961   {
 962      codeStream.emit(OP_DOCBLOCK_STR);
 963      codeStream.emit(index);
 964      return ip;
 965   }
 966
 967   // Otherwise, deal with it normally as a string literal case.
 968   switch (type)
 969   {
 970      case TypeReqString:
 971         codeStream.emit(tag ? OP_TAG_TO_STR : OP_LOADIMMED_STR);
 972         codeStream.emit(index);
 973         break;
 974      case TypeReqUInt:
 975         codeStream.emit(OP_LOADIMMED_UINT);
 976         codeStream.emit(U32(fVal));
 977         break;
 978      case TypeReqFloat:
 979         codeStream.emit(OP_LOADIMMED_FLT);
 980         codeStream.emit(index);
 981         break;
 982      case TypeReqNone:
 983         break;
 984   }
 985   return codeStream.tell();
 986}
 987
 988TypeReq StrConstNode::getPreferredType()
 989{
 990   return TypeReqString;
 991}
 992
 993//------------------------------------------------------------
 994
 995U32 ConstantNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
 996{
 997   if (type == TypeReqString)
 998   {
 999      precompileIdent(value);
1000   }
1001   else if (type != TypeReqNone)
1002   {
1003      fVal = consoleStringToNumber(value, dbgFileName, dbgLineNumber);
1004      if (type == TypeReqFloat)
1005         index = getCurrentFloatTable()->add(fVal);
1006   }
1007
1008   switch (type)
1009   {
1010      case TypeReqString:
1011         codeStream.emit(OP_LOADIMMED_IDENT);
1012         codeStream.emitSTE(value);
1013         break;
1014      case TypeReqUInt:
1015         codeStream.emit(OP_LOADIMMED_UINT);
1016         codeStream.emit(U32(fVal));
1017         break;
1018      case TypeReqFloat:
1019         codeStream.emit(OP_LOADIMMED_FLT);
1020         codeStream.emit(index);
1021         break;
1022      case TypeReqNone:
1023         break;
1024   }
1025   return ip;
1026}
1027
1028TypeReq ConstantNode::getPreferredType()
1029{
1030   return TypeReqString;
1031}
1032
1033//------------------------------------------------------------
1034
1035U32 AssignExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1036{
1037   subType = expr->getPreferredType();
1038   if (subType == TypeReqNone)
1039      subType = type;
1040   if (subType == TypeReqNone)
1041   {
1042      // What we need to do in this case is turn it into a VarNode reference. 
1043      // Unfortunately other nodes such as field access (SlotAccessNode) 
1044      // cannot be optimized in the same manner as all fields are exposed 
1045      // and set as strings.
1046      if (dynamic_cast<VarNode*>(expr) != NULL)
1047      {
1048         subType = TypeReqVar;
1049      }
1050      else
1051      {
1052         subType = TypeReqString;
1053      }
1054   }
1055
1056   //if we are an array index and we are gonna short circuit
1057   // eval expr
1058   // compute new varName
1059   // OP_SETCURVAR_CREATE
1060   // varName
1061   // OP_SAVEVAR
1062
1063   //else if it's an array expr and we don't short circuit, the formula is:
1064   // eval expr
1065   // (push and pop if it's TypeReqString) OP_ADVANCE_STR
1066   // if array lookup is varnode
1067   //    OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP
1068   //    varName
1069   //    varNodeVarName
1070   // else
1071   //    OP_LOADIMMED_IDENT
1072   //    varName
1073   //    OP_ADVANCE_STR
1074   //    eval array
1075   //    OP_REWIND_STR
1076   //    OP_SETCURVAR_ARRAY_CREATE
1077   // endif
1078   // OP_TERMINATE_REWIND_STR
1079   // OP_SAVEVAR
1080
1081   //else
1082   // eval expr
1083   // OP_SETCURVAR_CREATE
1084   // varname
1085   // OP_SAVEVAR
1086
1087   ip = expr->compile(codeStream, ip, subType);
1088
1089   bool shortCircuit = false;
1090   if (arrayIndex)
1091   {
1092      // If we have a constant, shortcircuit the array logic.
1093
1094      IntNode *intNode = dynamic_cast<IntNode*>(arrayIndex);
1095      StrConstNode *strNode = dynamic_cast<StrConstNode*>(arrayIndex);
1096      if (intNode)
1097      {
1098         varName = StringTable->insert(avar("%s%d", varName, intNode->value));
1099         shortCircuit = true;
1100      }
1101      else if (strNode)
1102      {
1103         varName = StringTable->insert(avar("%s%s", varName, strNode->str));
1104         shortCircuit = true;
1105      }
1106   }
1107
1108   precompileIdent(varName);
1109
1110   if (arrayIndex && !shortCircuit)
1111   {
1112      if (subType == TypeReqString)
1113         codeStream.emit(OP_ADVANCE_STR);
1114
1115      // Ok, lets try to optimize %var[%someothervar] as this is
1116      // a common case for array usage.
1117      StringTableEntry varNodeVarName;
1118      if (isSimpleVarLookup(arrayIndex, varNodeVarName))
1119      {
1120         codeStream.emit(OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP);
1121         codeStream.emitSTE(varName);
1122         codeStream.emitSTE(varNodeVarName);
1123      }
1124      else
1125      {
1126         codeStream.emit(OP_LOADIMMED_IDENT);
1127         codeStream.emitSTE(varName);
1128
1129         codeStream.emit(OP_ADVANCE_STR);
1130         ip = arrayIndex->compile(codeStream, ip, TypeReqString);
1131         codeStream.emit(OP_REWIND_STR);
1132         codeStream.emit(OP_SETCURVAR_ARRAY_CREATE);
1133      }
1134
1135      if (subType == TypeReqString)
1136         codeStream.emit(OP_TERMINATE_REWIND_STR);
1137   }
1138   else
1139   {
1140      codeStream.emit(OP_SETCURVAR_CREATE);
1141      codeStream.emitSTE(varName);
1142   }
1143   switch (subType)
1144   {
1145      case TypeReqString:
1146         codeStream.emit(OP_SAVEVAR_STR);
1147         break;
1148      case TypeReqUInt:
1149         codeStream.emit(OP_SAVEVAR_UINT);
1150         break;
1151      case TypeReqFloat:
1152         codeStream.emit(OP_SAVEVAR_FLT);
1153         break;
1154      case TypeReqVar:
1155         codeStream.emit(OP_SAVEVAR_VAR);
1156         break;
1157      case TypeReqNone:
1158         break;
1159   }
1160   if (type != subType)
1161      codeStream.emit(conversionOp(subType, type));
1162   return ip;
1163}
1164
1165TypeReq AssignExprNode::getPreferredType()
1166{
1167   return expr->getPreferredType();
1168}
1169
1170//------------------------------------------------------------
1171
1172static void getAssignOpTypeOp(S32 op, TypeReq &type, U32 &operand)
1173{
1174   switch (op)
1175   {
1176      case '+':
1177      case opPLUSPLUS:
1178         type = TypeReqFloat;
1179         operand = OP_ADD;
1180         break;
1181      case '-':
1182      case opMINUSMINUS:
1183         type = TypeReqFloat;
1184         operand = OP_SUB;
1185         break;
1186      case '*':
1187         type = TypeReqFloat;
1188         operand = OP_MUL;
1189         break;
1190      case '/':
1191         type = TypeReqFloat;
1192         operand = OP_DIV;
1193         break;
1194      case '%':
1195         type = TypeReqUInt;
1196         operand = OP_MOD;
1197         break;
1198      case '&':
1199         type = TypeReqUInt;
1200         operand = OP_BITAND;
1201         break;
1202      case '^':
1203         type = TypeReqUInt;
1204         operand = OP_XOR;
1205         break;
1206      case '|':
1207         type = TypeReqUInt;
1208         operand = OP_BITOR;
1209         break;
1210      case opSHL:
1211         type = TypeReqUInt;
1212         operand = OP_SHL;
1213         break;
1214      case opSHR:
1215         type = TypeReqUInt;
1216         operand = OP_SHR;
1217         break;
1218   }
1219}
1220
1221U32 AssignOpExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1222{
1223
1224   // goes like this...
1225   //
1226   // IF no array index && (op == OPPLUSPLUS or op == OPMINUSMINUS)
1227   //    if op == OPPLUSPLUS
1228   //       OP_INC
1229   //       varName
1230   //    else if op == OPMINUSMINUS
1231   //       OP_DEC
1232   //       varName
1233   //    else
1234   //       OP_INVALID
1235   //    endif
1236   // ELSE
1237   //    eval expr as float or int
1238   //    if there's an arrayIndex and we don't short circuit
1239   //       if arrayIndex is a var node
1240   //          OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP
1241   //          varName
1242   //          varNodeVarName
1243   //       else
1244   //          OP_LOADIMMED_IDENT
1245   //          varName
1246   //          OP_ADVANCE_STR
1247   //          eval arrayIndex stringwise
1248   //          OP_REWIND_STR
1249   //          OP_SETCURVAR_ARRAY_CREATE
1250   //       endif
1251   //    else
1252   //       OP_SETCURVAR_CREATE
1253   //       varName
1254   //    endif
1255   //    OP_LOADVAR_FLT or UINT
1256   //    operand
1257   //    OP_SAVEVAR_FLT or UINT
1258   // ENDIF
1259   //
1260   // if subtype != type
1261   //    convert type
1262   // endif
1263
1264   // conversion OP if necessary.
1265   getAssignOpTypeOp(op, subType, operand);
1266
1267   // ++ or -- optimization support for non indexed variables.
1268   if ((!arrayIndex) && (op == opPLUSPLUS || op == opMINUSMINUS))
1269   {
1270      precompileIdent(varName);
1271
1272      if (op == opPLUSPLUS)
1273      {
1274         codeStream.emit(OP_INC);
1275         codeStream.emitSTE(varName);
1276      }
1277      else if (op == opMINUSMINUS)
1278      {
1279         codeStream.emit(OP_DEC);
1280         codeStream.emitSTE(varName);
1281      }
1282      else
1283      {
1284         // This should NEVER happen. This is just for sanity.
1285         AssertISV(false, "Tried to use ++ or -- but something weird happened.");
1286         codeStream.emit(OP_INVALID);
1287      }
1288   }
1289   else
1290   {
1291      ip = expr->compile(codeStream, ip, subType);
1292
1293      bool shortCircuit = false;
1294      if (arrayIndex)
1295      {
1296         // If we have a constant, shortcircuit the array logic.
1297
1298         IntNode *intNode = dynamic_cast<IntNode*>(arrayIndex);
1299         StrConstNode *strNode = dynamic_cast<StrConstNode*>(arrayIndex);
1300         if (intNode)
1301         {
1302            varName = StringTable->insert(avar("%s%d", varName, intNode->value));
1303            shortCircuit = true;
1304         }
1305         else if (strNode)
1306         {
1307            varName = StringTable->insert(avar("%s%s", varName, strNode->str));
1308            shortCircuit = true;
1309         }
1310      }
1311
1312      precompileIdent(varName);
1313
1314      if (!arrayIndex || shortCircuit)
1315      {
1316         codeStream.emit(OP_SETCURVAR_CREATE);
1317         codeStream.emitSTE(varName);
1318      }
1319      else
1320      {
1321         // Ok, lets try to optimize %var[%someothervar] as this is
1322         // a common case for array usage.
1323         StringTableEntry varNodeVarName;
1324         if (isSimpleVarLookup(arrayIndex, varNodeVarName))
1325         {
1326            codeStream.emit(OP_SETCURVAR_ARRAY_CREATE_VARLOOKUP);
1327            codeStream.emitSTE(varName);
1328            codeStream.emitSTE(varNodeVarName);
1329         }
1330         else
1331         {
1332            codeStream.emit(OP_LOADIMMED_IDENT);
1333            codeStream.emitSTE(varName);
1334
1335            codeStream.emit(OP_ADVANCE_STR);
1336            ip = arrayIndex->compile(codeStream, ip, TypeReqString);
1337            codeStream.emit(OP_REWIND_STR);
1338            codeStream.emit(OP_SETCURVAR_ARRAY_CREATE);
1339         }
1340      }
1341      codeStream.emit((subType == TypeReqFloat) ? OP_LOADVAR_FLT : OP_LOADVAR_UINT);
1342      codeStream.emit(operand);
1343      codeStream.emit((subType == TypeReqFloat) ? OP_SAVEVAR_FLT : OP_SAVEVAR_UINT);
1344   }
1345   if (subType != type)
1346      codeStream.emit(conversionOp(subType, type));
1347   return codeStream.tell();
1348}
1349
1350TypeReq AssignOpExprNode::getPreferredType()
1351{
1352   getAssignOpTypeOp(op, subType, operand);
1353   return subType;
1354}
1355
1356//------------------------------------------------------------
1357
1358U32 TTagSetStmtNode::compileStmt(CodeStream&, U32 ip)
1359{
1360   return ip;
1361}
1362
1363//------------------------------------------------------------
1364
1365U32 TTagDerefNode::compile(CodeStream&, U32 ip, TypeReq)
1366{
1367   return ip;
1368}
1369
1370TypeReq TTagDerefNode::getPreferredType()
1371{
1372   return TypeReqNone;
1373}
1374
1375//------------------------------------------------------------
1376
1377U32 TTagExprNode::compile(CodeStream&, U32 ip, TypeReq)
1378{
1379   return ip;
1380}
1381
1382TypeReq TTagExprNode::getPreferredType()
1383{
1384   return TypeReqNone;
1385}
1386
1387//------------------------------------------------------------
1388
1389U32 FuncCallExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1390{
1391   // OP_PUSH_FRAME
1392   // arg OP_PUSH arg OP_PUSH arg OP_PUSH
1393   // eval all the args, then call the function.
1394
1395   // OP_CALLFUNC
1396   // function
1397   // namespace
1398   // isDot
1399
1400   precompileIdent(funcName);
1401   precompileIdent(nameSpace);
1402
1403   codeStream.emit(OP_PUSH_FRAME);
1404
1405   bool isThisCall = false;
1406
1407   ExprNode *walk = args;
1408
1409   // Try to optimize the this pointer call if it is a variable
1410   // that we are loading.
1411   if (callType == MethodCall)
1412   {
1413      // We cannot optimize array indices because it can have quite
1414      // a bit of code to figure out the array index.
1415      VarNode *var = dynamic_cast<VarNode*>(args);
1416      if (var && !var->arrayIndex)
1417      {
1418         precompileIdent(var->varName);
1419
1420         // Are we a %this call?
1421         isThisCall = (var->varName == StringTable->insert("%this"));
1422
1423         codeStream.emit(OP_PUSH_THIS);
1424         codeStream.emitSTE(var->varName);
1425
1426         // inc args since we took care of first arg.
1427         walk = (ExprNode*)walk ->getNext();
1428      }
1429   }
1430
1431   for (; walk; walk = (ExprNode *)walk->getNext())
1432   {
1433      TypeReq walkType = walk->getPreferredType();
1434      if (walkType == TypeReqNone) walkType = TypeReqString;
1435      ip = walk->compile(codeStream, ip, walkType);
1436      switch (walk->getPreferredType())
1437      {
1438         case TypeReqFloat:
1439            codeStream.emit(OP_PUSH_FLT);
1440            break;
1441         case TypeReqUInt:
1442            codeStream.emit(OP_PUSH_UINT);
1443            break;
1444         default:
1445            codeStream.emit(OP_PUSH);
1446            break;
1447      }
1448   }
1449
1450   if (isThisCall)
1451   {
1452      codeStream.emit(OP_CALLFUNC_THIS);
1453      codeStream.emitSTE(funcName);
1454   }
1455   else
1456   {
1457      if (callType == MethodCall || callType == ParentCall)
1458         codeStream.emit(OP_CALLFUNC);
1459      else
1460         codeStream.emit(OP_CALLFUNC_RESOLVE);
1461
1462      codeStream.emitSTE(funcName);
1463      codeStream.emitSTE(nameSpace);
1464      codeStream.emit(callType);
1465   }
1466
1467   if (type != TypeReqString)
1468      codeStream.emit(conversionOp(TypeReqString, type));
1469   return codeStream.tell();
1470}
1471
1472TypeReq FuncCallExprNode::getPreferredType()
1473{
1474   return TypeReqString;
1475}
1476
1477//------------------------------------------------------------
1478
1479U32 FuncPointerCallExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1480{
1481   // OP_PUSH_FRAME
1482   // arg OP_PUSH arg OP_PUSH arg OP_PUSH
1483   // eval all the args, then call the function.
1484
1485   // eval fn pointer
1486   // OP_CALLFUNC_POINTER
1487
1488   codeStream.emit(OP_PUSH_FRAME);
1489   for (ExprNode *walk = args; walk; walk = (ExprNode *)walk->getNext())
1490   {
1491      TypeReq walkType = walk->getPreferredType();
1492      if (walkType == TypeReqNone) walkType = TypeReqString;
1493      ip = walk->compile(codeStream, ip, walkType);
1494      switch (walk->getPreferredType())
1495      {
1496         case TypeReqFloat:
1497            codeStream.emit(OP_PUSH_FLT);
1498            break;
1499         case TypeReqUInt:
1500            codeStream.emit(OP_PUSH_UINT);
1501            break;
1502         default:
1503            codeStream.emit(OP_PUSH);
1504            break;
1505      }
1506   }
1507
1508   ip = funcPointer->compile(codeStream, ip, TypeReqString);
1509   codeStream.emit(OP_CALLFUNC_POINTER);
1510
1511   if (type != TypeReqString)
1512      codeStream.emit(conversionOp(TypeReqString, type));
1513   return codeStream.tell();
1514}
1515
1516TypeReq FuncPointerCallExprNode::getPreferredType()
1517{
1518   return TypeReqString;
1519}
1520
1521//------------------------------------------------------------
1522
1523U32 AssertCallExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1524{
1525#ifdef TORQUE_ENABLE_SCRIPTASSERTS
1526
1527   messageIndex = getCurrentStringTable()->add(message, true, false);
1528
1529   ip = testExpr->compile(codeStream, ip, TypeReqUInt);
1530   codeStream.emit(OP_ASSERT);
1531   codeStream.emit(messageIndex);
1532
1533#endif
1534
1535   return codeStream.tell();
1536}
1537
1538TypeReq AssertCallExprNode::getPreferredType()
1539{
1540   return TypeReqNone;
1541}
1542
1543//------------------------------------------------------------
1544
1545U32 SlotAccessNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1546{
1547   if (type == TypeReqNone)
1548      return ip;
1549
1550   precompileIdent(slotName);
1551
1552   // check if object is %this. If we are, we can do additional optimizations.
1553   if (isThisVar(objectExpr))
1554   {
1555      optimizeThisPointer(codeStream, arrayExpr, ip, slotName);
1556   }
1557   else
1558   {
1559      if (arrayExpr)
1560      {
1561         // eval array
1562         // OP_ADVANCE_STR
1563         // evaluate object expression sub (OP_SETCURFIELD)
1564         // OP_TERMINATE_REWIND_STR
1565         // OP_SETCURFIELDARRAY
1566         // total add of 4 + array precomp
1567
1568         ip = arrayExpr->compile(codeStream, ip, TypeReqString);
1569         codeStream.emit(OP_ADVANCE_STR);
1570      }
1571      ip = objectExpr->compile(codeStream, ip, TypeReqString);
1572      codeStream.emit(OP_SETCUROBJECT);
1573
1574      codeStream.emit(OP_SETCURFIELD);
1575
1576      codeStream.emitSTE(slotName);
1577
1578      if (arrayExpr)
1579      {
1580         codeStream.emit(OP_TERMINATE_REWIND_STR);
1581         codeStream.emit(OP_SETCURFIELD_ARRAY);
1582      }
1583   }
1584
1585   switch (type)
1586   {
1587      case TypeReqUInt:
1588         codeStream.emit(OP_LOADFIELD_UINT);
1589         break;
1590      case TypeReqFloat:
1591         codeStream.emit(OP_LOADFIELD_FLT);
1592         break;
1593      case TypeReqString:
1594         codeStream.emit(OP_LOADFIELD_STR);
1595         break;
1596      case TypeReqNone:
1597         break;
1598   }
1599   return codeStream.tell();
1600}
1601
1602TypeReq SlotAccessNode::getPreferredType()
1603{
1604   return TypeReqNone;
1605}
1606
1607//-----------------------------------------------------------------------------
1608
1609U32 InternalSlotAccessNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1610{
1611   if (type == TypeReqNone)
1612      return ip;
1613
1614   ip = objectExpr->compile(codeStream, ip, TypeReqString);
1615   codeStream.emit(OP_SETCUROBJECT);
1616
1617   ip = slotExpr->compile(codeStream, ip, TypeReqString);
1618   codeStream.emit(OP_SETCUROBJECT_INTERNAL);
1619   codeStream.emit(recurse);
1620
1621   if (type != TypeReqUInt)
1622      codeStream.emit(conversionOp(TypeReqUInt, type));
1623   return codeStream.tell();
1624}
1625
1626TypeReq InternalSlotAccessNode::getPreferredType()
1627{
1628   return TypeReqUInt;
1629}
1630
1631//-----------------------------------------------------------------------------
1632
1633U32 SlotAssignNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1634{
1635   // first eval the expression TypeReqString
1636
1637   // if it's an array:
1638
1639   // if OP_ADVANCE_STR 1
1640   // eval array
1641
1642   // OP_ADVANCE_STR 1
1643   // evaluate object expr
1644   // OP_SETCUROBJECT 1
1645   // OP_SETCURFIELD 1
1646   // fieldName 1
1647   // OP_TERMINATE_REWIND_STR 1
1648
1649   // OP_SETCURFIELDARRAY 1
1650   // OP_TERMINATE_REWIND_STR 1
1651
1652   // else
1653   // OP_ADVANCE_STR
1654   // evaluate object expr
1655   // OP_SETCUROBJECT
1656   // OP_SETCURFIELD
1657   // fieldName
1658   // OP_TERMINATE_REWIND_STR
1659
1660   // OP_SAVEFIELD
1661   // convert to return type if necessary.
1662
1663   precompileIdent(slotName);
1664
1665   ip = valueExpr->compile(codeStream, ip, TypeReqString);
1666
1667   if (isThisVar(objectExpr))
1668   {
1669      optimizeThisPointer(codeStream, arrayExpr, ip, slotName);
1670   }
1671   else
1672   {
1673      codeStream.emit(OP_ADVANCE_STR);
1674      if (arrayExpr)
1675      {
1676         ip = arrayExpr->compile(codeStream, ip, TypeReqString);
1677         codeStream.emit(OP_ADVANCE_STR);
1678      }
1679      if (objectExpr)
1680      {
1681         ip = objectExpr->compile(codeStream, ip, TypeReqString);
1682         codeStream.emit(OP_SETCUROBJECT);
1683      }
1684      else
1685         codeStream.emit(OP_SETCUROBJECT_NEW);
1686      codeStream.emit(OP_SETCURFIELD);
1687      codeStream.emitSTE(slotName);
1688
1689      if (arrayExpr)
1690      {
1691         codeStream.emit(OP_TERMINATE_REWIND_STR);
1692         codeStream.emit(OP_SETCURFIELD_ARRAY);
1693      }
1694
1695      codeStream.emit(OP_TERMINATE_REWIND_STR);
1696   }
1697
1698   codeStream.emit(OP_SAVEFIELD_STR);
1699
1700   if (typeID != -1)
1701   {
1702      codeStream.emit(OP_SETCURFIELD_TYPE);
1703      codeStream.emit(typeID);
1704   }
1705
1706   if (type != TypeReqString)
1707      codeStream.emit(conversionOp(TypeReqString, type));
1708   return codeStream.tell();
1709}
1710
1711TypeReq SlotAssignNode::getPreferredType()
1712{
1713   return TypeReqString;
1714}
1715
1716//------------------------------------------------------------
1717
1718U32 SlotAssignOpNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1719{
1720   // first eval the expression as its type
1721
1722   // if it's an array:
1723   // eval array
1724   // OP_ADVANCE_STR
1725   // evaluate object expr
1726   // OP_SETCUROBJECT
1727   // OP_SETCURFIELD
1728   // fieldName
1729   // OP_TERMINATE_REWIND_STR
1730   // OP_SETCURFIELDARRAY
1731
1732   // else
1733   // evaluate object expr
1734   // OP_SETCUROBJECT
1735   // OP_SETCURFIELD
1736   // fieldName
1737
1738   // OP_LOADFIELD of appropriate type
1739   // operand
1740   // OP_SAVEFIELD of appropriate type
1741   // convert to return type if necessary.
1742
1743   getAssignOpTypeOp(op, subType, operand);
1744   precompileIdent(slotName);
1745
1746   ip = valueExpr->compile(codeStream, ip, subType);
1747
1748   if (isThisVar(objectExpr))
1749   {
1750      optimizeThisPointer(codeStream, arrayExpr, ip, slotName);
1751   }
1752   else
1753   {
1754      if (arrayExpr)
1755      {
1756         ip = arrayExpr->compile(codeStream, ip, TypeReqString);
1757         codeStream.emit(OP_ADVANCE_STR);
1758      }
1759      ip = objectExpr->compile(codeStream, ip, TypeReqString);
1760      codeStream.emit(OP_SETCUROBJECT);
1761      codeStream.emit(OP_SETCURFIELD);
1762      codeStream.emitSTE(slotName);
1763
1764      if (arrayExpr)
1765      {
1766         codeStream.emit(OP_TERMINATE_REWIND_STR);
1767         codeStream.emit(OP_SETCURFIELD_ARRAY);
1768      }
1769   }
1770   codeStream.emit((subType == TypeReqFloat) ? OP_LOADFIELD_FLT : OP_LOADFIELD_UINT);
1771   codeStream.emit(operand);
1772   codeStream.emit((subType == TypeReqFloat) ? OP_SAVEFIELD_FLT : OP_SAVEFIELD_UINT);
1773   if (subType != type)
1774      codeStream.emit(conversionOp(subType, type));
1775   return codeStream.tell();
1776}
1777
1778TypeReq SlotAssignOpNode::getPreferredType()
1779{
1780   getAssignOpTypeOp(op, subType, operand);
1781   return subType;
1782}
1783
1784//------------------------------------------------------------
1785
1786U32 ObjectDeclNode::compileSubObject(CodeStream &codeStream, U32 ip, bool root)
1787{
1788   // goes
1789
1790   // OP_PUSHFRAME 1
1791   // name expr
1792   // OP_PUSH 1
1793   // args... PUSH
1794   // OP_CREATE_OBJECT 1
1795   // parentObject 1
1796   // isDatablock 1
1797   // internalName 1
1798   // isSingleton 1
1799   // lineNumber 1
1800   // fail point 1
1801
1802   // for each field, eval
1803   // OP_ADD_OBJECT (to UINT[0]) 1
1804   // root? 1
1805
1806   // add all the sub objects.
1807   // OP_END_OBJECT 1
1808   // root? 1
1809   // To fix the stack issue [7/9/2007 Black]
1810   // OP_FINISH_OBJECT <-- fail point jumps to this opcode
1811
1812   codeStream.emit(OP_PUSH_FRAME);
1813
1814   ip = classNameExpr->compile(codeStream, ip, TypeReqString);
1815   codeStream.emit(OP_PUSH);
1816
1817   ip = objectNameExpr->compile(codeStream, ip, TypeReqString);
1818   codeStream.emit(OP_PUSH);
1819   for (ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *)exprWalk->getNext())
1820   {
1821      TypeReq walkType = exprWalk->getPreferredType();
1822      if (walkType == TypeReqNone) walkType = TypeReqString;
1823      ip = exprWalk->compile(codeStream, ip, walkType);
1824      switch (exprWalk->getPreferredType())
1825      {
1826         case TypeReqFloat:
1827            codeStream.emit(OP_PUSH_FLT);
1828            break;
1829         case TypeReqUInt:
1830            codeStream.emit(OP_PUSH_UINT);
1831            break;
1832         default:
1833            codeStream.emit(OP_PUSH);
1834            break;
1835      }
1836   }
1837   codeStream.emit(OP_CREATE_OBJECT);
1838   codeStream.emitSTE(parentObject);
1839
1840   codeStream.emit(isDatablock);
1841   codeStream.emit(isClassNameInternal);
1842   codeStream.emit(isSingleton);
1843   codeStream.emit(dbgLineNumber);
1844   const U32 failIp = codeStream.emit(0);
1845   for (SlotAssignNode *slotWalk = slotDecls; slotWalk; slotWalk = (SlotAssignNode *)slotWalk->getNext())
1846      ip = slotWalk->compile(codeStream, ip, TypeReqNone);
1847   codeStream.emit(OP_ADD_OBJECT);
1848   codeStream.emit(root);
1849   for (ObjectDeclNode *objectWalk = subObjects; objectWalk; objectWalk = (ObjectDeclNode *)objectWalk->getNext())
1850      ip = objectWalk->compileSubObject(codeStream, ip, false);
1851   codeStream.emit(OP_END_OBJECT);
1852   codeStream.emit(root || isDatablock);
1853   // Added to fix the object creation issue [7/9/2007 Black]
1854   failOffset = codeStream.emit(OP_FINISH_OBJECT);
1855
1856   codeStream.patch(failIp, failOffset);
1857
1858   return codeStream.tell();
1859}
1860
1861U32 ObjectDeclNode::compile(CodeStream &codeStream, U32 ip, TypeReq type)
1862{
1863   // root object decl does:
1864
1865   // push 0 onto the UINT stack OP_LOADIMMED_UINT
1866   // precompiles the subObject(true)
1867   // UINT stack now has object id
1868   // type conv to type
1869
1870   codeStream.emit(OP_LOADIMMED_UINT);
1871   codeStream.emit(0);
1872   ip = compileSubObject(codeStream, ip, true);
1873   if (type != TypeReqUInt)
1874      codeStream.emit(conversionOp(TypeReqUInt, type));
1875   return codeStream.tell();
1876}
1877
1878TypeReq ObjectDeclNode::getPreferredType()
1879{
1880   return TypeReqUInt;
1881}
1882
1883//------------------------------------------------------------
1884
1885U32 FunctionDeclStmtNode::compileStmt(CodeStream &codeStream, U32 ip)
1886{
1887   // OP_FUNC_DECL
1888   // func name
1889   // namespace
1890   // package
1891   // hasBody?
1892   // func end ip
1893   // argc
1894   // ident array[argc]
1895   // code
1896   // OP_RETURN_VOID
1897   setCurrentStringTable(&getFunctionStringTable());
1898   setCurrentFloatTable(&getFunctionFloatTable());
1899
1900   argc = 0;
1901   for (VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext())
1902   {
1903      precompileIdent(walk->varName);
1904      argc++;
1905   }
1906
1907   CodeBlock::smInFunction = true;
1908
1909   precompileIdent(fnName);
1910   precompileIdent(nameSpace);
1911   precompileIdent(package);
1912
1913   CodeBlock::smInFunction = false;
1914
1915   codeStream.emit(OP_FUNC_DECL);
1916   codeStream.emitSTE(fnName);
1917   codeStream.emitSTE(nameSpace);
1918   codeStream.emitSTE(package);
1919
1920   codeStream.emit(U32(bool(stmts != NULL) ? 1 : 0) + U32(dbgLineNumber << 1));
1921   const U32 endIp = codeStream.emit(0);
1922   codeStream.emit(argc);
1923   for (VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext())
1924   {
1925      codeStream.emitSTE(walk->varName);
1926   }
1927   CodeBlock::smInFunction = true;
1928   ip = compileBlock(stmts, codeStream, ip);
1929
1930   // Add break so breakpoint can be set at closing brace or
1931   // in empty function.
1932   addBreakLine(codeStream);
1933
1934   CodeBlock::smInFunction = false;
1935   codeStream.emit(OP_RETURN_VOID);
1936
1937   codeStream.patch(endIp, codeStream.tell());
1938
1939   setCurrentStringTable(&getGlobalStringTable());
1940   setCurrentFloatTable(&getGlobalFloatTable());
1941
1942   return ip;
1943}
1944