compiledEval.cpp
Engine/source/console/compiledEval.cpp
Classes:
class
Frame data for a foreach/foreach$ loop.
class
Information for an object iterator loop.
class
Information for a string iterator loop.
Namespaces:
namespace
This namespace contains the core of the console functionality.
Public Enumerations
enum
EvalConstants { MaxStackSize = 1024 MethodOnComponent = -2 }
Public Variables
floatStack [MaxStackSize]
iterStack [MaxStackSize]
Public Functions
consoleStringToNumber(const char * str, StringTableEntry file, U32 line)
getFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField, char val)
setFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField)
Detailed Description
Public Enumerations
EvalConstants
Enumerator
- MaxStackSize = 1024
- MethodOnComponent = -2
Public Variables
U32 _FLT
Stack pointer for floatStack.
U32 _ITER
Stack pointer for iterStack.
U32 _UINT
Stack pointer for intStack.
ConsoleValueStack CSTK
F64 floatStack [MaxStackSize]
S64 intStack [MaxStackSize]
IterStackRecord iterStack [MaxStackSize]
StringStack STR
Public Functions
consoleStringToNumber(const char * str, StringTableEntry file, U32 line)
getFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField, char val)
setFieldComponent(SimObject * object, StringTableEntry field, const char * array, StringTableEntry subField)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "console/console.h" 26 27#include "console/ast.h" 28#include "core/tAlgorithm.h" 29 30#include "core/strings/findMatch.h" 31#include "core/strings/stringUnit.h" 32#include "console/consoleInternal.h" 33#include "core/stream/fileStream.h" 34#include "console/compiler.h" 35 36#include "console/simBase.h" 37#include "console/telnetDebugger.h" 38#include "sim/netStringTable.h" 39#include "console/ICallMethod.h" 40#include "console/stringStack.h" 41#include "util/messaging/message.h" 42#include "core/frameAllocator.h" 43 44#ifndef TORQUE_TGB_ONLY 45#include "materials/materialDefinition.h" 46#include "materials/materialManager.h" 47#endif 48 49// Uncomment to optimize function calls at the expense of potential invalid package lookups 50//#define COMPILER_OPTIMIZE_FUNCTION_CALLS 51 52using namespace Compiler; 53 54enum EvalConstants { 55 MaxStackSize = 1024, 56 MethodOnComponent = -2 57}; 58 59namespace Con 60{ 61// Current script file name and root, these are registered as 62// console variables. 63extern StringTableEntry gCurrentFile; 64extern StringTableEntry gCurrentRoot; 65extern S32 gObjectCopyFailures; 66} 67 68/// Frame data for a foreach/foreach$ loop. 69struct IterStackRecord 70{ 71 /// If true, this is a foreach$ loop; if not, it's a foreach loop. 72 bool mIsStringIter; 73 74 /// The iterator variable. 75 Dictionary::Entry* mVariable; 76 77 /// Information for an object iterator loop. 78 struct ObjectPos 79 { 80 /// The set being iterated over. 81 SimSet* mSet; 82 83 /// Current index in the set. 84 U32 mIndex; 85 }; 86 87 /// Information for a string iterator loop. 88 struct StringPos 89 { 90 /// The raw string data on the string stack. 91 StringStackPtr mString; 92 93 /// Current parsing position. 94 U32 mIndex; 95 }; 96 97 union 98 { 99 ObjectPos mObj; 100 StringPos mStr; 101 } mData; 102}; 103 104IterStackRecord iterStack[ MaxStackSize ]; 105 106F64 floatStack[MaxStackSize]; 107S64 intStack[MaxStackSize]; 108 109 110 111 112StringStack STR; 113ConsoleValueStack CSTK; 114 115U32 _FLT = 0; ///< Stack pointer for floatStack. 116U32 _UINT = 0; ///< Stack pointer for intStack. 117U32 _ITER = 0; ///< Stack pointer for iterStack. 118 119namespace Con 120{ 121 const char *getNamespaceList(Namespace *ns) 122 { 123 U32 size = 1; 124 Namespace * walk; 125 for(walk = ns; walk; walk = walk->mParent) 126 size += dStrlen(walk->mName) + 4; 127 char *ret = Con::getReturnBuffer(size); 128 ret[0] = 0; 129 for(walk = ns; walk; walk = walk->mParent) 130 { 131 dStrcat(ret, walk->mName); 132 if(walk->mParent) 133 dStrcat(ret, " -> "); 134 } 135 return ret; 136 } 137} 138 139//------------------------------------------------------------ 140 141F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line) 142{ 143 F64 val = dAtof(str); 144 if(val != 0) 145 return val; 146 else if(!dStricmp(str, "true")) 147 return 1; 148 else if(!dStricmp(str, "false")) 149 return 0; 150 else if(file) 151 { 152 Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line); 153 return 0; 154 } 155 return 0; 156} 157 158//------------------------------------------------------------ 159 160namespace Con 161{ 162 163 char *getReturnBuffer(U32 bufferSize) 164 165 { 166 return STR.getReturnBuffer(bufferSize); 167 } 168 169 char *getReturnBuffer( const char *stringToCopy ) 170 { 171 U32 len = dStrlen( stringToCopy ) + 1; 172 char *ret = STR.getReturnBuffer( len); 173 dMemcpy( ret, stringToCopy, len ); 174 return ret; 175 } 176 177 char* getReturnBuffer( const String& str ) 178 { 179 const U32 size = str.size(); 180 char* ret = STR.getReturnBuffer( size ); 181 dMemcpy( ret, str.c_str(), size ); 182 return ret; 183 } 184 185 char* getReturnBuffer( const StringBuilder& str ) 186 { 187 char* buffer = Con::getReturnBuffer( str.length() + 1 ); 188 str.copy( buffer ); 189 buffer[ str.length() ] = '\0'; 190 191 return buffer; 192 } 193 194 char *getArgBuffer(U32 bufferSize) 195 { 196 return STR.getArgBuffer(bufferSize); 197 } 198 199 char *getFloatArg(F64 arg) 200 { 201 char *ret = STR.getArgBuffer(32); 202 dSprintf(ret, 32, "%g", arg); 203 return ret; 204 } 205 206 char *getIntArg(S32 arg) 207 { 208 char *ret = STR.getArgBuffer(32); 209 dSprintf(ret, 32, "%d", arg); 210 return ret; 211 } 212 213 char* getBoolArg(bool arg) 214 { 215 char *ret = STR.getArgBuffer(32); 216 dSprintf(ret, 32, "%d", arg); 217 return ret; 218 } 219 220 char *getStringArg( const char *arg ) 221 { 222 U32 len = dStrlen( arg ) + 1; 223 char *ret = STR.getArgBuffer( len ); 224 dMemcpy( ret, arg, len ); 225 return ret; 226 } 227 228 char* getStringArg( const String& arg ) 229 { 230 const U32 size = arg.size(); 231 char* ret = STR.getArgBuffer( size ); 232 dMemcpy( ret, arg.c_str(), size ); 233 return ret; 234 } 235} 236 237//------------------------------------------------------------ 238 239inline void ExprEvalState::setCurVarName(StringTableEntry name) 240{ 241 if(name[0] == '$') 242 currentVariable = globalVars.lookup(name); 243 else if( getStackDepth() > 0 ) 244 currentVariable = getCurrentFrame().lookup(name); 245 if(!currentVariable && gWarnUndefinedScriptVariables) 246 Con::warnf(ConsoleLogEntry::Script, "Variable referenced before assignment: %s", name); 247} 248 249inline void ExprEvalState::setCurVarNameCreate(StringTableEntry name) 250{ 251 if(name[0] == '$') 252 currentVariable = globalVars.add(name); 253 else if( getStackDepth() > 0 ) 254 currentVariable = getCurrentFrame().add(name); 255 else 256 { 257 currentVariable = NULL; 258 Con::warnf(ConsoleLogEntry::Script, "Accessing local variable in global scope... failed: %s", name); 259 } 260} 261 262//------------------------------------------------------------ 263 264inline S32 ExprEvalState::getIntVariable() 265{ 266 return currentVariable ? currentVariable->getIntValue() : 0; 267} 268 269inline F64 ExprEvalState::getFloatVariable() 270{ 271 return currentVariable ? currentVariable->getFloatValue() : 0; 272} 273 274inline const char *ExprEvalState::getStringVariable() 275{ 276 return currentVariable ? currentVariable->getStringValue() : ""; 277} 278 279//------------------------------------------------------------ 280 281inline void ExprEvalState::setIntVariable(S32 val) 282{ 283 AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); 284 currentVariable->setIntValue(val); 285} 286 287inline void ExprEvalState::setFloatVariable(F64 val) 288{ 289 AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); 290 currentVariable->setFloatValue(val); 291} 292 293inline void ExprEvalState::setStringVariable(const char *val) 294{ 295 AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); 296 currentVariable->setStringValue(val); 297} 298 299inline void ExprEvalState::setStringStackPtrVariable(StringStackPtr str) 300{ 301 AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); 302 currentVariable->setStringStackPtrValue(str); 303} 304 305inline void ExprEvalState::setCopyVariable() 306{ 307 if (copyVariable) 308 { 309 switch (copyVariable->value.type) 310 { 311 case ConsoleValue::TypeInternalInt: 312 currentVariable->setIntValue(copyVariable->getIntValue()); 313 break; 314 case ConsoleValue::TypeInternalFloat: 315 currentVariable->setFloatValue(copyVariable->getFloatValue()); 316 break; 317 default: 318 currentVariable->setStringValue(copyVariable->getStringValue()); 319 break; 320 } 321 } 322} 323 324//------------------------------------------------------------ 325 326// Gets a component of an object's field value or a variable and returns it 327// in val. 328static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ) 329{ 330 const char* prevVal = NULL; 331 332 // Grab value from object. 333 if( object && field ) 334 prevVal = object->getDataField( field, array ); 335 336 // Otherwise, grab from the string stack. The value coming in will always 337 // be a string because that is how multicomponent variables are handled. 338 else 339 prevVal = STR.getStringValue(); 340 341 // Make sure we got a value. 342 if ( prevVal && *prevVal ) 343 { 344 static const StringTableEntry xyzw[] = 345 { 346 StringTable->insert( "x" ), 347 StringTable->insert( "y" ), 348 StringTable->insert( "z" ), 349 StringTable->insert( "w" ) 350 }; 351 352 static const StringTableEntry rgba[] = 353 { 354 StringTable->insert( "r" ), 355 StringTable->insert( "g" ), 356 StringTable->insert( "b" ), 357 StringTable->insert( "a" ) 358 }; 359 360 // Translate xyzw and rgba into the indexed component 361 // of the variable or field. 362 if ( subField == xyzw[0] || subField == rgba[0] ) 363 dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") ); 364 365 else if ( subField == xyzw[1] || subField == rgba[1] ) 366 dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") ); 367 368 else if ( subField == xyzw[2] || subField == rgba[2] ) 369 dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") ); 370 371 else if ( subField == xyzw[3] || subField == rgba[3] ) 372 dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") ); 373 374 else 375 val[0] = 0; 376 } 377 else 378 val[0] = 0; 379} 380 381// Sets a component of an object's field value based on the sub field. 'x' will 382// set the first field, 'y' the second, and 'z' the third. 383static void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ) 384{ 385 // Copy the current string value 386 char strValue[1024]; 387 dStrncpy( strValue, STR.getStringValue(), 1024 ); 388 389 char val[1024] = ""; 390 const char* prevVal = NULL; 391 392 // Set the value on an object field. 393 if( object && field ) 394 prevVal = object->getDataField( field, array ); 395 396 // Set the value on a variable. 397 else if( gEvalState.currentVariable ) 398 prevVal = gEvalState.getStringVariable(); 399 400 // Ensure that the variable has a value 401 if (!prevVal) 402 return; 403 404 static const StringTableEntry xyzw[] = 405 { 406 StringTable->insert( "x" ), 407 StringTable->insert( "y" ), 408 StringTable->insert( "z" ), 409 StringTable->insert( "w" ) 410 }; 411 412 static const StringTableEntry rgba[] = 413 { 414 StringTable->insert( "r" ), 415 StringTable->insert( "g" ), 416 StringTable->insert( "b" ), 417 StringTable->insert( "a" ) 418 }; 419 420 // Insert the value into the specified 421 // component of the string. 422 if ( subField == xyzw[0] || subField == rgba[0] ) 423 dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") ); 424 425 else if ( subField == xyzw[1] || subField == rgba[1] ) 426 dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") ); 427 428 else if ( subField == xyzw[2] || subField == rgba[2] ) 429 dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") ); 430 431 else if ( subField == xyzw[3] || subField == rgba[3] ) 432 dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") ); 433 434 if ( val[0] != 0 ) 435 { 436 // Update the field or variable. 437 if( object && field ) 438 object->setDataField( field, 0, val ); 439 else if( gEvalState.currentVariable ) 440 gEvalState.setStringVariable( val ); 441 } 442} 443 444ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName, S32 setFrame) 445{ 446 447#ifdef TORQUE_VALIDATE_STACK 448 U32 stackStart = STR.mStartStackSize; 449 U32 consoleStackStart = CSTK.mStackPos; 450#endif 451 452 //Con::printf("CodeBlock::exec(%s,%u)", functionName ? functionName : "??", ip); 453 454 static char traceBuffer[1024]; 455 S32 i; 456 457 U32 iterDepth = 0; 458 459 incRefCount(); 460 F64 *curFloatTable; 461 char *curStringTable; 462 S32 curStringTableLen = 0; //clint to ensure we dont overwrite it 463 STR.clearFunctionOffset(); // ensures arg buffer offset is back to 0 464 StringTableEntry thisFunctionName = NULL; 465 bool popFrame = false; 466 if(argv) 467 { 468 // assume this points into a function decl: 469 U32 fnArgc = code[ip + 2 + 6]; 470 thisFunctionName = CodeToSTE(code, ip); 471 S32 wantedArgc = getMin(argc-1, fnArgc); // argv[0] is func name 472 if(gEvalState.traceOn) 473 { 474 traceBuffer[0] = 0; 475 dStrcat(traceBuffer, "Entering "); 476 if(packageName) 477 { 478 dStrcat(traceBuffer, "["); 479 dStrcat(traceBuffer, packageName); 480 dStrcat(traceBuffer, "]"); 481 } 482 if(thisNamespace && thisNamespace->mName) 483 { 484 dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), 485 "%s::%s(", thisNamespace->mName, thisFunctionName); 486 } 487 else 488 { 489 dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), 490 "%s(", thisFunctionName); 491 } 492 for(i = 0; i < wantedArgc; i++) 493 { 494 dStrcat(traceBuffer, argv[i+1]); 495 if(i != wantedArgc - 1) 496 dStrcat(traceBuffer, ", "); 497 } 498 dStrcat(traceBuffer, ")"); 499 Con::printf("%s", traceBuffer); 500 } 501 gEvalState.pushFrame(thisFunctionName, thisNamespace); 502 popFrame = true; 503 504 for(i = 0; i < wantedArgc; i++) 505 { 506 StringTableEntry var = CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2)); 507 gEvalState.setCurVarNameCreate(var); 508 509 ConsoleValueRef ref = argv[i+1]; 510 511 switch(argv[i+1].getType()) 512 { 513 case ConsoleValue::TypeInternalInt: 514 gEvalState.setIntVariable(argv[i+1]); 515 break; 516 case ConsoleValue::TypeInternalFloat: 517 gEvalState.setFloatVariable(argv[i+1]); 518 break; 519 case ConsoleValue::TypeInternalStringStackPtr: 520 gEvalState.setStringStackPtrVariable(argv[i+1].getStringStackPtrValue()); 521 break; 522 case ConsoleValue::TypeInternalStackString: 523 case ConsoleValue::TypeInternalString: 524 default: 525 gEvalState.setStringVariable(argv[i+1]); 526 break; 527 } 528 } 529 530 ip = ip + (fnArgc * 2) + (2 + 6 + 1); 531 curFloatTable = functionFloats; 532 curStringTable = functionStrings; 533 curStringTableLen = functionStringsMaxLen; 534 } 535 else 536 { 537 curFloatTable = globalFloats; 538 curStringTable = globalStrings; 539 curStringTableLen = globalStringsMaxLen; 540 541 // If requested stack frame isn't available, request a new one 542 // (this prevents assert failures when creating local 543 // variables without a stack frame) 544 if (gEvalState.getStackDepth() <= setFrame) 545 setFrame = -1; 546 547 // Do we want this code to execute using a new stack frame? 548 if (setFrame < 0) 549 { 550 gEvalState.pushFrame(NULL, NULL); 551 popFrame = true; 552 } 553 else 554 { 555 // We want to copy a reference to an existing stack frame 556 // on to the top of the stack. Any change that occurs to 557 // the locals during this new frame will also occur in the 558 // original frame. 559 S32 stackIndex = gEvalState.getStackDepth() - setFrame - 1; 560 gEvalState.pushFrameRef( stackIndex ); 561 popFrame = true; 562 } 563 } 564 565 // Grab the state of the telenet debugger here once 566 // so that the push and pop frames are always balanced. 567 const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected(); 568 if ( telDebuggerOn && setFrame < 0 ) 569 TelDebugger->pushStackFrame(); 570 571 StringTableEntry var, objParent; 572 StringTableEntry fnName; 573 StringTableEntry fnNamespace, fnPackage; 574 575 // Add local object creation stack [7/9/2007 Black] 576 static const U32 objectCreationStackSize = 32; 577 U32 objectCreationStackIndex = 0; 578 struct { 579 SimObject *newObject; 580 U32 failJump; 581 } objectCreationStack[ objectCreationStackSize ]; 582 583 SimObject *currentNewObject = 0; 584 U32 failJump = 0; 585 StringTableEntry prevField = NULL; 586 StringTableEntry curField = NULL; 587 SimObject *prevObject = NULL; 588 SimObject *curObject = NULL; 589 SimObject *saveObject=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>; 590 Namespace::Entry *nsEntry; 591 Namespace *ns; 592 const char* curFNDocBlock = NULL; 593 const char* curNSDocBlock = NULL; 594 const S32 nsDocLength = 128; 595 char nsDocBlockClass[nsDocLength]; 596 597 U32 callArgc; 598 ConsoleValueRef *callArgv; 599 600 static char curFieldArray[256]; 601 static char prevFieldArray[256]; 602 603 CodeBlock *saveCodeBlock = smCurrentCodeBlock; 604 smCurrentCodeBlock = this; 605 if(this->name) 606 { 607 Con::gCurrentFile = this->name; 608 Con::gCurrentRoot = this->modPath; 609 } 610 const char * val; 611 StringStackPtr retValue; 612 613 // note: anything returned is pushed to CSTK and will be invalidated on the next exec() 614 ConsoleValueRef returnValue; 615 616 // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and 617 // OP_LOADFIELD_*) to store temporary values for the fields. 618 static S32 VAL_BUFFER_SIZE = 1024; 619 FrameTemp<char> valBuffer( VAL_BUFFER_SIZE ); 620 621 for(;;) 622 { 623 U32 instruction = code[ip++]; 624 nsEntry = NULL; 625breakContinue: 626 switch(instruction) 627 { 628 case OP_FUNC_DECL: 629 if(!noCalls) 630 { 631 fnName = CodeToSTE(code, ip); 632 fnNamespace = CodeToSTE(code, ip+2); 633 fnPackage = CodeToSTE(code, ip+4); 634 bool hasBody = ( code[ ip + 6 ] & 0x01 ) != 0; 635 U32 lineNumber = code[ ip + 6 ] >> 1; 636 637 Namespace::unlinkPackages(); 638 ns = Namespace::find(fnNamespace, fnPackage); 639 ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL, lineNumber );// if no body, set the IP to 0 640 if( curNSDocBlock ) 641 { 642 if( fnNamespace == StringTable->lookup( nsDocBlockClass ) ) 643 { 644 char *usageStr = dStrdup( curNSDocBlock ); 645 usageStr[dStrlen(usageStr)] = '\0'; 646 ns->mUsage = usageStr; 647 ns->mCleanUpUsage = true; 648 curNSDocBlock = NULL; 649 } 650 } 651 Namespace::relinkPackages(); 652 653 // If we had a docblock, it's definitely not valid anymore, so clear it out. 654 curFNDocBlock = NULL; 655 656 //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip); 657 } 658 ip = code[ip + 7]; 659 break; 660 661 case OP_CREATE_OBJECT: 662 { 663 // Read some useful info. 664 objParent = CodeToSTE(code, ip); 665 bool isDataBlock = code[ip + 2]; 666 bool isInternal = code[ip + 3]; 667 bool isSingleton = code[ip + 4]; 668 U32 lineNumber = code[ip + 5]; 669 failJump = code[ip + 6]; 670 671 // If we don't allow calls, we certainly don't allow creating objects! 672 // Moved this to after failJump is set. Engine was crashing when 673 // noCalls = true and an object was being created at the beginning of 674 // a file. ADL. 675 if(noCalls) 676 { 677 ip = failJump; 678 break; 679 } 680 681 // Push the old info to the stack 682 //Assert( objectCreationStackIndex < objectCreationStackSize ); 683 objectCreationStack[ objectCreationStackIndex ].newObject = currentNewObject; 684 objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump; 685 686 // Get the constructor information off the stack. 687 CSTK.getArgcArgv(NULL, &callArgc, &callArgv); 688 const char *objectName = callArgv[ 2 ]; 689 690 // Con::printf("Creating object..."); 691 692 // objectName = argv[1]... 693 currentNewObject = NULL; 694 695 // Are we creating a datablock? If so, deal with case where we override 696 // an old one. 697 if(isDataBlock) 698 { 699 // Con::printf(" - is a datablock"); 700 701 // Find the old one if any. 702 SimObject *db = Sim::getDataBlockGroup()->findObject( objectName ); 703 704 // Make sure we're not changing types on ourselves... 705 if(db && dStricmp(db->getClassName(), callArgv[1])) 706 { 707 Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName); 708 ip = failJump; 709 STR.popFrame(); 710 CSTK.popFrame(); 711 break; 712 } 713 714 // If there was one, set the currentNewObject and move on. 715 if(db) 716 currentNewObject = db; 717 } 718 else if (!isInternal) 719 { 720 // IF we aren't looking at a local/internal object, then check if 721 // this object already exists in the global space 722 723 AbstractClassRep* rep = AbstractClassRep::findClassRep( objectName ); 724 if (rep != NULL) { 725 Con::errorf(ConsoleLogEntry::General, "%s: Cannot name object [%s] the same name as a script class.", 726 getFileLine(ip), objectName); 727 ip = failJump; 728 STR.popFrame(); 729 break; 730 } 731 732 SimObject *obj = Sim::findObject( (const char*)objectName ); 733 if (obj /*&& !obj->isLocalName()*/) 734 { 735 if ( isSingleton ) 736 { 737 // Make sure we're not trying to change types 738 if ( dStricmp( obj->getClassName(), (const char*)callArgv[1] ) != 0 ) 739 { 740 Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].", 741 getFileLine(ip), objectName, (const char*)callArgv[1], obj->getClassName()); 742 ip = failJump; 743 STR.popFrame(); 744 CSTK.popFrame(); 745 break; 746 } 747 748 // We're creating a singleton, so use the found object 749 // instead of creating a new object. 750 currentNewObject = obj; 751 } 752 else 753 { 754 const char* redefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); 755 756 if( dStricmp( redefineBehavior, "replaceExisting" ) == 0 ) 757 { 758 // Save our constructor args as the argv vector is stored on the 759 // string stack and may get stomped if deleteObject triggers 760 // script execution. 761 762 ConsoleValueRef savedArgv[ StringStack::MaxArgs ]; 763 for (int i=0; i<callArgc; i++) { 764 savedArgv[i] = callArgv[i]; 765 } 766 //dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); 767 768 // Prevent stack value corruption 769 CSTK.pushFrame(); 770 STR.pushFrame(); 771 // -- 772 773 obj->deleteObject(); 774 obj = NULL; 775 776 // Prevent stack value corruption 777 CSTK.popFrame(); 778 STR.popFrame(); 779 // -- 780 781 //dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); 782 for (int i=0; i<callArgc; i++) { 783 callArgv[i] = savedArgv[i]; 784 } 785 } 786 else if( dStricmp( redefineBehavior, "renameNew" ) == 0 ) 787 { 788 for( U32 i = 1;; ++ i ) 789 { 790 String newName = String::ToString( "%s%i", objectName, i ); 791 if( !Sim::findObject( newName ) ) 792 { 793 objectName = StringTable->insert( newName ); 794 break; 795 } 796 } 797 } 798 else if( dStricmp( redefineBehavior, "unnameNew" ) == 0 ) 799 { 800 objectName = StringTable->insert( "" ); 801 } 802 else if( dStricmp( redefineBehavior, "postfixNew" ) == 0 ) 803 { 804 const char* postfix = Con::getVariable( "$Con::redefineBehaviorPostfix" ); 805 String newName = String::ToString( "%s%s", objectName, postfix ); 806 807 if( Sim::findObject( newName ) ) 808 { 809 Con::errorf( ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].", 810 getFileLine(ip), newName.c_str() ); 811 ip = failJump; 812 STR.popFrame(); 813 CSTK.popFrame(); 814 break; 815 } 816 else 817 objectName = StringTable->insert( newName ); 818 } 819 else 820 { 821 Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].", 822 getFileLine(ip), objectName); 823 ip = failJump; 824 STR.popFrame(); 825 CSTK.popFrame(); 826 break; 827 } 828 } 829 } 830 } 831 832 STR.popFrame(); 833 CSTK.popFrame(); 834 835 if(!currentNewObject) 836 { 837 // Well, looks like we have to create a new object. 838 ConsoleObject *object = ConsoleObject::create((const char*)callArgv[1]); 839 840 // Deal with failure! 841 if(!object) 842 { 843 Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine(ip), (const char*)callArgv[1]); 844 ip = failJump; 845 break; 846 } 847 848 // Do special datablock init if appropros 849 if(isDataBlock) 850 { 851 SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(object); 852 if(dataBlock) 853 { 854 dataBlock->assignId(); 855 } 856 else 857 { 858 // They tried to make a non-datablock with a datablock keyword! 859 Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine(ip), (const char*)callArgv[1]); 860 // Clean up... 861 delete object; 862 ip = failJump; 863 break; 864 } 865 } 866 867 // Finally, set currentNewObject to point to the new one. 868 currentNewObject = dynamic_cast<SimObject *>(object); 869 870 // Deal with the case of a non-SimObject. 871 if(!currentNewObject) 872 { 873 Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", getFileLine(ip), (const char*)callArgv[1]); 874 delete object; 875 currentNewObject = NULL; 876 ip = failJump; 877 break; 878 } 879 880 // Set the declaration line 881 currentNewObject->setDeclarationLine(lineNumber); 882 883 // Set the file that this object was created in 884 currentNewObject->setFilename(name); 885 886 // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance) 887 if(*objParent) 888 { 889 // Find it! 890 SimObject *parent; 891 if(Sim::findObject(objParent, parent)) 892 { 893 // Con::printf(" - Parent object found: %s", parent->getClassName()); 894 895 currentNewObject->setCopySource( parent ); 896 currentNewObject->assignFieldsFrom( parent ); 897 } 898 else 899 { 900 if ( Con::gObjectCopyFailures == -1 ) 901 Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip), objParent, (const char*)callArgv[1]); 902 else 903 ++Con::gObjectCopyFailures; 904 905 // Fail to create the object. 906 delete object; 907 currentNewObject = NULL; 908 ip = failJump; 909 break; 910 } 911 } 912 913 // If a name was passed, assign it. 914 if( objectName[ 0 ] ) 915 { 916 if( !isInternal ) 917 currentNewObject->assignName( objectName ); 918 else 919 currentNewObject->setInternalName( objectName ); 920 921 // Set the original name 922 currentNewObject->setOriginalName( objectName ); 923 } 924 925 // Prevent stack value corruption 926 CSTK.pushFrame(); 927 STR.pushFrame(); 928 // -- 929 930 // Do the constructor parameters. 931 if(!currentNewObject->processArguments(callArgc-3, callArgv+3)) 932 { 933 delete currentNewObject; 934 currentNewObject = NULL; 935 ip = failJump; 936 937 // Prevent stack value corruption 938 CSTK.popFrame(); 939 STR.popFrame(); 940 // -- 941 break; 942 } 943 944 // Prevent stack value corruption 945 CSTK.popFrame(); 946 STR.popFrame(); 947 // -- 948 949 // If it's not a datablock, allow people to modify bits of it. 950 if(!isDataBlock) 951 { 952 currentNewObject->setModStaticFields(true); 953 currentNewObject->setModDynamicFields(true); 954 } 955 } 956 957 // Advance the IP past the create info... 958 ip += 7; 959 break; 960 } 961 962 case OP_ADD_OBJECT: 963 { 964 // See OP_SETCURVAR for why we do this. 965 curFNDocBlock = NULL; 966 curNSDocBlock = NULL; 967 968 // Do we place this object at the root? 969 bool placeAtRoot = code[ip++]; 970 971 // Con::printf("Adding object %s", currentNewObject->getName()); 972 973 // Prevent stack value corruption 974 CSTK.pushFrame(); 975 STR.pushFrame(); 976 // -- 977 978 // Make sure it wasn't already added, then add it. 979 if(currentNewObject->isProperlyAdded() == false) 980 { 981 bool ret = false; 982 983 Message *msg = dynamic_cast<Message *>(currentNewObject); 984 if(msg) 985 { 986 SimObjectId id = Message::getNextMessageID(); 987 if(id != 0xffffffff) 988 ret = currentNewObject->registerObject(id); 989 else 990 Con::errorf("%s: No more object IDs available for messages", getFileLine(ip)); 991 } 992 else 993 ret = currentNewObject->registerObject(); 994 995 if(! ret) 996 { 997 // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields(). 998 Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName()); 999 delete currentNewObject; 1000 currentNewObject = NULL; 1001 ip = failJump; 1002 // Prevent stack value corruption 1003 CSTK.popFrame(); 1004 STR.popFrame(); 1005 // -- 1006 break; 1007 } 1008 } 1009 1010 // Are we dealing with a datablock? 1011 SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(currentNewObject); 1012 static String errorStr; 1013 1014 1015 1016 // If so, preload it. 1017 if(dataBlock && !dataBlock->preload(true, errorStr)) 1018 { 1019 Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip), 1020 currentNewObject->getName(), errorStr.c_str()); 1021 dataBlock->deleteObject(); 1022 currentNewObject = NULL; 1023 ip = failJump; 1024 1025 // Prevent stack value corruption 1026 CSTK.popFrame(); 1027 STR.popFrame(); 1028 // -- 1029 break; 1030 } 1031 1032 // What group will we be added to, if any? 1033 U32 groupAddId = intStack[_UINT]; 1034 SimGroup *grp = NULL; 1035 SimSet *set = NULL; 1036 bool isMessage = dynamic_cast<Message *>(currentNewObject) != NULL; 1037 1038 if(!placeAtRoot || !currentNewObject->getGroup()) 1039 { 1040 if(! isMessage) 1041 { 1042 if(! placeAtRoot) 1043 { 1044 // Otherwise just add to the requested group or set. 1045 if(!Sim::findObject(groupAddId, grp)) 1046 Sim::findObject(groupAddId, set); 1047 } 1048 1049 if(placeAtRoot) 1050 { 1051 // Deal with the instantGroup if we're being put at the root or we're adding to a component. 1052 if( Con::gInstantGroup.isEmpty() 1053 || !Sim::findObject( Con::gInstantGroup, grp ) ) 1054 grp = Sim::getRootGroup(); 1055 } 1056 } 1057 1058 // If we didn't get a group, then make sure we have a pointer to 1059 // the rootgroup. 1060 if(!grp) 1061 grp = Sim::getRootGroup(); 1062 1063 // add to the parent group 1064 grp->addObject(currentNewObject); 1065 1066 // If for some reason the add failed, add the object to the 1067 // root group so it won't leak. 1068 if( !currentNewObject->getGroup() ) 1069 Sim::getRootGroup()->addObject( currentNewObject ); 1070 1071 // add to any set we might be in 1072 if(set) 1073 set->addObject(currentNewObject); 1074 } 1075 1076 // store the new object's ID on the stack (overwriting the group/set 1077 // id, if one was given, otherwise getting pushed) 1078 if(placeAtRoot) 1079 intStack[_UINT] = currentNewObject->getId(); 1080 else 1081 intStack[++_UINT] = currentNewObject->getId(); 1082 1083 // Prevent stack value corruption 1084 CSTK.popFrame(); 1085 STR.popFrame(); 1086 // -- 1087 break; 1088 } 1089 1090 case OP_END_OBJECT: 1091 { 1092 // If we're not to be placed at the root, make sure we clean up 1093 // our group reference. 1094 bool placeAtRoot = code[ip++]; 1095 if(!placeAtRoot) 1096 _UINT--; 1097 break; 1098 } 1099 1100 case OP_FINISH_OBJECT: 1101 { 1102 if (currentNewObject) 1103 currentNewObject->onPostAdd(); 1104 1105 //Assert( objectCreationStackIndex >= 0 ); 1106 // Restore the object info from the stack [7/9/2007 Black] 1107 currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject; 1108 failJump = objectCreationStack[ objectCreationStackIndex ].failJump; 1109 break; 1110 } 1111 1112 case OP_JMPIFFNOT: 1113 if(floatStack[_FLT--]) 1114 { 1115 ip++; 1116 break; 1117 } 1118 ip = code[ip]; 1119 break; 1120 case OP_JMPIFNOT: 1121 if(intStack[_UINT--]) 1122 { 1123 ip++; 1124 break; 1125 } 1126 ip = code[ip]; 1127 break; 1128 case OP_JMPIFF: 1129 if(!floatStack[_FLT--]) 1130 { 1131 ip++; 1132 break; 1133 } 1134 ip = code[ip]; 1135 break; 1136 case OP_JMPIF: 1137 if(!intStack[_UINT--]) 1138 { 1139 ip ++; 1140 break; 1141 } 1142 ip = code[ip]; 1143 break; 1144 case OP_JMPIFNOT_NP: 1145 if(intStack[_UINT]) 1146 { 1147 _UINT--; 1148 ip++; 1149 break; 1150 } 1151 ip = code[ip]; 1152 break; 1153 case OP_JMPIF_NP: 1154 if(!intStack[_UINT]) 1155 { 1156 _UINT--; 1157 ip++; 1158 break; 1159 } 1160 ip = code[ip]; 1161 break; 1162 case OP_JMP: 1163 ip = code[ip]; 1164 break; 1165 1166 // This fixes a bug when not explicitly returning a value. 1167 case OP_RETURN_VOID: 1168 STR.setStringValue(""); 1169 // We're falling thru here on purpose. 1170 1171 case OP_RETURN: 1172 retValue = STR.getStringValuePtr(); 1173 1174 if( iterDepth > 0 ) 1175 { 1176 // Clear iterator state. 1177 while( iterDepth > 0 ) 1178 { 1179 iterStack[ -- _ITER ].mIsStringIter = false; 1180 -- iterDepth; 1181 } 1182 1183 STR.rewind(); 1184 STR.setStringValue( StringStackPtrRef(retValue).getPtr(&STR) ); // Not nice but works. 1185 retValue = STR.getStringValuePtr(); 1186 } 1187 1188 // Previously the return value was on the stack and would be returned using STR.getStringValue(). 1189 // Now though we need to wrap it in a ConsoleValueRef 1190 returnValue.value = CSTK.pushStringStackPtr(retValue); 1191 1192 goto execFinished; 1193 1194 case OP_RETURN_FLT: 1195 1196 if( iterDepth > 0 ) 1197 { 1198 // Clear iterator state. 1199 while( iterDepth > 0 ) 1200 { 1201 iterStack[ -- _ITER ].mIsStringIter = false; 1202 -- iterDepth; 1203 } 1204 1205 } 1206 1207 returnValue.value = CSTK.pushFLT(floatStack[_FLT]); 1208 _FLT--; 1209 1210 goto execFinished; 1211 1212 case OP_RETURN_UINT: 1213 1214 if( iterDepth > 0 ) 1215 { 1216 // Clear iterator state. 1217 while( iterDepth > 0 ) 1218 { 1219 iterStack[ -- _ITER ].mIsStringIter = false; 1220 -- iterDepth; 1221 } 1222 } 1223 1224 returnValue.value = CSTK.pushUINT(intStack[_UINT]); 1225 _UINT--; 1226 1227 goto execFinished; 1228 1229 case OP_CMPEQ: 1230 intStack[_UINT+1] = bool(floatStack[_FLT] == floatStack[_FLT-1]); 1231 _UINT++; 1232 _FLT -= 2; 1233 break; 1234 1235 case OP_CMPGR: 1236 intStack[_UINT+1] = bool(floatStack[_FLT] > floatStack[_FLT-1]); 1237 _UINT++; 1238 _FLT -= 2; 1239 break; 1240 1241 case OP_CMPGE: 1242 intStack[_UINT+1] = bool(floatStack[_FLT] >= floatStack[_FLT-1]); 1243 _UINT++; 1244 _FLT -= 2; 1245 break; 1246 1247 case OP_CMPLT: 1248 intStack[_UINT+1] = bool(floatStack[_FLT] < floatStack[_FLT-1]); 1249 _UINT++; 1250 _FLT -= 2; 1251 break; 1252 1253 case OP_CMPLE: 1254 intStack[_UINT+1] = bool(floatStack[_FLT] <= floatStack[_FLT-1]); 1255 _UINT++; 1256 _FLT -= 2; 1257 break; 1258 1259 case OP_CMPNE: 1260 intStack[_UINT+1] = bool(floatStack[_FLT] != floatStack[_FLT-1]); 1261 _UINT++; 1262 _FLT -= 2; 1263 break; 1264 1265 case OP_XOR: 1266 intStack[_UINT-1] = intStack[_UINT] ^ intStack[_UINT-1]; 1267 _UINT--; 1268 break; 1269 1270 case OP_MOD: 1271 if( intStack[_UINT-1] != 0 ) 1272 intStack[_UINT-1] = intStack[_UINT] % intStack[_UINT-1]; 1273 else 1274 intStack[_UINT-1] = 0; 1275 _UINT--; 1276 break; 1277 1278 case OP_BITAND: 1279 intStack[_UINT-1] = intStack[_UINT] & intStack[_UINT-1]; 1280 _UINT--; 1281 break; 1282 1283 case OP_BITOR: 1284 intStack[_UINT-1] = intStack[_UINT] | intStack[_UINT-1]; 1285 _UINT--; 1286 break; 1287 1288 case OP_NOT: 1289 intStack[_UINT] = !intStack[_UINT]; 1290 break; 1291 1292 case OP_NOTF: 1293 intStack[_UINT+1] = !floatStack[_FLT]; 1294 _FLT--; 1295 _UINT++; 1296 break; 1297 1298 case OP_ONESCOMPLEMENT: 1299 intStack[_UINT] = ~<a href="/coding/file/compiledeval_8cpp/#compiledeval_8cpp_1a585e657b7dadf3b57573aac845ef36e4">intStack</a>[_UINT]; 1300 break; 1301 1302 case OP_SHR: 1303 intStack[_UINT-1] = intStack[_UINT] >> intStack[_UINT-1]; 1304 _UINT--; 1305 break; 1306 1307 case OP_SHL: 1308 intStack[_UINT-1] = intStack[_UINT] << intStack[_UINT-1]; 1309 _UINT--; 1310 break; 1311 1312 case OP_AND: 1313 intStack[_UINT-1] = intStack[_UINT] && intStack[_UINT-1]; 1314 _UINT--; 1315 break; 1316 1317 case OP_OR: 1318 intStack[_UINT-1] = intStack[_UINT] || intStack[_UINT-1]; 1319 _UINT--; 1320 break; 1321 1322 case OP_ADD: 1323 floatStack[_FLT-1] = floatStack[_FLT] + floatStack[_FLT-1]; 1324 _FLT--; 1325 break; 1326 1327 case OP_SUB: 1328 floatStack[_FLT-1] = floatStack[_FLT] - floatStack[_FLT-1]; 1329 _FLT--; 1330 break; 1331 1332 case OP_MUL: 1333 floatStack[_FLT-1] = floatStack[_FLT] * floatStack[_FLT-1]; 1334 _FLT--; 1335 break; 1336 case OP_DIV: 1337 floatStack[_FLT-1] = floatStack[_FLT] / floatStack[_FLT-1]; 1338 _FLT--; 1339 break; 1340 case OP_NEG: 1341 floatStack[_FLT] = -floatStack[_FLT]; 1342 break; 1343 1344 case OP_SETCURVAR: 1345 var = CodeToSTE(code, ip); 1346 ip += 2; 1347 1348 // If a variable is set, then these must be NULL. It is necessary 1349 // to set this here so that the vector parser can appropriately 1350 // identify whether it's dealing with a vector. 1351 prevField = NULL; 1352 prevObject = NULL; 1353 curObject = NULL; 1354 1355 gEvalState.setCurVarName(var); 1356 1357 // In order to let docblocks work properly with variables, we have 1358 // clear the current docblock when we do an assign. This way it 1359 // won't inappropriately carry forward to following function decls. 1360 curFNDocBlock = NULL; 1361 curNSDocBlock = NULL; 1362 break; 1363 1364 case OP_SETCURVAR_CREATE: 1365 var = CodeToSTE(code, ip); 1366 ip += 2; 1367 1368 // See OP_SETCURVAR 1369 prevField = NULL; 1370 prevObject = NULL; 1371 curObject = NULL; 1372 1373 gEvalState.setCurVarNameCreate(var); 1374 1375 // See OP_SETCURVAR for why we do this. 1376 curFNDocBlock = NULL; 1377 curNSDocBlock = NULL; 1378 break; 1379 1380 case OP_SETCURVAR_ARRAY: 1381 var = STR.getSTValue(); 1382 1383 // See OP_SETCURVAR 1384 prevField = NULL; 1385 prevObject = NULL; 1386 curObject = NULL; 1387 1388 gEvalState.setCurVarName(var); 1389 1390 // See OP_SETCURVAR for why we do this. 1391 curFNDocBlock = NULL; 1392 curNSDocBlock = NULL; 1393 break; 1394 1395 case OP_SETCURVAR_ARRAY_CREATE: 1396 var = STR.getSTValue(); 1397 1398 // See OP_SETCURVAR 1399 prevField = NULL; 1400 prevObject = NULL; 1401 curObject = NULL; 1402 1403 gEvalState.setCurVarNameCreate(var); 1404 1405 // See OP_SETCURVAR for why we do this. 1406 curFNDocBlock = NULL; 1407 curNSDocBlock = NULL; 1408 break; 1409 1410 case OP_LOADVAR_UINT: 1411 intStack[_UINT+1] = gEvalState.getIntVariable(); 1412 _UINT++; 1413 break; 1414 1415 case OP_LOADVAR_FLT: 1416 floatStack[_FLT+1] = gEvalState.getFloatVariable(); 1417 _FLT++; 1418 break; 1419 1420 case OP_LOADVAR_STR: 1421 val = gEvalState.getStringVariable(); 1422 STR.setStringValue(val); 1423 break; 1424 1425 case OP_LOADVAR_VAR: 1426 // Sets current source of OP_SAVEVAR_VAR 1427 gEvalState.copyVariable = gEvalState.currentVariable; 1428 break; 1429 1430 case OP_SAVEVAR_UINT: 1431 gEvalState.setIntVariable(intStack[_UINT]); 1432 break; 1433 1434 case OP_SAVEVAR_FLT: 1435 gEvalState.setFloatVariable(floatStack[_FLT]); 1436 break; 1437 1438 case OP_SAVEVAR_STR: 1439 gEvalState.setStringVariable(STR.getStringValue()); 1440 break; 1441 1442 case OP_SAVEVAR_VAR: 1443 // this basically handles %var1 = %var2 1444 gEvalState.setCopyVariable(); 1445 break; 1446 1447 case OP_SETCUROBJECT: 1448 // Save the previous object for parsing vector fields. 1449 prevObject = curObject; 1450 val = STR.getStringValue(); 1451 1452 // Sim::findObject will sometimes find valid objects from 1453 // multi-component strings. This makes sure that doesn't 1454 // happen. 1455 for( const char* check = val; *check; check++ ) 1456 { 1457 if( *check == ' ' ) 1458 { 1459 val = ""; 1460 break; 1461 } 1462 } 1463 curObject = Sim::findObject(val); 1464 break; 1465 1466 case OP_SETCUROBJECT_INTERNAL: 1467 ++ip; // To skip the recurse flag if the object wasn't found 1468 if(curObject) 1469 { 1470 SimSet *set = dynamic_cast<SimSet *>(curObject); 1471 if(set) 1472 { 1473 StringTableEntry intName = StringTable->insert(STR.getStringValue()); 1474 bool recurse = code[ip-1]; 1475 SimObject *obj = set->findObjectByInternalName(intName, recurse); 1476 intStack[_UINT+1] = obj ? obj->getId() : 0; 1477 _UINT++; 1478 } 1479 else 1480 { 1481 Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", getFileLine(ip-2), curObject->getName(), curObject->getClassName()); 1482 intStack[_UINT] = 0; 1483 } 1484 } 1485 break; 1486 1487 case OP_SETCUROBJECT_NEW: 1488 curObject = currentNewObject; 1489 break; 1490 1491 case OP_SETCURFIELD: 1492 // Save the previous field for parsing vector fields. 1493 prevField = curField; 1494 dStrcpy( prevFieldArray, curFieldArray ); 1495 curField = CodeToSTE(code, ip); 1496 curFieldArray[0] = 0; 1497 ip += 2; 1498 break; 1499 1500 case OP_SETCURFIELD_ARRAY: 1501 dStrcpy(curFieldArray, STR.getStringValue()); 1502 break; 1503 1504 case OP_SETCURFIELD_TYPE: 1505 if(curObject) 1506 curObject->setDataFieldType(code[ip], curField, curFieldArray); 1507 ip++; 1508 break; 1509 1510 case OP_LOADFIELD_UINT: 1511 if(curObject) 1512 intStack[_UINT+1] = U32(dAtoi(curObject->getDataField(curField, curFieldArray))); 1513 else 1514 { 1515 // The field is not being retrieved from an object. Maybe it's 1516 // a special accessor? 1517 getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); 1518 intStack[_UINT+1] = dAtoi( valBuffer ); 1519 } 1520 _UINT++; 1521 break; 1522 1523 case OP_LOADFIELD_FLT: 1524 if(curObject) 1525 floatStack[_FLT+1] = dAtof(curObject->getDataField(curField, curFieldArray)); 1526 else 1527 { 1528 // The field is not being retrieved from an object. Maybe it's 1529 // a special accessor? 1530 getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); 1531 floatStack[_FLT+1] = dAtof( valBuffer ); 1532 } 1533 _FLT++; 1534 break; 1535 1536 case OP_LOADFIELD_STR: 1537 if(curObject) 1538 { 1539 val = curObject->getDataField(curField, curFieldArray); 1540 STR.setStringValue( val ); 1541 } 1542 else 1543 { 1544 // The field is not being retrieved from an object. Maybe it's 1545 // a special accessor? 1546 getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); 1547 STR.setStringValue( valBuffer ); 1548 } 1549 break; 1550 1551 case OP_SAVEFIELD_UINT: 1552 STR.setIntValue(intStack[_UINT]); 1553 if(curObject) 1554 curObject->setDataField(curField, curFieldArray, STR.getStringValue()); 1555 else 1556 { 1557 // The field is not being set on an object. Maybe it's 1558 // a special accessor? 1559 setFieldComponent( prevObject, prevField, prevFieldArray, curField ); 1560 prevObject = NULL; 1561 } 1562 break; 1563 1564 case OP_SAVEFIELD_FLT: 1565 STR.setFloatValue(floatStack[_FLT]); 1566 if(curObject) 1567 curObject->setDataField(curField, curFieldArray, STR.getStringValue()); 1568 else 1569 { 1570 // The field is not being set on an object. Maybe it's 1571 // a special accessor? 1572 setFieldComponent( prevObject, prevField, prevFieldArray, curField ); 1573 prevObject = NULL; 1574 } 1575 break; 1576 1577 case OP_SAVEFIELD_STR: 1578 if(curObject) 1579 curObject->setDataField(curField, curFieldArray, STR.getStringValue()); 1580 else 1581 { 1582 // The field is not being set on an object. Maybe it's 1583 // a special accessor? 1584 setFieldComponent( prevObject, prevField, prevFieldArray, curField ); 1585 prevObject = NULL; 1586 } 1587 break; 1588 1589 case OP_STR_TO_UINT: 1590 intStack[_UINT+1] = STR.getIntValue(); 1591 _UINT++; 1592 break; 1593 1594 case OP_STR_TO_FLT: 1595 floatStack[_FLT+1] = STR.getFloatValue(); 1596 _FLT++; 1597 break; 1598 1599 case OP_STR_TO_NONE: 1600 // This exists simply to deal with certain typecast situations. 1601 break; 1602 1603 case OP_FLT_TO_UINT: 1604 intStack[_UINT+1] = (S64)floatStack[_FLT]; 1605 _FLT--; 1606 _UINT++; 1607 break; 1608 1609 case OP_FLT_TO_STR: 1610 STR.setFloatValue(floatStack[_FLT]); 1611 _FLT--; 1612 break; 1613 1614 case OP_FLT_TO_NONE: 1615 _FLT--; 1616 break; 1617 1618 case OP_UINT_TO_FLT: 1619 floatStack[_FLT+1] = (F32)intStack[_UINT]; 1620 _UINT--; 1621 _FLT++; 1622 break; 1623 1624 case OP_UINT_TO_STR: 1625 STR.setIntValue(intStack[_UINT]); 1626 _UINT--; 1627 break; 1628 1629 case OP_UINT_TO_NONE: 1630 _UINT--; 1631 break; 1632 1633 case OP_COPYVAR_TO_NONE: 1634 gEvalState.copyVariable = NULL; 1635 break; 1636 1637 case OP_LOADIMMED_UINT: 1638 intStack[_UINT+1] = code[ip++]; 1639 _UINT++; 1640 break; 1641 1642 case OP_LOADIMMED_FLT: 1643 floatStack[_FLT+1] = curFloatTable[code[ip]]; 1644 ip++; 1645 _FLT++; 1646 break; 1647 1648 case OP_TAG_TO_STR: 1649 code[ip-1] = OP_LOADIMMED_STR; 1650 // it's possible the string has already been converted 1651 if(U8(curStringTable[code[ip]]) != StringTagPrefixByte) 1652 { 1653 U32 id = GameAddTaggedString(curStringTable + code[ip]); 1654 dSprintf(curStringTable + code[ip] + 1, 7, "%d", id); 1655 *(curStringTable + code[ip]) = StringTagPrefixByte; 1656 } 1657 case OP_LOADIMMED_STR: 1658 STR.setStringValue(curStringTable + code[ip++]); 1659 break; 1660 1661 case OP_DOCBLOCK_STR: 1662 { 1663 // If the first word of the doc is '\class' or '@class', then this 1664 // is a namespace doc block, otherwise it is a function doc block. 1665 const char* docblock = curStringTable + code[ip++]; 1666 1667 const char* sansClass = dStrstr( docblock, "@class" ); 1668 if( !sansClass ) 1669 sansClass = dStrstr( docblock, "\\class" ); 1670 1671 if( sansClass ) 1672 { 1673 // Don't save the class declaration. Scan past the 'class' 1674 // keyword and up to the first whitespace. 1675 sansClass += 7; 1676 S32 index = 0; 1677 while( ( *sansClass != ' ' ) && ( *sansClass != '\n' ) && *sansClass && ( index < ( nsDocLength - 1 ) ) ) 1678 { 1679 nsDocBlockClass[index++] = *sansClass; 1680 sansClass++; 1681 } 1682 nsDocBlockClass[index] = '\0'; 1683 1684 curNSDocBlock = sansClass + 1; 1685 } 1686 else 1687 curFNDocBlock = docblock; 1688 } 1689 1690 break; 1691 1692 case OP_LOADIMMED_IDENT: 1693 STR.setStringValue(CodeToSTE(code, ip)); 1694 ip += 2; 1695 break; 1696 1697 case OP_CALLFUNC_RESOLVE: 1698 // This deals with a function that is potentially living in a namespace. 1699 fnNamespace = CodeToSTE(code, ip+2); 1700 fnName = CodeToSTE(code, ip); 1701 1702 // Try to look it up. 1703 ns = Namespace::find(fnNamespace); 1704 nsEntry = ns->lookup(fnName); 1705 if(!nsEntry) 1706 { 1707 ip+= 5; 1708 Con::warnf(ConsoleLogEntry::General, 1709 "%s: Unable to find function %s%s%s", 1710 getFileLine(ip-7), fnNamespace ? fnNamespace : "", 1711 fnNamespace ? "::" : "", fnName); 1712 STR.popFrame(); 1713 CSTK.popFrame(); 1714 break; 1715 } 1716 1717#ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS 1718 // Now fall through to OP_CALLFUNC... 1719 // Now, rewrite our code a bit (ie, avoid future lookups) and fall 1720 // through to OP_CALLFUNC 1721#ifdef TORQUE_CPU_X64 1722 *((U64*)(code+ip+2)) = ((U64)nsEntry); 1723#else 1724 code[ip+2] = ((U32)nsEntry); 1725#endif 1726 code[ip-1] = OP_CALLFUNC; 1727#endif 1728 1729 case OP_CALLFUNC: 1730 { 1731 // This routingId is set when we query the object as to whether 1732 // it handles this method. It is set to an enum from the table 1733 // above indicating whether it handles it on a component it owns 1734 // or just on the object. 1735 S32 routingId = 0; 1736 1737 fnName = CodeToSTE(code, ip); 1738 1739 //if this is called from inside a function, append the ip and codeptr 1740 if( gEvalState.getStackDepth() > 0 ) 1741 { 1742 gEvalState.getCurrentFrame().code = this; 1743 gEvalState.getCurrentFrame().ip = ip - 1; 1744 } 1745 1746 U32 callType = code[ip+4]; 1747 1748 ip += 5; 1749 CSTK.getArgcArgv(fnName, &callArgc, &callArgv); 1750 1751 const char *componentReturnValue = ""; 1752 1753 if(callType == FuncCallExprNode::FunctionCall) 1754 { 1755 if( !nsEntry ) 1756 { 1757#ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS 1758#ifdef TORQUE_CPU_X64 1759 nsEntry = ((Namespace::Entry *) *((U64*)(code+ip-3))); 1760#else 1761 nsEntry = ((Namespace::Entry *) *(code+ip-3)); 1762#endif 1763#else 1764 nsEntry = Namespace::global()->lookup( fnName ); 1765#endif 1766 ns = NULL; 1767 } 1768 ns = NULL; 1769 } 1770 else if(callType == FuncCallExprNode::MethodCall) 1771 { 1772 saveObject = gEvalState.thisObject; 1773 gEvalState.thisObject = Sim::findObject((const char*)callArgv[1]); 1774 if(!gEvalState.thisObject) 1775 { 1776 // Go back to the previous saved object. 1777 gEvalState.thisObject = saveObject; 1778 1779 Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), (const char*)callArgv[1], fnName); 1780 STR.popFrame(); 1781 CSTK.popFrame(); 1782 STR.setStringValue(""); 1783 break; 1784 } 1785 1786 bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId); 1787 if( handlesMethod && routingId == MethodOnComponent ) 1788 { 1789 ICallMethod *pComponent = dynamic_cast<ICallMethod *>( gEvalState.thisObject ); 1790 if( pComponent ) 1791 componentReturnValue = pComponent->callMethodArgList( callArgc, callArgv, false ); 1792 } 1793 1794 ns = gEvalState.thisObject->getNamespace(); 1795 if(ns) 1796 nsEntry = ns->lookup(fnName); 1797 else 1798 nsEntry = NULL; 1799 } 1800 else // it's a ParentCall 1801 { 1802 if(thisNamespace) 1803 { 1804 ns = thisNamespace->mParent; 1805 if(ns) 1806 nsEntry = ns->lookup(fnName); 1807 else 1808 nsEntry = NULL; 1809 } 1810 else 1811 { 1812 ns = NULL; 1813 nsEntry = NULL; 1814 } 1815 } 1816 1817 Namespace::Entry::CallbackUnion * nsCb = NULL; 1818 const char * nsUsage = NULL; 1819 if (nsEntry) 1820 { 1821 nsCb = &nsEntry->cb; 1822 nsUsage = nsEntry->mUsage; 1823 routingId = 0; 1824 } 1825 if(!nsEntry || noCalls) 1826 { 1827 if(!noCalls && !( routingId == MethodOnComponent ) ) 1828 { 1829 Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-6), fnName); 1830 if(callType == FuncCallExprNode::MethodCall) 1831 { 1832 Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", 1833 gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "", 1834 gEvalState.thisObject->getId(), Con::getNamespaceList(ns) ); 1835 } 1836 } 1837 STR.popFrame(); 1838 CSTK.popFrame(); 1839 1840 if( routingId == MethodOnComponent ) 1841 STR.setStringValue( componentReturnValue ); 1842 else 1843 STR.setStringValue( "" ); 1844 1845 break; 1846 } 1847 if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType) 1848 { 1849 ConsoleValueRef ret; 1850 if(nsEntry->mFunctionOffset) 1851 ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); 1852 1853 STR.popFrame(); 1854 // Functions are assumed to return strings, so look ahead to see if we can skip the conversion 1855 if(code[ip] == OP_STR_TO_UINT) 1856 { 1857 ip++; 1858 intStack[++_UINT] = (U32)((S32)ret); 1859 } 1860 else if(code[ip] == OP_STR_TO_FLT) 1861 { 1862 ip++; 1863 floatStack[++_FLT] = (F32)ret; 1864 } 1865 else if(code[ip] == OP_STR_TO_NONE) 1866 { 1867 STR.setStringValue(ret.getStringValue()); 1868 ip++; 1869 } 1870 else 1871 STR.setStringValue((const char*)ret); 1872 1873 // This will clear everything including returnValue 1874 CSTK.popFrame(); 1875 //STR.clearFunctionOffset(); 1876 } 1877 else 1878 { 1879 const char* nsName = ns? ns->mName: ""; 1880#ifndef TORQUE_DEBUG 1881 // [tom, 12/13/2006] This stops tools functions from working in the console, 1882 // which is useful behavior when debugging so I'm ifdefing this out for debug builds. 1883 if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript()) 1884 { 1885 Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-6), nsName, fnName); 1886 } 1887 else 1888#endif 1889 if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) 1890 { 1891 Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).", 1892 getFileLine(ip-6), nsName, fnName, 1893 callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); 1894 Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-6), nsEntry->mUsage); 1895 STR.popFrame(); 1896 CSTK.popFrame(); 1897 } 1898 else 1899 { 1900 switch(nsEntry->mType) 1901 { 1902 case Namespace::Entry::StringCallbackType: 1903 { 1904 const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); 1905 STR.popFrame(); 1906 CSTK.popFrame(); 1907 if(ret != STR.getStringValue()) 1908 STR.setStringValue(ret); 1909 //else 1910 // STR.setLen(dStrlen(ret)); 1911 break; 1912 } 1913 case Namespace::Entry::IntCallbackType: 1914 { 1915 S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); 1916 STR.popFrame(); 1917 CSTK.popFrame(); 1918 if(code[ip] == OP_STR_TO_UINT) 1919 { 1920 ip++; 1921 intStack[++_UINT] = result; 1922 break; 1923 } 1924 else if(code[ip] == OP_STR_TO_FLT) 1925 { 1926 ip++; 1927 floatStack[++_FLT] = result; 1928 break; 1929 } 1930 else if(code[ip] == OP_STR_TO_NONE) 1931 ip++; 1932 else 1933 STR.setIntValue(result); 1934 break; 1935 } 1936 case Namespace::Entry::FloatCallbackType: 1937 { 1938 F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); 1939 STR.popFrame(); 1940 CSTK.popFrame(); 1941 if(code[ip] == OP_STR_TO_UINT) 1942 { 1943 ip++; 1944 intStack[++_UINT] = (S64)result; 1945 break; 1946 } 1947 else if(code[ip] == OP_STR_TO_FLT) 1948 { 1949 ip++; 1950 floatStack[++_FLT] = result; 1951 break; 1952 } 1953 else if(code[ip] == OP_STR_TO_NONE) 1954 ip++; 1955 else 1956 STR.setFloatValue(result); 1957 break; 1958 } 1959 case Namespace::Entry::VoidCallbackType: 1960 nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv); 1961 if( code[ ip ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) ) 1962 Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-6), fnName, functionName); 1963 1964 STR.popFrame(); 1965 CSTK.popFrame(); 1966 STR.setStringValue(""); 1967 break; 1968 case Namespace::Entry::BoolCallbackType: 1969 { 1970 bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); 1971 STR.popFrame(); 1972 CSTK.popFrame(); 1973 if(code[ip] == OP_STR_TO_UINT) 1974 { 1975 ip++; 1976 intStack[++_UINT] = result; 1977 break; 1978 } 1979 else if(code[ip] == OP_STR_TO_FLT) 1980 { 1981 ip++; 1982 floatStack[++_FLT] = result; 1983 break; 1984 } 1985 else if(code[ip] == OP_STR_TO_NONE) 1986 ip++; 1987 else 1988 STR.setIntValue(result); 1989 break; 1990 } 1991 } 1992 } 1993 } 1994 1995 if(callType == FuncCallExprNode::MethodCall) 1996 gEvalState.thisObject = saveObject; 1997 break; 1998 } 1999 case OP_ADVANCE_STR: 2000 STR.advance(); 2001 break; 2002 case OP_ADVANCE_STR_APPENDCHAR: 2003 STR.advanceChar(code[ip++]); 2004 break; 2005 2006 case OP_ADVANCE_STR_COMMA: 2007 STR.advanceChar('_'); 2008 break; 2009 2010 case OP_ADVANCE_STR_NUL: 2011 STR.advanceChar(0); 2012 break; 2013 2014 case OP_REWIND_STR: 2015 STR.rewind(); 2016 break; 2017 2018 case OP_TERMINATE_REWIND_STR: 2019 STR.rewindTerminate(); 2020 break; 2021 2022 case OP_COMPARE_STR: 2023 intStack[++_UINT] = STR.compare(); 2024 break; 2025 case OP_PUSH: 2026 STR.push(); 2027 CSTK.pushStringStackPtr(STR.getPreviousStringValuePtr()); 2028 break; 2029 case OP_PUSH_UINT: 2030 CSTK.pushUINT(intStack[_UINT]); 2031 _UINT--; 2032 break; 2033 case OP_PUSH_FLT: 2034 CSTK.pushFLT(floatStack[_FLT]); 2035 _FLT--; 2036 break; 2037 case OP_PUSH_VAR: 2038 if (gEvalState.currentVariable) 2039 CSTK.pushValue(gEvalState.currentVariable->value); 2040 else 2041 CSTK.pushString(""); 2042 break; 2043 2044 case OP_PUSH_FRAME: 2045 STR.pushFrame(); 2046 CSTK.pushFrame(); 2047 break; 2048 2049 case OP_ASSERT: 2050 { 2051 if( !intStack[_UINT--] ) 2052 { 2053 const char *message = curStringTable + code[ip]; 2054 2055 U32 breakLine, inst; 2056 findBreakLine( ip - 1, breakLine, inst ); 2057 2058 if ( PlatformAssert::processAssert( PlatformAssert::Fatal, 2059 name ? name : "eval", 2060 breakLine, 2061 message ) ) 2062 { 2063 if ( TelDebugger && TelDebugger->isConnected() && breakLine > 0 ) 2064 { 2065 TelDebugger->breakProcess(); 2066 } 2067 else 2068 Platform::debugBreak(); 2069 } 2070 } 2071 2072 ip++; 2073 break; 2074 } 2075 2076 case OP_BREAK: 2077 { 2078 //append the ip and codeptr before managing the breakpoint! 2079 AssertFatal( gEvalState.getStackDepth() > 0, "Empty eval stack on break!"); 2080 gEvalState.getCurrentFrame().code = this; 2081 gEvalState.getCurrentFrame().ip = ip - 1; 2082 2083 U32 breakLine; 2084 findBreakLine(ip-1, breakLine, instruction); 2085 if(!breakLine) 2086 goto breakContinue; 2087 TelDebugger->executionStopped(this, breakLine); 2088 goto breakContinue; 2089 } 2090 2091 case OP_ITER_BEGIN_STR: 2092 { 2093 iterStack[ _ITER ].mIsStringIter = true; 2094 /* fallthrough */ 2095 } 2096 2097 case OP_ITER_BEGIN: 2098 { 2099 StringTableEntry varName = CodeToSTE(code, ip); 2100 U32 failIp = code[ ip + 2 ]; 2101 2102 IterStackRecord& iter = iterStack[ _ITER ]; 2103 2104 iter.mVariable = gEvalState.getCurrentFrame().add( varName ); 2105 2106 if( iter.mIsStringIter ) 2107 { 2108 iter.mData.mStr.mString = STR.getStringValuePtr(); 2109 iter.mData.mStr.mIndex = 0; 2110 } 2111 else 2112 { 2113 // Look up the object. 2114 2115 SimSet* set; 2116 if( !Sim::findObject( STR.getStringValue(), set ) ) 2117 { 2118 Con::errorf( ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue() ); 2119 Con::errorf( ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?" ); 2120 ip = failIp; 2121 continue; 2122 } 2123 2124 // Set up. 2125 2126 iter.mData.mObj.mSet = set; 2127 iter.mData.mObj.mIndex = 0; 2128 } 2129 2130 _ITER ++; 2131 iterDepth ++; 2132 2133 STR.push(); 2134 2135 ip += 3; 2136 break; 2137 } 2138 2139 case OP_ITER: 2140 { 2141 U32 breakIp = code[ ip ]; 2142 IterStackRecord& iter = iterStack[ _ITER - 1 ]; 2143 2144 if( iter.mIsStringIter ) 2145 { 2146 const char* str = StringStackPtrRef(iter.mData.mStr.mString).getPtr(&STR); 2147 2148 U32 startIndex = iter.mData.mStr.mIndex; 2149 U32 endIndex = startIndex; 2150 2151 // Break if at end. 2152 2153 if( !str[ startIndex ] ) 2154 { 2155 ip = breakIp; 2156 continue; 2157 } 2158 2159 // Find right end of current component. 2160 2161 if( !dIsspace( str[ endIndex ] ) ) 2162 do ++ endIndex; 2163 while( str[ endIndex ] && !dIsspace( str[ endIndex ] ) ); 2164 2165 // Extract component. 2166 2167 if( endIndex != startIndex ) 2168 { 2169 char savedChar = str[ endIndex ]; 2170 const_cast< char* >( str )[ endIndex ] = '\0'; // We are on the string stack so this is okay. 2171 iter.mVariable->setStringValue( &str[ startIndex ] ); 2172 const_cast< char* >( str )[ endIndex ] = savedChar; 2173 } 2174 else 2175 iter.mVariable->setStringValue( "" ); 2176 2177 // Skip separator. 2178 if( str[ endIndex ] != '\0' ) 2179 ++ endIndex; 2180 2181 iter.mData.mStr.mIndex = endIndex; 2182 } 2183 else 2184 { 2185 U32 index = iter.mData.mObj.mIndex; 2186 SimSet* set = iter.mData.mObj.mSet; 2187 2188 if( index >= set->size() ) 2189 { 2190 ip = breakIp; 2191 continue; 2192 } 2193 2194 iter.mVariable->setIntValue( set->at( index )->getId() ); 2195 iter.mData.mObj.mIndex = index + 1; 2196 } 2197 2198 ++ ip; 2199 break; 2200 } 2201 2202 case OP_ITER_END: 2203 { 2204 -- _ITER; 2205 -- iterDepth; 2206 2207 STR.rewind(); 2208 2209 iterStack[ _ITER ].mIsStringIter = false; 2210 break; 2211 } 2212 2213 case OP_INVALID: 2214 2215 default: 2216 // error! 2217 goto execFinished; 2218 } 2219 } 2220execFinished: 2221 2222 if ( telDebuggerOn && setFrame < 0 ) 2223 TelDebugger->popStackFrame(); 2224 2225 if ( popFrame ) 2226 gEvalState.popFrame(); 2227 2228 if(argv) 2229 { 2230 if(gEvalState.traceOn) 2231 { 2232 traceBuffer[0] = 0; 2233 dStrcat(traceBuffer, "Leaving "); 2234 2235 if(packageName) 2236 { 2237 dStrcat(traceBuffer, "["); 2238 dStrcat(traceBuffer, packageName); 2239 dStrcat(traceBuffer, "]"); 2240 } 2241 if(thisNamespace && thisNamespace->mName) 2242 { 2243 dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), 2244 "%s::%s() - return %s", thisNamespace->mName, thisFunctionName, STR.getStringValue()); 2245 } 2246 else 2247 { 2248 dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), 2249 "%s() - return %s", thisFunctionName, STR.getStringValue()); 2250 } 2251 Con::printf("%s", traceBuffer); 2252 } 2253 } 2254 2255 smCurrentCodeBlock = saveCodeBlock; 2256 if(saveCodeBlock && saveCodeBlock->name) 2257 { 2258 Con::gCurrentFile = saveCodeBlock->name; 2259 Con::gCurrentRoot = saveCodeBlock->modPath; 2260 } 2261 2262 decRefCount(); 2263 2264#ifdef TORQUE_VALIDATE_STACK 2265 AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec"); 2266 AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec"); 2267#endif 2268 2269 return returnValue; 2270} 2271 2272//------------------------------------------------------------ 2273
