astNodes.cpp
Engine/source/console/astNodes.cpp
Classes:
class
Namespaces:
namespace
Public Functions
conversionOp(TypeReq src, TypeReq dst)
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