T3D VM opcodes to function opcodes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "console/console.h" | |
| #include "console/compiler.h" | |
| #include "console/codeBlock.h" | |
| #include "console/telnetDebugger.h" | |
| #include "console/ast.h" | |
| #include "core/strings/unicode.h" | |
| #include "core/strings/stringFunctions.h" | |
| #include "core/stringTable.h" | |
| #include "core/stream/fileStream.h" | |
| using namespace Compiler; | |
| bool CodeBlock::smInFunction = false; | |
| U32 CodeBlock::smBreakLineCount = 0; | |
| CodeBlock * CodeBlock::smCodeBlockList = NULL; | |
| CodeBlock * CodeBlock::smCurrentCodeBlock = NULL; | |
| ConsoleParser *CodeBlock::smCurrentParser = NULL; | |
| //------------------------------------------------------------------------- | |
| CodeBlock::CodeBlock() | |
| { | |
| globalStrings = NULL; | |
| functionStrings = NULL; | |
| functionStringsMaxLen = 0; | |
| globalStringsMaxLen = 0; | |
| globalFloats = NULL; | |
| functionFloats = NULL; | |
| lineBreakPairs = NULL; | |
| breakList = NULL; | |
| breakListSize = 0; | |
| refCount = 0; | |
| code = NULL; | |
| name = NULL; | |
| fullPath = NULL; | |
| modPath = NULL; | |
| // FHC - compiled eval | |
| #define NEW_EXEC | |
| #ifdef NEW_EXEC | |
| execPtr = &CodeBlock::execNew; | |
| #else | |
| execPtr = &CodeBlock::execOrig; | |
| #endif | |
| _FLT = 0; ///< Stack pointer for floatStack. | |
| _UINT = 0; ///< Stack pointer for intStack. | |
| _ITER = 0; ///< Stack pointer for iterStack. | |
| // populate opcode list | |
| for(int count=0; count<MAX_OPCODE_SLOTS; count++) | |
| { | |
| opcodeList[count] = &CodeBlock::op_invalid; // set this to the empty opcode | |
| } | |
| opcodeList[OP_FUNC_DECL] = &CodeBlock::op_func_decl; | |
| opcodeList[OP_CREATE_OBJECT] = &CodeBlock::op_create_object; | |
| opcodeList[OP_ADD_OBJECT] = &CodeBlock::op_add_object; | |
| opcodeList[OP_END_OBJECT] = &CodeBlock::op_end_object; | |
| opcodeList[OP_FINISH_OBJECT] = &CodeBlock::op_finish_object; | |
| opcodeList[OP_JMPIFFNOT] = &CodeBlock::op_jmpiffnot; | |
| opcodeList[OP_JMPIFNOT] = &CodeBlock::op_jmpifnot; | |
| opcodeList[OP_JMPIFF] = &CodeBlock::op_jmpiff; | |
| opcodeList[OP_JMPIF] = &CodeBlock::op_jmpif; | |
| opcodeList[OP_JMPIFNOT_NP] = &CodeBlock::op_jmpifnot_np; | |
| opcodeList[OP_JMPIF_NP] = &CodeBlock::op_jmpif_np; | |
| opcodeList[OP_JMP] = &CodeBlock::op_jmp; | |
| opcodeList[OP_RETURN_VOID] = &CodeBlock::op_return_void; | |
| opcodeList[OP_RETURN] = &CodeBlock::op_return; | |
| opcodeList[OP_CMPEQ] = &CodeBlock::op_cmpeq; | |
| opcodeList[OP_CMPGR] = &CodeBlock::op_cmpgr; | |
| opcodeList[OP_CMPGE] = &CodeBlock::op_cmpge; | |
| opcodeList[OP_CMPLT] = &CodeBlock::op_cmplt; | |
| opcodeList[OP_CMPLE] = &CodeBlock::op_cmple; | |
| opcodeList[OP_CMPNE] = &CodeBlock::op_cmpne; | |
| opcodeList[OP_XOR] = &CodeBlock::op_xor; | |
| opcodeList[OP_MOD] = &CodeBlock::op_mod; | |
| opcodeList[OP_BITAND] = &CodeBlock::op_bitand; | |
| opcodeList[OP_BITOR] = &CodeBlock::op_bitor; | |
| opcodeList[OP_NOT] = &CodeBlock::op_not; | |
| opcodeList[OP_NOTF] = &CodeBlock::op_notf; | |
| opcodeList[OP_ONESCOMPLEMENT] = &CodeBlock::op_onescomplement; | |
| opcodeList[OP_SHR] = &CodeBlock::op_shr; | |
| opcodeList[OP_SHL] = &CodeBlock::op_shl; | |
| opcodeList[OP_AND] = &CodeBlock::op_and; | |
| opcodeList[OP_OR] = &CodeBlock::op_or; | |
| opcodeList[OP_ADD] = &CodeBlock::op_add; | |
| opcodeList[OP_SUB] = &CodeBlock::op_sub; | |
| opcodeList[OP_MUL] = &CodeBlock::op_mul; | |
| opcodeList[OP_DIV] = &CodeBlock::op_div; | |
| opcodeList[OP_NEG] = &CodeBlock::op_neg; | |
| opcodeList[OP_SETCURVAR] = &CodeBlock::op_setcurvar; | |
| opcodeList[OP_SETCURVAR_CREATE] = &CodeBlock::op_setcurvar_create; | |
| opcodeList[OP_SETCURVAR_ARRAY] = &CodeBlock::op_setcurvar_array; | |
| opcodeList[OP_SETCURVAR_ARRAY_CREATE] = &CodeBlock::op_setcurvar_array_create; | |
| opcodeList[OP_LOADVAR_UINT] = &CodeBlock::op_loadvar_uint; | |
| opcodeList[OP_LOADVAR_FLT] = &CodeBlock::op_loadvar_flt; | |
| opcodeList[OP_LOADVAR_STR] = &CodeBlock::op_loadvar_str; | |
| opcodeList[OP_SAVEVAR_UINT] = &CodeBlock::op_savevar_uint; | |
| opcodeList[OP_SAVEVAR_FLT] = &CodeBlock::op_savevar_flt; | |
| opcodeList[OP_SAVEVAR_STR] = &CodeBlock::op_savevar_str; | |
| opcodeList[OP_SETCUROBJECT] = &CodeBlock::op_setcurobject; | |
| opcodeList[OP_SETCUROBJECT_INTERNAL] = &CodeBlock::op_setcurobject_internal; | |
| opcodeList[OP_SETCUROBJECT_NEW] = &CodeBlock::op_setcurobject_new; | |
| opcodeList[OP_SETCURFIELD] = &CodeBlock::op_setcurfield; | |
| opcodeList[OP_SETCURFIELD_ARRAY] = &CodeBlock::op_setcurfield_array; | |
| opcodeList[OP_SETCURFIELD_TYPE] = &CodeBlock::op_setcurfield_type; | |
| opcodeList[OP_LOADFIELD_UINT] = &CodeBlock::op_loadfield_uint; | |
| opcodeList[OP_LOADFIELD_FLT] = &CodeBlock::op_loadfield_flt; | |
| opcodeList[OP_LOADFIELD_STR] = &CodeBlock::op_loadfield_str; | |
| opcodeList[OP_SAVEFIELD_UINT] = &CodeBlock::op_savefield_uint; | |
| opcodeList[OP_SAVEFIELD_FLT] = &CodeBlock::op_savefield_flt; | |
| opcodeList[OP_SAVEFIELD_STR] = &CodeBlock::op_savefield_str; | |
| opcodeList[OP_STR_TO_UINT] = &CodeBlock::op_str_to_uint; | |
| opcodeList[OP_STR_TO_FLT] = &CodeBlock::op_str_to_flt; | |
| opcodeList[OP_STR_TO_NONE] = &CodeBlock::op_str_to_none; | |
| opcodeList[OP_FLT_TO_UINT] = &CodeBlock::op_flt_to_uint; | |
| opcodeList[OP_FLT_TO_STR] = &CodeBlock::op_flt_to_str; | |
| opcodeList[OP_FLT_TO_NONE] = &CodeBlock::op_flt_to_none; | |
| opcodeList[OP_UINT_TO_FLT] = &CodeBlock::op_uint_to_flt; | |
| opcodeList[OP_UINT_TO_STR] = &CodeBlock::op_uint_to_str; | |
| opcodeList[OP_UINT_TO_NONE] = &CodeBlock::op_uint_to_none; | |
| opcodeList[OP_LOADIMMED_UINT] = &CodeBlock::op_loadimmed_uint; | |
| opcodeList[OP_LOADIMMED_FLT] = &CodeBlock::op_loadimmed_flt; | |
| opcodeList[OP_TAG_TO_STR] = &CodeBlock::op_tag_to_str; | |
| opcodeList[OP_LOADIMMED_STR] = &CodeBlock::op_loadimmed_str; | |
| opcodeList[OP_DOCBLOCK_STR] = &CodeBlock::op_docblock_str; | |
| opcodeList[OP_LOADIMMED_IDENT] = &CodeBlock::op_loadimmed_ident; | |
| opcodeList[OP_CALLFUNC_RESOLVE] = &CodeBlock::op_callfunc_resolve; | |
| opcodeList[OP_CALLFUNC] = &CodeBlock::op_callfunc; | |
| opcodeList[OP_ADVANCE_STR] = &CodeBlock::op_advance_str; | |
| opcodeList[OP_ADVANCE_STR_APPENDCHAR] = &CodeBlock::op_advance_str_appendchar; | |
| opcodeList[OP_ADVANCE_STR_COMMA] = &CodeBlock::op_advance_str_comma; | |
| opcodeList[OP_ADVANCE_STR_NUL] = &CodeBlock::op_advance_str_nul; | |
| opcodeList[OP_REWIND_STR] = &CodeBlock::op_rewind_str; | |
| opcodeList[OP_TERMINATE_REWIND_STR] = &CodeBlock::op_terminate_rewind_str; | |
| opcodeList[OP_COMPARE_STR] = &CodeBlock::op_compare_str; | |
| opcodeList[OP_PUSH] = &CodeBlock::op_push; | |
| opcodeList[OP_PUSH_FRAME] = &CodeBlock::op_push_frame; | |
| opcodeList[OP_ASSERT] = &CodeBlock::op_assert; | |
| opcodeList[OP_BREAK] = &CodeBlock::op_break; | |
| opcodeList[OP_ITER_BEGIN_STR] = &CodeBlock::op_iter_begin_str; | |
| opcodeList[OP_ITER_BEGIN] = &CodeBlock::op_iter_begin; | |
| opcodeList[OP_ITER] = &CodeBlock::op_iter; | |
| opcodeList[OP_ITER_END] = &CodeBlock::op_iter_end; | |
| opcodeList[OP_INVALID] = &CodeBlock::op_invalid; | |
| // initial stack | |
| //{0, NULL, NULL, 0, NULL, 0, NULL, 0} | |
| if(ExecCallStack.size() < 1){ | |
| ExecCallStackEntry *tmp = new ExecCallStackEntry(0, NULL, NULL, 0, NULL, 0, NULL, 0); | |
| stackPush(tmp); | |
| delete tmp; | |
| } | |
| } | |
| CodeBlock::~CodeBlock() | |
| { | |
| // Make sure we aren't lingering in the current code block... | |
| AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!") | |
| if(name) | |
| removeFromCodeList(); | |
| delete[] const_cast<char*>(globalStrings); | |
| delete[] const_cast<char*>(functionStrings); | |
| functionStringsMaxLen = 0; | |
| globalStringsMaxLen = 0; | |
| delete[] globalFloats; | |
| delete[] functionFloats; | |
| delete[] code; | |
| delete[] breakList; | |
| } | |
| //------------------------------------------------------------------------- | |
| StringTableEntry CodeBlock::getCurrentCodeBlockName() | |
| { | |
| if (CodeBlock::getCurrentBlock()) | |
| return CodeBlock::getCurrentBlock()->name; | |
| else | |
| return NULL; | |
| } | |
| StringTableEntry CodeBlock::getCurrentCodeBlockFullPath() | |
| { | |
| if (CodeBlock::getCurrentBlock()) | |
| return CodeBlock::getCurrentBlock()->fullPath; | |
| else | |
| return NULL; | |
| } | |
| StringTableEntry CodeBlock::getCurrentCodeBlockModName() | |
| { | |
| if (CodeBlock::getCurrentBlock()) | |
| return CodeBlock::getCurrentBlock()->modPath; | |
| else | |
| return NULL; | |
| } | |
| CodeBlock *CodeBlock::find(StringTableEntry name) | |
| { | |
| for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile) | |
| if(walk->name == name) | |
| return walk; | |
| return NULL; | |
| } | |
| //------------------------------------------------------------------------- | |
| void CodeBlock::addToCodeList() | |
| { | |
| // remove any code blocks with my name | |
| for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile)) | |
| { | |
| if((*walk)->name == name) | |
| { | |
| *walk = (*walk)->nextFile; | |
| break; | |
| } | |
| } | |
| nextFile = smCodeBlockList; | |
| smCodeBlockList = this; | |
| } | |
| void CodeBlock::clearAllBreaks() | |
| { | |
| if(!lineBreakPairs) | |
| return; | |
| for(U32 i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| code[p[1]] = p[0] & 0xFF; | |
| } | |
| } | |
| void CodeBlock::clearBreakpoint(U32 lineNumber) | |
| { | |
| if(!lineBreakPairs) | |
| return; | |
| for(U32 i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| if((p[0] >> 8) == lineNumber) | |
| { | |
| code[p[1]] = p[0] & 0xFF; | |
| return; | |
| } | |
| } | |
| } | |
| void CodeBlock::setAllBreaks() | |
| { | |
| if(!lineBreakPairs) | |
| return; | |
| for(U32 i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| code[p[1]] = OP_BREAK; | |
| } | |
| } | |
| bool CodeBlock::setBreakpoint(U32 lineNumber) | |
| { | |
| if(!lineBreakPairs) | |
| return false; | |
| for(U32 i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| if((p[0] >> 8) == lineNumber) | |
| { | |
| code[p[1]] = OP_BREAK; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| U32 CodeBlock::findFirstBreakLine(U32 lineNumber) | |
| { | |
| if(!lineBreakPairs) | |
| return 0; | |
| for(U32 i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| U32 line = (p[0] >> 8); | |
| if( lineNumber <= line ) | |
| return line; | |
| } | |
| return 0; | |
| } | |
| struct LinePair | |
| { | |
| U32 instLine; | |
| U32 ip; | |
| }; | |
| void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction) | |
| { | |
| U32 min = 0; | |
| U32 max = lineBreakPairCount - 1; | |
| LinePair *p = (LinePair *) lineBreakPairs; | |
| U32 found; | |
| if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip) | |
| { | |
| line = 0; | |
| instruction = OP_INVALID; | |
| return; | |
| } | |
| else if(p[min].ip == ip) | |
| found = min; | |
| else if(p[max].ip == ip) | |
| found = max; | |
| else | |
| { | |
| for(;;) | |
| { | |
| if(min == max - 1) | |
| { | |
| found = min; | |
| break; | |
| } | |
| U32 mid = (min + max) >> 1; | |
| if(p[mid].ip == ip) | |
| { | |
| found = mid; | |
| break; | |
| } | |
| else if(p[mid].ip > ip) | |
| max = mid; | |
| else | |
| min = mid; | |
| } | |
| } | |
| instruction = p[found].instLine & 0xFF; | |
| line = p[found].instLine >> 8; | |
| } | |
| const char *CodeBlock::getFileLine(U32 ip) | |
| { | |
| static char nameBuffer[256]; | |
| U32 line, inst; | |
| findBreakLine(ip, line, inst); | |
| dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "<input>", line); | |
| return nameBuffer; | |
| } | |
| void CodeBlock::removeFromCodeList() | |
| { | |
| for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile)) | |
| { | |
| if(*walk == this) | |
| { | |
| *walk = nextFile; | |
| // clear out all breakpoints | |
| clearAllBreaks(); | |
| break; | |
| } | |
| } | |
| // Let the telnet debugger know that this code | |
| // block has been unloaded and that it needs to | |
| // remove references to it. | |
| if ( TelDebugger ) | |
| TelDebugger->clearCodeBlockPointers( this ); | |
| } | |
| void CodeBlock::calcBreakList() | |
| { | |
| U32 size = 0; | |
| S32 line = -1; | |
| U32 seqCount = 0; | |
| U32 i; | |
| for(i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 lineNumber = lineBreakPairs[i * 2]; | |
| if(lineNumber == U32(line + 1)) | |
| seqCount++; | |
| else | |
| { | |
| if(seqCount) | |
| size++; | |
| size++; | |
| seqCount = 1; | |
| } | |
| line = lineNumber; | |
| } | |
| if(seqCount) | |
| size++; | |
| breakList = new U32[size]; | |
| breakListSize = size; | |
| line = -1; | |
| seqCount = 0; | |
| size = 0; | |
| for(i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 lineNumber = lineBreakPairs[i * 2]; | |
| if(lineNumber == U32(line + 1)) | |
| seqCount++; | |
| else | |
| { | |
| if(seqCount) | |
| breakList[size++] = seqCount; | |
| breakList[size++] = lineNumber - getMax(0, line) - 1; | |
| seqCount = 1; | |
| } | |
| line = lineNumber; | |
| } | |
| if(seqCount) | |
| breakList[size++] = seqCount; | |
| for(i = 0; i < lineBreakPairCount; i++) | |
| { | |
| U32 *p = lineBreakPairs + i * 2; | |
| p[0] = (p[0] << 8) | code[p[1]]; | |
| } | |
| // Let the telnet debugger know that this code | |
| // block has been loaded and that it can add break | |
| // points it has for it. | |
| if ( TelDebugger ) | |
| TelDebugger->addAllBreakpoints( this ); | |
| } | |
| bool CodeBlock::read(StringTableEntry fileName, Stream &st) | |
| { | |
| const StringTableEntry exePath = Platform::getMainDotCsDir(); | |
| const StringTableEntry cwd = Platform::getCurrentDirectory(); | |
| name = fileName; | |
| if(fileName) | |
| { | |
| fullPath = NULL; | |
| if(Platform::isFullPath(fileName)) | |
| fullPath = fileName; | |
| if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0) | |
| name = StringTable->insert(fileName + dStrlen(exePath) + 1, true); | |
| else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0) | |
| name = StringTable->insert(fileName + dStrlen(cwd) + 1, true); | |
| if(fullPath == NULL) | |
| { | |
| char buf[1024]; | |
| fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true); | |
| } | |
| modPath = Con::getModNameFromPath(fileName); | |
| } | |
| // | |
| addToCodeList(); | |
| U32 globalSize,size,i; | |
| st.read(&size); | |
| if(size) | |
| { | |
| globalStrings = new char[size]; | |
| globalStringsMaxLen = size; | |
| st.read(size, globalStrings); | |
| } | |
| globalSize = size; | |
| st.read(&size); | |
| if(size) | |
| { | |
| functionStrings = new char[size]; | |
| functionStringsMaxLen = size; | |
| st.read(size, functionStrings); | |
| } | |
| st.read(&size); | |
| if(size) | |
| { | |
| globalFloats = new F64[size]; | |
| for(U32 i = 0; i < size; i++) | |
| st.read(&globalFloats[i]); | |
| } | |
| st.read(&size); | |
| if(size) | |
| { | |
| functionFloats = new F64[size]; | |
| for(U32 i = 0; i < size; i++) | |
| st.read(&functionFloats[i]); | |
| } | |
| U32 codeSize; | |
| st.read(&codeSize); | |
| st.read(&lineBreakPairCount); | |
| U32 totSize = codeSize + lineBreakPairCount * 2; | |
| code = new U32[totSize]; | |
| for(i = 0; i < codeSize; i++) | |
| { | |
| U8 b; | |
| st.read(&b); | |
| if(b == 0xFF) | |
| st.read(&code[i]); | |
| else | |
| code[i] = b; | |
| } | |
| for(i = codeSize; i < totSize; i++) | |
| st.read(&code[i]); | |
| lineBreakPairs = code + codeSize; | |
| // StringTable-ize our identifiers. | |
| U32 identCount; | |
| st.read(&identCount); | |
| while(identCount--) | |
| { | |
| U32 offset; | |
| st.read(&offset); | |
| StringTableEntry ste; | |
| if(offset < globalSize) | |
| ste = StringTable->insert(globalStrings + offset); | |
| else | |
| ste = StringTable->insert(""); | |
| U32 count; | |
| st.read(&count); | |
| while(count--) | |
| { | |
| U32 ip; | |
| st.read(&ip); | |
| code[ip] = *((U32 *) &ste); | |
| } | |
| } | |
| if(lineBreakPairCount) | |
| calcBreakList(); | |
| return true; | |
| } | |
| bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *inScript, bool overrideNoDso) | |
| { | |
| // This will return true, but return value is ignored | |
| char *script; | |
| chompUTF8BOM( inScript, &script ); | |
| gSyntaxError = false; | |
| consoleAllocReset(); | |
| STEtoU32 = compileSTEtoU32; | |
| gStatementList = NULL; | |
| // Set up the parser. | |
| smCurrentParser = getParserForFile(fileName); | |
| AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName)); | |
| // Now do some parsing. | |
| smCurrentParser->setScanBuffer(script, fileName); | |
| smCurrentParser->restart(NULL); | |
| smCurrentParser->parse(); | |
| if(gSyntaxError) | |
| { | |
| consoleAllocReset(); | |
| return false; | |
| } | |
| #ifdef TORQUE_NO_DSO_GENERATION | |
| if(!overrideNoDso) | |
| return false; | |
| #endif // !TORQUE_NO_DSO_GENERATION | |
| FileStream st; | |
| if(!st.open(codeFileName, Torque::FS::File::Write)) | |
| return false; | |
| st.write(U32(Con::DSOVersion)); | |
| // Reset all our value tables... | |
| resetTables(); | |
| smInFunction = false; | |
| smBreakLineCount = 0; | |
| setBreakCodeBlock(this); | |
| if(gStatementList) | |
| codeSize = precompileBlock(gStatementList, 0) + 1; | |
| else | |
| codeSize = 1; | |
| lineBreakPairCount = smBreakLineCount; | |
| code = new U32[codeSize + smBreakLineCount * 2]; | |
| lineBreakPairs = code + codeSize; | |
| // Write string table data... | |
| getGlobalStringTable().write(st); | |
| getFunctionStringTable().write(st); | |
| // Write float table data... | |
| getGlobalFloatTable().write(st); | |
| getFunctionFloatTable().write(st); | |
| smBreakLineCount = 0; | |
| U32 lastIp; | |
| if(gStatementList) | |
| lastIp = compileBlock(gStatementList, code, 0, 0, 0); | |
| else | |
| lastIp = 0; | |
| if(lastIp != codeSize - 1) | |
| Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched."); | |
| code[lastIp++] = OP_RETURN; | |
| U32 totSize = codeSize + smBreakLineCount * 2; | |
| st.write(codeSize); | |
| st.write(lineBreakPairCount); | |
| // Write out our bytecode, doing a bit of compression for low numbers. | |
| U32 i; | |
| for(i = 0; i < codeSize; i++) | |
| { | |
| if(code[i] < 0xFF) | |
| st.write(U8(code[i])); | |
| else | |
| { | |
| st.write(U8(0xFF)); | |
| st.write(code[i]); | |
| } | |
| } | |
| // Write the break info... | |
| for(i = codeSize; i < totSize; i++) | |
| st.write(code[i]); | |
| getIdentTable().write(st); | |
| consoleAllocReset(); | |
| st.close(); | |
| return true; | |
| } | |
| const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, int setFrame) | |
| { | |
| // Check for a UTF8 script file | |
| char *string; | |
| chompUTF8BOM( inString, &string ); | |
| STEtoU32 = evalSTEtoU32; | |
| consoleAllocReset(); | |
| name = fileName; | |
| if(fileName) | |
| { | |
| const StringTableEntry exePath = Platform::getMainDotCsDir(); | |
| const StringTableEntry cwd = Platform::getCurrentDirectory(); | |
| fullPath = NULL; | |
| if(Platform::isFullPath(fileName)) | |
| fullPath = fileName; | |
| if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0) | |
| name = StringTable->insert(fileName + dStrlen(exePath) + 1, true); | |
| else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0) | |
| name = StringTable->insert(fileName + dStrlen(cwd) + 1, true); | |
| if(fullPath == NULL) | |
| { | |
| char buf[1024]; | |
| fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true); | |
| } | |
| modPath = Con::getModNameFromPath(fileName); | |
| } | |
| if(name) | |
| addToCodeList(); | |
| gStatementList = NULL; | |
| // Set up the parser. | |
| smCurrentParser = getParserForFile(fileName); | |
| AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName)); | |
| // Now do some parsing. | |
| smCurrentParser->setScanBuffer(string, fileName); | |
| smCurrentParser->restart(NULL); | |
| smCurrentParser->parse(); | |
| if(!gStatementList) | |
| { | |
| delete this; | |
| return ""; | |
| } | |
| resetTables(); | |
| smInFunction = false; | |
| smBreakLineCount = 0; | |
| setBreakCodeBlock(this); | |
| codeSize = precompileBlock(gStatementList, 0) + 1; | |
| lineBreakPairCount = smBreakLineCount; | |
| globalStrings = getGlobalStringTable().build(); | |
| globalStringsMaxLen = getGlobalStringTable().totalLen; | |
| functionStrings = getFunctionStringTable().build(); | |
| functionStringsMaxLen = getFunctionStringTable().totalLen; | |
| globalFloats = getGlobalFloatTable().build(); | |
| functionFloats = getFunctionFloatTable().build(); | |
| code = new U32[codeSize + lineBreakPairCount * 2]; | |
| lineBreakPairs = code + codeSize; | |
| smBreakLineCount = 0; | |
| U32 lastIp = compileBlock(gStatementList, code, 0, 0, 0); | |
| code[lastIp++] = OP_RETURN; | |
| consoleAllocReset(); | |
| if(lineBreakPairCount && fileName) | |
| calcBreakList(); | |
| if(lastIp != codeSize) | |
| Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); | |
| return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame); | |
| } | |
| //------------------------------------------------------------------------- | |
| void CodeBlock::incRefCount() | |
| { | |
| refCount++; | |
| } | |
| void CodeBlock::decRefCount() | |
| { | |
| refCount--; | |
| if(!refCount) | |
| delete this; | |
| } | |
| //------------------------------------------------------------------------- | |
| String CodeBlock::getFunctionArgs( U32 ip ) | |
| { | |
| StringBuilder str; | |
| U32 fnArgc = code[ ip + 5 ]; | |
| for( U32 i = 0; i < fnArgc; ++ i ) | |
| { | |
| StringTableEntry var = U32toSTE( code[ ip + i + 6 ] ); | |
| if( i != 0 ) | |
| str.append( ", " ); | |
| str.append( "string " ); | |
| // Try to capture junked parameters | |
| if( var[ 0 ] ) | |
| str.append( var + 1 ); | |
| else | |
| str.append( "JUNK" ); | |
| } | |
| return str.end(); | |
| } | |
| //------------------------------------------------------------------------- | |
| void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) | |
| { | |
| U32 ip = startIp; | |
| while( ip < codeSize ) | |
| { | |
| switch( code[ ip ++ ] ) | |
| { | |
| case OP_FUNC_DECL: | |
| { | |
| StringTableEntry fnName = U32toSTE(code[ip]); | |
| StringTableEntry fnNamespace = U32toSTE(code[ip+1]); | |
| StringTableEntry fnPackage = U32toSTE(code[ip+2]); | |
| bool hasBody = bool(code[ip+3]); | |
| U32 newIp = code[ ip + 4 ]; | |
| U32 argc = code[ ip + 5 ]; | |
| Con::printf( "%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i", | |
| ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc ); | |
| // Skip args. | |
| ip += 6 + argc; | |
| break; | |
| } | |
| case OP_CREATE_OBJECT: | |
| { | |
| StringTableEntry objParent = U32toSTE(code[ip ]); | |
| bool isDataBlock = code[ip + 1]; | |
| bool isInternal = code[ip + 2]; | |
| bool isSingleton = code[ip + 3]; | |
| U32 lineNumber = code[ip + 4]; | |
| U32 failJump = code[ip + 5]; | |
| Con::printf( "%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i", | |
| ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump ); | |
| ip += 6; | |
| break; | |
| } | |
| case OP_ADD_OBJECT: | |
| { | |
| bool placeAtRoot = code[ip++]; | |
| Con::printf( "%i: OP_ADD_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot ); | |
| break; | |
| } | |
| case OP_END_OBJECT: | |
| { | |
| bool placeAtRoot = code[ip++]; | |
| Con::printf( "%i: OP_END_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot ); | |
| break; | |
| } | |
| case OP_FINISH_OBJECT: | |
| { | |
| Con::printf( "%i: OP_FINISH_OBJECT", ip - 1 ); | |
| break; | |
| } | |
| case OP_JMPIFFNOT: | |
| { | |
| Con::printf( "%i: OP_JMPIFFNOT ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMPIFNOT: | |
| { | |
| Con::printf( "%i: OP_JMPIFNOT ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMPIFF: | |
| { | |
| Con::printf( "%i: OP_JMPIFF ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMPIF: | |
| { | |
| Con::printf( "%i: OP_JMPIF ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMPIFNOT_NP: | |
| { | |
| Con::printf( "%i: OP_JMPIFNOT_NP ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMPIF_NP: | |
| { | |
| Con::printf( "%i: OP_JMPIF_NP ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_JMP: | |
| { | |
| Con::printf( "%i: OP_JMP ip=%i", ip - 1, code[ ip ] ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_RETURN: | |
| { | |
| Con::printf( "%i: OP_RETURN", ip - 1 ); | |
| if( upToReturn ) | |
| return; | |
| break; | |
| } | |
| case OP_RETURN_VOID: | |
| { | |
| Con::printf( "%i: OP_RETURNVOID", ip - 1 ); | |
| if( upToReturn ) | |
| return; | |
| break; | |
| } | |
| case OP_CMPEQ: | |
| { | |
| Con::printf( "%i: OP_CMPEQ", ip - 1 ); | |
| break; | |
| } | |
| case OP_CMPGR: | |
| { | |
| Con::printf( "%i: OP_CMPGR", ip - 1 ); | |
| break; | |
| } | |
| case OP_CMPGE: | |
| { | |
| Con::printf( "%i: OP_CMPGE", ip - 1 ); | |
| break; | |
| } | |
| case OP_CMPLT: | |
| { | |
| Con::printf( "%i: OP_CMPLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_CMPLE: | |
| { | |
| Con::printf( "%i: OP_CMPLE", ip - 1 ); | |
| break; | |
| } | |
| case OP_CMPNE: | |
| { | |
| Con::printf( "%i: OP_CMPNE", ip - 1 ); | |
| break; | |
| } | |
| case OP_XOR: | |
| { | |
| Con::printf( "%i: OP_XOR", ip - 1 ); | |
| break; | |
| } | |
| case OP_MOD: | |
| { | |
| Con::printf( "%i: OP_MOD", ip - 1 ); | |
| break; | |
| } | |
| case OP_BITAND: | |
| { | |
| Con::printf( "%i: OP_BITAND", ip - 1 ); | |
| break; | |
| } | |
| case OP_BITOR: | |
| { | |
| Con::printf( "%i: OP_BITOR", ip - 1 ); | |
| break; | |
| } | |
| case OP_NOT: | |
| { | |
| Con::printf( "%i: OP_NOT", ip - 1 ); | |
| break; | |
| } | |
| case OP_NOTF: | |
| { | |
| Con::printf( "%i: OP_NOTF", ip - 1 ); | |
| break; | |
| } | |
| case OP_ONESCOMPLEMENT: | |
| { | |
| Con::printf( "%i: OP_ONESCOMPLEMENT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SHR: | |
| { | |
| Con::printf( "%i: OP_SHR", ip - 1 ); | |
| break; | |
| } | |
| case OP_SHL: | |
| { | |
| Con::printf( "%i: OP_SHL", ip - 1 ); | |
| break; | |
| } | |
| case OP_AND: | |
| { | |
| Con::printf( "%i: OP_AND", ip - 1 ); | |
| break; | |
| } | |
| case OP_OR: | |
| { | |
| Con::printf( "%i: OP_OR", ip - 1 ); | |
| break; | |
| } | |
| case OP_ADD: | |
| { | |
| Con::printf( "%i: OP_ADD", ip - 1 ); | |
| break; | |
| } | |
| case OP_SUB: | |
| { | |
| Con::printf( "%i: OP_SUB", ip - 1 ); | |
| break; | |
| } | |
| case OP_MUL: | |
| { | |
| Con::printf( "%i: OP_MUL", ip - 1 ); | |
| break; | |
| } | |
| case OP_DIV: | |
| { | |
| Con::printf( "%i: OP_DIV", ip - 1 ); | |
| break; | |
| } | |
| case OP_NEG: | |
| { | |
| Con::printf( "%i: OP_NEG", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCURVAR: | |
| { | |
| StringTableEntry var = U32toSTE(code[ip]); | |
| Con::printf( "%i: OP_SETCURVAR var=%s", ip - 1, var ); | |
| ip++; | |
| break; | |
| } | |
| case OP_SETCURVAR_CREATE: | |
| { | |
| StringTableEntry var = U32toSTE(code[ip]); | |
| Con::printf( "%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var ); | |
| ip++; | |
| break; | |
| } | |
| case OP_SETCURVAR_ARRAY: | |
| { | |
| Con::printf( "%i: OP_SETCURVAR_ARRAY", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCURVAR_ARRAY_CREATE: | |
| { | |
| Con::printf( "%i: OP_SETCURVAR_ARRAY_CREATE", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADVAR_UINT: | |
| { | |
| Con::printf( "%i: OP_LOADVAR_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADVAR_FLT: | |
| { | |
| Con::printf( "%i: OP_LOADVAR_FLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADVAR_STR: | |
| { | |
| Con::printf( "%i: OP_LOADVAR_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEVAR_UINT: | |
| { | |
| Con::printf( "%i: OP_SAVEVAR_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEVAR_FLT: | |
| { | |
| Con::printf( "%i: OP_SAVEVAR_FLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEVAR_STR: | |
| { | |
| Con::printf( "%i: OP_SAVEVAR_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCUROBJECT: | |
| { | |
| Con::printf( "%i: OP_SETCUROBJECT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCUROBJECT_NEW: | |
| { | |
| Con::printf( "%i: OP_SETCUROBJECT_NEW", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCUROBJECT_INTERNAL: | |
| { | |
| Con::printf( "%i: OP_SETCUROBJECT_INTERNAL", ip - 1 ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_SETCURFIELD: | |
| { | |
| StringTableEntry curField = U32toSTE(code[ip]); | |
| Con::printf( "%i: OP_SETCURFIELD field=%s", ip - 1, curField ); | |
| ++ ip; | |
| } | |
| case OP_SETCURFIELD_ARRAY: | |
| { | |
| Con::printf( "%i: OP_SETCURFIELD_ARRAY", ip - 1 ); | |
| break; | |
| } | |
| case OP_SETCURFIELD_TYPE: | |
| { | |
| U32 type = code[ ip ]; | |
| Con::printf( "%i: OP_SETCURFIELD_TYPE type=%i", ip - 1, type ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_LOADFIELD_UINT: | |
| { | |
| Con::printf( "%i: OP_LOADFIELD_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADFIELD_FLT: | |
| { | |
| Con::printf( "%i: OP_LOADFIELD_FLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADFIELD_STR: | |
| { | |
| Con::printf( "%i: OP_LOADFIELD_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEFIELD_UINT: | |
| { | |
| Con::printf( "%i: OP_SAVEFIELD_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEFIELD_FLT: | |
| { | |
| Con::printf( "%i: OP_SAVEFIELD_FLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_SAVEFIELD_STR: | |
| { | |
| Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_STR_TO_UINT: | |
| { | |
| Con::printf( "%i: OP_STR_TO_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_STR_TO_FLT: | |
| { | |
| Con::printf( "%i: OP_STR_TO_FLT", ip - 1 ); | |
| break; | |
| } | |
| case OP_STR_TO_NONE: | |
| { | |
| Con::printf( "%i: OP_STR_TO_NONE", ip - 1 ); | |
| break; | |
| } | |
| case OP_FLT_TO_UINT: | |
| { | |
| Con::printf( "%i: OP_FLT_TO_UINT", ip - 1 ); | |
| break; | |
| } | |
| case OP_FLT_TO_STR: | |
| { | |
| Con::printf( "%i: OP_FLT_TO_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_FLT_TO_NONE: | |
| { | |
| Con::printf( "%i: OP_FLT_TO_NONE", ip - 1 ); | |
| break; | |
| } | |
| case OP_UINT_TO_FLT: | |
| { | |
| Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_UINT_TO_STR: | |
| { | |
| Con::printf( "%i: OP_UINT_TO_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_UINT_TO_NONE: | |
| { | |
| Con::printf( "%i: OP_UINT_TO_NONE", ip - 1 ); | |
| break; | |
| } | |
| case OP_LOADIMMED_UINT: | |
| { | |
| U32 val = code[ ip ]; | |
| Con::printf( "%i: OP_LOADIMMED_UINT val=%i", ip - 1, val ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_LOADIMMED_FLT: | |
| { | |
| F64 val = functionFloats[ code[ ip ] ]; | |
| Con::printf( "%i: OP_LOADIMMED_FLT val=%f", ip - 1, val ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_TAG_TO_STR: | |
| { | |
| const char* str = functionStrings + code[ ip ]; | |
| Con::printf( "%i: OP_TAG_TO_STR str=%s", ip - 1, str ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_LOADIMMED_STR: | |
| { | |
| const char* str = functionStrings + code[ ip ]; | |
| Con::printf( "%i: OP_LOADIMMED_STR str=%s", ip - 1, str ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_DOCBLOCK_STR: | |
| { | |
| const char* str = functionStrings + code[ ip ]; | |
| Con::printf( "%i: OP_DOCBLOCK_STR str=%s", ip - 1, str ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_LOADIMMED_IDENT: | |
| { | |
| StringTableEntry str = U32toSTE( code[ ip ] ); | |
| Con::printf( "%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_CALLFUNC_RESOLVE: | |
| { | |
| StringTableEntry fnNamespace = U32toSTE(code[ip+1]); | |
| StringTableEntry fnName = U32toSTE(code[ip]); | |
| U32 callType = code[ip+2]; | |
| Con::printf( "%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, | |
| callType == FuncCallExprNode::FunctionCall ? "FunctionCall" | |
| : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); | |
| ip += 3; | |
| break; | |
| } | |
| case OP_CALLFUNC: | |
| { | |
| StringTableEntry fnNamespace = U32toSTE(code[ip+1]); | |
| StringTableEntry fnName = U32toSTE(code[ip]); | |
| U32 callType = code[ip+2]; | |
| Con::printf( "%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, | |
| callType == FuncCallExprNode::FunctionCall ? "FunctionCall" | |
| : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); | |
| ip += 3; | |
| break; | |
| } | |
| case OP_ADVANCE_STR: | |
| { | |
| Con::printf( "%i: OP_ADVANCE_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_ADVANCE_STR_APPENDCHAR: | |
| { | |
| char ch = code[ ip ]; | |
| Con::printf( "%i: OP_ADVANCE_STR_APPENDCHAR char=%c", ip - 1, ch ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_ADVANCE_STR_COMMA: | |
| { | |
| Con::printf( "%i: OP_ADVANCE_STR_COMMA", ip - 1 ); | |
| break; | |
| } | |
| case OP_ADVANCE_STR_NUL: | |
| { | |
| Con::printf( "%i: OP_ADVANCE_STR_NUL", ip - 1 ); | |
| break; | |
| } | |
| case OP_REWIND_STR: | |
| { | |
| Con::printf( "%i: OP_REWIND_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_TERMINATE_REWIND_STR: | |
| { | |
| Con::printf( "%i: OP_TERMINATE_REWIND_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_COMPARE_STR: | |
| { | |
| Con::printf( "%i: OP_COMPARE_STR", ip - 1 ); | |
| break; | |
| } | |
| case OP_PUSH: | |
| { | |
| Con::printf( "%i: OP_PUSH", ip - 1 ); | |
| break; | |
| } | |
| case OP_PUSH_FRAME: | |
| { | |
| Con::printf( "%i: OP_PUSH_FRAME", ip - 1 ); | |
| break; | |
| } | |
| case OP_ASSERT: | |
| { | |
| const char* message = functionStrings + code[ ip ]; | |
| Con::printf( "%i: OP_ASSERT message=%s", ip - 1, message ); | |
| ++ ip; | |
| break; | |
| } | |
| case OP_BREAK: | |
| { | |
| Con::printf( "%i: OP_BREAK", ip - 1 ); | |
| break; | |
| } | |
| case OP_ITER_BEGIN: | |
| { | |
| StringTableEntry varName = U32toSTE( code[ ip ] ); | |
| U32 failIp = code[ ip + 1 ]; | |
| Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); | |
| ++ ip; | |
| } | |
| case OP_ITER_BEGIN_STR: | |
| { | |
| StringTableEntry varName = U32toSTE( code[ ip ] ); | |
| U32 failIp = code[ ip + 1 ]; | |
| Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); | |
| ip += 2; | |
| } | |
| case OP_ITER: | |
| { | |
| U32 breakIp = code[ ip ]; | |
| Con::printf( "%i: OP_ITER breakIp=%i", breakIp ); | |
| ++ ip; | |
| } | |
| case OP_ITER_END: | |
| { | |
| Con::printf( "%i: OP_ITER_END", ip - 1 ); | |
| break; | |
| } | |
| default: | |
| Con::printf( "%i: !!INVALID!!", ip - 1 ); | |
| break; | |
| } | |
| } | |
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef _CODEBLOCK_H_ | |
| #define _CODEBLOCK_H_ | |
| #include "console/compiler.h" | |
| #include "console/consoleParser.h" | |
| // FHC - compiled eval | |
| #include "console/consoleInternal.h" | |
| #include "core/frameAllocator.h" | |
| #include "console/stringStack.h" | |
| class Stream; | |
| /// Core TorqueScript code management class. | |
| /// | |
| /// This class represents a block of code, usually mapped directly to a file. | |
| class CodeBlock | |
| { | |
| private: | |
| static CodeBlock* smCodeBlockList; | |
| static CodeBlock* smCurrentCodeBlock; | |
| public: | |
| static U32 smBreakLineCount; | |
| static bool smInFunction; | |
| static Compiler::ConsoleParser * smCurrentParser; | |
| static CodeBlock* getCurrentBlock() | |
| { | |
| return smCurrentCodeBlock; | |
| } | |
| static CodeBlock *getCodeBlockList() | |
| { | |
| return smCodeBlockList; | |
| } | |
| static StringTableEntry getCurrentCodeBlockName(); | |
| static StringTableEntry getCurrentCodeBlockFullPath(); | |
| static StringTableEntry getCurrentCodeBlockModName(); | |
| static CodeBlock *find(StringTableEntry); | |
| CodeBlock(); | |
| ~CodeBlock(); | |
| StringTableEntry name; | |
| StringTableEntry fullPath; | |
| StringTableEntry modPath; | |
| char *globalStrings; | |
| char *functionStrings; | |
| U32 functionStringsMaxLen; | |
| U32 globalStringsMaxLen; | |
| F64 *globalFloats; | |
| F64 *functionFloats; | |
| U32 codeSize; | |
| U32 *code; | |
| U32 refCount; | |
| U32 lineBreakPairCount; | |
| U32 *lineBreakPairs; | |
| U32 breakListSize; | |
| U32 *breakList; | |
| CodeBlock *nextFile; | |
| void addToCodeList(); | |
| void removeFromCodeList(); | |
| void calcBreakList(); | |
| void clearAllBreaks(); | |
| void setAllBreaks(); | |
| void dumpInstructions( U32 startIp = 0, bool upToReturn = false ); | |
| /// Returns the first breakable line or 0 if none was found. | |
| /// @param lineNumber The one based line number. | |
| U32 findFirstBreakLine(U32 lineNumber); | |
| void clearBreakpoint(U32 lineNumber); | |
| /// Set a OP_BREAK instruction on a line. If a break | |
| /// is not possible on that line it returns false. | |
| /// @param lineNumber The one based line number. | |
| bool setBreakpoint(U32 lineNumber); | |
| void findBreakLine(U32 ip, U32 &line, U32 &instruction); | |
| const char *getFileLine(U32 ip); | |
| /// | |
| String getFunctionArgs( U32 offset ); | |
| bool read(StringTableEntry fileName, Stream &st); | |
| bool compile(const char *dsoName, StringTableEntry fileName, const char *script, bool overrideNoDso = false); | |
| void incRefCount(); | |
| void decRefCount(); | |
| /// Compiles and executes a block of script storing the compiled code in this | |
| /// CodeBlock. If there is no filename breakpoints will not be generated and | |
| /// the CodeBlock will not be added to the linked list of loaded CodeBlocks. | |
| /// Note that if the script contains no executable statements the CodeBlock | |
| /// will delete itself on return an empty string. The return string is any | |
| /// result of the code executed, if any, or an empty string. | |
| /// | |
| /// @param fileName The file name, including path and extension, for the | |
| /// block of code or an empty string. | |
| /// @param script The script code to compile and execute. | |
| /// @param noCalls Skips calling functions from the script. | |
| /// @param setFrame A zero based index of the stack frame to execute the code | |
| /// with, zero being the top of the stack. If the the index is | |
| /// -1 a new frame is created. If the index is out of range the | |
| /// top stack frame is used. | |
| const char *compileExec(StringTableEntry fileName, const char *script, | |
| bool noCalls, int setFrame = -1 ); | |
| /// Executes the existing code in the CodeBlock. The return string is any | |
| /// result of the code executed, if any, or an empty string. | |
| /// | |
| /// @param offset The instruction offset to start executing from. | |
| /// @param fnName The name of the function to execute or null. | |
| /// @param ns The namespace of the function to execute or null. | |
| /// @param argc The number of parameters passed to the function or | |
| /// zero to execute code outside of a function. | |
| /// @param argv The function parameter list. | |
| /// @param noCalls Skips calling functions from the script. | |
| /// @param setFrame A zero based index of the stack frame to execute the code | |
| /// with, zero being the top of the stack. If the the index is | |
| /// -1 a new frame is created. If the index is out of range the | |
| /// top stack frame is used. | |
| /// @param packageName The code package name or null. | |
| const char *exec(U32 offset, const char *fnName, Namespace *ns, U32 argc, | |
| const char **argv, bool noCalls, StringTableEntry packageName, | |
| S32 setFrame = -1); | |
| // FHC - compiled eval | |
| private: | |
| const char *execOrig(U32 offset, const char *fnName, Namespace *ns, U32 argc, | |
| const char **argv, bool noCalls, StringTableEntry packageName, | |
| S32 setFrame); | |
| const char *execNew(U32 offset, const char *fnName, Namespace *ns, U32 argc, | |
| const char **argv, bool noCalls, StringTableEntry packageName, | |
| S32 setFrame); | |
| const char *(CodeBlock::*execPtr)(U32 offset, const char *fnName, Namespace *ns, U32 argc, | |
| const char **argv, bool noCalls, StringTableEntry packageName, | |
| S32 setFrame); | |
| // OpCode lookup | |
| // There are currently 90 opcodes including OP_EMPTY. | |
| // Will use a binary mask to limit access to the end of the array. | |
| // The amount of opcodes slots allocated must be a power of 2 and greater than OP_EMPTY (128,256,512,etc) | |
| // Any access to opcodes up and including OP_EMPTY opcode will result in the compile eval existing. | |
| // This is currently how the loop operates except it does an explicit check. Theorectically bad | |
| // opcodes could appear at a number below 90. But this is likely to appear above and cause an exit | |
| // of the script. Also, if we have bad opcodes making it here, we have other issues. | |
| #define MAX_OPCODE_SLOTS 128 | |
| typedef void (CodeBlock::*opcode_func_ptr)(); | |
| opcode_func_ptr opcodeList[MAX_OPCODE_SLOTS]; | |
| // function call vars passed to exec | |
| // these are used by the opcode functions | |
| static U32 *ip; | |
| static const char **functionName; | |
| static Namespace **thisNamespace; | |
| static U32 *argc; | |
| static const char ***argv; | |
| static bool *noCalls; | |
| static StringTableEntry *packageName; | |
| static S32 *setFrame; | |
| static SimObject **currentNewObject; | |
| typedef struct ExecCallStackEntry{ | |
| U32 ip; | |
| const char *functionName; | |
| Namespace *thisNamespace; | |
| U32 argc; | |
| const char **argv; | |
| bool noCalls; | |
| StringTableEntry packageName; | |
| S32 setFrame; | |
| SimObject *currentNewObject; | |
| ExecCallStackEntry(){} | |
| ExecCallStackEntry(U32 _ip, const char *_functionName, Namespace *_thisNamespace, U32 _argc, const char **_argv, bool _noCalls, StringTableEntry _packageName, S32 _setFrame){ | |
| ip=_ip; | |
| functionName=_functionName; | |
| thisNamespace=_thisNamespace; | |
| argc=_argc; | |
| argv=_argv; | |
| noCalls=_noCalls; | |
| packageName=_packageName; | |
| setFrame=_setFrame; | |
| currentNewObject=NULL; | |
| } | |
| }; | |
| static Vector<ExecCallStackEntry> ExecCallStack; | |
| void stackPush(ExecCallStackEntry *tmp){ | |
| ExecCallStack.push_front(*tmp); | |
| tmp = &(ExecCallStack.front()); | |
| ip = &(tmp->ip); | |
| functionName = &(tmp->functionName); | |
| thisNamespace = &(tmp->thisNamespace); | |
| argc = &(tmp->argc); | |
| argv = &(tmp->argv); | |
| noCalls = &(tmp->noCalls); | |
| packageName = &(tmp->packageName); | |
| setFrame = &(tmp->setFrame); | |
| currentNewObject = &(tmp->currentNewObject); | |
| } | |
| void stackPop(){ | |
| //Con::printf("stack size: %d",ExecCallStack.size()); | |
| ExecCallStack.pop_front(); | |
| if(ExecCallStack.size() > 0){ | |
| ExecCallStackEntry *tmp = &(ExecCallStack.front()); | |
| ip = &(tmp->ip); | |
| functionName = &(tmp->functionName); | |
| thisNamespace = &(tmp->thisNamespace); | |
| argc = &(tmp->argc); | |
| argv = &(tmp->argv); | |
| noCalls = &(tmp->noCalls); | |
| packageName = &(tmp->packageName); | |
| setFrame = &(tmp->setFrame); | |
| currentNewObject = &(tmp->currentNewObject); | |
| } | |
| } | |
| // variables declared in the course the exec call | |
| char traceBuffer[1024]; | |
| U32 i; | |
| U32 iterDepth; | |
| F64 *curFloatTable; | |
| char *curStringTable; | |
| S32 curStringTableLen; | |
| StringTableEntry thisFunctionName; | |
| bool popFrame; | |
| bool telDebuggerOn; | |
| StringTableEntry var, objParent; | |
| StringTableEntry fnName; | |
| StringTableEntry fnNamespace, fnPackage; | |
| static const U32 objectCreationStackSize = 32; | |
| U32 objectCreationStackIndex; | |
| struct { | |
| SimObject *newObject; | |
| U32 failJump; | |
| } objectCreationStack[ objectCreationStackSize ]; | |
| //SimObject *currentNewObject; | |
| U32 failJump; | |
| StringTableEntry prevField; | |
| StringTableEntry curField; | |
| SimObject *prevObject; | |
| SimObject *curObject; | |
| SimObject *saveObject; | |
| Namespace::Entry *nsEntry; | |
| Namespace *ns; | |
| const char* curFNDocBlock; | |
| const char* curNSDocBlock; | |
| static const S32 nsDocLength = 128; | |
| char nsDocBlockClass[nsDocLength]; | |
| U32 callArgc; | |
| const char **callArgv; | |
| static char curFieldArray[256]; | |
| static char prevFieldArray[256]; | |
| //CodeBlock *saveCodeBlock; | |
| const char * val; | |
| // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and | |
| // OP_LOADFIELD_*) to store temporary values for the fields. | |
| static const S32 VAL_BUFFER_SIZE = 1024; | |
| FrameTemp<char> valBuffer; | |
| U32 instruction; | |
| private: | |
| enum EvalConstants { | |
| MaxStackSize = 1024, | |
| MethodOnComponent = -2 | |
| }; | |
| U32 _FLT; ///< Stack pointer for floatStack. | |
| U32 _UINT; ///< Stack pointer for intStack. | |
| U32 _ITER; ///< Stack pointer for iterStack. | |
| /// Frame data for a foreach/foreach$ loop. | |
| struct IterStackRecord | |
| { | |
| /// If true, this is a foreach$ loop; if not, it's a foreach loop. | |
| bool mIsStringIter; | |
| /// The iterator variable. | |
| Dictionary::Entry* mVariable; | |
| /// Information for an object iterator loop. | |
| struct ObjectPos | |
| { | |
| /// The set being iterated over. | |
| SimSet* mSet; | |
| /// Current index in the set. | |
| U32 mIndex; | |
| }; | |
| /// Information for a string iterator loop. | |
| struct StringPos | |
| { | |
| /// The raw string data on the string stack. | |
| const char* mString; | |
| /// Current parsing position. | |
| U32 mIndex; | |
| }; | |
| union | |
| { | |
| ObjectPos mObj; | |
| StringPos mStr; | |
| } mData; | |
| }; | |
| IterStackRecord iterStack[ MaxStackSize ]; | |
| F64 floatStack[MaxStackSize]; | |
| S64 intStack[MaxStackSize]; | |
| // support functions | |
| void CodeBlock::_getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ); | |
| void CodeBlock::_setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ); | |
| // return functions | |
| inline void CodeBlock::op_next_call(); | |
| inline void CodeBlock::op_next_call_nobreak(); | |
| // OpCode functions | |
| // object ops | |
| void op_func_decl(); | |
| void op_create_object(); | |
| void op_add_object(); | |
| void op_end_object(); | |
| void op_finish_object(); | |
| // jmp ops | |
| void op_jmpiffnot(); | |
| void op_jmpifnot(); | |
| void op_jmpiff(); | |
| void op_jmpif(); | |
| void op_jmpifnot_np(); | |
| void op_jmpif_np(); | |
| void op_jmp(); | |
| // return | |
| void op_return_void(); | |
| void op_return(); | |
| // cmp | |
| void op_cmpeq(); | |
| void op_cmpgr(); | |
| void op_cmpge(); | |
| void op_cmplt(); | |
| void op_cmple(); | |
| void op_cmpne(); | |
| // logic | |
| void op_xor(); | |
| void op_mod(); | |
| void op_bitand(); | |
| void op_bitor(); | |
| void op_not(); | |
| void op_notf(); | |
| void op_onescomplement(); | |
| void op_shr(); | |
| void op_shl(); | |
| void op_and(); | |
| void op_or(); | |
| void op_add(); | |
| void op_sub(); | |
| void op_mul(); | |
| void op_div(); | |
| void op_neg(); | |
| // vars | |
| void op_setcurvar(); | |
| void op_setcurvar_create(); | |
| void op_setcurvar_array(); | |
| void op_setcurvar_array_create(); | |
| void op_loadvar_uint(); | |
| void op_loadvar_flt(); | |
| void op_loadvar_str(); | |
| void op_savevar_uint(); | |
| void op_savevar_flt(); | |
| void op_savevar_str(); | |
| // objects | |
| void op_setcurobject(); | |
| void op_setcurobject_internal(); | |
| void op_setcurobject_new(); | |
| // fields | |
| void op_setcurfield(); | |
| void op_setcurfield_array(); | |
| void op_setcurfield_type(); | |
| void op_loadfield_uint(); | |
| void op_loadfield_flt(); | |
| void op_loadfield_str(); | |
| void op_savefield_uint(); | |
| void op_savefield_flt(); | |
| void op_savefield_str(); | |
| // convert | |
| void op_str_to_uint(); | |
| void op_str_to_flt(); | |
| void op_str_to_none(); | |
| void op_flt_to_uint(); | |
| void op_flt_to_str(); | |
| void op_flt_to_none(); | |
| void op_uint_to_flt(); | |
| void op_uint_to_str(); | |
| void op_uint_to_none(); | |
| void op_loadimmed_uint(); | |
| void op_loadimmed_flt(); | |
| void op_tag_to_str(); | |
| void op_loadimmed_str(); | |
| void op_docblock_str(); | |
| void op_loadimmed_ident(); | |
| // callfunc | |
| void op_callfunc_resolve(); | |
| void op_callfunc(); | |
| // advance | |
| void op_advance_str(); | |
| void op_advance_str_appendchar(); | |
| void op_advance_str_comma(); | |
| void op_advance_str_nul(); | |
| void op_rewind_str(); | |
| void op_terminate_rewind_str(); | |
| // compare | |
| void op_compare_str(); | |
| // stack | |
| void op_push(); | |
| void op_push_frame(); | |
| // debug | |
| void op_assert(); | |
| void op_break(); | |
| // iter | |
| void op_iter_begin_str(); | |
| void op_iter_begin(); | |
| void op_iter(); | |
| void op_iter_end(); | |
| // invalid | |
| void op_invalid(); | |
| // debug | |
| static bool printopcodes; | |
| }; | |
| // global | |
| extern StringStack STR; | |
| //#define _STR STR | |
| // pointer to member macro | |
| #define CALL_MEMBER_FN_PTR(object,ptrToMember) ((object).*(ptrToMember)) | |
| #define DB_IP 0 | |
| #define DB_OPCODE 71 | |
| #endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "platform/platform.h" | |
| #include "console/console.h" | |
| #include "console/ast.h" | |
| #include "core/tAlgorithm.h" | |
| #include "core/strings/findMatch.h" | |
| #include "core/strings/stringUnit.h" | |
| #include "console/consoleInternal.h" | |
| #include "core/stream/fileStream.h" | |
| #include "console/compiler.h" | |
| #include "console/simBase.h" | |
| #include "console/telnetDebugger.h" | |
| #include "sim/netStringTable.h" | |
| #include "console/ICallMethod.h" | |
| #include "console/stringStack.h" | |
| #include "util/messaging/message.h" | |
| #include "core/frameAllocator.h" | |
| #ifndef TORQUE_TGB_ONLY | |
| #include "materials/materialDefinition.h" | |
| #include "materials/materialManager.h" | |
| #endif | |
| using namespace Compiler; | |
| enum EvalConstants { | |
| MaxStackSize = 1024, | |
| MethodOnComponent = -2 | |
| }; | |
| namespace Con | |
| { | |
| // Current script file name and root, these are registered as | |
| // console variables. | |
| extern StringTableEntry gCurrentFile; | |
| extern StringTableEntry gCurrentRoot; | |
| extern S32 gObjectCopyFailures; | |
| } | |
| /// Frame data for a foreach/foreach$ loop. | |
| struct IterStackRecord | |
| { | |
| /// If true, this is a foreach$ loop; if not, it's a foreach loop. | |
| bool mIsStringIter; | |
| /// The iterator variable. | |
| Dictionary::Entry* mVariable; | |
| /// Information for an object iterator loop. | |
| struct ObjectPos | |
| { | |
| /// The set being iterated over. | |
| SimSet* mSet; | |
| /// Current index in the set. | |
| U32 mIndex; | |
| }; | |
| /// Information for a string iterator loop. | |
| struct StringPos | |
| { | |
| /// The raw string data on the string stack. | |
| const char* mString; | |
| /// Current parsing position. | |
| U32 mIndex; | |
| }; | |
| union | |
| { | |
| ObjectPos mObj; | |
| StringPos mStr; | |
| } mData; | |
| }; | |
| IterStackRecord iterStack[ MaxStackSize ]; | |
| F64 floatStack[MaxStackSize]; | |
| S64 intStack[MaxStackSize]; | |
| StringStack STR; | |
| // codeblock stack | |
| U32* CodeBlock::ip; | |
| const char** CodeBlock::functionName; | |
| Namespace** CodeBlock::thisNamespace; | |
| U32* CodeBlock::argc; | |
| const char*** CodeBlock::argv; | |
| bool* CodeBlock::noCalls; | |
| StringTableEntry* CodeBlock::packageName; | |
| S32* CodeBlock::setFrame; | |
| SimObject** CodeBlock::currentNewObject; | |
| Vector<CodeBlock::ExecCallStackEntry> CodeBlock::ExecCallStack; | |
| //U32 _FLT = 0; ///< Stack pointer for floatStack. | |
| //U32 _UINT = 0; ///< Stack pointer for intStack. | |
| //U32 _ITER = 0; ///< Stack pointer for iterStack. | |
| namespace Con | |
| { | |
| const char *getNamespaceList(Namespace *ns) | |
| { | |
| U32 size = 1; | |
| Namespace * walk; | |
| for(walk = ns; walk; walk = walk->mParent) | |
| size += dStrlen(walk->mName) + 4; | |
| char *ret = Con::getReturnBuffer(size); | |
| ret[0] = 0; | |
| for(walk = ns; walk; walk = walk->mParent) | |
| { | |
| dStrcat(ret, walk->mName); | |
| if(walk->mParent) | |
| dStrcat(ret, " -> "); | |
| } | |
| return ret; | |
| } | |
| } | |
| //------------------------------------------------------------ | |
| F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line) | |
| { | |
| F64 val = dAtof(str); | |
| if(val != 0) | |
| return val; | |
| else if(!dStricmp(str, "true")) | |
| return 1; | |
| else if(!dStricmp(str, "false")) | |
| return 0; | |
| else if(file) | |
| { | |
| Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line); | |
| return 0; | |
| } | |
| return 0; | |
| } | |
| //------------------------------------------------------------ | |
| namespace Con | |
| { | |
| char *getReturnBuffer(U32 bufferSize) | |
| { | |
| return STR.getReturnBuffer(bufferSize); | |
| } | |
| char *getReturnBuffer( const char *stringToCopy ) | |
| { | |
| U32 len = dStrlen( stringToCopy ) + 1; | |
| char *ret = STR.getReturnBuffer( len); | |
| dMemcpy( ret, stringToCopy, len ); | |
| return ret; | |
| } | |
| char* getReturnBuffer( const String& str ) | |
| { | |
| const U32 size = str.size(); | |
| char* ret = STR.getReturnBuffer( size ); | |
| dMemcpy( ret, str.c_str(), size ); | |
| return ret; | |
| } | |
| char* getReturnBuffer( const StringBuilder& str ) | |
| { | |
| char* buffer = Con::getReturnBuffer( str.length() + 1 ); | |
| str.copy( buffer ); | |
| buffer[ str.length() ] = '\0'; | |
| return buffer; | |
| } | |
| char *getArgBuffer(U32 bufferSize) | |
| { | |
| return STR.getArgBuffer(bufferSize); | |
| } | |
| char *getFloatArg(F64 arg) | |
| { | |
| char *ret = STR.getArgBuffer(32); | |
| dSprintf(ret, 32, "%g", arg); | |
| return ret; | |
| } | |
| char *getIntArg(S32 arg) | |
| { | |
| char *ret = STR.getArgBuffer(32); | |
| dSprintf(ret, 32, "%d", arg); | |
| return ret; | |
| } | |
| char *getStringArg( const char *arg ) | |
| { | |
| U32 len = dStrlen( arg ) + 1; | |
| char *ret = STR.getArgBuffer( len ); | |
| dMemcpy( ret, arg, len ); | |
| return ret; | |
| } | |
| char* getStringArg( const String& arg ) | |
| { | |
| const U32 size = arg.size(); | |
| char* ret = STR.getArgBuffer( size ); | |
| dMemcpy( ret, arg.c_str(), size ); | |
| return ret; | |
| } | |
| } | |
| //------------------------------------------------------------ | |
| inline void ExprEvalState::setCurVarName(StringTableEntry name) | |
| { | |
| if(name[0] == '$') | |
| currentVariable = globalVars.lookup(name); | |
| else if( getStackDepth() > 0 ) | |
| currentVariable = getCurrentFrame().lookup(name); | |
| if(!currentVariable && gWarnUndefinedScriptVariables) | |
| Con::warnf(ConsoleLogEntry::Script, "Variable referenced before assignment: %s", name); | |
| } | |
| inline void ExprEvalState::setCurVarNameCreate(StringTableEntry name) | |
| { | |
| if(name[0] == '$') | |
| currentVariable = globalVars.add(name); | |
| else if( getStackDepth() > 0 ) | |
| currentVariable = getCurrentFrame().add(name); | |
| else | |
| { | |
| currentVariable = NULL; | |
| Con::warnf(ConsoleLogEntry::Script, "Accessing local variable in global scope... failed: %s", name); | |
| } | |
| } | |
| //------------------------------------------------------------ | |
| inline S32 ExprEvalState::getIntVariable() | |
| { | |
| return currentVariable ? currentVariable->getIntValue() : 0; | |
| } | |
| inline F64 ExprEvalState::getFloatVariable() | |
| { | |
| return currentVariable ? currentVariable->getFloatValue() : 0; | |
| } | |
| inline const char *ExprEvalState::getStringVariable() | |
| { | |
| return currentVariable ? currentVariable->getStringValue() : ""; | |
| } | |
| //------------------------------------------------------------ | |
| inline void ExprEvalState::setIntVariable(S32 val) | |
| { | |
| AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); | |
| currentVariable->setIntValue(val); | |
| } | |
| inline void ExprEvalState::setFloatVariable(F64 val) | |
| { | |
| AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); | |
| currentVariable->setFloatValue(val); | |
| } | |
| inline void ExprEvalState::setStringVariable(const char *val) | |
| { | |
| AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); | |
| currentVariable->setStringValue(val); | |
| } | |
| //------------------------------------------------------------ | |
| // Gets a component of an object's field value or a variable and returns it | |
| // in val. | |
| static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ) | |
| { | |
| const char* prevVal = NULL; | |
| // Grab value from object. | |
| if( object && field ) | |
| prevVal = object->getDataField( field, array ); | |
| // Otherwise, grab from the string stack. The value coming in will always | |
| // be a string because that is how multicomponent variables are handled. | |
| else | |
| prevVal = STR.getStringValue(); | |
| // Make sure we got a value. | |
| if ( prevVal && *prevVal ) | |
| { | |
| static const StringTableEntry xyzw[] = | |
| { | |
| StringTable->insert( "x" ), | |
| StringTable->insert( "y" ), | |
| StringTable->insert( "z" ), | |
| StringTable->insert( "w" ) | |
| }; | |
| static const StringTableEntry rgba[] = | |
| { | |
| StringTable->insert( "r" ), | |
| StringTable->insert( "g" ), | |
| StringTable->insert( "b" ), | |
| StringTable->insert( "a" ) | |
| }; | |
| // Translate xyzw and rgba into the indexed component | |
| // of the variable or field. | |
| if ( subField == xyzw[0] || subField == rgba[0] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") ); | |
| else if ( subField == xyzw[1] || subField == rgba[1] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") ); | |
| else if ( subField == xyzw[2] || subField == rgba[2] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") ); | |
| else if ( subField == xyzw[3] || subField == rgba[3] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") ); | |
| else | |
| val[0] = 0; | |
| } | |
| else | |
| val[0] = 0; | |
| } | |
| // Sets a component of an object's field value based on the sub field. 'x' will | |
| // set the first field, 'y' the second, and 'z' the third. | |
| static void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ) | |
| { | |
| // Copy the current string value | |
| char strValue[1024]; | |
| dStrncpy( strValue, STR.getStringValue(), 1024 ); | |
| char val[1024] = ""; | |
| const char* prevVal = NULL; | |
| // Set the value on an object field. | |
| if( object && field ) | |
| prevVal = object->getDataField( field, array ); | |
| // Set the value on a variable. | |
| else if( gEvalState.currentVariable ) | |
| prevVal = gEvalState.getStringVariable(); | |
| // Ensure that the variable has a value | |
| if (!prevVal) | |
| return; | |
| static const StringTableEntry xyzw[] = | |
| { | |
| StringTable->insert( "x" ), | |
| StringTable->insert( "y" ), | |
| StringTable->insert( "z" ), | |
| StringTable->insert( "w" ) | |
| }; | |
| static const StringTableEntry rgba[] = | |
| { | |
| StringTable->insert( "r" ), | |
| StringTable->insert( "g" ), | |
| StringTable->insert( "b" ), | |
| StringTable->insert( "a" ) | |
| }; | |
| // Insert the value into the specified | |
| // component of the string. | |
| if ( subField == xyzw[0] || subField == rgba[0] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") ); | |
| else if ( subField == xyzw[1] || subField == rgba[1] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") ); | |
| else if ( subField == xyzw[2] || subField == rgba[2] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") ); | |
| else if ( subField == xyzw[3] || subField == rgba[3] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") ); | |
| if ( val[0] != 0 ) | |
| { | |
| // Update the field or variable. | |
| if( object && field ) | |
| object->setDataField( field, 0, val ); | |
| else if( gEvalState.currentVariable ) | |
| gEvalState.setStringVariable( val ); | |
| } | |
| } | |
| // FHC - compiled eval | |
| const char *CodeBlock::exec(U32 _ip, const char *_functionName, Namespace *_thisNamespace, U32 _argc, const char **_argv, bool _noCalls, StringTableEntry _packageName, S32 _setFrame) | |
| { | |
| //return ((this)->*(execPtr))(ip,functionName,thisNamespace,argc,argv,noCalls,packageName,setFrame); | |
| return CALL_MEMBER_FN_PTR(*this,execPtr)(_ip,_functionName,_thisNamespace,_argc,_argv,_noCalls,_packageName,_setFrame); | |
| } | |
| // FHC - compiled eval | |
| const char *CodeBlock::execNew(U32 _ip, const char *_functionName, Namespace *_thisNamespace, U32 _argc, const char **_argv, bool _noCalls, StringTableEntry _packageName, S32 _setFrame) | |
| { | |
| // push data onto stack | |
| ExecCallStackEntry *tmp = new ExecCallStackEntry(_ip, _functionName, _thisNamespace, _argc, _argv, _noCalls, _packageName, _setFrame); | |
| stackPush(tmp); | |
| delete tmp; | |
| // setup precall state | |
| /* | |
| ip = _ip; | |
| functionName = _functionName; | |
| thisNamespace = _thisNamespace; | |
| argc = _argc; | |
| argv = _argv; | |
| packageName = _packageName; | |
| setFrame = _setFrame; | |
| noCalls = _noCalls; | |
| */ | |
| #ifdef TORQUE_DEBUG | |
| U32 stackStart = STR.mStartStackSize; | |
| #endif | |
| iterDepth = 0; | |
| incRefCount(); | |
| curStringTableLen = 0; //clint to ensure we dont overwrite it | |
| STR.clearFunctionOffset(); | |
| thisFunctionName = NULL; | |
| popFrame = false; | |
| if(*argv) | |
| { | |
| // assume this points into a function decl: | |
| U32 fnArgc = code[(*ip) + 5]; | |
| thisFunctionName = U32toSTE(code[(*ip)]); | |
| *argc = getMin((*argc)-1, fnArgc); // argv[0] is func name | |
| if(gEvalState.traceOn) | |
| { | |
| traceBuffer[0] = 0; | |
| dStrcat(traceBuffer, "Entering "); | |
| if(*packageName) | |
| { | |
| dStrcat(traceBuffer, "["); | |
| dStrcat(traceBuffer, *packageName); | |
| dStrcat(traceBuffer, "]"); | |
| } | |
| if(*thisNamespace && (*thisNamespace)->mName) | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s::%s(", (*thisNamespace)->mName, thisFunctionName); | |
| } | |
| else | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s(", thisFunctionName); | |
| } | |
| for(i = 0; i < *argc; i++) | |
| { | |
| dStrcat(traceBuffer, (*argv)[i+1]); | |
| if(i != *argc - 1) | |
| dStrcat(traceBuffer, ", "); | |
| } | |
| dStrcat(traceBuffer, ")"); | |
| Con::printf("%s", traceBuffer); | |
| } | |
| gEvalState.pushFrame(thisFunctionName, *thisNamespace); | |
| popFrame = true; | |
| for(i = 0; i < *argc; i++) | |
| { | |
| StringTableEntry var = U32toSTE(code[(*ip) + i + 6]); | |
| gEvalState.setCurVarNameCreate(var); | |
| gEvalState.setStringVariable((*argv)[i+1]); | |
| } | |
| (*ip) = (*ip) + fnArgc + 6; | |
| curFloatTable = functionFloats; | |
| curStringTable = functionStrings; | |
| curStringTableLen = functionStringsMaxLen; | |
| } | |
| else | |
| { | |
| curFloatTable = globalFloats; | |
| curStringTable = globalStrings; | |
| curStringTableLen = globalStringsMaxLen; | |
| // If requested stack frame isn't available, request a new one | |
| // (this prevents assert failures when creating local | |
| // variables without a stack frame) | |
| if (gEvalState.getStackDepth() <= *setFrame) | |
| *setFrame = -1; | |
| // Do we want this code to execute using a new stack frame? | |
| if (*setFrame < 0) | |
| { | |
| gEvalState.pushFrame(NULL, NULL); | |
| popFrame = true; | |
| } | |
| else | |
| { | |
| // We want to copy a reference to an existing stack frame | |
| // on to the top of the stack. Any change that occurs to | |
| // the locals during this new frame will also occur in the | |
| // original frame. | |
| S32 stackIndex = gEvalState.getStackDepth() - *setFrame - 1; | |
| gEvalState.pushFrameRef( stackIndex ); | |
| popFrame = true; | |
| } | |
| } | |
| // Grab the state of the telenet debugger here once | |
| // so that the push and pop frames are always balanced. | |
| telDebuggerOn = TelDebugger && TelDebugger->isConnected(); | |
| if ( telDebuggerOn && *setFrame < 0 ) | |
| TelDebugger->pushStackFrame(); | |
| objectCreationStackIndex = 0; | |
| (*currentNewObject) = 0; | |
| failJump = 0; | |
| prevField = NULL; | |
| curField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| saveObject=NULL; | |
| ns = NULL; //? | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| CodeBlock *saveCodeBlock = smCurrentCodeBlock; | |
| smCurrentCodeBlock = this; | |
| if(this->name) | |
| { | |
| Con::gCurrentFile = this->name; | |
| Con::gCurrentRoot = this->modPath; | |
| } | |
| valBuffer = FrameTemp<char>(VAL_BUFFER_SIZE); | |
| // start going through code | |
| instruction = code[(*ip)++]; | |
| nsEntry = NULL; | |
| if( ((*ip)-1) == DB_IP && instruction == DB_OPCODE ){ | |
| //printopcodes = true; | |
| } | |
| if( printopcodes ){ | |
| Con::printf("opcode: %d, ip: %d", instruction, (*ip)-1); | |
| } | |
| // update before call | |
| /* | |
| _ip = ip; | |
| _functionName = functionName; | |
| _thisNamespace = thisNamespace; | |
| _argc = argc; | |
| _argv = argv; | |
| _packageName = packageName; | |
| _setFrame = setFrame; | |
| _noCalls = noCalls; | |
| */ | |
| // call first opcode function here | |
| CALL_MEMBER_FN_PTR(*this,opcodeList[instruction])(); | |
| // done running opcodes | |
| // restore precall state | |
| /* | |
| ip = _ip; | |
| functionName = _functionName; | |
| thisNamespace = _thisNamespace; | |
| argc = _argc; | |
| argv = _argv; | |
| packageName = _packageName; | |
| setFrame = _setFrame; | |
| noCalls = _noCalls; | |
| */ | |
| // done running opcodes | |
| if ( telDebuggerOn && (*setFrame) < 0 ) | |
| TelDebugger->popStackFrame(); | |
| if ( popFrame ) | |
| gEvalState.popFrame(); | |
| if(*argv) | |
| { | |
| if(gEvalState.traceOn) | |
| { | |
| traceBuffer[0] = 0; | |
| dStrcat(traceBuffer, "Leaving "); | |
| if(*packageName) | |
| { | |
| dStrcat(traceBuffer, "["); | |
| dStrcat(traceBuffer, *packageName); | |
| dStrcat(traceBuffer, "]"); | |
| } | |
| if(*thisNamespace && (*thisNamespace)->mName) | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s::%s() - return %s", (*thisNamespace)->mName, thisFunctionName, STR.getStringValue()); | |
| } | |
| else | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s() - return %s", thisFunctionName, STR.getStringValue()); | |
| } | |
| Con::printf("%s", traceBuffer); | |
| } | |
| } | |
| else | |
| { | |
| delete[] globalStrings; | |
| globalStringsMaxLen = 0; | |
| delete[] globalFloats; | |
| globalStrings = NULL; | |
| globalFloats = NULL; | |
| } | |
| smCurrentCodeBlock = saveCodeBlock; | |
| if(saveCodeBlock && saveCodeBlock->name) | |
| { | |
| Con::gCurrentFile = saveCodeBlock->name; | |
| Con::gCurrentRoot = saveCodeBlock->modPath; | |
| } | |
| stackPop(); | |
| decRefCount(); | |
| #ifdef TORQUE_DEBUG | |
| AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec"); | |
| AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec"); | |
| #endif | |
| return STR.getStringValue(); | |
| } | |
| // FHC - compiled eval | |
| const char *CodeBlock::execOrig(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, const char **argv, bool noCalls, StringTableEntry packageName, S32 setFrame) | |
| { | |
| #ifdef TORQUE_DEBUG | |
| U32 stackStart = STR.mStartStackSize; | |
| #endif | |
| static char traceBuffer[1024]; | |
| U32 i; | |
| U32 iterDepth = 0; | |
| incRefCount(); | |
| F64 *curFloatTable; | |
| char *curStringTable; | |
| S32 curStringTableLen = 0; //clint to ensure we dont overwrite it | |
| STR.clearFunctionOffset(); | |
| StringTableEntry thisFunctionName = NULL; | |
| bool popFrame = false; | |
| if(argv) | |
| { | |
| // assume this points into a function decl: | |
| U32 fnArgc = code[ip + 5]; | |
| thisFunctionName = U32toSTE(code[ip]); | |
| argc = getMin(argc-1, fnArgc); // argv[0] is func name | |
| if(gEvalState.traceOn) | |
| { | |
| traceBuffer[0] = 0; | |
| dStrcat(traceBuffer, "Entering "); | |
| if(packageName) | |
| { | |
| dStrcat(traceBuffer, "["); | |
| dStrcat(traceBuffer, packageName); | |
| dStrcat(traceBuffer, "]"); | |
| } | |
| if(thisNamespace && thisNamespace->mName) | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s::%s(", thisNamespace->mName, thisFunctionName); | |
| } | |
| else | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s(", thisFunctionName); | |
| } | |
| for(i = 0; i < argc; i++) | |
| { | |
| dStrcat(traceBuffer, argv[i+1]); | |
| if(i != argc - 1) | |
| dStrcat(traceBuffer, ", "); | |
| } | |
| dStrcat(traceBuffer, ")"); | |
| Con::printf("%s", traceBuffer); | |
| } | |
| gEvalState.pushFrame(thisFunctionName, thisNamespace); | |
| popFrame = true; | |
| for(i = 0; i < argc; i++) | |
| { | |
| StringTableEntry var = U32toSTE(code[ip + i + 6]); | |
| gEvalState.setCurVarNameCreate(var); | |
| gEvalState.setStringVariable(argv[i+1]); | |
| } | |
| ip = ip + fnArgc + 6; | |
| curFloatTable = functionFloats; | |
| curStringTable = functionStrings; | |
| curStringTableLen = functionStringsMaxLen; | |
| } | |
| else | |
| { | |
| curFloatTable = globalFloats; | |
| curStringTable = globalStrings; | |
| curStringTableLen = globalStringsMaxLen; | |
| // If requested stack frame isn't available, request a new one | |
| // (this prevents assert failures when creating local | |
| // variables without a stack frame) | |
| if (gEvalState.getStackDepth() <= setFrame) | |
| setFrame = -1; | |
| // Do we want this code to execute using a new stack frame? | |
| if (setFrame < 0) | |
| { | |
| gEvalState.pushFrame(NULL, NULL); | |
| popFrame = true; | |
| } | |
| else | |
| { | |
| // We want to copy a reference to an existing stack frame | |
| // on to the top of the stack. Any change that occurs to | |
| // the locals during this new frame will also occur in the | |
| // original frame. | |
| S32 stackIndex = gEvalState.getStackDepth() - setFrame - 1; | |
| gEvalState.pushFrameRef( stackIndex ); | |
| popFrame = true; | |
| } | |
| } | |
| // Grab the state of the telenet debugger here once | |
| // so that the push and pop frames are always balanced. | |
| const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected(); | |
| if ( telDebuggerOn && setFrame < 0 ) | |
| TelDebugger->pushStackFrame(); | |
| StringTableEntry var, objParent; | |
| StringTableEntry fnName; | |
| StringTableEntry fnNamespace, fnPackage; | |
| // Add local object creation stack [7/9/2007 Black] | |
| static const U32 objectCreationStackSize = 32; | |
| U32 objectCreationStackIndex = 0; | |
| struct { | |
| SimObject *newObject; | |
| U32 failJump; | |
| } objectCreationStack[ objectCreationStackSize ]; | |
| SimObject *currentNewObject = 0; | |
| U32 failJump = 0; | |
| StringTableEntry prevField = NULL; | |
| StringTableEntry curField = NULL; | |
| SimObject *prevObject = NULL; | |
| SimObject *curObject = NULL; | |
| SimObject *saveObject=NULL; | |
| Namespace::Entry *nsEntry; | |
| Namespace *ns; | |
| const char* curFNDocBlock = NULL; | |
| const char* curNSDocBlock = NULL; | |
| const S32 nsDocLength = 128; | |
| char nsDocBlockClass[nsDocLength]; | |
| U32 callArgc; | |
| const char **callArgv; | |
| static char curFieldArray[256]; | |
| static char prevFieldArray[256]; | |
| CodeBlock *saveCodeBlock = smCurrentCodeBlock; | |
| smCurrentCodeBlock = this; | |
| if(this->name) | |
| { | |
| Con::gCurrentFile = this->name; | |
| Con::gCurrentRoot = this->modPath; | |
| } | |
| const char * val; | |
| // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and | |
| // OP_LOADFIELD_*) to store temporary values for the fields. | |
| static S32 VAL_BUFFER_SIZE = 1024; | |
| FrameTemp<char> valBuffer( VAL_BUFFER_SIZE ); | |
| static bool printopcodes = false; | |
| for(;;) | |
| { | |
| U32 instruction = code[ip++]; | |
| nsEntry = NULL; | |
| if( (ip-1) == DB_IP && instruction == DB_OPCODE ){ | |
| //printopcodes = true; | |
| } | |
| if( printopcodes ){ | |
| Con::printf("opcode: %d, ip: %d", instruction, ip-1); | |
| } | |
| if( (ip-1) == 1035 && instruction == 13 ){ | |
| int tmpvar = 1; | |
| } | |
| if( (ip-1) == DB_IP && instruction == DB_OPCODE ){ | |
| int tmpvar = 1; | |
| } | |
| breakContinue: | |
| switch(instruction) | |
| { | |
| case OP_FUNC_DECL: | |
| if(!noCalls) | |
| { | |
| fnName = U32toSTE(code[ip]); | |
| fnNamespace = U32toSTE(code[ip+1]); | |
| fnPackage = U32toSTE(code[ip+2]); | |
| bool hasBody = ( code[ ip + 3 ] & 0x01 ) != 0; | |
| U32 lineNumber = code[ ip + 3 ] >> 1; | |
| Namespace::unlinkPackages(); | |
| ns = Namespace::find(fnNamespace, fnPackage); | |
| ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL, lineNumber );// if no body, set the IP to 0 | |
| if( curNSDocBlock ) | |
| { | |
| if( fnNamespace == StringTable->lookup( nsDocBlockClass ) ) | |
| { | |
| char *usageStr = dStrdup( curNSDocBlock ); | |
| usageStr[dStrlen(usageStr)] = '\0'; | |
| ns->mUsage = usageStr; | |
| ns->mCleanUpUsage = true; | |
| curNSDocBlock = NULL; | |
| } | |
| } | |
| Namespace::relinkPackages(); | |
| // If we had a docblock, it's definitely not valid anymore, so clear it out. | |
| curFNDocBlock = NULL; | |
| //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip); | |
| } | |
| ip = code[ip + 4]; | |
| break; | |
| case OP_CREATE_OBJECT: | |
| { | |
| // Read some useful info. | |
| objParent = U32toSTE(code[ip ]); | |
| bool isDataBlock = code[ip + 1]; | |
| bool isInternal = code[ip + 2]; | |
| bool isSingleton = code[ip + 3]; | |
| U32 lineNumber = code[ip + 4]; | |
| failJump = code[ip + 5]; | |
| // If we don't allow calls, we certainly don't allow creating objects! | |
| // Moved this to after failJump is set. Engine was crashing when | |
| // noCalls = true and an object was being created at the beginning of | |
| // a file. ADL. | |
| if(noCalls) | |
| { | |
| ip = failJump; | |
| break; | |
| } | |
| // Push the old info to the stack | |
| //Assert( objectCreationStackIndex < objectCreationStackSize ); | |
| objectCreationStack[ objectCreationStackIndex ].newObject = currentNewObject; | |
| objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump; | |
| // Get the constructor information off the stack. | |
| STR.getArgcArgv(NULL, &callArgc, &callArgv); | |
| const char* objectName = callArgv[ 2 ]; | |
| // Con::printf("Creating object..."); | |
| // objectName = argv[1]... | |
| currentNewObject = NULL; | |
| // Are we creating a datablock? If so, deal with case where we override | |
| // an old one. | |
| if(isDataBlock) | |
| { | |
| // Con::printf(" - is a datablock"); | |
| // Find the old one if any. | |
| SimObject *db = Sim::getDataBlockGroup()->findObject( objectName ); | |
| // Make sure we're not changing types on ourselves... | |
| if(db && dStricmp(db->getClassName(), callArgv[1])) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName); | |
| ip = failJump; | |
| STR.popFrame(); | |
| break; | |
| } | |
| // If there was one, set the currentNewObject and move on. | |
| if(db) | |
| currentNewObject = db; | |
| } | |
| else if (!isInternal) | |
| { | |
| // IF we aren't looking at a local/internal object, then check if | |
| // this object already exists in the global space | |
| SimObject *obj = Sim::findObject( objectName ); | |
| if (obj /*&& !obj->isLocalName()*/) | |
| { | |
| if ( isSingleton ) | |
| { | |
| // Make sure we're not trying to change types | |
| if ( dStricmp( obj->getClassName(), callArgv[1] ) != 0 ) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].", | |
| getFileLine(ip), objectName, callArgv[1], obj->getClassName()); | |
| ip = failJump; | |
| STR.popFrame(); | |
| break; | |
| } | |
| // We're creating a singleton, so use the found object | |
| // instead of creating a new object. | |
| currentNewObject = obj; | |
| } | |
| else | |
| { | |
| const char* redefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); | |
| if( dStricmp( redefineBehavior, "replaceExisting" ) == 0 ) | |
| { | |
| // Save our constructor args as the argv vector is stored on the | |
| // string stack and may get stomped if deleteObject triggers | |
| // script execution. | |
| const char* savedArgv[ StringStack::MaxArgs ]; | |
| dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); | |
| obj->deleteObject(); | |
| obj = NULL; | |
| dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); | |
| } | |
| else if( dStricmp( redefineBehavior, "renameNew" ) == 0 ) | |
| { | |
| for( U32 i = 1;; ++ i ) | |
| { | |
| String newName = String::ToString( "%s%i", objectName, i ); | |
| if( !Sim::findObject( newName ) ) | |
| { | |
| objectName = StringTable->insert( newName ); | |
| break; | |
| } | |
| } | |
| } | |
| else if( dStricmp( redefineBehavior, "unnameNew" ) == 0 ) | |
| { | |
| objectName = StringTable->insert( "" ); | |
| } | |
| else if( dStricmp( redefineBehavior, "postfixNew" ) == 0 ) | |
| { | |
| const char* postfix = Con::getVariable( "$Con::redefineBehaviorPostfix" ); | |
| String newName = String::ToString( "%s%s", objectName, postfix ); | |
| if( Sim::findObject( newName ) ) | |
| { | |
| Con::errorf( ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].", | |
| getFileLine(ip), newName.c_str() ); | |
| ip = failJump; | |
| STR.popFrame(); | |
| break; | |
| } | |
| else | |
| objectName = StringTable->insert( newName ); | |
| } | |
| else | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].", | |
| getFileLine(ip), objectName); | |
| ip = failJump; | |
| STR.popFrame(); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| STR.popFrame(); | |
| if(!currentNewObject) | |
| { | |
| // Well, looks like we have to create a new object. | |
| ConsoleObject *object = ConsoleObject::create(callArgv[1]); | |
| // Deal with failure! | |
| if(!object) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine(ip), callArgv[1]); | |
| ip = failJump; | |
| break; | |
| } | |
| // Do special datablock init if appropros | |
| if(isDataBlock) | |
| { | |
| SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(object); | |
| if(dataBlock) | |
| { | |
| dataBlock->assignId(); | |
| } | |
| else | |
| { | |
| // They tried to make a non-datablock with a datablock keyword! | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine(ip), callArgv[1]); | |
| // Clean up... | |
| delete object; | |
| ip = failJump; | |
| break; | |
| } | |
| } | |
| // Finally, set currentNewObject to point to the new one. | |
| currentNewObject = dynamic_cast<SimObject *>(object); | |
| // Deal with the case of a non-SimObject. | |
| if(!currentNewObject) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", getFileLine(ip), callArgv[1]); | |
| delete object; | |
| ip = failJump; | |
| break; | |
| } | |
| // Set the declaration line | |
| currentNewObject->setDeclarationLine(lineNumber); | |
| // Set the file that this object was created in | |
| currentNewObject->setFilename(name); | |
| // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance) | |
| if(*objParent) | |
| { | |
| // Find it! | |
| SimObject *parent; | |
| if(Sim::findObject(objParent, parent)) | |
| { | |
| // Con::printf(" - Parent object found: %s", parent->getClassName()); | |
| currentNewObject->setCopySource( parent ); | |
| currentNewObject->assignFieldsFrom( parent ); | |
| } | |
| else | |
| { | |
| if ( Con::gObjectCopyFailures == -1 ) | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip), objParent, callArgv[1]); | |
| else | |
| ++Con::gObjectCopyFailures; | |
| // Fail to create the object. | |
| delete object; | |
| ip = failJump; | |
| break; | |
| } | |
| } | |
| // If a name was passed, assign it. | |
| if( objectName[ 0 ] ) | |
| { | |
| if( !isInternal ) | |
| currentNewObject->assignName( objectName ); | |
| else | |
| currentNewObject->setInternalName( objectName ); | |
| // Set the original name | |
| currentNewObject->setOriginalName( objectName ); | |
| } | |
| // Do the constructor parameters. | |
| if(!currentNewObject->processArguments(callArgc-3, callArgv+3)) | |
| { | |
| delete currentNewObject; | |
| currentNewObject = NULL; | |
| ip = failJump; | |
| break; | |
| } | |
| // If it's not a datablock, allow people to modify bits of it. | |
| if(!isDataBlock) | |
| { | |
| currentNewObject->setModStaticFields(true); | |
| currentNewObject->setModDynamicFields(true); | |
| } | |
| } | |
| // Advance the IP past the create info... | |
| ip += 6; | |
| break; | |
| } | |
| case OP_ADD_OBJECT: | |
| { | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| // Do we place this object at the root? | |
| bool placeAtRoot = code[ip++]; | |
| // Con::printf("Adding object %s", currentNewObject->getName()); | |
| // Make sure it wasn't already added, then add it. | |
| if(currentNewObject->isProperlyAdded() == false) | |
| { | |
| bool ret = false; | |
| Message *msg = dynamic_cast<Message *>(currentNewObject); | |
| if(msg) | |
| { | |
| SimObjectId id = Message::getNextMessageID(); | |
| if(id != 0xffffffff) | |
| ret = currentNewObject->registerObject(id); | |
| else | |
| Con::errorf("%s: No more object IDs available for messages", getFileLine(ip)); | |
| } | |
| else | |
| ret = currentNewObject->registerObject(); | |
| if(! ret) | |
| { | |
| // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields(). | |
| Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName()); | |
| delete currentNewObject; | |
| ip = failJump; | |
| break; | |
| } | |
| } | |
| // Are we dealing with a datablock? | |
| SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(currentNewObject); | |
| static String errorStr; | |
| // If so, preload it. | |
| if(dataBlock && !dataBlock->preload(true, errorStr)) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip), | |
| currentNewObject->getName(), errorStr.c_str()); | |
| dataBlock->deleteObject(); | |
| ip = failJump; | |
| break; | |
| } | |
| // What group will we be added to, if any? | |
| U32 groupAddId = intStack[_UINT]; | |
| SimGroup *grp = NULL; | |
| SimSet *set = NULL; | |
| bool isMessage = dynamic_cast<Message *>(currentNewObject) != NULL; | |
| if(!placeAtRoot || !currentNewObject->getGroup()) | |
| { | |
| if(! isMessage) | |
| { | |
| if(! placeAtRoot) | |
| { | |
| // Otherwise just add to the requested group or set. | |
| if(!Sim::findObject(groupAddId, grp)) | |
| Sim::findObject(groupAddId, set); | |
| } | |
| if(placeAtRoot) | |
| { | |
| // Deal with the instantGroup if we're being put at the root or we're adding to a component. | |
| if( Con::gInstantGroup.isEmpty() | |
| || !Sim::findObject( Con::gInstantGroup, grp ) ) | |
| grp = Sim::getRootGroup(); | |
| } | |
| } | |
| // If we didn't get a group, then make sure we have a pointer to | |
| // the rootgroup. | |
| if(!grp) | |
| grp = Sim::getRootGroup(); | |
| // add to the parent group | |
| grp->addObject(currentNewObject); | |
| // If for some reason the add failed, add the object to the | |
| // root group so it won't leak. | |
| if( !currentNewObject->getGroup() ) | |
| Sim::getRootGroup()->addObject( currentNewObject ); | |
| // add to any set we might be in | |
| if(set) | |
| set->addObject(currentNewObject); | |
| } | |
| // store the new object's ID on the stack (overwriting the group/set | |
| // id, if one was given, otherwise getting pushed) | |
| if(placeAtRoot) | |
| intStack[_UINT] = currentNewObject->getId(); | |
| else | |
| intStack[++_UINT] = currentNewObject->getId(); | |
| break; | |
| } | |
| case OP_END_OBJECT: | |
| { | |
| // If we're not to be placed at the root, make sure we clean up | |
| // our group reference. | |
| bool placeAtRoot = code[ip++]; | |
| if(!placeAtRoot) | |
| _UINT--; | |
| break; | |
| } | |
| case OP_FINISH_OBJECT: | |
| { | |
| //Assert( objectCreationStackIndex >= 0 ); | |
| // Restore the object info from the stack [7/9/2007 Black] | |
| currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject; | |
| failJump = objectCreationStack[ objectCreationStackIndex ].failJump; | |
| break; | |
| } | |
| case OP_JMPIFFNOT: | |
| if(floatStack[_FLT--]) | |
| { | |
| ip++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMPIFNOT: | |
| if(intStack[_UINT--]) | |
| { | |
| ip++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMPIFF: | |
| if(!floatStack[_FLT--]) | |
| { | |
| ip++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMPIF: | |
| if(!intStack[_UINT--]) | |
| { | |
| ip ++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMPIFNOT_NP: | |
| if(intStack[_UINT]) | |
| { | |
| _UINT--; | |
| ip++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMPIF_NP: | |
| if(!intStack[_UINT]) | |
| { | |
| _UINT--; | |
| ip++; | |
| break; | |
| } | |
| ip = code[ip]; | |
| break; | |
| case OP_JMP: | |
| ip = code[ip]; | |
| break; | |
| // This fixes a bug when not explicitly returning a value. | |
| case OP_RETURN_VOID: | |
| STR.setStringValue(""); | |
| // We're falling thru here on purpose. | |
| case OP_RETURN: | |
| if( iterDepth > 0 ) | |
| { | |
| // Clear iterator state. | |
| while( iterDepth > 0 ) | |
| { | |
| iterStack[ -- _ITER ].mIsStringIter = false; | |
| -- iterDepth; | |
| } | |
| const char* returnValue = STR.getStringValue(); | |
| STR.rewind(); | |
| STR.setStringValue( returnValue ); // Not nice but works. | |
| } | |
| goto execFinished; | |
| case OP_CMPEQ: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] == floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_CMPGR: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] > floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_CMPGE: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] >= floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_CMPLT: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] < floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_CMPLE: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] <= floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_CMPNE: | |
| intStack[_UINT+1] = bool(floatStack[_FLT] != floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| break; | |
| case OP_XOR: | |
| intStack[_UINT-1] = intStack[_UINT] ^ intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_MOD: | |
| if( intStack[_UINT-1] != 0 ) | |
| intStack[_UINT-1] = intStack[_UINT] % intStack[_UINT-1]; | |
| else | |
| intStack[_UINT-1] = 0; | |
| _UINT--; | |
| break; | |
| case OP_BITAND: | |
| intStack[_UINT-1] = intStack[_UINT] & intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_BITOR: | |
| intStack[_UINT-1] = intStack[_UINT] | intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_NOT: | |
| intStack[_UINT] = !intStack[_UINT]; | |
| break; | |
| case OP_NOTF: | |
| intStack[_UINT+1] = !floatStack[_FLT]; | |
| _FLT--; | |
| _UINT++; | |
| break; | |
| case OP_ONESCOMPLEMENT: | |
| intStack[_UINT] = ~intStack[_UINT]; | |
| break; | |
| case OP_SHR: | |
| intStack[_UINT-1] = intStack[_UINT] >> intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_SHL: | |
| intStack[_UINT-1] = intStack[_UINT] << intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_AND: | |
| intStack[_UINT-1] = intStack[_UINT] && intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_OR: | |
| intStack[_UINT-1] = intStack[_UINT] || intStack[_UINT-1]; | |
| _UINT--; | |
| break; | |
| case OP_ADD: | |
| floatStack[_FLT-1] = floatStack[_FLT] + floatStack[_FLT-1]; | |
| _FLT--; | |
| break; | |
| case OP_SUB: | |
| floatStack[_FLT-1] = floatStack[_FLT] - floatStack[_FLT-1]; | |
| _FLT--; | |
| break; | |
| case OP_MUL: | |
| floatStack[_FLT-1] = floatStack[_FLT] * floatStack[_FLT-1]; | |
| _FLT--; | |
| break; | |
| case OP_DIV: | |
| floatStack[_FLT-1] = floatStack[_FLT] / floatStack[_FLT-1]; | |
| _FLT--; | |
| break; | |
| case OP_NEG: | |
| floatStack[_FLT] = -floatStack[_FLT]; | |
| break; | |
| case OP_SETCURVAR: | |
| var = U32toSTE(code[ip]); | |
| ip++; | |
| // If a variable is set, then these must be NULL. It is necessary | |
| // to set this here so that the vector parser can appropriately | |
| // identify whether it's dealing with a vector. | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarName(var); | |
| // In order to let docblocks work properly with variables, we have | |
| // clear the current docblock when we do an assign. This way it | |
| // won't inappropriately carry forward to following function decls. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| break; | |
| case OP_SETCURVAR_CREATE: | |
| var = U32toSTE(code[ip]); | |
| #if _DEBUG | |
| if( !dStrcmp(var,"%dirPath") ){ | |
| int tmpvar = 1; | |
| } | |
| #endif | |
| ip++; | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarNameCreate(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| break; | |
| case OP_SETCURVAR_ARRAY: | |
| var = STR.getSTValue(); | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarName(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| break; | |
| case OP_SETCURVAR_ARRAY_CREATE: | |
| var = STR.getSTValue(); | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarNameCreate(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| break; | |
| case OP_LOADVAR_UINT: | |
| intStack[_UINT+1] = gEvalState.getIntVariable(); | |
| _UINT++; | |
| break; | |
| case OP_LOADVAR_FLT: | |
| floatStack[_FLT+1] = gEvalState.getFloatVariable(); | |
| _FLT++; | |
| break; | |
| case OP_LOADVAR_STR: | |
| val = gEvalState.getStringVariable(); | |
| STR.setStringValue(val); | |
| break; | |
| case OP_SAVEVAR_UINT: | |
| gEvalState.setIntVariable(intStack[_UINT]); | |
| break; | |
| case OP_SAVEVAR_FLT: | |
| gEvalState.setFloatVariable(floatStack[_FLT]); | |
| break; | |
| case OP_SAVEVAR_STR: | |
| gEvalState.setStringVariable(STR.getStringValue()); | |
| break; | |
| case OP_SETCUROBJECT: | |
| // Save the previous object for parsing vector fields. | |
| prevObject = curObject; | |
| val = STR.getStringValue(); | |
| // Sim::findObject will sometimes find valid objects from | |
| // multi-component strings. This makes sure that doesn't | |
| // happen. | |
| for( const char* check = val; *check; check++ ) | |
| { | |
| if( *check == ' ' ) | |
| { | |
| val = ""; | |
| break; | |
| } | |
| } | |
| curObject = Sim::findObject(val); | |
| break; | |
| case OP_SETCUROBJECT_INTERNAL: | |
| ++ip; // To skip the recurse flag if the object wasn't found | |
| if(curObject) | |
| { | |
| SimSet *set = dynamic_cast<SimSet *>(curObject); | |
| if(set) | |
| { | |
| StringTableEntry intName = StringTable->insert(STR.getStringValue()); | |
| bool recurse = code[ip-1]; | |
| SimObject *obj = set->findObjectByInternalName(intName, recurse); | |
| intStack[_UINT+1] = obj ? obj->getId() : 0; | |
| _UINT++; | |
| } | |
| else | |
| { | |
| Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", getFileLine(ip-2), curObject->getName(), curObject->getClassName()); | |
| intStack[_UINT] = 0; | |
| } | |
| } | |
| break; | |
| case OP_SETCUROBJECT_NEW: | |
| curObject = currentNewObject; | |
| break; | |
| case OP_SETCURFIELD: | |
| // Save the previous field for parsing vector fields. | |
| prevField = curField; | |
| dStrcpy( prevFieldArray, curFieldArray ); | |
| curField = U32toSTE(code[ip]); | |
| curFieldArray[0] = 0; | |
| ip++; | |
| break; | |
| case OP_SETCURFIELD_ARRAY: | |
| dStrcpy(curFieldArray, STR.getStringValue()); | |
| break; | |
| case OP_SETCURFIELD_TYPE: | |
| if(curObject) | |
| curObject->setDataFieldType(code[ip], curField, curFieldArray); | |
| ip++; | |
| break; | |
| case OP_LOADFIELD_UINT: | |
| if(curObject) | |
| intStack[_UINT+1] = U32(dAtoi(curObject->getDataField(curField, curFieldArray))); | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| intStack[_UINT+1] = dAtoi( valBuffer ); | |
| } | |
| _UINT++; | |
| break; | |
| case OP_LOADFIELD_FLT: | |
| if(curObject) | |
| floatStack[_FLT+1] = dAtof(curObject->getDataField(curField, curFieldArray)); | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| floatStack[_FLT+1] = dAtof( valBuffer ); | |
| } | |
| _FLT++; | |
| break; | |
| case OP_LOADFIELD_STR: | |
| if(curObject) | |
| { | |
| val = curObject->getDataField(curField, curFieldArray); | |
| STR.setStringValue( val ); | |
| } | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| STR.setStringValue( valBuffer ); | |
| } | |
| break; | |
| case OP_SAVEFIELD_UINT: | |
| STR.setIntValue(intStack[_UINT]); | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| break; | |
| case OP_SAVEFIELD_FLT: | |
| STR.setFloatValue(floatStack[_FLT]); | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| break; | |
| case OP_SAVEFIELD_STR: | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| break; | |
| case OP_STR_TO_UINT: | |
| intStack[_UINT+1] = STR.getIntValue(); | |
| _UINT++; | |
| break; | |
| case OP_STR_TO_FLT: | |
| floatStack[_FLT+1] = STR.getFloatValue(); | |
| _FLT++; | |
| break; | |
| case OP_STR_TO_NONE: | |
| // This exists simply to deal with certain typecast situations. | |
| break; | |
| case OP_FLT_TO_UINT: | |
| intStack[_UINT+1] = (S64)floatStack[_FLT]; | |
| _FLT--; | |
| _UINT++; | |
| break; | |
| case OP_FLT_TO_STR: | |
| STR.setFloatValue(floatStack[_FLT]); | |
| _FLT--; | |
| break; | |
| case OP_FLT_TO_NONE: | |
| _FLT--; | |
| break; | |
| case OP_UINT_TO_FLT: | |
| floatStack[_FLT+1] = (F32)intStack[_UINT]; | |
| _UINT--; | |
| _FLT++; | |
| break; | |
| case OP_UINT_TO_STR: | |
| STR.setIntValue(intStack[_UINT]); | |
| _UINT--; | |
| break; | |
| case OP_UINT_TO_NONE: | |
| _UINT--; | |
| break; | |
| case OP_LOADIMMED_UINT: | |
| intStack[_UINT+1] = code[ip++]; | |
| _UINT++; | |
| break; | |
| case OP_LOADIMMED_FLT: | |
| floatStack[_FLT+1] = curFloatTable[code[ip]]; | |
| ip++; | |
| _FLT++; | |
| break; | |
| case OP_TAG_TO_STR: | |
| code[ip-1] = OP_LOADIMMED_STR; | |
| // it's possible the string has already been converted | |
| if(U8(curStringTable[code[ip]]) != StringTagPrefixByte) | |
| { | |
| U32 id = GameAddTaggedString(curStringTable + code[ip]); | |
| dSprintf(curStringTable + code[ip] + 1, 7, "%d", id); | |
| *(curStringTable + code[ip]) = StringTagPrefixByte; | |
| } | |
| case OP_LOADIMMED_STR: | |
| STR.setStringValue(curStringTable + code[ip++]); | |
| break; | |
| case OP_DOCBLOCK_STR: | |
| { | |
| // If the first word of the doc is '\class' or '@class', then this | |
| // is a namespace doc block, otherwise it is a function doc block. | |
| const char* docblock = curStringTable + code[ip++]; | |
| const char* sansClass = dStrstr( docblock, "@class" ); | |
| if( !sansClass ) | |
| sansClass = dStrstr( docblock, "\\class" ); | |
| if( sansClass ) | |
| { | |
| // Don't save the class declaration. Scan past the 'class' | |
| // keyword and up to the first whitespace. | |
| sansClass += 7; | |
| S32 index = 0; | |
| while( ( *sansClass != ' ' ) && ( *sansClass != '\n' ) && *sansClass && ( index < ( nsDocLength - 1 ) ) ) | |
| { | |
| nsDocBlockClass[index++] = *sansClass; | |
| sansClass++; | |
| } | |
| nsDocBlockClass[index] = '\0'; | |
| curNSDocBlock = sansClass + 1; | |
| } | |
| else | |
| curFNDocBlock = docblock; | |
| } | |
| break; | |
| case OP_LOADIMMED_IDENT: | |
| STR.setStringValue(U32toSTE(code[ip++])); | |
| break; | |
| case OP_CALLFUNC_RESOLVE: | |
| // This deals with a function that is potentially living in a namespace. | |
| fnNamespace = U32toSTE(code[ip+1]); | |
| fnName = U32toSTE(code[ip]); | |
| // Try to look it up. | |
| ns = Namespace::find(fnNamespace); | |
| nsEntry = ns->lookup(fnName); | |
| if(!nsEntry) | |
| { | |
| ip+= 3; | |
| Con::warnf(ConsoleLogEntry::General, | |
| "%s: Unable to find function %s%s%s", | |
| getFileLine(ip-4), fnNamespace ? fnNamespace : "", | |
| fnNamespace ? "::" : "", fnName); | |
| STR.popFrame(); | |
| break; | |
| } | |
| // Now fall through to OP_CALLFUNC... | |
| case OP_CALLFUNC: | |
| { | |
| // This routingId is set when we query the object as to whether | |
| // it handles this method. It is set to an enum from the table | |
| // above indicating whether it handles it on a component it owns | |
| // or just on the object. | |
| S32 routingId = 0; | |
| fnName = U32toSTE(code[ip]); | |
| //if this is called from inside a function, append the ip and codeptr | |
| if( gEvalState.getStackDepth() > 0 ) | |
| { | |
| gEvalState.getCurrentFrame().code = this; | |
| gEvalState.getCurrentFrame().ip = ip - 1; | |
| } | |
| U32 callType = code[ip+2]; | |
| ip += 3; | |
| STR.getArgcArgv(fnName, &callArgc, &callArgv); | |
| const char *componentReturnValue = ""; | |
| if(callType == FuncCallExprNode::FunctionCall) | |
| { | |
| if( !nsEntry ) | |
| { | |
| // We must not have come from OP_CALLFUNC_RESOLVE, so figure out | |
| // our own entry. | |
| nsEntry = Namespace::global()->lookup( fnName ); | |
| } | |
| ns = NULL; | |
| } | |
| else if(callType == FuncCallExprNode::MethodCall) | |
| { | |
| saveObject = gEvalState.thisObject; | |
| gEvalState.thisObject = Sim::findObject(callArgv[1]); | |
| if(!gEvalState.thisObject) | |
| { | |
| // Go back to the previous saved object. | |
| gEvalState.thisObject = saveObject; | |
| Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), callArgv[1], fnName); | |
| STR.popFrame(); | |
| break; | |
| } | |
| bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId); | |
| if( handlesMethod && routingId == MethodOnComponent ) | |
| { | |
| ICallMethod *pComponent = dynamic_cast<ICallMethod *>( gEvalState.thisObject ); | |
| if( pComponent ) | |
| componentReturnValue = pComponent->callMethodArgList( callArgc, callArgv, false ); | |
| } | |
| ns = gEvalState.thisObject->getNamespace(); | |
| if(ns) | |
| nsEntry = ns->lookup(fnName); | |
| else | |
| nsEntry = NULL; | |
| } | |
| else // it's a ParentCall | |
| { | |
| if(thisNamespace) | |
| { | |
| ns = thisNamespace->mParent; | |
| if(ns) | |
| nsEntry = ns->lookup(fnName); | |
| else | |
| nsEntry = NULL; | |
| } | |
| else | |
| { | |
| ns = NULL; | |
| nsEntry = NULL; | |
| } | |
| } | |
| Namespace::Entry::CallbackUnion * nsCb = NULL; | |
| const char * nsUsage = NULL; | |
| if (nsEntry) | |
| { | |
| nsCb = &nsEntry->cb; | |
| nsUsage = nsEntry->mUsage; | |
| routingId = 0; | |
| } | |
| if(!nsEntry || noCalls) | |
| { | |
| if(!noCalls && !( routingId == MethodOnComponent ) ) | |
| { | |
| Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-4), fnName); | |
| if(callType == FuncCallExprNode::MethodCall) | |
| { | |
| Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", | |
| gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "", | |
| gEvalState.thisObject->getId(), Con::getNamespaceList(ns) ); | |
| } | |
| } | |
| STR.popFrame(); | |
| if( routingId == MethodOnComponent ) | |
| STR.setStringValue( componentReturnValue ); | |
| else | |
| STR.setStringValue( "" ); | |
| break; | |
| } | |
| if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType) | |
| { | |
| const char *ret = ""; | |
| if(nsEntry->mFunctionOffset) | |
| ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); | |
| STR.popFrame(); | |
| STR.setStringValue(ret); | |
| } | |
| else | |
| { | |
| const char* nsName = ns? ns->mName: ""; | |
| #ifndef TORQUE_DEBUG | |
| // [tom, 12/13/2006] This stops tools functions from working in the console, | |
| // which is useful behavior when debugging so I'm ifdefing this out for debug builds. | |
| if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript()) | |
| { | |
| Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-4), nsName, fnName); | |
| } | |
| else | |
| #endif | |
| if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) | |
| { | |
| Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).", | |
| getFileLine(ip-4), nsName, fnName, | |
| callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); | |
| Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-4), nsEntry->mUsage); | |
| STR.popFrame(); | |
| } | |
| else | |
| { | |
| switch(nsEntry->mType) | |
| { | |
| case Namespace::Entry::StringCallbackType: | |
| { | |
| const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(ret != STR.getStringValue()) | |
| STR.setStringValue(ret); | |
| else | |
| STR.setLen(dStrlen(ret)); | |
| break; | |
| } | |
| case Namespace::Entry::IntCallbackType: | |
| { | |
| S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[ip] == OP_STR_TO_UINT) | |
| { | |
| ip++; | |
| intStack[++_UINT] = result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_FLT) | |
| { | |
| ip++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_NONE) | |
| ip++; | |
| else | |
| STR.setIntValue(result); | |
| break; | |
| } | |
| case Namespace::Entry::FloatCallbackType: | |
| { | |
| F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[ip] == OP_STR_TO_UINT) | |
| { | |
| ip++; | |
| intStack[++_UINT] = (S64)result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_FLT) | |
| { | |
| ip++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_NONE) | |
| ip++; | |
| else | |
| STR.setFloatValue(result); | |
| break; | |
| } | |
| case Namespace::Entry::VoidCallbackType: | |
| nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| if( code[ ip ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) ) | |
| Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-4), fnName, functionName); | |
| STR.popFrame(); | |
| STR.setStringValue(""); | |
| break; | |
| case Namespace::Entry::BoolCallbackType: | |
| { | |
| bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[ip] == OP_STR_TO_UINT) | |
| { | |
| ip++; | |
| intStack[++_UINT] = result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_FLT) | |
| { | |
| ip++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[ip] == OP_STR_TO_NONE) | |
| ip++; | |
| else | |
| STR.setIntValue(result); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if(callType == FuncCallExprNode::MethodCall) | |
| gEvalState.thisObject = saveObject; | |
| break; | |
| } | |
| case OP_ADVANCE_STR: | |
| STR.advance(); | |
| break; | |
| case OP_ADVANCE_STR_APPENDCHAR: | |
| STR.advanceChar(code[ip++]); | |
| break; | |
| case OP_ADVANCE_STR_COMMA: | |
| STR.advanceChar('_'); | |
| break; | |
| case OP_ADVANCE_STR_NUL: | |
| STR.advanceChar(0); | |
| break; | |
| case OP_REWIND_STR: | |
| STR.rewind(); | |
| break; | |
| case OP_TERMINATE_REWIND_STR: | |
| STR.rewindTerminate(); | |
| break; | |
| case OP_COMPARE_STR: | |
| intStack[++_UINT] = STR.compare(); | |
| break; | |
| case OP_PUSH: | |
| STR.push(); | |
| break; | |
| case OP_PUSH_FRAME: | |
| STR.pushFrame(); | |
| break; | |
| case OP_ASSERT: | |
| { | |
| if( !intStack[_UINT--] ) | |
| { | |
| const char *message = curStringTable + code[ip]; | |
| U32 breakLine, inst; | |
| findBreakLine( ip - 1, breakLine, inst ); | |
| if ( PlatformAssert::processAssert( PlatformAssert::Fatal, | |
| name ? name : "eval", | |
| breakLine, | |
| message ) ) | |
| { | |
| if ( TelDebugger && TelDebugger->isConnected() && breakLine > 0 ) | |
| { | |
| TelDebugger->breakProcess(); | |
| } | |
| else | |
| Platform::debugBreak(); | |
| } | |
| } | |
| ip++; | |
| break; | |
| } | |
| case OP_BREAK: | |
| { | |
| //append the ip and codeptr before managing the breakpoint! | |
| AssertFatal( gEvalState.getStackDepth() > 0, "Empty eval stack on break!"); | |
| gEvalState.getCurrentFrame().code = this; | |
| gEvalState.getCurrentFrame().ip = ip - 1; | |
| U32 breakLine; | |
| findBreakLine(ip-1, breakLine, instruction); | |
| if(!breakLine) | |
| goto breakContinue; | |
| TelDebugger->executionStopped(this, breakLine); | |
| goto breakContinue; | |
| } | |
| case OP_ITER_BEGIN_STR: | |
| { | |
| iterStack[ _ITER ].mIsStringIter = true; | |
| /* fallthrough */ | |
| } | |
| case OP_ITER_BEGIN: | |
| { | |
| StringTableEntry varName = U32toSTE( code[ ip ] ); | |
| U32 failIp = code[ ip + 1 ]; | |
| IterStackRecord& iter = iterStack[ _ITER ]; | |
| iter.mVariable = gEvalState.getCurrentFrame().add( varName ); | |
| if( iter.mIsStringIter ) | |
| { | |
| iter.mData.mStr.mString = STR.getStringValue(); | |
| iter.mData.mStr.mIndex = 0; | |
| } | |
| else | |
| { | |
| // Look up the object. | |
| SimSet* set; | |
| if( !Sim::findObject( STR.getStringValue(), set ) ) | |
| { | |
| Con::errorf( ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue() ); | |
| Con::errorf( ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?" ); | |
| ip = failIp; | |
| continue; | |
| } | |
| // Set up. | |
| iter.mData.mObj.mSet = set; | |
| iter.mData.mObj.mIndex = 0; | |
| } | |
| _ITER ++; | |
| iterDepth ++; | |
| STR.push(); | |
| ip += 2; | |
| break; | |
| } | |
| case OP_ITER: | |
| { | |
| U32 breakIp = code[ ip ]; | |
| IterStackRecord& iter = iterStack[ _ITER - 1 ]; | |
| if( iter.mIsStringIter ) | |
| { | |
| const char* str = iter.mData.mStr.mString; | |
| U32 startIndex = iter.mData.mStr.mIndex; | |
| U32 endIndex = startIndex; | |
| // Break if at end. | |
| if( !str[ startIndex ] ) | |
| { | |
| ip = breakIp; | |
| continue; | |
| } | |
| // Find right end of current component. | |
| if( !dIsspace( str[ endIndex ] ) ) | |
| do ++ endIndex; | |
| while( str[ endIndex ] && !dIsspace( str[ endIndex ] ) ); | |
| // Extract component. | |
| if( endIndex != startIndex ) | |
| { | |
| char savedChar = str[ endIndex ]; | |
| const_cast< char* >( str )[ endIndex ] = '\0'; // We are on the string stack so this is okay. | |
| iter.mVariable->setStringValue( &str[ startIndex ] ); | |
| const_cast< char* >( str )[ endIndex ] = savedChar; | |
| } | |
| else | |
| iter.mVariable->setStringValue( "" ); | |
| // Skip separator. | |
| if( str[ endIndex ] != '\0' ) | |
| ++ endIndex; | |
| iter.mData.mStr.mIndex = endIndex; | |
| } | |
| else | |
| { | |
| U32 index = iter.mData.mObj.mIndex; | |
| SimSet* set = iter.mData.mObj.mSet; | |
| if( index >= set->size() ) | |
| { | |
| ip = breakIp; | |
| continue; | |
| } | |
| iter.mVariable->setIntValue( set->at( index )->getId() ); | |
| iter.mData.mObj.mIndex = index + 1; | |
| } | |
| ++ ip; | |
| break; | |
| } | |
| case OP_ITER_END: | |
| { | |
| -- _ITER; | |
| -- iterDepth; | |
| STR.rewind(); | |
| iterStack[ _ITER ].mIsStringIter = false; | |
| break; | |
| } | |
| case OP_INVALID: | |
| default: | |
| // error! | |
| goto execFinished; | |
| } | |
| } | |
| execFinished: | |
| if ( telDebuggerOn && setFrame < 0 ) | |
| TelDebugger->popStackFrame(); | |
| if ( popFrame ) | |
| gEvalState.popFrame(); | |
| if(argv) | |
| { | |
| if(gEvalState.traceOn) | |
| { | |
| traceBuffer[0] = 0; | |
| dStrcat(traceBuffer, "Leaving "); | |
| if(packageName) | |
| { | |
| dStrcat(traceBuffer, "["); | |
| dStrcat(traceBuffer, packageName); | |
| dStrcat(traceBuffer, "]"); | |
| } | |
| if(thisNamespace && thisNamespace->mName) | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s::%s() - return %s", thisNamespace->mName, thisFunctionName, STR.getStringValue()); | |
| } | |
| else | |
| { | |
| dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), | |
| "%s() - return %s", thisFunctionName, STR.getStringValue()); | |
| } | |
| Con::printf("%s", traceBuffer); | |
| } | |
| } | |
| else | |
| { | |
| delete[] globalStrings; | |
| globalStringsMaxLen = 0; | |
| delete[] globalFloats; | |
| globalStrings = NULL; | |
| globalFloats = NULL; | |
| } | |
| smCurrentCodeBlock = saveCodeBlock; | |
| if(saveCodeBlock && saveCodeBlock->name) | |
| { | |
| Con::gCurrentFile = saveCodeBlock->name; | |
| Con::gCurrentRoot = saveCodeBlock->modPath; | |
| } | |
| decRefCount(); | |
| #ifdef TORQUE_DEBUG | |
| AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec"); | |
| AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec"); | |
| #endif | |
| return STR.getStringValue(); | |
| } | |
| //------------------------------------------------------------ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "platform/platform.h" | |
| #include "console/console.h" | |
| #include "console/ast.h" | |
| #include "core/tAlgorithm.h" | |
| #include "core/strings/findMatch.h" | |
| #include "core/strings/stringUnit.h" | |
| #include "console/consoleInternal.h" | |
| #include "core/stream/fileStream.h" | |
| #include "console/compiler.h" | |
| #include "console/simBase.h" | |
| #include "console/telnetDebugger.h" | |
| #include "sim/netStringTable.h" | |
| #include "console/ICallMethod.h" | |
| #include "console/stringStack.h" | |
| #include "util/messaging/message.h" | |
| #include "core/frameAllocator.h" | |
| #ifndef TORQUE_TGB_ONLY | |
| #include "materials/materialDefinition.h" | |
| #include "materials/materialManager.h" | |
| #endif | |
| #include "console/codeBlock.h" | |
| using namespace Compiler; | |
| namespace Con | |
| { | |
| // Current script file name and root, these are registered as | |
| // console variables. | |
| extern StringTableEntry gCurrentFile; | |
| extern StringTableEntry gCurrentRoot; | |
| extern S32 gObjectCopyFailures; | |
| } | |
| char CodeBlock::curFieldArray[256]; | |
| char CodeBlock::prevFieldArray[256]; | |
| bool CodeBlock::printopcodes = false; | |
| // use in lieu of break | |
| // prepare for next call | |
| // call next function in opcode list | |
| // use explicit return to force tail call optimization | |
| #define OP_NEXT_CALL instruction = code[(*ip)++]; nsEntry = NULL; CALL_MEMBER_FN_PTR(*this,opcodeList[instruction])(); return; | |
| #define OP_NEXT_CALL_NOBREAK CALL_MEMBER_FN_PTR(*this,opcodeList[instruction])(); return; | |
| // not an opcode | |
| inline void CodeBlock::op_next_call(){ | |
| int tmpvar = 0; | |
| instruction = code[(*ip)++]; | |
| nsEntry = NULL; | |
| if( ((*ip)-1) == 1034 && instruction == 60 ){ | |
| //tmpvar = 1; | |
| } | |
| if( ((*ip)-1) == DB_IP && instruction == DB_OPCODE ){ | |
| tmpvar = 1; | |
| //printopcodes = true; | |
| } | |
| if(tmpvar){ | |
| Con::printf("break"); | |
| tmpvar = 0; | |
| } | |
| if( printopcodes ){ | |
| Con::printf("opcode: %d, ip: %d", instruction, (*ip)-1); | |
| } | |
| CALL_MEMBER_FN_PTR(*this,opcodeList[instruction])(); | |
| return; | |
| } | |
| // not an opcode | |
| inline void CodeBlock::op_next_call_nobreak(){ | |
| CALL_MEMBER_FN_PTR(*this,opcodeList[instruction])(); | |
| return; | |
| } | |
| // support functions | |
| // Gets a component of an object's field value or a variable and returns it | |
| // in val. | |
| void CodeBlock::_getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ) | |
| { | |
| const char* prevVal = NULL; | |
| // Grab value from object. | |
| if( object && field ) | |
| prevVal = object->getDataField( field, array ); | |
| // Otherwise, grab from the string stack. The value coming in will always | |
| // be a string because that is how multicomponent variables are handled. | |
| else | |
| prevVal = STR.getStringValue(); | |
| // Make sure we got a value. | |
| if ( prevVal && *prevVal ) | |
| { | |
| static const StringTableEntry xyzw[] = | |
| { | |
| StringTable->insert( "x" ), | |
| StringTable->insert( "y" ), | |
| StringTable->insert( "z" ), | |
| StringTable->insert( "w" ) | |
| }; | |
| static const StringTableEntry rgba[] = | |
| { | |
| StringTable->insert( "r" ), | |
| StringTable->insert( "g" ), | |
| StringTable->insert( "b" ), | |
| StringTable->insert( "a" ) | |
| }; | |
| // Translate xyzw and rgba into the indexed component | |
| // of the variable or field. | |
| if ( subField == xyzw[0] || subField == rgba[0] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") ); | |
| else if ( subField == xyzw[1] || subField == rgba[1] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") ); | |
| else if ( subField == xyzw[2] || subField == rgba[2] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") ); | |
| else if ( subField == xyzw[3] || subField == rgba[3] ) | |
| dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") ); | |
| else | |
| val[0] = 0; | |
| } | |
| else | |
| val[0] = 0; | |
| } | |
| // Sets a component of an object's field value based on the sub field. 'x' will | |
| // set the first field, 'y' the second, and 'z' the third. | |
| void CodeBlock::_setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ) | |
| { | |
| // Copy the current string value | |
| char strValue[1024]; | |
| dStrncpy( strValue, STR.getStringValue(), 1024 ); | |
| char val[1024] = ""; | |
| const char* prevVal = NULL; | |
| // Set the value on an object field. | |
| if( object && field ) | |
| prevVal = object->getDataField( field, array ); | |
| // Set the value on a variable. | |
| else if( gEvalState.currentVariable ) | |
| prevVal = gEvalState.getStringVariable(); | |
| // Ensure that the variable has a value | |
| if (!prevVal) | |
| return; | |
| static const StringTableEntry xyzw[] = | |
| { | |
| StringTable->insert( "x" ), | |
| StringTable->insert( "y" ), | |
| StringTable->insert( "z" ), | |
| StringTable->insert( "w" ) | |
| }; | |
| static const StringTableEntry rgba[] = | |
| { | |
| StringTable->insert( "r" ), | |
| StringTable->insert( "g" ), | |
| StringTable->insert( "b" ), | |
| StringTable->insert( "a" ) | |
| }; | |
| // Insert the value into the specified | |
| // component of the string. | |
| if ( subField == xyzw[0] || subField == rgba[0] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") ); | |
| else if ( subField == xyzw[1] || subField == rgba[1] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") ); | |
| else if ( subField == xyzw[2] || subField == rgba[2] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") ); | |
| else if ( subField == xyzw[3] || subField == rgba[3] ) | |
| dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") ); | |
| if ( val[0] != 0 ) | |
| { | |
| // Update the field or variable. | |
| if( object && field ) | |
| object->setDataField( field, 0, val ); | |
| else if( gEvalState.currentVariable ) | |
| gEvalState.setStringVariable( val ); | |
| } | |
| } | |
| // opcode functions | |
| // case OP_FUNC_DECL: | |
| void CodeBlock::op_func_decl(){ | |
| if(!(*noCalls)) | |
| { | |
| fnName = U32toSTE(code[(*ip)]); | |
| fnNamespace = U32toSTE(code[(*ip)+1]); | |
| fnPackage = U32toSTE(code[(*ip)+2]); | |
| bool hasBody = ( code[ (*ip) + 3 ] & 0x01 ) != 0; | |
| U32 lineNumber = code[ (*ip) + 3 ] >> 1; | |
| Namespace::unlinkPackages(); | |
| ns = Namespace::find(fnNamespace, fnPackage); | |
| ns->addFunction(fnName, this, hasBody ? (*ip) : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL, lineNumber );// if no body, set the IP to 0 | |
| if( curNSDocBlock ) | |
| { | |
| if( fnNamespace == StringTable->lookup( nsDocBlockClass ) ) | |
| { | |
| char *usageStr = dStrdup( curNSDocBlock ); | |
| usageStr[dStrlen(usageStr)] = '\0'; | |
| ns->mUsage = usageStr; | |
| ns->mCleanUpUsage = true; | |
| curNSDocBlock = NULL; | |
| } | |
| } | |
| Namespace::relinkPackages(); | |
| // If we had a docblock, it's definitely not valid anymore, so clear it out. | |
| curFNDocBlock = NULL; | |
| //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, (*ip)); | |
| } | |
| (*ip) = code[(*ip) + 4]; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_create_object(){ | |
| // Read some useful info. | |
| objParent = U32toSTE(code[(*ip) ]); | |
| bool isDataBlock = code[(*ip) + 1]; | |
| bool isInternal = code[(*ip) + 2]; | |
| bool isSingleton = code[(*ip) + 3]; | |
| U32 lineNumber = code[(*ip) + 4]; | |
| failJump = code[(*ip) + 5]; | |
| // If we don't allow calls, we certainly don't allow creating objects! | |
| // Moved this to after failJump is set. Engine was crashing when | |
| // noCalls = true and an object was being created at the beginning of | |
| // a file. ADL. | |
| if((*noCalls)) | |
| { | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| } | |
| // Push the old info to the stack | |
| //Assert( objectCreationStackIndex < objectCreationStackSize ); | |
| objectCreationStack[ objectCreationStackIndex ].newObject = (*currentNewObject); | |
| objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump; | |
| // Get the constructor information off the stack. | |
| STR.getArgcArgv(NULL, &callArgc, &callArgv); | |
| const char* objectName = callArgv[ 2 ]; | |
| // Con::printf("Creating object..."); | |
| // objectName = argv[1]... | |
| (*currentNewObject) = NULL; | |
| // Are we creating a datablock? If so, deal with case where we override | |
| // an old one. | |
| if(isDataBlock) | |
| { | |
| // Con::printf(" - is a datablock"); | |
| // Find the old one if any. | |
| SimObject *db = Sim::getDataBlockGroup()->findObject( objectName ); | |
| // Make sure we're not changing types on ourselves... | |
| if(db && dStricmp(db->getClassName(), callArgv[1])) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine((*ip)), objectName); | |
| (*ip) = failJump; | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // If there was one, set the currentNewObject and move on. | |
| if(db) | |
| (*currentNewObject) = db; | |
| } | |
| else if (!isInternal) | |
| { | |
| // IF we aren't looking at a local/internal object, then check if | |
| // this object already exists in the global space | |
| SimObject *obj = Sim::findObject( objectName ); | |
| if (obj) //&& !obj->isLocalName()) | |
| { | |
| if ( isSingleton ) | |
| { | |
| // Make sure we're not trying to change types | |
| if ( dStricmp( obj->getClassName(), callArgv[1] ) != 0 ) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].", | |
| getFileLine((*ip)), objectName, callArgv[1], obj->getClassName()); | |
| (*ip) = failJump; | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // We're creating a singleton, so use the found object | |
| // instead of creating a new object. | |
| (*currentNewObject) = obj; | |
| } | |
| else | |
| { | |
| const char* redefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); | |
| if( dStricmp( redefineBehavior, "replaceExisting" ) == 0 ) | |
| { | |
| // Save our constructor args as the argv vector is stored on the | |
| // string stack and may get stomped if deleteObject triggers | |
| // script execution. | |
| const char* savedArgv[ StringStack::MaxArgs ]; | |
| dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); | |
| obj->deleteObject(); | |
| obj = NULL; | |
| dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); | |
| } | |
| else if( dStricmp( redefineBehavior, "renameNew" ) == 0 ) | |
| { | |
| for( U32 i = 1;; ++ i ) | |
| { | |
| String newName = String::ToString( "%s%i", objectName, i ); | |
| if( !Sim::findObject( newName ) ) | |
| { | |
| objectName = StringTable->insert( newName ); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| } | |
| } | |
| else if( dStricmp( redefineBehavior, "unnameNew" ) == 0 ) | |
| { | |
| objectName = StringTable->insert( "" ); | |
| } | |
| else if( dStricmp( redefineBehavior, "postfixNew" ) == 0 ) | |
| { | |
| const char* postfix = Con::getVariable( "$Con::redefineBehaviorPostfix" ); | |
| String newName = String::ToString( "%s%s", objectName, postfix ); | |
| if( Sim::findObject( newName ) ) | |
| { | |
| Con::errorf( ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].", | |
| getFileLine((*ip)), newName.c_str() ); | |
| (*ip) = failJump; | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| else | |
| objectName = StringTable->insert( newName ); | |
| } | |
| else | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].", | |
| getFileLine((*ip)), objectName); | |
| (*ip) = failJump; | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| STR.popFrame(); | |
| if(!(*currentNewObject)) | |
| { | |
| // Well, looks like we have to create a new object. | |
| ConsoleObject *object = ConsoleObject::create(callArgv[1]); | |
| // Deal with failure! | |
| if(!object) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine((*ip)), callArgv[1]); | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // Do special datablock init if appropros | |
| if(isDataBlock) | |
| { | |
| SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(object); | |
| if(dataBlock) | |
| { | |
| dataBlock->assignId(); | |
| } | |
| else | |
| { | |
| // They tried to make a non-datablock with a datablock keyword! | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine((*ip)), callArgv[1]); | |
| // Clean up... | |
| delete object; | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| } | |
| // Finally, set currentNewObject to point to the new one. | |
| (*currentNewObject) = dynamic_cast<SimObject *>(object); | |
| // Deal with the case of a non-SimObject. | |
| if(!(*currentNewObject)) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", getFileLine((*ip)), callArgv[1]); | |
| delete object; | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // Set the declaration line | |
| (*currentNewObject)->setDeclarationLine(lineNumber); | |
| // Set the file that this object was created in | |
| (*currentNewObject)->setFilename(name); | |
| // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance) | |
| if(*objParent) | |
| { | |
| // Find it! | |
| SimObject *parent; | |
| if(Sim::findObject(objParent, parent)) | |
| { | |
| // Con::printf(" - Parent object found: %s", parent->getClassName()); | |
| (*currentNewObject)->setCopySource( parent ); | |
| (*currentNewObject)->assignFieldsFrom( parent ); | |
| } | |
| else | |
| { | |
| if ( Con::gObjectCopyFailures == -1 ) | |
| Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine((*ip)), objParent, callArgv[1]); | |
| else | |
| ++Con::gObjectCopyFailures; | |
| // Fail to create the object. | |
| delete object; | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| } | |
| // If a name was passed, assign it. | |
| if( objectName[ 0 ] ) | |
| { | |
| if( !isInternal ) | |
| (*currentNewObject)->assignName( objectName ); | |
| else | |
| (*currentNewObject)->setInternalName( objectName ); | |
| // Set the original name | |
| (*currentNewObject)->setOriginalName( objectName ); | |
| } | |
| // Do the constructor parameters. | |
| if(!(*currentNewObject)->processArguments(callArgc-3, callArgv+3)) | |
| { | |
| delete (*currentNewObject); | |
| (*currentNewObject) = NULL; | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // If it's not a datablock, allow people to modify bits of it. | |
| if(!isDataBlock) | |
| { | |
| (*currentNewObject)->setModStaticFields(true); | |
| (*currentNewObject)->setModDynamicFields(true); | |
| } | |
| } | |
| // Advance the IP past the create info... | |
| (*ip) += 6; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_add_object(){ | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| // Do we place this object at the root? | |
| bool placeAtRoot = code[(*ip)++]; | |
| // Con::printf("Adding object %s", currentNewObject->getName()); | |
| // Make sure it wasn't already added, then add it. | |
| if((*currentNewObject)->isProperlyAdded() == false) | |
| { | |
| bool ret = false; | |
| Message *msg = dynamic_cast<Message *>((*currentNewObject)); | |
| if(msg) | |
| { | |
| SimObjectId id = Message::getNextMessageID(); | |
| if(id != 0xffffffff) | |
| ret = (*currentNewObject)->registerObject(id); | |
| else | |
| Con::errorf("%s: No more object IDs available for messages", getFileLine((*ip))); | |
| } | |
| else{ | |
| ret = (*currentNewObject)->registerObject(); | |
| } | |
| if(! ret) | |
| { | |
| // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields(). | |
| Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine((*ip)), (*currentNewObject)->getName(), (*currentNewObject)->getClassName()); | |
| delete (*currentNewObject); | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| } | |
| // Are we dealing with a datablock? | |
| SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>((*currentNewObject)); | |
| static String errorStr; | |
| // If so, preload it. | |
| if(dataBlock && !dataBlock->preload(true, errorStr)) | |
| { | |
| Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine((*ip)), | |
| (*currentNewObject)->getName(), errorStr.c_str()); | |
| dataBlock->deleteObject(); | |
| (*ip) = failJump; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // What group will we be added to, if any? | |
| U32 groupAddId = intStack[_UINT]; | |
| SimGroup *grp = NULL; | |
| SimSet *set = NULL; | |
| bool isMessage = dynamic_cast<Message *>((*currentNewObject)) != NULL; | |
| if(!placeAtRoot || !(*currentNewObject)->getGroup()) | |
| { | |
| if(! isMessage) | |
| { | |
| if(! placeAtRoot) | |
| { | |
| // Otherwise just add to the requested group or set. | |
| if(!Sim::findObject(groupAddId, grp)) | |
| Sim::findObject(groupAddId, set); | |
| } | |
| if(placeAtRoot) | |
| { | |
| // Deal with the instantGroup if we're being put at the root or we're adding to a component. | |
| if( Con::gInstantGroup.isEmpty() | |
| || !Sim::findObject( Con::gInstantGroup, grp ) ) | |
| grp = Sim::getRootGroup(); | |
| } | |
| } | |
| // If we didn't get a group, then make sure we have a pointer to | |
| // the rootgroup. | |
| if(!grp) | |
| grp = Sim::getRootGroup(); | |
| // add to the parent group | |
| grp->addObject((*currentNewObject)); | |
| // If for some reason the add failed, add the object to the | |
| // root group so it won't leak. | |
| if( !(*currentNewObject)->getGroup() ) | |
| Sim::getRootGroup()->addObject( (*currentNewObject) ); | |
| // add to any set we might be in | |
| if(set) | |
| set->addObject((*currentNewObject)); | |
| } | |
| // store the new object's ID on the stack (overwriting the group/set | |
| // id, if one was given, otherwise getting pushed) | |
| if(placeAtRoot) | |
| intStack[_UINT] = (*currentNewObject)->getId(); | |
| else | |
| intStack[++_UINT] = (*currentNewObject)->getId(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_end_object(){ | |
| // If we're not to be placed at the root, make sure we clean up | |
| // our group reference. | |
| bool placeAtRoot = code[(*ip)++]; | |
| if(!placeAtRoot) | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_finish_object(){ | |
| //Assert( objectCreationStackIndex >= 0 ); | |
| // Restore the object info from the stack [7/9/2007 Black] | |
| (*currentNewObject) = objectCreationStack[ --objectCreationStackIndex ].newObject; | |
| failJump = objectCreationStack[ objectCreationStackIndex ].failJump; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpiffnot(){ | |
| if(floatStack[_FLT--]) | |
| { | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpifnot(){ | |
| if(intStack[_UINT--]) | |
| { | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpiff(){ | |
| if(!floatStack[_FLT--]) | |
| { | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpif(){ | |
| if(!intStack[_UINT--]) | |
| { | |
| (*ip) ++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpifnot_np(){ | |
| if(intStack[_UINT]) | |
| { | |
| _UINT--; | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmpif_np(){ | |
| if(!intStack[_UINT]) | |
| { | |
| _UINT--; | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_jmp(){ | |
| (*ip) = code[(*ip)]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_return_void(){ | |
| STR.setStringValue(""); | |
| op_return(); | |
| } | |
| void CodeBlock::op_return(){ | |
| if( iterDepth > 0 ) | |
| { | |
| // Clear iterator state. | |
| while( iterDepth > 0 ) | |
| { | |
| iterStack[ -- _ITER ].mIsStringIter = false; | |
| -- iterDepth; | |
| } | |
| const char* returnValue = STR.getStringValue(); | |
| STR.rewind(); | |
| STR.setStringValue( returnValue ); // Not nice but works. | |
| } | |
| // end of the line | |
| } | |
| void CodeBlock::op_cmpeq(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] == floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_cmpgr(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] > floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_cmpge(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] >= floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_cmplt(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] < floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_cmple(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] <= floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_cmpne(){ | |
| intStack[_UINT+1] = bool(floatStack[_FLT] != floatStack[_FLT-1]); | |
| _UINT++; | |
| _FLT -= 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_xor(){ | |
| intStack[_UINT-1] = intStack[_UINT] ^ intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_mod(){ | |
| if( intStack[_UINT-1] != 0 ) | |
| intStack[_UINT-1] = intStack[_UINT] % intStack[_UINT-1]; | |
| else | |
| intStack[_UINT-1] = 0; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_bitand(){ | |
| intStack[_UINT-1] = intStack[_UINT] & intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_bitor(){ | |
| intStack[_UINT-1] = intStack[_UINT] | intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_not(){ | |
| intStack[_UINT] = !intStack[_UINT]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_notf(){ | |
| intStack[_UINT+1] = !floatStack[_FLT]; | |
| _FLT--; | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_onescomplement(){ | |
| intStack[_UINT] = ~intStack[_UINT]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_shr(){ | |
| intStack[_UINT-1] = intStack[_UINT] >> intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_shl(){ | |
| intStack[_UINT-1] = intStack[_UINT] << intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_and(){ | |
| intStack[_UINT-1] = intStack[_UINT] && intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_or(){ | |
| intStack[_UINT-1] = intStack[_UINT] || intStack[_UINT-1]; | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_add(){ | |
| floatStack[_FLT-1] = floatStack[_FLT] + floatStack[_FLT-1]; | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_sub(){ | |
| floatStack[_FLT-1] = floatStack[_FLT] - floatStack[_FLT-1]; | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_mul(){ | |
| floatStack[_FLT-1] = floatStack[_FLT] * floatStack[_FLT-1]; | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_div(){ | |
| floatStack[_FLT-1] = floatStack[_FLT] / floatStack[_FLT-1]; | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_neg(){ | |
| floatStack[_FLT] = -floatStack[_FLT]; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurvar(){ | |
| var = U32toSTE(code[(*ip)]); | |
| (*ip)++; | |
| // If a variable is set, then these must be NULL. It is necessary | |
| // to set this here so that the vector parser can appropriately | |
| // identify whether it's dealing with a vector. | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarName(var); | |
| // In order to let docblocks work properly with variables, we have | |
| // clear the current docblock when we do an assign. This way it | |
| // won't inappropriately carry forward to following function decls. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurvar_create(){ | |
| var = U32toSTE(code[(*ip)]); | |
| #if _DEBUG | |
| if( !dStrcmp(var,"%dirPath") ){ | |
| int tmpvar = 1; | |
| } | |
| #endif | |
| (*ip)++; | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarNameCreate(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurvar_array(){ | |
| var = STR.getSTValue(); | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarName(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurvar_array_create(){ | |
| var = STR.getSTValue(); | |
| // See OP_SETCURVAR | |
| prevField = NULL; | |
| prevObject = NULL; | |
| curObject = NULL; | |
| gEvalState.setCurVarNameCreate(var); | |
| // See OP_SETCURVAR for why we do this. | |
| curFNDocBlock = NULL; | |
| curNSDocBlock = NULL; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadvar_uint(){ | |
| intStack[_UINT+1] = gEvalState.getIntVariable(); | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadvar_flt(){ | |
| floatStack[_FLT+1] = gEvalState.getFloatVariable(); | |
| _FLT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadvar_str(){ | |
| val = gEvalState.getStringVariable(); | |
| STR.setStringValue(val); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savevar_uint(){ | |
| gEvalState.setIntVariable(intStack[_UINT]); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savevar_flt(){ | |
| gEvalState.setFloatVariable(floatStack[_FLT]); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savevar_str(){ | |
| gEvalState.setStringVariable(STR.getStringValue()); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurobject(){ | |
| // Save the previous object for parsing vector fields. | |
| prevObject = curObject; | |
| val = STR.getStringValue(); | |
| // Sim::findObject will sometimes find valid objects from | |
| // multi-component strings. This makes sure that doesn't | |
| // happen. | |
| for( const char* check = val; *check; check++ ) | |
| { | |
| if( *check == ' ' ) | |
| { | |
| val = ""; | |
| break; | |
| } | |
| } | |
| curObject = Sim::findObject(val); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurobject_internal(){ | |
| ++(*ip); // To skip the recurse flag if the object wasn't found | |
| if(curObject) | |
| { | |
| SimSet *set = dynamic_cast<SimSet *>(curObject); | |
| if(set) | |
| { | |
| StringTableEntry intName = StringTable->insert(STR.getStringValue()); | |
| bool recurse = code[(*ip)-1]; | |
| SimObject *obj = set->findObjectByInternalName(intName, recurse); | |
| intStack[_UINT+1] = obj ? obj->getId() : 0; | |
| _UINT++; | |
| } | |
| else | |
| { | |
| Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", getFileLine((*ip)-2), curObject->getName(), curObject->getClassName()); | |
| intStack[_UINT] = 0; | |
| } | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurobject_new(){ | |
| curObject = (*currentNewObject); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurfield(){ | |
| // Save the previous field for parsing vector fields. | |
| prevField = curField; | |
| dStrcpy( prevFieldArray, curFieldArray ); | |
| curField = U32toSTE(code[(*ip)]); | |
| curFieldArray[0] = 0; | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurfield_array(){ | |
| dStrcpy(curFieldArray, STR.getStringValue()); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_setcurfield_type(){ | |
| if(curObject) | |
| curObject->setDataFieldType(code[(*ip)], curField, curFieldArray); | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadfield_uint(){ | |
| if(curObject) | |
| intStack[_UINT+1] = U32(dAtoi(curObject->getDataField(curField, curFieldArray))); | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| _getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| intStack[_UINT+1] = dAtoi( valBuffer ); | |
| } | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadfield_flt(){ | |
| if(curObject) | |
| floatStack[_FLT+1] = dAtof(curObject->getDataField(curField, curFieldArray)); | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| _getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| floatStack[_FLT+1] = dAtof( valBuffer ); | |
| } | |
| _FLT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadfield_str(){ | |
| if(curObject) | |
| { | |
| val = curObject->getDataField(curField, curFieldArray); | |
| STR.setStringValue( val ); | |
| } | |
| else | |
| { | |
| // The field is not being retrieved from an object. Maybe it's | |
| // a special accessor? | |
| _getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); | |
| STR.setStringValue( valBuffer ); | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savefield_uint(){ | |
| STR.setIntValue(intStack[_UINT]); | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| _setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savefield_flt(){ | |
| STR.setFloatValue(floatStack[_FLT]); | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| _setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_savefield_str(){ | |
| if(curObject) | |
| curObject->setDataField(curField, curFieldArray, STR.getStringValue()); | |
| else | |
| { | |
| // The field is not being set on an object. Maybe it's | |
| // a special accessor? | |
| _setFieldComponent( prevObject, prevField, prevFieldArray, curField ); | |
| prevObject = NULL; | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_str_to_uint(){ | |
| intStack[_UINT+1] = STR.getIntValue(); | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_str_to_flt(){ | |
| floatStack[_FLT+1] = STR.getFloatValue(); | |
| _FLT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_str_to_none(){ | |
| // This exists simply to deal with certain typecast situations. | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_flt_to_uint(){ | |
| intStack[_UINT+1] = (S64)floatStack[_FLT]; | |
| _FLT--; | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_flt_to_str(){ | |
| STR.setFloatValue(floatStack[_FLT]); | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_flt_to_none(){ | |
| _FLT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_uint_to_flt(){ | |
| floatStack[_FLT+1] = (F32)intStack[_UINT]; | |
| _UINT--; | |
| _FLT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_uint_to_str(){ | |
| STR.setIntValue(intStack[_UINT]); | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_uint_to_none(){ | |
| _UINT--; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadimmed_uint(){ | |
| intStack[_UINT+1] = code[(*ip)++]; | |
| _UINT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadimmed_flt(){ | |
| floatStack[_FLT+1] = curFloatTable[code[(*ip)]]; | |
| (*ip)++; | |
| _FLT++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_tag_to_str(){ | |
| code[(*ip)-1] = OP_LOADIMMED_STR; | |
| // it's possible the string has already been converted | |
| if(U8(curStringTable[code[(*ip)]]) != StringTagPrefixByte) | |
| { | |
| U32 id = GameAddTaggedString(curStringTable + code[(*ip)]); | |
| dSprintf(curStringTable + code[(*ip)] + 1, 7, "%d", id); | |
| *(curStringTable + code[(*ip)]) = StringTagPrefixByte; | |
| } | |
| // falls through next opcode | |
| op_loadimmed_str(); | |
| } | |
| void CodeBlock::op_loadimmed_str(){ | |
| STR.setStringValue(curStringTable + code[(*ip)++]); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_docblock_str(){ | |
| { | |
| // If the first word of the doc is '\class' or '@class', then this | |
| // is a namespace doc block, otherwise it is a function doc block. | |
| const char* docblock = curStringTable + code[(*ip)++]; | |
| const char* sansClass = dStrstr( docblock, "@class" ); | |
| if( !sansClass ) | |
| sansClass = dStrstr( docblock, "\\class" ); | |
| if( sansClass ) | |
| { | |
| // Don't save the class declaration. Scan past the 'class' | |
| // keyword and up to the first whitespace. | |
| sansClass += 7; | |
| S32 index = 0; | |
| while( ( *sansClass != ' ' ) && ( *sansClass != '\n' ) && *sansClass && ( index < ( nsDocLength - 1 ) ) ) | |
| { | |
| nsDocBlockClass[index++] = *sansClass; | |
| sansClass++; | |
| } | |
| nsDocBlockClass[index] = '\0'; | |
| curNSDocBlock = sansClass + 1; | |
| } | |
| else | |
| curFNDocBlock = docblock; | |
| } | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_loadimmed_ident(){ | |
| STR.setStringValue(U32toSTE(code[(*ip)++])); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_callfunc_resolve(){ | |
| // This deals with a function that is potentially living in a namespace. | |
| fnNamespace = U32toSTE(code[(*ip)+1]); | |
| fnName = U32toSTE(code[(*ip)]); | |
| if( ((*ip)-1) == DB_OPCODE ){ | |
| int tmpvar = 1; | |
| } | |
| // Try to look it up. | |
| ns = Namespace::find(fnNamespace); | |
| nsEntry = ns->lookup(fnName); | |
| if(!nsEntry) | |
| { | |
| (*ip)+= 3; | |
| Con::warnf(ConsoleLogEntry::General, | |
| "%s: Unable to find function %s%s%s", | |
| getFileLine((*ip)-4), fnNamespace ? fnNamespace : "", | |
| fnNamespace ? "::" : "", fnName); | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| // Now fall through to OP_CALLFUNC... | |
| op_callfunc(); | |
| } | |
| void CodeBlock::op_callfunc(){ | |
| // This routingId is set when we query the object as to whether | |
| // it handles this method. It is set to an enum from the table | |
| // above indicating whether it handles it on a component it owns | |
| // or just on the object. | |
| S32 routingId = 0; | |
| if( ((*ip)-1) == DB_OPCODE ){ | |
| int tmpvar = 1; | |
| } | |
| fnName = U32toSTE(code[(*ip)]); | |
| //if this is called from inside a function, append the ip and codeptr | |
| if( gEvalState.getStackDepth() > 0 ) | |
| { | |
| gEvalState.getCurrentFrame().code = this; | |
| gEvalState.getCurrentFrame().ip = (*ip) - 1; | |
| } | |
| U32 callType = code[(*ip)+2]; | |
| (*ip) += 3; | |
| STR.getArgcArgv(fnName, &callArgc, &callArgv); | |
| const char *componentReturnValue = ""; | |
| if(callType == FuncCallExprNode::FunctionCall) | |
| { | |
| if( !nsEntry ) | |
| { | |
| // We must not have come from OP_CALLFUNC_RESOLVE, so figure out | |
| // our own entry. | |
| nsEntry = Namespace::global()->lookup( fnName ); | |
| } | |
| ns = NULL; | |
| } | |
| else if(callType == FuncCallExprNode::MethodCall) | |
| { | |
| saveObject = gEvalState.thisObject; | |
| gEvalState.thisObject = Sim::findObject(callArgv[1]); | |
| if(!gEvalState.thisObject) | |
| { | |
| // Go back to the previous saved object. | |
| gEvalState.thisObject = saveObject; | |
| Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine((*ip)-4), callArgv[1], fnName); | |
| STR.popFrame(); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId); | |
| if( handlesMethod && routingId == MethodOnComponent ) | |
| { | |
| ICallMethod *pComponent = dynamic_cast<ICallMethod *>( gEvalState.thisObject ); | |
| if( pComponent ){ | |
| componentReturnValue = pComponent->callMethodArgList( callArgc, callArgv, false ); | |
| } | |
| } | |
| ns = gEvalState.thisObject->getNamespace(); | |
| if(ns) | |
| nsEntry = ns->lookup(fnName); | |
| else | |
| nsEntry = NULL; | |
| } | |
| else // it's a ParentCall | |
| { | |
| if(*thisNamespace) | |
| { | |
| ns = (*thisNamespace)->mParent; | |
| if(ns) | |
| nsEntry = ns->lookup(fnName); | |
| else | |
| nsEntry = NULL; | |
| } | |
| else | |
| { | |
| ns = NULL; | |
| nsEntry = NULL; | |
| } | |
| } | |
| Namespace::Entry::CallbackUnion * nsCb = NULL; | |
| const char * nsUsage = NULL; | |
| if (nsEntry) | |
| { | |
| nsCb = &nsEntry->cb; | |
| nsUsage = nsEntry->mUsage; | |
| routingId = 0; | |
| } | |
| if(!nsEntry || (*noCalls)) | |
| { | |
| if(!(*noCalls) && !( routingId == MethodOnComponent ) ) | |
| { | |
| Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine((*ip)-4), fnName); | |
| if(callType == FuncCallExprNode::MethodCall) | |
| { | |
| Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", | |
| gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "", | |
| gEvalState.thisObject->getId(), Con::getNamespaceList(ns) ); | |
| } | |
| } | |
| STR.popFrame(); | |
| if( routingId == MethodOnComponent ) | |
| STR.setStringValue( componentReturnValue ); | |
| else | |
| STR.setStringValue( "" ); | |
| //break; | |
| op_next_call(); | |
| return; | |
| } | |
| if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType) | |
| { | |
| const char *ret = ""; | |
| if(nsEntry->mFunctionOffset){ | |
| ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); | |
| } | |
| STR.popFrame(); | |
| STR.setStringValue(ret); | |
| } | |
| else | |
| { | |
| const char* nsName = ns? ns->mName: ""; | |
| #ifndef TORQUE_DEBUG | |
| // [tom, 12/13/2006] This stops tools functions from working in the console, | |
| // which is useful behavior when debugging so I'm ifdefing this out for debug builds. | |
| if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript()) | |
| { | |
| Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine((*ip)-4), nsName, fnName); | |
| } | |
| else | |
| #endif | |
| if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) | |
| { | |
| Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).", | |
| getFileLine((*ip)-4), nsName, fnName, | |
| callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); | |
| Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine((*ip)-4), nsEntry->mUsage); | |
| STR.popFrame(); | |
| } | |
| else | |
| { | |
| switch(nsEntry->mType) | |
| { | |
| case Namespace::Entry::StringCallbackType: | |
| { | |
| const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(ret != STR.getStringValue()) | |
| STR.setStringValue(ret); | |
| else | |
| STR.setLen(dStrlen(ret)); | |
| break; | |
| } | |
| case Namespace::Entry::IntCallbackType: | |
| { | |
| S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[(*ip)] == OP_STR_TO_UINT) | |
| { | |
| (*ip)++; | |
| intStack[++_UINT] = result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_FLT) | |
| { | |
| (*ip)++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_NONE) | |
| (*ip)++; | |
| else | |
| STR.setIntValue(result); | |
| break; | |
| } | |
| case Namespace::Entry::FloatCallbackType: | |
| { | |
| F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[(*ip)] == OP_STR_TO_UINT) | |
| { | |
| (*ip)++; | |
| intStack[++_UINT] = (S64)result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_FLT) | |
| { | |
| (*ip)++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_NONE) | |
| (*ip)++; | |
| else | |
| STR.setFloatValue(result); | |
| break; | |
| } | |
| case Namespace::Entry::VoidCallbackType: | |
| { | |
| nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| if( code[ (*ip) ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) ) | |
| Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine((*ip)-4), fnName, *functionName); | |
| STR.popFrame(); | |
| STR.setStringValue(""); | |
| break; | |
| } | |
| case Namespace::Entry::BoolCallbackType: | |
| { | |
| bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); | |
| STR.popFrame(); | |
| if(code[(*ip)] == OP_STR_TO_UINT) | |
| { | |
| (*ip)++; | |
| intStack[++_UINT] = result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_FLT) | |
| { | |
| (*ip)++; | |
| floatStack[++_FLT] = result; | |
| break; | |
| } | |
| else if(code[(*ip)] == OP_STR_TO_NONE) | |
| (*ip)++; | |
| else | |
| STR.setIntValue(result); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if(callType == FuncCallExprNode::MethodCall) | |
| gEvalState.thisObject = saveObject; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_advance_str(){ | |
| STR.advance(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_advance_str_appendchar(){ | |
| STR.advanceChar(code[(*ip)++]); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_advance_str_comma(){ | |
| STR.advanceChar('_'); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_advance_str_nul(){ | |
| STR.advanceChar(0); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_rewind_str(){ | |
| STR.rewind(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_terminate_rewind_str(){ | |
| STR.rewindTerminate(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_compare_str(){ | |
| intStack[++_UINT] = STR.compare(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_push(){ | |
| STR.push(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_push_frame(){ | |
| STR.pushFrame(); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_assert(){ | |
| if( !intStack[_UINT--] ) | |
| { | |
| const char *message = curStringTable + code[(*ip)]; | |
| U32 breakLine, inst; | |
| findBreakLine( (*ip) - 1, breakLine, inst ); | |
| if ( PlatformAssert::processAssert( PlatformAssert::Fatal, | |
| name ? name : "eval", | |
| breakLine, | |
| message ) ) | |
| { | |
| if ( TelDebugger && TelDebugger->isConnected() && breakLine > 0 ) | |
| { | |
| TelDebugger->breakProcess(); | |
| } | |
| else | |
| Platform::debugBreak(); | |
| } | |
| } | |
| (*ip)++; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_break(){ | |
| //append the ip and codeptr before managing the breakpoint! | |
| AssertFatal( gEvalState.getStackDepth() > 0, "Empty eval stack on break!"); | |
| gEvalState.getCurrentFrame().code = this; | |
| gEvalState.getCurrentFrame().ip = (*ip) - 1; | |
| U32 breakLine; | |
| findBreakLine((*ip)-1, breakLine, instruction); | |
| if(!breakLine){ | |
| //goto breakContinue; | |
| op_next_call_nobreak(); | |
| return; | |
| } | |
| TelDebugger->executionStopped(this, breakLine); | |
| //goto breakContinue; | |
| op_next_call_nobreak(); | |
| } | |
| void CodeBlock::op_iter_begin_str(){ | |
| iterStack[ _ITER ].mIsStringIter = true; | |
| // fallthrough | |
| op_iter_begin(); | |
| } | |
| void CodeBlock::op_iter_begin(){ | |
| StringTableEntry varName = U32toSTE( code[ (*ip) ] ); | |
| U32 failIp = code[ (*ip) + 1 ]; | |
| IterStackRecord& iter = iterStack[ _ITER ]; | |
| iter.mVariable = gEvalState.getCurrentFrame().add( varName ); | |
| if( iter.mIsStringIter ) | |
| { | |
| iter.mData.mStr.mString = STR.getStringValue(); | |
| iter.mData.mStr.mIndex = 0; | |
| } | |
| else | |
| { | |
| // Look up the object. | |
| SimSet* set; | |
| if( !Sim::findObject( STR.getStringValue(), set ) ) | |
| { | |
| Con::errorf( ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue() ); | |
| Con::errorf( ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?" ); | |
| (*ip) = failIp; | |
| //continue; | |
| op_next_call(); | |
| return; | |
| } | |
| // Set up. | |
| iter.mData.mObj.mSet = set; | |
| iter.mData.mObj.mIndex = 0; | |
| } | |
| _ITER ++; | |
| iterDepth ++; | |
| STR.push(); | |
| (*ip) += 2; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_iter(){ | |
| U32 breakIp = code[ (*ip) ]; | |
| IterStackRecord& iter = iterStack[ _ITER - 1 ]; | |
| if( iter.mIsStringIter ) | |
| { | |
| const char* str = iter.mData.mStr.mString; | |
| U32 startIndex = iter.mData.mStr.mIndex; | |
| U32 endIndex = startIndex; | |
| // Break if at end. | |
| if( !str[ startIndex ] ) | |
| { | |
| (*ip) = breakIp; | |
| //continue; | |
| op_next_call(); | |
| return; | |
| } | |
| // Find right end of current component. | |
| if( !dIsspace( str[ endIndex ] ) ) | |
| do ++ endIndex; | |
| while( str[ endIndex ] && !dIsspace( str[ endIndex ] ) ); | |
| // Extract component. | |
| if( endIndex != startIndex ) | |
| { | |
| char savedChar = str[ endIndex ]; | |
| const_cast< char* >( str )[ endIndex ] = '\0'; // We are on the string stack so this is okay. | |
| iter.mVariable->setStringValue( &str[ startIndex ] ); | |
| const_cast< char* >( str )[ endIndex ] = savedChar; | |
| } | |
| else | |
| iter.mVariable->setStringValue( "" ); | |
| // Skip separator. | |
| if( str[ endIndex ] != '\0' ) | |
| ++ endIndex; | |
| iter.mData.mStr.mIndex = endIndex; | |
| } | |
| else | |
| { | |
| U32 index = iter.mData.mObj.mIndex; | |
| SimSet* set = iter.mData.mObj.mSet; | |
| if( index >= set->size() ) | |
| { | |
| (*ip) = breakIp; | |
| //continue; | |
| op_next_call(); | |
| return; | |
| } | |
| iter.mVariable->setIntValue( set->at( index )->getId() ); | |
| iter.mData.mObj.mIndex = index + 1; | |
| } | |
| ++ (*ip); | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_iter_end(){ | |
| -- _ITER; | |
| -- iterDepth; | |
| STR.rewind(); | |
| iterStack[ _ITER ].mIsStringIter = false; | |
| //break; | |
| op_next_call(); | |
| } | |
| void CodeBlock::op_invalid(){ | |
| // end of the road | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment