Permalink
Cannot retrieve contributors at this time
//----------------------------------------------------------------------------- | |
// Copyright (c) 2013 GarageGames, LLC | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to | |
// deal in the Software without restriction, including without limitation the | |
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
// sell copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
// IN THE SOFTWARE. | |
//----------------------------------------------------------------------------- | |
#include "platform/platform.h" | |
#include "console/console.h" | |
#include "console/ast.h" | |
#include "collection/findIterator.h" | |
#include "io/resource/resourceManager.h" | |
#include "string/findMatch.h" | |
#include "string/stringUnit.h" | |
#include "console/consoleInternal.h" | |
#include "io/fileStream.h" | |
#include "console/compiler.h" | |
#include "sim/simBase.h" | |
#include "network/netStringTable.h" | |
#include "component/dynamicConsoleMethodComponent.h" | |
#include "string/stringStack.h" | |
#include "messaging/message.h" | |
#include "memory/frameAllocator.h" | |
#include "debug/telnetDebugger.h" | |
#ifndef _REMOTE_DEBUGGER_BASE_H_ | |
#include "debug/remote/RemoteDebuggerBase.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; | |
} | |
F64 floatStack[MaxStackSize]; | |
S64 intStack[MaxStackSize]; | |
StringStack STR; | |
U32 FLT = 0; | |
U32 UINT = 0; | |
static 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 ) | |
{ | |
char *ret = STR.getReturnBuffer( dStrlen( stringToCopy ) + 1 ); | |
dStrcpy( ret, stringToCopy ); | |
ret[dStrlen( stringToCopy )] = '\0'; | |
return ret; | |
} | |
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* getBoolArg(bool arg) | |
{ | |
char *ret = STR.getArgBuffer(32); | |
dSprintf(ret, 32, "%d", arg); | |
return ret; | |
} | |
} | |
//------------------------------------------------------------ | |
inline void ExprEvalState::setCurVarName(StringTableEntry name) | |
{ | |
if(name[0] == '$') | |
currentVariable = globalVars.lookup(name); | |
else if(stack.size()) | |
currentVariable = stack.last()->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(stack.size()) | |
currentVariable = stack.last()->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((F32)val); | |
} | |
inline void ExprEvalState::setStringVariable(const char *val) | |
{ | |
AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); | |
currentVariable->setStringValue(val); | |
} | |
//------------------------------------------------------------ | |
void CodeBlock::getFunctionArgs(char buffer[1024], U32 ip) | |
{ | |
U32 fnArgc = code[ip + 5]; | |
buffer[0] = 0; | |
for(U32 i = 0; i < fnArgc; i++) | |
{ | |
StringTableEntry var = CodeToSTE(code, ip + (i*2) + 6); | |
// Add a comma so it looks nice! | |
if(i != 0) | |
dStrcat(buffer, ", "); | |
dStrcat(buffer, "var "); | |
// Try to capture junked parameters | |
if(var[0]) | |
dStrcat(buffer, var+1); | |
else | |
dStrcat(buffer, "JUNK"); | |
} | |
} | |
// Returns, in 'val', the specified component of a string. | |
static void getUnit(const char *string, U32 index, const char *set, char val[], S32 len) | |
{ | |
U32 sz; | |
while(index--) | |
{ | |
if(!*string) | |
return; | |
sz = dStrcspn(string, set); | |
if (string[sz] == 0) | |
return; | |
string += (sz + 1); | |
} | |
sz = dStrcspn(string, set); | |
if (sz == 0) | |
return; | |
if( ( sz + 1 ) > (U32)len ) | |
return; | |
dStrncpy(val, string, sz); | |
val[sz] = '\0'; | |
} | |
// Copies a string, replacing the (space separated) specified component. The | |
// return value is stored in 'val'. | |
static void setUnit(const char *string, U32 index, const char *replace, const char *set, char val[], S32 len) | |
{ | |
U32 sz; | |
const char *start = string; | |
if( ( dStrlen(string) + dStrlen(replace) + 1 ) > (U32)len ) | |
return; | |
U32 padCount = 0; | |
while(index--) | |
{ | |
sz = dStrcspn(string, set); | |
if(string[sz] == 0) | |
{ | |
string += sz; | |
padCount = index + 1; | |
break; | |
} | |
else | |
string += (sz + 1); | |
} | |
// copy first chunk | |
sz = (U32)(string-start); | |
dStrncpy(val, start, sz); | |
for(U32 i = 0; i < padCount; i++) | |
val[sz++] = set[0]; | |
// replace this unit | |
val[sz] = '\0'; | |
dStrcat(val, replace); | |
// copy remaining chunks | |
sz = dStrcspn(string, set); // skip chunk we're replacing | |
if(!sz && !string[sz]) | |
return; | |
string += sz; | |
dStrcat(val, string); | |
return; | |
} | |
//----------------------------------------------------------------------------- | |
static bool isDigitsOnly( const char* pString ) | |
{ | |
// Sanity. | |
AssertFatal( pString != NULL, "isDigits() - Cannot check a NULL string." ); | |
const char* pDigitCursor = pString; | |
if ( *pDigitCursor == 0 ) | |
return false; | |
// Check for digits only. | |
do | |
{ | |
if ( dIsdigit( *pDigitCursor++ ) ) | |
continue; | |
return false; | |
} | |
while( *pDigitCursor != 0 ); | |
return true; | |
} | |
//----------------------------------------------------------------------------- | |
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" ) | |
}; | |
static const StringTableEntry _size[] = | |
{ | |
StringTable->insert( "width" ), | |
StringTable->insert( "height" ) | |
}; | |
static const StringTableEntry _count = StringTable->insert( "count" ); | |
//----------------------------------------------------------------------------- | |
// 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 U32 bufferSize ) | |
{ | |
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 multi-component variables are handled. | |
else | |
prevVal = STR.getStringValue(); | |
// Make sure we got a value. | |
if ( prevVal && *prevVal ) | |
{ | |
if ( subField == _count ) | |
dSprintf( val, bufferSize, "%d", StringUnit::getUnitCount( prevVal, " \t\n" ) ); | |
else if ( subField == _xyzw[0] || subField == _rgba[0] || subField == _size[0] ) | |
dStrncpy( val, StringUnit::getUnit( prevVal, 0, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[1] || subField == _rgba[1] || subField == _size[1] ) | |
dStrncpy( val, StringUnit::getUnit( prevVal, 1, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[2] || subField == _rgba[2] ) | |
dStrncpy( val, StringUnit::getUnit( prevVal, 2, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[3] || subField == _rgba[3] ) | |
dStrncpy( val, StringUnit::getUnit( prevVal, 3, " \t\n"), bufferSize ); | |
else if ( *subField == '_' && isDigitsOnly(subField+1) ) | |
dStrncpy( val, StringUnit::getUnit( prevVal, dAtoi(subField+1), " \t\n"), bufferSize ); | |
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(), sizeof(strValue) ); | |
char val[1024] = ""; | |
const U32 bufferSize = sizeof(val); | |
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; | |
if ( subField == _xyzw[0] || subField == _rgba[0] || subField == _size[0] ) | |
dStrncpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[1] || subField == _rgba[1] || subField == _size[1] ) | |
dStrncpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[2] || subField == _rgba[2] ) | |
dStrncpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n"), bufferSize ); | |
else if ( subField == _xyzw[3] || subField == _rgba[3] ) | |
dStrncpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n"), bufferSize ); | |
else if ( *subField == '_' && isDigitsOnly(subField+1) ) | |
dStrncpy( val, StringUnit::setUnit( prevVal, dAtoi(subField+1), strValue, " \t\n"), bufferSize ); | |
if ( val[0] != 0 ) | |
{ | |
// Update the field or variable. | |
if( object && field ) | |
object->setDataField( field, 0, val ); | |
else if( gEvalState.currentVariable ) | |
gEvalState.setStringVariable( val ); | |
} | |
} | |
const char *CodeBlock::exec(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; | |
incRefCount(); | |
F64 *curFloatTable; | |
char *curStringTable; | |
STR.clearFunctionOffset(); | |
StringTableEntry thisFunctionName = NULL; | |
bool popFrame = false; | |
if(argv) | |
{ | |
// assume this points into a function decl: | |
U32 fnArgc = code[ip + 2 + 6]; | |
thisFunctionName = CodeToSTE(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 = CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2)); | |
gEvalState.setCurVarNameCreate(var); | |
gEvalState.setStringVariable(argv[i+1]); | |
} | |
ip = ip + (fnArgc * 2) + (2 + 6 + 1); | |
curFloatTable = functionFloats; | |
curStringTable = functionStrings; | |
} | |
else | |
{ | |
curFloatTable = globalFloats; | |
curStringTable = globalStrings; | |
// Do we want this code to execute using a new stack frame? | |
if (setFrame < 0) | |
{ | |
gEvalState.pushFrame(NULL, NULL); | |
popFrame = true; | |
} | |
else if (!gEvalState.stack.empty()) | |
{ | |
// 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.stack.size() - 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(); | |
// Notify the remote debugger. | |
RemoteDebuggerBase* pRemoteDebugger = RemoteDebuggerBase::getRemoteDebugger(); | |
if ( pRemoteDebugger != NULL && setFrame < 0 ) | |
pRemoteDebugger->pushStackFrame(); | |
StringTableEntry var, objParent; | |
U32 failJump; | |
StringTableEntry fnName; | |
StringTableEntry fnNamespace, fnPackage; | |
SimObject *currentNewObject = 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 = mRoot; | |
} | |
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 ); | |
for(;;) | |
{ | |
U32 instruction = code[ip++]; | |
breakContinue: | |
switch(instruction) | |
{ | |
case OP_FUNC_DECL: | |
if(!noCalls) | |
{ | |
fnName = CodeToSTE(code, ip); | |
fnNamespace = CodeToSTE(code, ip+2); | |
fnPackage = CodeToSTE(code, ip+4); | |
bool hasBody = bool(code[ip+6]); | |
Namespace::unlinkPackages(); | |
ns = Namespace::find(fnNamespace, fnPackage); | |
ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL );// 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 + 7]; | |
break; | |
case OP_CREATE_OBJECT: | |
{ | |
// Read some useful info. | |
objParent = CodeToSTE(code, ip); | |
bool isDataBlock = code[ip + 2]; | |
bool isInternal = code[ip + 3]; | |
bool isMessage = 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; | |
} | |
// Get the constructor information off the stack. | |
STR.getArgcArgv(NULL, &callArgc, &callArgv, true); | |
// 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(callArgv[2]); | |
// Make sure we're not changing types on ourselves... | |
if(db && dStricmp(db->getClassName(), callArgv[1])) | |
{ | |
Con::errorf(ConsoleLogEntry::General, "Cannot re-declare data block %s with a different class.", callArgv[2]); | |
ip = failJump; | |
break; | |
} | |
// If there was one, set the currentNewObject and move on. | |
if(db) | |
currentNewObject = db; | |
} | |
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-1), 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-1), 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-1), callArgv[1]); | |
delete object; | |
ip = failJump; | |
break; | |
} | |
// Does it have a parent object? (ie, the copy constructor : syntax, not inheriance) | |
// [tom, 9/8/2006] it is inheritance if it's a message ... muwahahah! | |
if(!isMessage && *objParent) | |
{ | |
// Find it! | |
SimObject *parent; | |
if(Sim::findObject(objParent, parent)) | |
{ | |
// Con::printf(" - Parent object found: %s", parent->getClassName()); | |
// and suck the juices from it! | |
currentNewObject->assignFieldsFrom(parent); | |
} | |
else | |
Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip-1), objParent, callArgv[1]); | |
// Mm! Juices! | |
} | |
// If a name was passed, assign it. | |
if(callArgv[2][0]) | |
{ | |
if(! isMessage) | |
{ | |
if(! isInternal) | |
currentNewObject->assignName(callArgv[2]); | |
else | |
currentNewObject->setInternalName(callArgv[2]); | |
} | |
else | |
{ | |
Message *msg = dynamic_cast<Message *>(currentNewObject); | |
if(msg) | |
{ | |
msg->setClassNamespace(callArgv[2]); | |
msg->setSuperClassNamespace(objParent); | |
} | |
else | |
{ | |
Con::errorf(ConsoleLogEntry::General, "%s: Attempting to use newmsg on non-message type %s", getFileLine(ip-1), callArgv[1]); | |
delete currentNewObject; | |
currentNewObject = NULL; | |
ip = failJump; | |
break; | |
} | |
} | |
} | |
// 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 == NULL) | |
{ | |
break; | |
} | |
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-2)); | |
} | |
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-2), currentNewObject->getName(), currentNewObject->getClassName()); | |
delete currentNewObject; | |
ip = failJump; | |
break; | |
} | |
} | |
// Are we dealing with a datablock? | |
SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(currentNewObject); | |
static char errorBuffer[256]; | |
// If so, preload it. | |
if(dataBlock && !dataBlock->preload(true, errorBuffer)) | |
{ | |
Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip-2), | |
currentNewObject->getName(), errorBuffer); | |
dataBlock->deleteObject(); | |
ip = failJump; | |
break; | |
} | |
// What group will we be added to, if any? | |
U32 groupAddId = (U32)intStack[UINT]; | |
SimGroup *grp = NULL; | |
SimSet *set = NULL; | |
SimComponent *comp = 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)) | |
if(!Sim::findObject(groupAddId, comp)) | |
Sim::findObject(groupAddId, set); | |
} | |
if(placeAtRoot || comp != NULL) | |
{ | |
// Deal with the instantGroup if we're being put at the root or we're adding to a component. | |
const char *addGroupName = Con::getVariable("instantGroup"); | |
if(!Sim::findObject(addGroupName, grp)) | |
Sim::findObject(RootGroupId, grp); | |
} | |
if(comp) | |
{ | |
SimComponent *newComp = dynamic_cast<SimComponent *>(currentNewObject); | |
if(newComp) | |
{ | |
if(! comp->addComponent(newComp)) | |
Con::errorf("%s: Unable to add component %s, template not loaded?", getFileLine(ip-2), currentNewObject->getName() ? currentNewObject->getName() : currentNewObject->getIdString()); | |
} | |
} | |
} | |
// If we didn't get a group, then make sure we have a pointer to | |
// the rootgroup. | |
if(!grp) | |
Sim::findObject(RootGroupId, grp); | |
// add to the parent group | |
grp->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_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; | |
case OP_RETURN: | |
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 = CodeToSTE(code, ip); | |
ip += 2; | |
// 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 = CodeToSTE(code, ip); | |
ip += 2; | |
// 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((S32)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 wasnt found | |
if(curObject) | |
{ | |
SimGroup *group = dynamic_cast<SimGroup *>(curObject); | |
if(group) | |
{ | |
StringTableEntry intName = StringTable->insert(STR.getStringValue()); | |
bool recurse = code[ip-1]; | |
SimObject *obj = group->findObjectByInternalName(intName, recurse); | |
intStack[UINT+1] = obj ? obj->getId() : 0; | |
UINT++; | |
} | |
else | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-group %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 = CodeToSTE(code, ip); | |
curFieldArray[0] = 0; | |
ip += 2; | |
break; | |
case OP_SETCURFIELD_ARRAY: | |
dStrcpy(curFieldArray, STR.getStringValue()); | |
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, VAL_BUFFER_SIZE ); | |
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, VAL_BUFFER_SIZE ); | |
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, VAL_BUFFER_SIZE ); | |
STR.setStringValue( valBuffer ); | |
} | |
break; | |
case OP_SAVEFIELD_UINT: | |
STR.setIntValue((U32)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] = (F64)intStack[UINT]; | |
UINT--; | |
FLT++; | |
break; | |
case OP_UINT_TO_STR: | |
STR.setIntValue((U32)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(CodeToSTE(code, ip)); | |
ip += 2; | |
break; | |
case OP_CALLFUNC_RESOLVE: | |
// This deals with a function that is potentially living in a namespace. | |
fnNamespace = CodeToSTE(code, ip+2); | |
fnName = CodeToSTE(code, ip); | |
// Try to look it up. | |
ns = Namespace::find(fnNamespace); | |
nsEntry = ns->lookup(fnName); | |
if(!nsEntry) | |
{ | |
ip+= 5; | |
Con::warnf(ConsoleLogEntry::General, | |
"%s: Unable to find function %s%s%s", | |
getFileLine(ip-4), fnNamespace ? fnNamespace : "", | |
fnNamespace ? "::" : "", fnName); | |
STR.popFrame(); | |
break; | |
} | |
// Now, rewrite our code a bit (ie, avoid future lookups) and fall | |
// through to OP_CALLFUNC | |
#ifdef TORQUE_64 | |
*((U64*)(code+ip+2)) = ((U64)nsEntry); | |
#else | |
code[ip+2] = ((U32)nsEntry); | |
#endif | |
code[ip-1] = 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 = CodeToSTE(code, ip); | |
//if this is called from inside a function, append the ip and codeptr | |
if (!gEvalState.stack.empty()) | |
{ | |
gEvalState.stack.last()->code = this; | |
gEvalState.stack.last()->ip = ip - 1; | |
} | |
U32 callType = code[ip+4]; | |
ip += 5; | |
STR.getArgcArgv(fnName, &callArgc, &callArgv); | |
if(callType == FuncCallExprNode::FunctionCall) | |
{ | |
#ifdef TORQUE_64 | |
nsEntry = ((Namespace::Entry *) *((U64*)(code+ip-3))); | |
#else | |
nsEntry = ((Namespace::Entry *) *(code+ip-3)); | |
#endif | |
ns = NULL; | |
} | |
else if(callType == FuncCallExprNode::MethodCall) | |
{ | |
saveObject = gEvalState.thisObject; | |
gEvalState.thisObject = Sim::findObject(callArgv[1]); | |
if(!gEvalState.thisObject) | |
{ | |
gEvalState.thisObject = 0; | |
Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-6), callArgv[1], fnName); | |
STR.popFrame(); // [neo, 5/7/2007 - #2974] | |
STR.setStringValue(""); | |
break; | |
} | |
bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId); | |
if( handlesMethod && routingId == MethodOnComponent ) | |
{ | |
DynamicConsoleMethodComponent *pComponent = dynamic_cast<DynamicConsoleMethodComponent*>( gEvalState.thisObject ); | |
if( pComponent ) | |
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; | |
} | |
} | |
S32 nsType = -1; | |
S32 nsMinArgs = 0; | |
S32 nsMaxArgs = 0; | |
Namespace::Entry::CallbackUnion * nsCb = NULL; | |
//Namespace::Entry::CallbackUnion cbu; | |
const char * nsUsage = NULL; | |
if (nsEntry) | |
{ | |
nsType = nsEntry->mType; | |
nsMinArgs = nsEntry->mMinArgs; | |
nsMaxArgs = nsEntry->mMaxArgs; | |
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-6), fnName); | |
if(callType == FuncCallExprNode::MethodCall) | |
{ | |
Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", | |
gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "", | |
gEvalState.thisObject->getId(), getNamespaceList(ns) ); | |
} | |
} | |
STR.popFrame(); | |
STR.setStringValue(""); | |
break; | |
} | |
if(nsEntry->mType == Namespace::Entry::ScriptFunctionType) | |
{ | |
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: ""; | |
if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments.", getFileLine(ip-6), nsName, fnName); | |
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::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-6), 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_BREAK: | |
{ | |
//append the ip and codeptr before managing the breakpoint! | |
AssertFatal( !gEvalState.stack.empty(), "Empty eval stack on break!"); | |
gEvalState.stack.last()->code = this; | |
gEvalState.stack.last()->ip = ip - 1; | |
U32 breakLine; | |
findBreakLine(ip-1, breakLine, instruction); | |
if(!breakLine) | |
goto breakContinue; | |
TelDebugger->executionStopped(this, breakLine); | |
// Notify the remote debugger. | |
if ( pRemoteDebugger != NULL ) | |
pRemoteDebugger->executionStopped(this, breakLine); | |
goto breakContinue; | |
} | |
case OP_INVALID: | |
default: | |
// error! | |
goto execFinished; | |
} | |
} | |
execFinished: | |
if ( telDebuggerOn && setFrame < 0 ) | |
TelDebugger->popStackFrame(); | |
// Notify the remote debugger. | |
if ( pRemoteDebugger != NULL && setFrame < 0 ) | |
pRemoteDebugger->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[] const_cast<char*>(globalStrings); | |
delete[] globalFloats; | |
globalStrings = NULL; | |
globalFloats = NULL; | |
} | |
smCurrentCodeBlock = saveCodeBlock; | |
if(saveCodeBlock && saveCodeBlock->name) | |
{ | |
Con::gCurrentFile = saveCodeBlock->name; | |
Con::gCurrentRoot = saveCodeBlock->mRoot; | |
} | |
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(); | |
} | |
//------------------------------------------------------------ |