Created
May 6, 2022 17:24
-
-
Save jamesu/13186f10fee36c0a387024065fd8b668 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// | |
// Copyright (c) 2014 James S Urquhart. All rights reserved. | |
// | |
// 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 "ScriptEngine.h" | |
#include "string/stringTable.h" | |
#include "sim/simBase.h" | |
#include "console/consoleNamespace.h" | |
#include "io/resource/resourceManager.h" | |
#include "console/consoleInternal.h" | |
#include "console/consoleDictionary.h" | |
extern ExprEvalState gEvalState; | |
// Lua includes | |
extern "C" | |
{ | |
#include <lua.h> | |
#include <lauxlib.h> | |
#include <lualib.h> | |
} | |
class LuaValueRef : public ScriptEngineValueRef | |
{ | |
int func; | |
virtual ScriptEngineValueRef& operator=(const ScriptEngineValueRef &other) | |
{ | |
static_cast<LuaValueRef&>(other).value = value; | |
} | |
}; | |
class LuaScriptStack : public ScriptStack | |
{ | |
public: | |
LuaScriptStack() | |
{ | |
} | |
virtual ~LuaScriptStack() | |
{ | |
} | |
// Push values | |
virtual int pushNull() | |
{ | |
lua_pushnil(mLua); | |
return lua_gettop(mLua); | |
} | |
virtual int pushIndex(int value) | |
{ | |
lua_pushvalue(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushInteger(U32 value) | |
{ | |
lua_pushunsigned(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushSignedInteger(S32 value) | |
{ | |
lua_pushinteger(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushNumber(F32 value) | |
{ | |
lua_pushnumber(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushBool(bool value) | |
{ | |
lua_pushboolean(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushSimFields(SimFieldDictionary *values) | |
{ | |
// basically: make a table | |
lua_newtable(mLua); | |
int table = lua_gettop(mLua); | |
for(SimFieldDictionaryIterator ditr(values); *ditr; ++ditr) | |
{ | |
SimFieldDictionary::Entry * entry = (*ditr); | |
lua_pushstring(mLua, entry->slotName); | |
lua_pushstring(mLua, entry->value); | |
lua_settable(mLua, table); | |
} | |
return lua_gettop(mLua); | |
} | |
virtual int pushString(const char *value) | |
{ | |
lua_pushstring(mLua, value); | |
return lua_gettop(mLua); | |
} | |
virtual int pushSimObject(SimObject *value) | |
{ | |
// basically: lookup this SimObject in the instance table | |
lua_pushglobaltable(mLua); | |
int table = lua_gettop(mLua); | |
lua_pushstring(mLua, "InstanceTable"); | |
lua_gettable(mLua, table); | |
// Get Object from InstanceTable | |
lua_pushinteger(mLua, value->getId()); | |
lua_gettable(mLua, -2); | |
// Return object | |
lua_replace(mLua, table); | |
lua_settop(mLua, table); | |
return table; | |
} | |
// Get values | |
virtual const char* getString(int index) | |
{ | |
return lua_tostring(mLua, index); | |
} | |
virtual U32 getInteger(int index) | |
{ | |
return lua_tounsigned(mLua, index); | |
} | |
virtual S32 getSignedInteger(int index) | |
{ | |
return lua_tointeger(mLua, index); | |
} | |
virtual F32 getNumber(int index) | |
{ | |
return lua_tonumber(mLua, index); | |
} | |
virtual void getSimFields(int index, SimFieldDictionary *outValues) | |
{ | |
lua_pushvalue(mLua, index); | |
int table = lua_gettop(mLua); | |
lua_pushnil(mLua); /* first key */ | |
while (lua_next(mLua, table) != 0) { | |
/* uses 'key' (at index -2) and 'value' (at index -1) */ | |
printf("%s - %s\n", | |
lua_typename(mLua, lua_type(mLua, -2)), | |
lua_typename(mLua, lua_type(mLua, -1))); | |
/* removes 'value'; keeps 'key' for next iteration */ | |
outValues->setFieldValue(StringTable->insert(lua_tostring(mLua, -2)), lua_tostring(mLua, -1)); | |
lua_pop(mLua, 1); | |
} | |
lua_pop(mLua, 1); | |
} | |
virtual bool getBool(int index) | |
{ | |
return lua_toboolean(mLua, index); | |
} | |
virtual ScriptValueBaseType getTypeAtIndex(int index) | |
{ | |
int tid = lua_type(mLua, index); | |
switch (tid) | |
{ | |
case LUA_TNIL: | |
return SCRIPTTYPE_NULL; | |
case LUA_TNUMBER: | |
return SCRIPTTYPE_NUMBER; | |
case LUA_TBOOLEAN: | |
return SCRIPTTYPE_BOOL; | |
case LUA_TSTRING: | |
return SCRIPTTYPE_STRING; | |
case LUA_TTABLE: | |
return SCRIPTTYPE_TABLE; | |
case LUA_TFUNCTION: | |
return SCRIPTTYPE_NULL; | |
case LUA_TUSERDATA: | |
return SCRIPTTYPE_OBJECT; | |
default: | |
return SCRIPTTYPE_NULL; | |
} | |
} | |
// Set values | |
virtual void setString(int index, const char *value) | |
{ | |
lua_pushstring(mLua, value); | |
lua_replace(mLua, index); | |
} | |
virtual void setInteger(int index, U32 value) | |
{ | |
lua_pushunsigned(mLua, value); | |
lua_replace(mLua, index); | |
} | |
virtual void setSignedInteger(int index, S32 value) | |
{ | |
lua_pushinteger(mLua, value); | |
lua_replace(mLua, index); | |
} | |
virtual void setNumber(int index, F32 value) | |
{ | |
lua_pushnumber(mLua, value); | |
lua_replace(mLua, index); | |
} | |
virtual void setSimFields(int index, SimFieldDictionary *values) | |
{ | |
lua_pushvalue(mLua, index); | |
int table = lua_gettop(mLua); | |
for(SimFieldDictionaryIterator ditr(values); *ditr; ++ditr) | |
{ | |
SimFieldDictionary::Entry * entry = (*ditr); | |
lua_pushstring(mLua, entry->slotName); | |
lua_pushstring(mLua, entry->value); | |
lua_settable(mLua, table); | |
} | |
lua_pop(mLua, 1); | |
} | |
virtual void setSimObject(int index, SimObject *value) | |
{ | |
// basically: lookup this SimObject in the instance table | |
lua_pushglobaltable(mLua); | |
int table = lua_gettop(mLua); | |
lua_pushstring(mLua, "InstanceTable"); | |
lua_gettable(mLua, -1); | |
// Get Object from InstanceTable | |
lua_pushinteger(mLua, value->getId()); | |
lua_gettable(mLua, table+1); | |
// Replace old index with new object | |
lua_replace(mLua, index); | |
lua_settop(mLua, table-1); | |
} | |
void setBool(int index, bool value) | |
{ | |
lua_pushboolean(mLua, value); | |
lua_replace(mLua, index); | |
} | |
public: | |
lua_State *mLua; | |
}; | |
class ScriptStackValueRef; | |
class LuaScriptEngine; | |
extern ScriptEngine *sScriptInstance; | |
// taken from lua code | |
static int typeerror (lua_State *L, int narg, const char *tname) { | |
const char *msg = lua_pushfstring(L, "%s expected, got %s", | |
tname, luaL_typename(L, narg)); | |
return luaL_argerror(L, narg, msg); | |
} | |
class LuaScriptEngine : public ScriptEngine | |
{ | |
public: | |
lua_State *mLuaState; | |
LuaScriptStack mLuaStack; | |
bool mRootExec; // flag to determine if an exec call is the root | |
bool mShouldReset; // flag to determine if next exec call should reset the stack | |
LuaScriptEngine() | |
{ | |
mLuaState = luaL_newstate(); | |
mLuaStack.mLua = mLuaState; | |
sScriptInstance = this; | |
mRootExec = false; | |
mShouldReset = false; | |
luaL_openlibs(mLuaState); | |
//lua_debug_print_stack(mLuaState); | |
// Need to setup a few things in the script engine first! | |
lua_newtable(mLuaState); | |
lua_setglobal(mLuaState, "InstanceTable"); | |
// Globals table | |
lua_newtable(mLuaState); | |
int globalsTable = lua_gettop(mLuaState); | |
lua_newtable(mLuaState); | |
int globalsMetaTable = globalsTable+1; | |
lua_pushstring(mLuaState, "__index"); | |
lua_pushlightuserdata(mLuaState, &(gEvalState.globalVars)); | |
lua_pushcclosure(mLuaState, &getGlobalVariable, 1); | |
lua_settable(mLuaState, globalsMetaTable); | |
lua_pushstring(mLuaState, "__newindex"); | |
lua_pushlightuserdata(mLuaState, &(gEvalState.globalVars)); | |
lua_pushcclosure(mLuaState, &setGlobalVariable, 1); | |
lua_settable(mLuaState, globalsMetaTable); | |
lua_setmetatable(mLuaState, globalsTable); | |
lua_setglobal(mLuaState, "TorqueVars"); | |
//lua_debug_print_stack(mLuaState); | |
} | |
virtual ~LuaScriptEngine() | |
{ | |
lua_close(mLuaState); | |
} | |
static int getGlobalVariable(lua_State *L) | |
{ | |
Dictionary *globals = (Dictionary*)lua_touserdata(L, lua_upvalueindex(1)); | |
bool valid = false; | |
const char *var = globals->getVariable(StringTable->insert(lua_tostring(L, -1)), &valid); | |
if (valid) | |
{ | |
lua_pushstring(L, var); | |
} | |
else | |
{ | |
lua_pushnil(L); | |
} | |
return 1; | |
} | |
static int setGlobalVariable(lua_State *L) | |
{ | |
Dictionary *globals = (Dictionary*)lua_touserdata(L, lua_upvalueindex(1)); | |
globals->setVariable(StringTable->insert(lua_tostring(L, -2)), lua_tostring(L, -1)); | |
return 0; | |
} | |
static void setObjectFieldArray(lua_State *L, int table, StringTableEntry fieldName, SimObject *obj) | |
{ | |
lua_pushnil(L); | |
while (lua_next(L, table) != 0) { | |
/* uses 'key' (at index -2) and 'value' (at index -1) */ | |
printf("%s - %s\n", | |
lua_typename(L, lua_type(L, -2)), | |
lua_typename(L, lua_type(L, -1))); | |
// Use current index | |
char idxBuf[16]; | |
dSprintf(idxBuf, 16, "%u", dAtoi(lua_tostring(L, -2))+1); | |
obj->setDataField(fieldName, idxBuf, lua_tostring(L, -1)); | |
lua_pop(L, 1); | |
} | |
lua_pop(L, 1); | |
} | |
// Constructs an instance of an object in lua | |
static int makeObject(lua_State *L) | |
{ | |
StringTableEntry className = StringTable->insert(lua_tostring(L, lua_upvalueindex(1))); | |
ConsoleObject *conObject = ConsoleObject::create(className); | |
SimObject *instance = dynamic_cast<SimObject*>(conObject); | |
SimDataBlock *dataBlock = dynamic_cast<SimDataBlock *>(instance); | |
SimGroup *grp = NULL; | |
if (!instance) | |
{ | |
if (conObject) | |
delete conObject; | |
Con::errorf("Could not create object of type %s!", className); | |
lua_pushnil(L); | |
return 1; | |
} | |
// Second parameter: fields to set | |
int param = lua_gettop(L); | |
int startArgs = 2; | |
if (param > 1) | |
{ | |
int table = 1; | |
// Is first parameter a class name? | |
if (lua_isstring(L, 1)) | |
{ | |
table++; | |
instance->assignName(lua_tostring(L, 1)); | |
startArgs++; | |
} | |
// Check table parameter for initial field values | |
if (param >= table && lua_istable(L, table)) | |
{ | |
lua_pushnil(L); /* first key */ | |
while (lua_next(L, table) != 0) { | |
/* uses 'key' (at index -2) and 'value' (at index -1) */ | |
printf("%s - %s\n", | |
lua_typename(L, lua_type(L, -2)), | |
lua_typename(L, lua_type(L, -1))); | |
if (lua_istable(L, -1)) | |
{ | |
// If this field is a table, we need to be a bit more clever... | |
setObjectFieldArray(L, -1, StringTable->insert(lua_tostring(L, -2)), instance); | |
} | |
else | |
{ | |
// Just set the field the dumb way | |
instance->setDataField(StringTable->insert(lua_tostring(L, -2)), NULL, lua_tostring(L, -1)); | |
} | |
lua_pop(L, 1); | |
} | |
} | |
} | |
// Do the constructor parameters. | |
const char* fakeArgs[3] = {NULL, NULL, NULL}; | |
if(!instance->processArguments(0, fakeArgs)) | |
{ | |
delete instance; | |
Con::errorf("Could not create object of type %s!", className); | |
lua_pushnil(L); | |
return 1; | |
} | |
// If it's not a datablock, allow people to modify bits of it. | |
if(dataBlock == NULL) | |
{ | |
instance->setModStaticFields(true); | |
instance->setModDynamicFields(true); | |
} | |
int rest = lua_gettop(L); | |
if (instance->registerObject()) | |
{ | |
// Handle RootGroup & instantGroup | |
if(!instance->getGroup()) | |
{ | |
// 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 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(instance); | |
} | |
// Are we dealing with a datablock? | |
static char errorBuffer[256]; | |
// If so, preload it. | |
if(dataBlock && !dataBlock->preload(true, errorBuffer)) | |
{ | |
Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", "", | |
instance->getName(), errorBuffer); | |
dataBlock->deleteObject(); | |
lua_pushnil(L); | |
return 1; | |
} | |
// Grab instance from InstanceTable | |
lua_pushglobaltable(L); | |
int table = lua_gettop(L); | |
lua_pushstring(L, "InstanceTable"); | |
lua_gettable(L, -2); | |
lua_pushinteger(L, instance->getId()); | |
lua_gettable(L, -2); | |
return 1; | |
} | |
else | |
{ | |
// This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields(). | |
Con::warnf(ConsoleLogEntry::General, "Register object failed for object %s of class %s.", instance->getName(), instance->getClassName()); | |
delete instance; | |
lua_pushnil(L); | |
return 1; | |
} | |
} | |
static int gcObject(lua_State *L) | |
{ | |
// NOTE: we don't garbage collect since the lifetime of the SimObject is controlled by deleteObject. | |
// TODO: handle case where deleteObject is called but lua still retains a reference to the object. | |
return 0; | |
/* | |
int ud = lua_gettop(L); | |
const char *className = lua_tostring(L, lua_upvalueindex(1)); | |
SimObject** obj = static_cast<SimObject**>(lua_touserdata(L, ud)); | |
if (obj != NULL) | |
{ | |
if (lua_getmetatable(L, ud)) | |
{ | |
luaL_getmetatable(L, className); | |
if (!lua_rawequal(L, -1, -2)) // not the same? | |
obj = NULL; | |
lua_pop(L, 2); | |
} | |
else | |
{ | |
obj = NULL; | |
} | |
} | |
if (obj == NULL) | |
{ | |
typeerror(L, ud, className); | |
return 0; | |
} | |
else | |
{ | |
(*obj)->unregisterObject(); | |
}*/ | |
return 0; | |
} | |
static int getObjectField(lua_State *L) | |
{ | |
int ud = lua_gettop(L)-1; | |
const char *className = lua_tostring(L, lua_upvalueindex(1)); | |
#ifdef LUA_USE_DIRECT_SIMOBJECT_PTR | |
SimObject** objPtr = static_cast<SimObject**>(lua_touserdata(L, ud)); | |
SimObject *obj = objPtr ? *objPtr : NULL; | |
#else | |
SimObjectId* objPtr = static_cast<SimObjectId*>(lua_touserdata(L, ud)); | |
SimObject *obj = objPtr ? Sim::findObject(*objPtr) : NULL; | |
#endif | |
if (obj != NULL) | |
{ | |
if (lua_getmetatable(L, ud)) | |
{ | |
luaL_getmetatable(L, className); | |
if (!lua_rawequal(L, -1, -2)) // not the same? | |
obj = NULL; | |
lua_pop(L, 2); | |
} | |
else | |
{ | |
obj = NULL; | |
} | |
} | |
if (obj == NULL) | |
{ | |
typeerror(L, ud, className); | |
lua_pushnil(L); | |
return 1; | |
} | |
else | |
{ | |
// Grab the field name | |
luaL_getmetatable(L, className); | |
int meta = lua_gettop(L); | |
// First, see if klassMetaTable has this... | |
lua_pushstring(L, "__mthd"); | |
lua_rawget(L, meta); // __mthd | |
const char *typeOfTop = lua_typename(L, lua_type(L, -1)); | |
lua_pushvalue(L, meta-1); | |
lua_gettable(L, -2); // fieldName | |
//lua_debug_print_stack(L); | |
typeOfTop = lua_typename(L, lua_type(L, -1)); | |
// Grab the field from the object | |
if (lua_isuserdata(L, -1)) | |
{ | |
StringTableEntry ste = (StringTableEntry)lua_touserdata(L, -1); | |
const char *data = obj->getDataField(ste, NULL); | |
if (data) | |
lua_pushstring(L, data); | |
else | |
lua_pushnil(L); | |
} | |
else if (lua_isnil(L, -1)) | |
{ | |
// nil? check user fields | |
StringTableEntry ste = StringTable->insert(lua_tostring(L, meta-1)); | |
const char *data = obj->getDataField(ste, NULL); | |
if (data) | |
lua_pushstring(L, data); | |
else | |
lua_pushnil(L); | |
} | |
} | |
// Otherwise could be anything, just return it. | |
return 1; | |
} | |
static int setObjectField(lua_State *L) | |
{ | |
int ud = lua_gettop(L)-2; | |
int value = lua_gettop(L); | |
const char *className = lua_tostring(L, lua_upvalueindex(1)); | |
#ifdef LUA_USE_DIRECT_SIMOBJECT_PTR | |
SimObject** objPtr = static_cast<SimObject**>(lua_touserdata(L, ud)); | |
SimObject *obj = objPtr ? *objPtr : NULL; | |
#else | |
SimObjectId* objPtr = static_cast<SimObjectId*>(lua_touserdata(L, ud)); | |
SimObject *obj = objPtr ? Sim::findObject(*objPtr) : NULL; | |
#endif | |
if (obj != NULL) | |
{ | |
if (lua_getmetatable(L, ud)) | |
{ | |
luaL_getmetatable(L, className); | |
if (!lua_rawequal(L, -1, -2)) // not the same? | |
obj = NULL; | |
lua_pop(L, 2); | |
} | |
else | |
{ | |
obj = NULL; | |
} | |
} | |
if (obj == NULL) | |
{ | |
typeerror(L, ud, className); | |
return 0; | |
} | |
else | |
{ | |
// Grab the field name | |
luaL_getmetatable(L, className); | |
lua_pushvalue(L, -3); | |
const char *fieldName = lua_tostring(L, -1); | |
lua_rawget(L, -2); | |
// Grab the field from the object | |
if (lua_isuserdata(L, -1)) | |
{ | |
StringTableEntry ste = (StringTableEntry)lua_touserdata(L, -1); | |
// Determine what type the incoming data is | |
if (lua_istable(L, value)) | |
{ | |
// Table? Gotta set the array! | |
setObjectFieldArray(L, value, ste, obj); | |
} | |
else | |
{ | |
// Something else? just set it for now | |
obj->setDataField(ste, NULL, lua_tostring(L, value)); | |
} | |
} | |
else if (lua_isnil(L, -1)) | |
{ | |
StringTableEntry ste = StringTable->insert(fieldName); | |
// Determine what type the incoming data is | |
if (lua_istable(L, value)) | |
{ | |
// Table? Gotta set the array! | |
setObjectFieldArray(L, value, ste, obj); | |
} | |
else | |
{ | |
// Something else? just set it for now | |
obj->setDataField(ste, NULL, lua_tostring(L, value)); | |
} | |
} | |
} | |
return 0; | |
} | |
#define MAX_THUNKARGS 32 | |
static int thunkDummy(lua_State *L) | |
{ | |
return 0; | |
} | |
static inline void* thunkCheckAndReturnObject(lua_State *L, Namespace *requiredNS) | |
{ | |
#ifdef LUA_USE_DIRECT_SIMOBJECT_PTR | |
SimObject** objPtr = static_cast<SimObject**>(lua_touserdata(L, 1)); | |
SimObject *obj = objPtr ? *objPtr : NULL; | |
#else | |
SimObjectId* objPtr = static_cast<SimObjectId*>(lua_touserdata(L, 1)); | |
SimObject* obj = objPtr ? Sim::findObject(*objPtr) : NULL; | |
#endif | |
if (obj != NULL) | |
{ | |
if (lua_getmetatable(L, 1)) | |
{ | |
lua_pushstring(L, "__ns"); | |
lua_rawget(L, -2); | |
Namespace *objNS = (Namespace*)lua_touserdata(L, -1); | |
if (objNS) | |
{ | |
SimObject *oldObj = obj; | |
obj = NULL; | |
for (Namespace *itr = objNS; itr != NULL; itr = itr->mParent) | |
{ | |
if (itr == requiredNS) | |
{ | |
// Found the NS, the object is ok! | |
obj = oldObj; | |
break; | |
} | |
} | |
} | |
else | |
{ | |
obj = NULL; | |
} | |
lua_pop(L, 1); | |
if (obj != NULL) | |
{ | |
return obj; | |
} | |
} | |
else | |
{ | |
obj = NULL; | |
} | |
} | |
return NULL; | |
} | |
static int thunkString(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
SimObject *obj = (SimObject*)thunkCheckAndReturnObject(L, ns); | |
if (obj == NULL) | |
{ | |
typeerror(L, 1, ns->mName); | |
return 0; | |
} | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || args+1 > maxArgs) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
thunkValues[1] = obj->getIdString(); | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+2] = lua_tostring(L, i+2); | |
} | |
StringCallback func = (StringCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushstring(L, func(obj, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushstring(L, ""); | |
} | |
return 1; | |
} | |
static int thunkInt(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
SimObject *obj = (SimObject*)thunkCheckAndReturnObject(L, ns); | |
if (obj == NULL) | |
{ | |
typeerror(L, 1, ns->mName); | |
return 0; | |
} | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || args+1 > maxArgs) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
thunkValues[1] = obj->getIdString(); | |
// Populate incomming args | |
for (int i=0; i<args-1; i++) | |
{ | |
thunkValues[i+2] = lua_tostring(L, i+2); | |
} | |
IntCallback func = (IntCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushinteger(L, func(obj, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushinteger(L, 0); | |
} | |
return 1; | |
} | |
static int thunkFloat(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
SimObject *obj = (SimObject*)thunkCheckAndReturnObject(L, ns); | |
if (obj == NULL) | |
{ | |
typeerror(L, 1, ns->mName); | |
return 0; | |
} | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || args+1 > maxArgs) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
thunkValues[1] = obj->getIdString(); | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+2] = lua_tostring(L, i+2); | |
} | |
FloatCallback func = (FloatCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushnumber(L, func(obj, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushnumber(L, 0); | |
} | |
return 1; | |
} | |
static int thunkBool(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
SimObject *obj = (SimObject*)thunkCheckAndReturnObject(L, ns); | |
if (obj == NULL) | |
{ | |
typeerror(L, 1, ns->mName); | |
return 0; | |
} | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || args+1 > maxArgs) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
thunkValues[1] = obj->getIdString(); | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+2] = lua_tostring(L, i+2); | |
} | |
BoolCallback func = (BoolCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushboolean(L, func(obj, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushboolean(L, false); | |
} | |
return 1; | |
} | |
static int thunkVoid(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
SimObject *obj = (SimObject*)thunkCheckAndReturnObject(L, ns); | |
if (obj == NULL) | |
{ | |
typeerror(L, 1, ns->mName); | |
return 0; | |
} | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || args+1 > maxArgs) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
thunkValues[1] = obj->getIdString(); | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+2] = lua_tostring(L, i+2); | |
} | |
VoidCallback func = (VoidCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
func(obj, args+1, thunkValues); | |
return 0; | |
} | |
// Global thunks | |
static int thunkGlobalString(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || (maxArgs != 0 && (args+1 > maxArgs))) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+1] = lua_tostring(L, i+1); | |
} | |
StringCallback func = (StringCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushstring(L, func(NULL, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushstring(L, ""); | |
} | |
return 1; | |
} | |
static int thunkGlobalInt(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || (maxArgs != 0 && (args+1 > maxArgs))) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+1] = lua_tostring(L, i+1); | |
} | |
IntCallback func = (IntCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushinteger(L, func(NULL, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushinteger(L, 0); | |
} | |
return 1; | |
} | |
static int thunkGlobalFloat(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || (maxArgs != 0 && (args+1 > maxArgs))) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+1] = lua_tostring(L, i+1); | |
} | |
FloatCallback func = (FloatCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushnumber(L, func(NULL, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushnumber(L, 0); | |
} | |
return 1; | |
} | |
static int thunkGlobalBool(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || (maxArgs != 0 && (args+1 > maxArgs))) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+1] = lua_tostring(L, i+1); | |
} | |
BoolCallback func = (BoolCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
{ | |
lua_pushboolean(L, func(NULL, args+1, thunkValues)); | |
} | |
else | |
{ | |
lua_pushboolean(L, false); | |
} | |
return 1; | |
} | |
static int thunkGlobalVoid(lua_State *L) | |
{ | |
int args = lua_gettop(L); | |
Namespace *ns = (Namespace*)lua_touserdata(L, lua_upvalueindex(5)); | |
int minArgs = lua_tointeger(L, lua_upvalueindex(1)); | |
int maxArgs = lua_tointeger(L, lua_upvalueindex(2)); | |
const char *usage = lua_tostring(L, lua_upvalueindex(3)); | |
StringTableEntry fnName = (StringTableEntry)lua_touserdata(L, lua_upvalueindex(4)); | |
if (args+1 < minArgs || (maxArgs != 0 && (args+1 > maxArgs))) | |
{ | |
Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", ns->mName, fnName); | |
Con::warnf(ConsoleLogEntry::Script, "usage: %s", usage); | |
return 0; | |
} | |
// We have enough args, prepare the list | |
const char *thunkValues[MAX_THUNKARGS]; | |
// Set function name and %this | |
thunkValues[0] = fnName; | |
// Populate incomming args | |
for (int i=0; i<args; i++) | |
{ | |
thunkValues[i+1] = lua_tostring(L, i+1); | |
} | |
VoidCallback func = (VoidCallback)lua_touserdata(L, lua_upvalueindex(6)); | |
if (func) | |
func(NULL, args+1, thunkValues); | |
return 0; | |
} | |
void registerClassWithNS(AbstractClassRep *rep, Namespace *ns) | |
{ | |
// Make master class table | |
lua_newtable(mLuaState); | |
int klassTable = lua_gettop(mLuaState); | |
lua_pushvalue(mLuaState, klassTable); | |
lua_setglobal(mLuaState, ns->mName); | |
lua_newtable(mLuaState); | |
int klassMetaTable = lua_gettop(mLuaState); | |
// Define constructor function | |
lua_pushstring(mLuaState, "__call"); | |
lua_pushstring(mLuaState, ns->mName); | |
lua_pushcclosure(mLuaState, &makeObject, 1); | |
lua_settable(mLuaState, klassMetaTable); | |
int newTop = lua_gettop(mLuaState); | |
// Register userdata metatable in the lua registry | |
luaL_newmetatable(mLuaState, ns->mName); | |
int metatable = lua_gettop(mLuaState); | |
// Assign field StringTableEntry map for property get/set | |
int fieldIndex = 0; | |
for (Vector<AbstractClassRep::Field>::const_iterator itr = rep->mFieldList.begin(); itr != rep->mFieldList.end(); itr++) | |
{ | |
if (itr->type == AbstractClassRep::StartGroupFieldType || itr->type == AbstractClassRep::EndGroupFieldType) { | |
fieldIndex++; | |
continue; | |
} | |
lua_pushlightuserdata(mLuaState, (void*)StringTable->insert(itr->pFieldname)); | |
lua_pushstring(mLuaState, itr->pFieldname); | |
lua_settable(mLuaState, metatable); | |
fieldIndex++; | |
} | |
// Assign function pointers for functions on native class | |
Vector<Namespace::Entry*> entryList; | |
Namespace *klassNS = rep->getNameSpace(); | |
klassNS->getLocalEntryList(&entryList); | |
for (Vector<Namespace::Entry*>::const_iterator itr = entryList.begin(); itr != entryList.end(); itr++) | |
{ | |
const Namespace::Entry *ent = *itr; | |
// Skip doc entries | |
if (ent->mType < Namespace::Entry::ScriptFunctionType) | |
continue; | |
// function name | |
lua_pushstring(mLuaState, ent->mFunctionName); | |
int funcName = lua_gettop(mLuaState); | |
// min, max args, usage, STE name | |
lua_pushinteger(mLuaState, ent->mMinArgs); | |
lua_pushinteger(mLuaState, ent->mMaxArgs); | |
lua_pushstring(mLuaState, ent->mUsage); | |
lua_pushlightuserdata(mLuaState, (void*)ent->mFunctionName); | |
lua_pushlightuserdata(mLuaState, ns); | |
// function pointer | |
lua_pushlightuserdata(mLuaState, (void*)ent->cb.mStringCallbackFunc); | |
switch (ent->mType) | |
{ | |
case Namespace::Entry::ScriptFunctionType: | |
lua_pushcclosure(mLuaState, &thunkDummy, 6); | |
break; | |
case Namespace::Entry::StringCallbackType: | |
lua_pushcclosure(mLuaState, &thunkString, 6); | |
break; | |
case Namespace::Entry::IntCallbackType: | |
lua_pushcclosure(mLuaState, &thunkInt, 6); | |
break; | |
case Namespace::Entry::FloatCallbackType: | |
lua_pushcclosure(mLuaState, &thunkFloat, 6); | |
break; | |
case Namespace::Entry::BoolCallbackType: | |
lua_pushcclosure(mLuaState, &thunkBool, 6); | |
break; | |
case Namespace::Entry::VoidCallbackType: | |
default: | |
lua_pushcclosure(mLuaState, &thunkVoid, 6); | |
break; | |
} | |
int topNow = lua_gettop(mLuaState); | |
// Set entry | |
lua_settable(mLuaState, klassTable); | |
} | |
// garbage collection | |
lua_pushstring(mLuaState, "__gc"); | |
lua_pushstring(mLuaState, ns->mName); | |
lua_pushcclosure(mLuaState, &gcObject, 1); | |
lua_settable(mLuaState, metatable); | |
// property setter | |
lua_pushstring(mLuaState, "__newindex"); | |
lua_pushstring(mLuaState, ns->mName); | |
lua_pushcclosure(mLuaState, &setObjectField, 1); | |
lua_settable(mLuaState, metatable); | |
lua_pushstring(mLuaState, "__ns"); | |
lua_pushlightuserdata(mLuaState, ns); | |
lua_settable(mLuaState, metatable); | |
// Set class metatable | |
lua_pushvalue(mLuaState, klassMetaTable); | |
lua_setmetatable(mLuaState, klassTable); | |
// parent namespace | |
Namespace *rootNS = rep->getNameSpace(); | |
if (rootNS != ns) | |
{ | |
Namespace *itrNS = ns; | |
for (itrNS = ns; itrNS != rootNS; itrNS = itrNS->mParent) | |
{ | |
// Basically what we need to do is as follows: | |
// - Ensure the table exists for any namespaces | |
// - Link the previous namespace to the table | |
lua_getglobal(mLuaState, itrNS->mName); | |
// create table if not present | |
if (lua_isnil(mLuaState, -1)) | |
{ | |
lua_pop(mLuaState, 1); | |
lua_newtable(mLuaState); | |
lua_pushvalue(mLuaState, -1); | |
lua_pushvalue(mLuaState, -1); | |
lua_setglobal(mLuaState, ns->mName); | |
} | |
// create metatable if not present | |
lua_getmetatable(mLuaState, -1); | |
if (lua_isnil(mLuaState, -1)) | |
{ | |
lua_pop(mLuaState, 1); | |
lua_newtable(mLuaState); | |
lua_pushvalue(mLuaState, -1); | |
lua_pushvalue(mLuaState, -1); | |
lua_setmetatable(mLuaState, -3); | |
} | |
// create parent table if it doesn't exist (metatable be set on the next iteration) | |
lua_getglobal(mLuaState, ns->mParent->mName); | |
if (lua_isnil(mLuaState, -1)) | |
{ | |
lua_pop(mLuaState, 1); | |
lua_newtable(mLuaState); | |
lua_pushvalue(mLuaState, -1); | |
lua_pushvalue(mLuaState, -1); | |
lua_setglobal(mLuaState, ns->mParent->mName); | |
} | |
// now we have: [parent] [namespace metatable] | |
// finally set the metatable __index to the parent table | |
lua_pushstring(mLuaState, "__index"); | |
lua_settable(mLuaState, -2); | |
} | |
// We should now be linked up! | |
} | |
else if (rep->getParentClass()) // parent class (only applicable for inbuilt classes) | |
{ | |
lua_pushstring(mLuaState, "__index"); | |
lua_getglobal(mLuaState, rep->getParentClass()->getClassName()); | |
if (!lua_isnil(mLuaState, -1)) | |
{ | |
// defer to the users parent class table | |
lua_settable(mLuaState, klassMetaTable); // klassMetaTable.__index == $G[rep->getParentClass()->getClassName()] | |
} | |
else | |
{ | |
lua_pop(mLuaState, 1); | |
} | |
} | |
// set __mthd fallback table | |
lua_pushstring(mLuaState, "__mthd"); | |
lua_pushvalue(mLuaState, klassTable); | |
lua_settable(mLuaState, metatable); | |
// Root class table property getter goes through c function | |
lua_pushstring(mLuaState, "__index"); | |
lua_pushstring(mLuaState, ns->mName); | |
lua_pushcclosure(mLuaState, &getObjectField, 1); | |
lua_settable(mLuaState, metatable); | |
lua_settop(mLuaState, klassTable-1); | |
} | |
// register class | |
virtual void registerClass(AbstractClassRep *rep) | |
{ | |
registerClassWithNS(rep, rep->getNameSpace()); | |
} | |
int linkLuaNamespaces(AbstractClassRep *rep, Namespace *ns) | |
{ | |
Namespace *rootNS = rep->getNameSpace(); | |
// TODO: since linkNamespaces is called in onAdd, this won't work without refactoring! | |
//if (rootNS == NULL) { | |
// This will usually happen when Sim is not init'd yet. In this case lets just use the class name | |
luaL_getmetatable(mLuaState, rep->getClassName()); | |
return lua_gettop(mLuaState); | |
//} | |
/* | |
// First check the desired class name. If it's different, we'll likely have to register a new class | |
if (ns != rootNS) | |
{ | |
// Firstly, is the name already registered? If so we can skip this | |
lua_pushstring(mLuaState, ns->mName); | |
lua_getmetatable(mLuaState, -1); | |
if (!lua_isnil(mLuaState, -1)) | |
{ | |
return lua_gettop(mLuaState); | |
} | |
// Register this new class | |
registerClassWithNS(rep, rep->getNameSpace()); | |
} | |
// Grab the global class metatable | |
luaL_getmetatable(mLuaState, ns->mName); | |
return lua_gettop(mLuaState);*/ | |
} | |
// register object instance | |
virtual void registerObject(SimObject *object) | |
{ | |
int top = lua_gettop(mLuaState); | |
Namespace *ns = object->getNamespace(); | |
int objNS = linkLuaNamespaces(object->getClassRep(), ns); | |
int klassMetatable = -1; | |
// | |
// Basically make a userdata object bound to our class name | |
#ifdef LUA_USE_DIRECT_SIMOBJECT_PTR | |
SimObject** ptr = (SimObject**)lua_newuserdata(mLuaState, sizeof(SimObject*)); | |
*ptr = object; | |
#else | |
SimObjectId* ptr = (SimObjectId*)lua_newuserdata(mLuaState, sizeof(SimObjectId)); | |
*ptr = object->getId(); | |
#endif | |
int userdata = lua_gettop(mLuaState); | |
lua_pushvalue(mLuaState, objNS); | |
lua_setmetatable(mLuaState, userdata); | |
// Now store this in InstanceTable | |
lua_pushglobaltable(mLuaState); | |
lua_pushstring(mLuaState, "InstanceTable"); | |
lua_gettable(mLuaState, -2); | |
lua_pushinteger(mLuaState, object->getId()); | |
lua_pushvalue(mLuaState, userdata); | |
lua_settable(mLuaState, -3); | |
// Restore stack | |
lua_settop(mLuaState, top); | |
} | |
virtual void registerFunctions(Namespace *ns) | |
{ | |
// Add all functions from namespace | |
Vector<Namespace::Entry*> entryList; | |
ns->getLocalEntryList(&entryList); | |
lua_newtable(mLuaState); | |
int nsTable = lua_gettop(mLuaState); | |
lua_pushglobaltable(mLuaState); | |
// Globals go in Torque, otherwise we use the namespace name | |
if (Namespace::global() == ns) | |
{ | |
lua_pushstring(mLuaState, "Torque"); | |
lua_pushvalue(mLuaState, 1); | |
lua_rawset(mLuaState, -3); | |
} | |
else | |
{ | |
lua_pushstring(mLuaState, ns->mName); | |
lua_pushvalue(mLuaState, 1); | |
lua_rawset(mLuaState, -3); | |
} | |
lua_pop(mLuaState, 1); | |
for (Vector<Namespace::Entry*>::const_iterator itr = entryList.begin(); itr != entryList.end(); itr++) | |
{ | |
const Namespace::Entry *ent = *itr; | |
// Skip doc entries | |
if (ent->mType < Namespace::Entry::ScriptFunctionType) | |
continue; | |
// function name | |
lua_pushstring(mLuaState, ent->mFunctionName); | |
int funcName = lua_gettop(mLuaState); | |
// min, max args, usage, STE name | |
lua_pushinteger(mLuaState, ent->mMinArgs); | |
lua_pushinteger(mLuaState, ent->mMaxArgs); | |
lua_pushstring(mLuaState, ent->mUsage); | |
lua_pushlightuserdata(mLuaState, (void*)ent->mFunctionName); | |
lua_pushlightuserdata(mLuaState, ns); | |
// function pointer | |
lua_pushlightuserdata(mLuaState, (void*)ent->cb.mStringCallbackFunc); | |
switch (ent->mType) | |
{ | |
case Namespace::Entry::ScriptFunctionType: | |
lua_pushcclosure(mLuaState, &thunkDummy, 6); | |
break; | |
case Namespace::Entry::StringCallbackType: | |
lua_pushcclosure(mLuaState, &thunkGlobalString, 6); | |
break; | |
case Namespace::Entry::IntCallbackType: | |
lua_pushcclosure(mLuaState, &thunkGlobalInt, 6); | |
break; | |
case Namespace::Entry::FloatCallbackType: | |
lua_pushcclosure(mLuaState, &thunkGlobalFloat, 6); | |
break; | |
case Namespace::Entry::BoolCallbackType: | |
lua_pushcclosure(mLuaState, &thunkGlobalBool, 6); | |
break; | |
case Namespace::Entry::VoidCallbackType: | |
default: | |
lua_pushcclosure(mLuaState, &thunkGlobalVoid, 6); | |
break; | |
} | |
// Set entry | |
lua_settable(mLuaState, nsTable); | |
} | |
lua_pop(mLuaState, 1); | |
} | |
virtual void removeObject(SimObject *object) | |
{ | |
// Remove object from InstanceTable | |
lua_pushglobaltable(mLuaState); | |
int table = lua_gettop(mLuaState); | |
lua_pushstring(mLuaState, "InstanceTable"); | |
lua_rawget(mLuaState, -2); | |
if (!lua_isnil(mLuaState, -1)) | |
{ | |
lua_pushinteger(mLuaState, object->getId()); | |
lua_pushnil(mLuaState); | |
lua_settable(mLuaState, -3); | |
} | |
lua_settop(mLuaState, table-1); | |
} | |
// gets current stack | |
virtual ScriptStack *getStack() | |
{ | |
return &mLuaStack; | |
} | |
virtual ScriptStackValueRef execute(S32 argc, ScriptStackValueRef argv[]) | |
{ | |
ScriptStackValueRef ret; | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = -1; | |
int start = lua_gettop(mLuaState) - argc; | |
bool wasRootExec = false; | |
if (!mRootExec) | |
{ | |
wasRootExec = true; | |
mRootExec = true; | |
} | |
// Resolve function | |
lua_pushglobaltable(mLuaState); | |
lua_pushvalue(mLuaState, argv[0].index); | |
const char *funcName = lua_tostring(mLuaState, argv[0].index); | |
lua_gettable(mLuaState, -2); | |
lua_replace(mLuaState, argv[0].index); | |
lua_pop(mLuaState, 1); | |
lua_debug_print_stack(mLuaState); | |
if (lua_pcall(mLuaState, argc-1, 1, 0) != 0) | |
{ | |
Con::errorf("Error calling %s (%s)", funcName, lua_tostring(mLuaState, -1)); | |
lua_settop(mLuaState, 0); | |
lua_pushnil(mLuaState); | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = 1; | |
if (wasRootExec) | |
{ | |
mShouldReset = true; | |
} | |
return ret; | |
} | |
if (wasRootExec) | |
{ | |
mShouldReset = true; | |
} | |
// Return result | |
ret.index = lua_gettop(mLuaState); | |
ret.type = mLuaStack.getTypeAtIndex(-1); | |
return ret; | |
} | |
// print stack. useful if you get confused. | |
static void lua_debug_print_stack(lua_State *L) | |
{ | |
int newtop = lua_gettop(L); | |
printf("--------\n"); | |
for (int i=1; i<newtop+1; i++) | |
{ | |
printf("Stack value[%i]: %s [%s]\n", i, lua_tostring(L, i), lua_typename(L, lua_type(L, i))); | |
} | |
printf("--------\n"); | |
} | |
virtual ScriptStackValueRef executeOnObject(ScriptStackValueRef obj, S32 argc, ScriptStackValueRef argv[]) | |
{ | |
ScriptStackValueRef ret; | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = -1; | |
bool wasRootExec = false; | |
if (!mRootExec) | |
{ | |
wasRootExec = true; | |
mRootExec = true; | |
} | |
if (mShouldReset) | |
{ | |
lua_settop(mLuaState, 0); | |
} | |
int start = lua_gettop(mLuaState) - argc; // i.e. first parameter | |
// Make sure obj is second parameter | |
if (obj.index < start) | |
{ | |
// Move obj to start | |
lua_pushvalue(mLuaState, obj.index); | |
lua_insert(mLuaState, start+1); | |
lua_remove(mLuaState, obj.index); | |
obj.index = start; | |
argv[0].index -= 1; | |
} | |
else if (obj.index == lua_gettop(mLuaState)) | |
{ | |
lua_insert(mLuaState, start+1); | |
obj.index = start+1; | |
} | |
// Resolve object field, replace arg 0 | |
lua_pushvalue(mLuaState, argv[0].index); | |
const char *funcName = lua_tostring(mLuaState, -1); | |
lua_gettable(mLuaState, obj.index); | |
lua_replace(mLuaState, argv[0].index); | |
//lua_debug_print_stack(mLuaState); | |
if (lua_pcall(mLuaState, argc, 1, 0) != 0) | |
{ | |
Con::errorf("Error calling %s (%s)", funcName, lua_tostring(mLuaState, -1)); | |
lua_settop(mLuaState, 0); | |
lua_pushnil(mLuaState); | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = 1; | |
if (wasRootExec) | |
{ | |
mShouldReset = true; | |
} | |
return ret; | |
} | |
if (wasRootExec) | |
{ | |
mShouldReset = true; | |
} | |
// Return result | |
ret.index = lua_gettop(mLuaState); | |
ret.type = mLuaStack.getTypeAtIndex(-1); | |
return ret; | |
} | |
/// Evaluate an arbitrary chunk of code. | |
/// | |
/// @param string Buffer containing code to execute. | |
/// @param echo Should we echo the string to the console? | |
/// @param fileName Indicate what file this code is coming from; used in error reporting and such. | |
virtual ScriptStackValueRef evaluate(const char* string, bool echo = false, const char *fileName = NULL) | |
{ | |
ScriptStackValueRef ret; | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = -1; | |
int start = lua_gettop(mLuaState); | |
bool wasRootExec = false; | |
if (!mRootExec) | |
{ | |
wasRootExec = true; | |
mRootExec = true; | |
} | |
if (mShouldReset) | |
{ | |
lua_settop(mLuaState, 0); | |
} | |
// Load the script | |
if (luaL_loadbuffer(mLuaState, string, dStrlen(string), fileName) != 0) | |
{ | |
Con::errorf("Error parsing script %s: %s\n", fileName ? fileName : "", lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
lua_pushnil(mLuaState); | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = lua_gettop(mLuaState); | |
return ret; | |
} | |
//lua_debug_print_stack(mLuaState); | |
if (lua_pcall(mLuaState, 0, 1, 0) != 0) | |
{ | |
Con::errorf("Error running script: %s\n", lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
lua_pushnil(mLuaState); | |
ret.type = SCRIPTTYPE_NULL; | |
ret.index = lua_gettop(mLuaState); | |
return ret; | |
} | |
if (wasRootExec) | |
{ | |
mShouldReset = true; | |
} | |
// Return result | |
ret.index = lua_gettop(mLuaState); | |
ret.type = mLuaStack.getTypeAtIndex(-1); | |
return ret; | |
} | |
static int doLuaWrite(lua_State *L, | |
const void* p, | |
size_t sz, | |
void* ud) | |
{ | |
Stream *s = static_cast<Stream*>(ud); | |
s->write(sz, p); | |
return 0; | |
} | |
virtual bool compileFile(const char *filename) | |
{ | |
char pathBuffer[1024]; | |
Con::expandPath(pathBuffer, sizeof(pathBuffer), filename); | |
// Load the script | |
Stream *s = ResourceManager->openStream(pathBuffer); | |
if (!s) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Could not load script %s", pathBuffer); | |
return false; | |
} | |
U32 size = s->getStreamSize(); | |
char *buffer = (char*)dMalloc(size+1); | |
s->read(size, buffer); | |
ResourceManager->closeStream(s); | |
if (luaL_loadbuffer(mLuaState, buffer, size, pathBuffer) != 0) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error parsing script %s: %s\n", pathBuffer, lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
dFree(buffer); | |
// Add extension and dump to bytecode file | |
char *ext = dStrrchr( pathBuffer, '.' ); | |
if (!ext) { | |
Con::errorf(ConsoleLogEntry::Script, "No extension for script %s", pathBuffer); | |
return false; | |
} | |
dStrcpy(ext, ".luc"); | |
FileStream outS; | |
if (!ResourceManager->openFileForWrite(outS, pathBuffer)) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error opening %s for write", pathBuffer); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
lua_dump(mLuaState, &doLuaWrite, &outS); | |
lua_pop(mLuaState, 1); | |
return true; | |
} | |
virtual bool executeFile(const char *filename) | |
{ | |
char pathBuffer[1024]; | |
Con::expandPath(pathBuffer, sizeof(pathBuffer), filename); | |
FileTime comModifyTime; | |
FileTime scrModifyTime; | |
ResourceObject *rScr = ResourceManager->find(pathBuffer); | |
ResourceObject *rCom = NULL; | |
// Check if .luc exists | |
char *ext = dStrrchr( pathBuffer, '.' ); | |
if (!ext) { | |
Con::errorf(ConsoleLogEntry::Script, "No extension for script %s", pathBuffer); | |
return false; | |
} | |
dStrcpy(ext, ".luc"); | |
rCom = ResourceManager->find(pathBuffer); | |
if(rCom) | |
rCom->getFileTimes(NULL, &comModifyTime); | |
if(rScr) | |
rScr->getFileTimes(NULL, &scrModifyTime); | |
// If com exists and its newer than the script, just load the compiled version. | |
if (rCom && ( rScr == NULL || (rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) >= 0)) ) | |
{ | |
// Load the script | |
Stream *s = ResourceManager->openStream(rCom); | |
if (!s) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Could not load script %s", pathBuffer); | |
return false; | |
} | |
U32 size = s->getStreamSize(); | |
char *buffer = (char*)dMalloc(size+1); | |
s->read(size, buffer); | |
ResourceManager->closeStream(s); | |
// re-eval original filename | |
Con::expandPath(pathBuffer, sizeof(pathBuffer), filename); | |
if (luaL_loadbuffer(mLuaState, buffer, size, pathBuffer) != 0) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error loading script %s: %s\n", pathBuffer, lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
dFree(buffer); | |
if (lua_pcall(mLuaState, 0, 0, 0) != 0) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error running script: %s\n", lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
lua_pop(mLuaState, 1); | |
return true; | |
} | |
// Otherwise, compile script | |
if (rScr) | |
{ | |
// Load the script | |
Stream *s = ResourceManager->openStream(rScr); | |
if (!s) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Could not load script %s", pathBuffer); | |
return false; | |
} | |
U32 size = s->getStreamSize(); | |
char *buffer = (char*)dMalloc(size+1); | |
s->read(size, buffer); | |
ResourceManager->closeStream(s); | |
// Open compiled version file | |
FileStream outF; | |
Stream *outS = NULL; | |
if (ResourceManager->openFileForWrite(outF, pathBuffer)) | |
{ | |
outS = &outF; | |
} | |
// re-eval original filename | |
Con::expandPath(pathBuffer, sizeof(pathBuffer), filename); | |
if (luaL_loadbuffer(mLuaState, buffer, size, pathBuffer) != 0) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error parsing script %s: %s\n", pathBuffer, lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
dFree(buffer); | |
// dump to compiled bytecode file | |
if (outS) | |
{ | |
lua_dump(mLuaState, &doLuaWrite, outS); | |
outF.close(); | |
} | |
if (lua_pcall(mLuaState, 0, 0, 0) != 0) | |
{ | |
Con::errorf(ConsoleLogEntry::Script, "Error running script: %s\n", lua_tostring(mLuaState, -1)); | |
lua_pop(mLuaState, 1); | |
return false; | |
} | |
lua_pop(mLuaState, 1); | |
return true; | |
} | |
Con::expandPath(pathBuffer, sizeof(pathBuffer), filename); | |
Con::errorf(ConsoleLogEntry::Script, "Could not execute script %s\n", pathBuffer); | |
return false; | |
} | |
}; | |
extern ScriptEngine *sScriptInstance; | |
void SetupLuaInterpreter() | |
{ | |
ScriptEngine *script = ScriptEngine::getInstance(); | |
if (!script) | |
{ | |
script = new LuaScriptEngine(); | |
script->registerFunctions(Namespace::global()); | |
AbstractClassRep *klass = AbstractClassRep::findClassRep("SimObject"); | |
script->registerClass(klass); | |
klass = AbstractClassRep::findClassRep("AssetBase"); | |
script->registerClass(klass); | |
} | |
} | |
void ShutdownLuaInterpreter() | |
{ | |
if (sScriptInstance) | |
delete sScriptInstance; | |
sScriptInstance = NULL; | |
} | |
ConsoleFunction(evalLua, const char*, 2, 2, "") | |
{ | |
return ScriptEngine::getInstance()->evaluate(argv[1], false, "evalLua.lua"); | |
} | |
ConsoleFunction(evalLuaTF, const char*, 2, 2, "") | |
{ | |
SimObject *obj; | |
if (Sim::findObject(argv[1], obj)) | |
{ | |
return ScriptEngine::getInstance()->executef(obj, "testFunction", 123); | |
} | |
return ""; | |
} | |
ConsoleFunction(evalLuaF, const char*, 2, 12, "") | |
{ | |
ScriptStackValueRef args[MAX_THUNKARGS]; | |
for (int i=1; i<argc; i++) | |
{ | |
args[i-1] = argv[i]; | |
} | |
return ScriptEngine::getInstance()->execute(argc-1, args); | |
} | |
ConsoleFunction(evalLuaO, const char*, 2, 12, "") | |
{ | |
SimObject *obj; | |
if (Sim::findObject(argv[1], obj)) | |
{ | |
ScriptStackValueRef args[MAX_THUNKARGS]; | |
for (int i=2; i<argc; i++) | |
{ | |
args[i-2] = argv[i]; | |
} | |
return ScriptEngine::getInstance()->executeOnObject(obj, argc-2, args); | |
} | |
return ""; | |
} | |
ConsoleFunction(executeLua, bool, 2, 2, "") | |
{ | |
return ScriptEngine::getInstance()->executeFile(argv[1]); | |
} |
This file contains hidden or 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
// | |
// Copyright (c) 2014 James S Urquhart. All rights reserved. | |
// | |
// 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 "ScriptEngine.h" | |
ScriptStackValueRef ScriptStackValueRef::valueAtIndex(int index) | |
{ | |
ScriptStackValueRef newValue; | |
newValue.type = ScriptEngine::getInstance()->getStack()->getTypeAtIndex(index); | |
newValue.index = ScriptEngine::getInstance()->getStack()->pushIndex(index); | |
return newValue; | |
} | |
ScriptStack::ScriptStack() | |
{ | |
} | |
ScriptStack::~ScriptStack() | |
{ | |
} | |
int ScriptStack::pushNull() | |
{ | |
} | |
int ScriptStack::pushIndex(int value) | |
{ | |
} | |
int ScriptStack::pushInteger(U32 value) | |
{ | |
} | |
int ScriptStack::pushSignedInteger(S32 value) | |
{ | |
} | |
int ScriptStack::pushNumber(F32 value) | |
{ | |
} | |
int ScriptStack::pushSimFields(SimFieldDictionary *values) | |
{ | |
} | |
int ScriptStack::pushString(const char *value) | |
{ | |
} | |
int ScriptStack::pushSimObject(SimObject *value) | |
{ | |
} | |
int ScriptStack::pushBool(bool value) | |
{ | |
} | |
// Get values | |
const char* ScriptStack::getString(int index) | |
{ | |
} | |
U32 ScriptStack::getInteger(int index) | |
{ | |
} | |
S32 ScriptStack::getSignedInteger(int index) | |
{ | |
} | |
F32 ScriptStack::getNumber(int index) | |
{ | |
} | |
bool ScriptStack::getBool(int index) | |
{ | |
} | |
void ScriptStack::getSimFields(int index, SimFieldDictionary *outValues) | |
{ | |
} | |
ScriptValueBaseType ScriptStack::getTypeAtIndex(int index) | |
{ | |
} | |
void ScriptStack::setString(int index, const char *value) | |
{ | |
} | |
void ScriptStack::setInteger(int index, U32 value) | |
{ | |
} | |
void ScriptStack::setSignedInteger(int index, S32 value) | |
{ | |
} | |
void ScriptStack::setNumber(int index, F32 value) | |
{ | |
} | |
void ScriptStack::setSimFields(int index, SimFieldDictionary *values) | |
{ | |
} | |
void ScriptStack::setSimObject(int index, SimObject *value) | |
{ | |
} | |
void ScriptStack::setBool(int index, bool value) | |
{ | |
} | |
ScriptEngine *sScriptInstance = NULL; | |
ScriptEngine::ScriptEngine() | |
{ | |
sScriptInstance = this; | |
} | |
ScriptEngine::~ScriptEngine() | |
{ | |
} | |
ScriptEngine *ScriptEngine::getInstance() | |
{ | |
return sScriptInstance; | |
} | |
void ScriptEngine::registerClass(AbstractClassRep *rep) | |
{ | |
} | |
void ScriptEngine::registerFunctions(Namespace *ns) | |
{ | |
} | |
void ScriptEngine::registerObject(SimObject *object) | |
{ | |
} | |
void ScriptEngine::removeObject(SimObject *object) | |
{ | |
} | |
ScriptStack *ScriptEngine::getStack() | |
{ | |
return NULL; | |
} | |
ScriptStackValueRef ScriptEngine::execute(S32 argc, ScriptStackValueRef argv[]) | |
{ | |
return ScriptStackValueRef(); | |
} | |
ScriptStackValueRef ScriptEngine::executeOnObject(ScriptStackValueRef obj, S32 argc, ScriptStackValueRef argv[]) | |
{ | |
return ScriptStackValueRef(); | |
} | |
ScriptStackValueRef ScriptEngine::evaluate(const char* string, bool echo, const char *fileName) | |
{ | |
} | |
ScriptStackValueRef ScriptEngine::evaluatef(const char* string, ...) | |
{ | |
} | |
// Note: operators replace value | |
ScriptStackValueRef& ScriptStackValueRef::operator=(const ScriptStackValueRef &other) | |
{ | |
index = other.index; | |
type = other.type; | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(bool newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushBool(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setBool(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(const char *newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushString(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setString(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(U32 newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushInteger(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setInteger(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(S32 newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushSignedInteger(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setSignedInteger(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(F32 newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushNumber(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setNumber(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(F64 newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushNumber(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setNumber(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(SimFieldDictionary *newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushSimFields(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setSimFields(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef& ScriptStackValueRef::operator=(SimObject *newValue) | |
{ | |
if (index == -1) | |
index = ScriptEngine::getInstance()->getStack()->pushSimObject(newValue); | |
else | |
ScriptEngine::getInstance()->getStack()->setSimObject(index, newValue); | |
return *this; | |
} | |
ScriptStackValueRef ScriptStackValueRef::clone() | |
{ | |
ScriptStackValueRef newValue; | |
newValue.type = type; | |
newValue.index = index == -1 ? ScriptEngine::getInstance()->getStack()->pushNull() : ScriptEngine::getInstance()->getStack()->pushIndex(index); | |
return newValue; | |
} | |
This file contains hidden or 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
// | |
// Copyright (c) 2014 James S Urquhart. All rights reserved. | |
// | |
// 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. | |
// | |
#ifndef __Torque2D__ScriptEngine__ | |
#define __Torque2D__ScriptEngine__ | |
#include "platform/platform.h" | |
#include "sim/simFieldDictionary.h" | |
#include "console/console.h" | |
class Namespace; | |
class SimObject; | |
class AbstractClassRep; | |
// Basic type for a stack value | |
typedef enum | |
{ | |
SCRIPTTYPE_NULL=0, | |
SCRIPTTYPE_STRING=1, | |
SCRIPTTYPE_INTEGER=2, | |
SCRIPTTYPE_NUMBER=3, | |
SCRIPTTYPE_BOOL=4, | |
SCRIPTTYPE_TABLE=5, // i.e. a script Dictionary | |
SCRIPTTYPE_OBJECT=6 // Instance of class | |
} ScriptValueBaseType; | |
class ScriptStack | |
{ | |
public: | |
ScriptStack(); | |
virtual ~ScriptStack(); | |
// Push values | |
virtual int pushNull(); | |
virtual int pushBool(bool value); | |
virtual int pushIndex(int value); | |
virtual int pushInteger(U32 value); | |
virtual int pushSignedInteger(S32 value); | |
virtual int pushNumber(F32 value); | |
virtual int pushSimFields(SimFieldDictionary *values); | |
virtual int pushString(const char *value); | |
virtual int pushSimObject(SimObject *value); | |
// Get values | |
virtual const char* getString(int index); | |
virtual U32 getInteger(int index); | |
virtual S32 getSignedInteger(int index); | |
virtual F32 getNumber(int index); | |
virtual void getSimFields(int index, SimFieldDictionary *outValues); | |
virtual bool getBool(int index); | |
virtual ScriptValueBaseType getTypeAtIndex(int index); | |
// Set values | |
virtual void setBool(int index, bool value); | |
virtual void setString(int index, const char *value); | |
virtual void setInteger(int index, U32 value); | |
virtual void setSignedInteger(int index, S32 value); | |
virtual void setNumber(int index, F32 value); | |
virtual void setSimFields(int index, SimFieldDictionary *values); | |
virtual void setSimObject(int index, SimObject *value); | |
}; | |
// A value which has already been pushed to the stack | |
// e.g. | |
// ScriptStackValueRef value1 = "carrots"; | |
// ScriptStackValueRef value2 = 123; | |
// ScriptEngine::getInstance()->call(); | |
// or: | |
// ScriptEngine::getInstance()->executef("carrots", 123); | |
class ScriptStackValueRef | |
{ | |
public: | |
int index; | |
ScriptValueBaseType type; | |
ScriptStackValueRef() : index(-1), type(SCRIPTTYPE_NULL) {;} | |
~ScriptStackValueRef() {;} | |
ScriptStackValueRef(const ScriptStackValueRef &ref) : index(ref.index), type(ref.type) { ; } | |
ScriptStackValueRef(const char *value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(U32 value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(S32 value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(F32 value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(F64 value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(SimObject *value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef(bool value) : index(-1) | |
{ | |
*this = value; | |
} | |
ScriptStackValueRef clone(); | |
inline const char* getStringValue(); | |
inline U32 getIntValue(); | |
inline S32 getSignedIntValue(); | |
inline F32 getFloatValue(); | |
inline bool getBoolValue(); | |
inline operator const char*() { return getStringValue(); } | |
inline operator U32() { return getIntValue(); } | |
inline operator S32() { return getSignedIntValue(); } | |
inline operator F32() { return getFloatValue(); } | |
inline bool isString() { return type == SCRIPTTYPE_STRING; } | |
inline bool isInt() { return type == SCRIPTTYPE_INTEGER; } | |
inline bool isFloat() { return type == SCRIPTTYPE_NUMBER; } | |
inline bool isTable() { return type == SCRIPTTYPE_TABLE; } | |
inline bool isObject() { return type == SCRIPTTYPE_OBJECT; } | |
// Note: operators replace value | |
ScriptStackValueRef& operator=(const ScriptStackValueRef &other); | |
ScriptStackValueRef& operator=(const char *newValue); | |
ScriptStackValueRef& operator=(U32 newValue); | |
ScriptStackValueRef& operator=(S32 newValue); | |
ScriptStackValueRef& operator=(F32 newValue); | |
ScriptStackValueRef& operator=(F64 newValue); | |
ScriptStackValueRef& operator=(SimFieldDictionary *dict); | |
ScriptStackValueRef& operator=(SimObject *object); | |
ScriptStackValueRef& operator=(bool newValue); | |
static ScriptStackValueRef valueAtIndex(int index); | |
}; | |
class ScriptStackValueRef; | |
// | |
class ScriptEngineValueRef | |
{ | |
public: | |
virtual ScriptStackValueRef& operator=(const ScriptEngineValueRef &other) { return *this;} | |
}; | |
class ScriptFunction() | |
class ScriptEngine | |
{ | |
public: | |
ScriptEngine(); | |
virtual ~ScriptEngine(); | |
static ScriptEngine *getInstance(); | |
// iterates and registers bindings | |
virtual void registerBindings(ConsoleConstructor *root); | |
// register class | |
virtual void registerClass(AbstractClassRep *rep); | |
// register or re-register namespace functions, ensuring proper class heirachy with parent. | |
virtual void registerFunctions(Namespace *ns); | |
// register object instance | |
virtual void registerObject(SimObject *object); | |
// unregister object instance | |
virtual void removeObject(SimObject *object); | |
// gets current stack | |
virtual ScriptStack *getStack(); | |
virtual ScriptStackValueRef execute(S32 argc, ScriptStackValueRef argv[]); | |
virtual ScriptStackValueRef executeOnObject(ScriptStackValueRef obj, S32 argc, ScriptStackValueRef argv[]); | |
#define ARG ScriptStackValueRef | |
// Plain versions | |
inline ScriptStackValueRef executef( ARG); | |
inline ScriptStackValueRef executef( ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
// Object versions | |
inline ScriptStackValueRef executef(SimObject *, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
inline ScriptStackValueRef executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); | |
#undef ARG | |
/// Evaluate an arbitrary chunk of code. | |
/// | |
/// @param string Buffer containing code to execute. | |
/// @param echo Should we echo the string to the console? | |
/// @param fileName Indicate what file this code is coming from; used in error reporting and such. | |
virtual ScriptStackValueRef evaluate(const char* string, bool echo = false, const char *fileName = NULL); | |
/// Evaluate an arbitrary line of script. | |
/// | |
/// This wraps dVsprintf(), so you can substitute parameters into the code being executed. | |
virtual ScriptStackValueRef evaluatef(const char* string, ...); | |
// Compiles a file to a binary script | |
virtual bool compileFile(const char *filename) = 0; | |
// Executes a file to a binary script | |
virtual bool executeFile(const char *filename) = 0; | |
}; | |
#define A ScriptStackValueRef | |
#define OBJ SimObject* obj | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a) { ScriptStackValueRef params[] = {a}; return executeOnObject(obj, 1, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b) { ScriptStackValueRef params[] = {a,b}; return executeOnObject(obj, 2, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c) { ScriptStackValueRef params[] = {a,b,c}; return executeOnObject(obj, 3, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d) { ScriptStackValueRef params[] = {a,b,c,d}; return executeOnObject(obj, 4, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e) { ScriptStackValueRef params[] = {a,b,c,d,e}; return executeOnObject(obj, 5, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f) { ScriptStackValueRef params[] = {a,b,c,d,e,f}; return executeOnObject(obj, 6, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f, A g) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g}; return executeOnObject(obj, 7, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h}; return executeOnObject(obj, 8, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h,i}; return executeOnObject(obj, 9, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h,i,j}; return executeOnObject(obj, 10, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j, A k) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h,i,j,k}; return executeOnObject(obj, 11, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a) { ScriptStackValueRef params[] = {a}; return execute(1, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b) { ScriptStackValueRef params[] = {a,b}; return execute(2, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c) { ScriptStackValueRef params[] = {a,b,c}; return execute(3, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d) { ScriptStackValueRef params[] = {a,b,c,d}; return execute(4, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e) { ScriptStackValueRef params[] = {a,b,c,d,e}; return execute(5, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e, A f) { ScriptStackValueRef params[] = {a,b,c,d,e,f}; return execute(6, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e, A f, A g) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g}; return execute(7, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e, A f, A g, A h) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h}; return execute(8, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e, A f, A g, A h, A i) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h,i}; return execute(9, params); } | |
inline ScriptStackValueRef ScriptEngine::executef(A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { ScriptStackValueRef params[] = {a,b,c,d,e,f,g,h,i,j}; return execute(10, params); } | |
#undef A | |
#undef OBJ | |
inline const char* ScriptStackValueRef::getStringValue() { return ScriptEngine::getInstance()->getStack()->getString(index); } | |
inline U32 ScriptStackValueRef::getIntValue() { return ScriptEngine::getInstance()->getStack()->getInteger(index); } | |
inline S32 ScriptStackValueRef::getSignedIntValue() { return ScriptEngine::getInstance()->getStack()->getSignedInteger(index); } | |
inline F32 ScriptStackValueRef::getFloatValue() { return ScriptEngine::getInstance()->getStack()->getNumber(index); } | |
inline bool ScriptStackValueRef::getBoolValue() { return ScriptEngine::getInstance()->getStack()->getInteger(index); } | |
#endif /* defined(__Torque2D__ScriptEngine__) */ |
This file contains hidden or 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
//----------------------------------------------------------------------------- | |
// 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 | |
#include "script/ScriptEngine.h" | |
#include "script/TSScriptEngine.h" | |
using namespace Compiler; | |
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; | |
} | |
//------------------------------------------------------------ | |
// TODO: ABSTRACT | |
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. | |
void TSScriptEngine::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) | |
{ | |
StringStack &STR = mScriptEngine->STR; | |
#ifdef TORQUE_DEBUG | |
U32 stackStart = STR.mStartStackSize; | |
#endif | |
static char traceBuffer[1024]; | |
U32 i; | |
U32& UINT = mScriptEngine->UINT; | |
U32& FLT = mScriptEngine->FLT; | |
F64 *floatStack = mScriptEngine->floatStack; | |
S64 *intStack = mScriptEngine->intStack; | |
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 = mScriptEngine->smCurrentCodeBlock; | |
mScriptEngine->smCurrentCodeBlock = this; | |
if(this->name) | |
{ | |
mScriptEngine->gCurrentFile = this->name; | |
mScriptEngine->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? | |
mScriptEngine->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? | |
mScriptEngine->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? | |
mScriptEngine->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] | |
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; | |
} | |
mScriptEngine->smCurrentCodeBlock = saveCodeBlock; | |
if(saveCodeBlock && saveCodeBlock->name) | |
{ | |
mScriptEngine->gCurrentFile = saveCodeBlock->name; | |
mScriptEngine->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(); | |
} | |
//------------------------------------------------------------ | |
TSScriptEngine::TSScriptEngine() | |
{ | |
} | |
TSScriptEngine::~TSScriptEngine() | |
{ | |
} | |
// register class | |
void TSScriptEngine::registerClass(AbstractClassRep *rep) | |
{ | |
} | |
// register namespace functions | |
void TSScriptEngine::registerNamespace(Namespace *ns) | |
{ | |
} | |
// register object instance | |
void TSScriptEngine::registerObject(SimObject *object) | |
{ | |
} | |
// unregister object instance | |
void TSScriptEngine::removeObject(SimObject *object) | |
{ | |
} | |
// gets current stack | |
ScriptStack *TSScriptEngine::getStack() | |
{ | |
} | |
ScriptStackValueRef TSScriptEngine::execute(S32 argc, ScriptStackValueRef argv[]) | |
{ | |
} | |
ScriptStackValueRef TSScriptEngine::executeOnObject(ScriptStackValueRef obj, S32 argc, ScriptStackValueRef argv[]) | |
{ | |
} | |
/// Evaluate an arbitrary chunk of code. | |
/// | |
/// @param string Buffer containing code to execute. | |
/// @param echo Should we echo the string to the console? | |
/// @param fileName Indicate what file this code is coming from; used in error reporting and such. | |
ScriptStackValueRef TSScriptEngine::evaluate(const char* string, bool echo = false, const char *fileName = NULL) | |
{ | |
} | |
/// Evaluate an arbitrary line of script. | |
/// | |
/// This wraps dVsprintf(), so you can substitute parameters into the code being executed. | |
ScriptStackValueRef TSScriptEngine::evaluatef(const char* string, ...) | |
{ | |
} | |
// Compiles a file to a binary script | |
bool TSScriptEngine::compileFile(const char *filename) | |
{ | |
} | |
// Executes a file to a binary script | |
bool TSScriptEngine::executeFile(const char *filename) | |
{ | |
} | |
//------------------------------------------------------------------------- | |
StringTableEntry TSScriptEngine::getCurrentCodeBlockName() | |
{ | |
if (getCurrentBlock()) | |
return getCurrentBlock()->name; | |
else | |
return NULL; | |
} | |
StringTableEntry TSScriptEngine::getCurrentCodeBlockFullPath() | |
{ | |
if (getCurrentBlock()) | |
return getCurrentBlock()->fullPath; | |
else | |
return NULL; | |
} | |
StringTableEntry TSScriptEngine::getCurrentCodeBlockModName() | |
{ | |
if (getCurrentBlock()) | |
return getCurrentBlock()->modPath; | |
else | |
return NULL; | |
} | |
CodeBlock *TSScriptEngine::find(StringTableEntry name) | |
{ | |
for(CodeBlock *walk = getCodeBlockList(); walk; walk = walk->nextFile) | |
if(walk->name == name) | |
return walk; | |
return NULL; | |
} | |
void TSScriptEngine::incCodeBlockRef(void *code) | |
{ | |
CodeBlock *block = dynamic_cast<CodeBlock*>(code); | |
if (block) | |
{ | |
block->incRefCount(); | |
} | |
} | |
void TSScriptEngine::decCodeBlockRef(void *code) | |
{ | |
CodeBlock *block = dynamic_cast<CodeBlock*>(code); | |
if (block) | |
{ | |
block->decRefCount(); | |
} | |
} | |
void TSScriptEngine::getFunctionArgs(char buffer[1024], void* code, U32 offset) | |
{ | |
CodeBlock *block = dynamic_cast<CodeBlock*>(code); | |
if (block) | |
{ | |
block->getFunctionArgs(buffer, offset); | |
} | |
} | |
const char *TSScriptEngine::execCodeBlock(void* code, U32 offset, const char *functionName, Namespace *thisNamespace, U32 argc, ScriptStackValueRef argv[], bool noCalls, StringTableEntry packageName) | |
{ | |
CodeBlock *block = dynamic_cast<CodeBlock*>(code); | |
if (block) | |
{ | |
// TODO block->decRefCount(); | |
} | |
} |
This file contains hidden or 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
// | |
// TSScriptEngine.h | |
// Torque2D | |
// | |
// Created by James Urquhart on 29/10/2014. | |
// Copyright (c) 2014 Michael Perry. All rights reserved. | |
// | |
#ifndef __Torque2D__TSScriptEngine__ | |
#define __Torque2D__TSScriptEngine__ | |
#include "script/ScriptEngine.h" | |
#include "console/consoleInternal.h" | |
#include "string/stringStack.h" | |
/* | |
TODOS | |
Refer to current ScriptEngine using "script" parameter in ConsoleFUnction etc | |
Remove Codeblock dependence from other files | |
Find better way of returning values from console functions | |
FINAL STUFF | |
Move TS specific stuff from console | |
*/ | |
class TSScriptEngine | |
{ | |
public: | |
enum EvalConstants { | |
MaxStackSize = 1024, | |
MethodOnComponent = -2 | |
}; | |
TSScriptEngine(); | |
virtual ~TSScriptEngine(); | |
ExprEvalState gEvalState; | |
StringStack STR; | |
U32 FLT; | |
U32 UINT; | |
F64 floatStack[MaxStackSize]; | |
S64 intStack[MaxStackSize]; | |
StringTableEntry gCurrentFile; | |
StringTableEntry gCurrentRoot; | |
static ScriptEngine *getInstance(); | |
// register class | |
virtual void registerClass(AbstractClassRep *rep); | |
// register namespace functions | |
virtual void registerNamespace(Namespace *ns); | |
// register object instance | |
virtual void registerObject(SimObject *object); | |
// unregister object instance | |
virtual void removeObject(SimObject *object); | |
// gets current stack | |
virtual ScriptStack *getStack(); | |
virtual ScriptStackValueRef execute(S32 argc, ScriptStackValueRef argv[]); | |
virtual ScriptStackValueRef executeOnObject(ScriptStackValueRef obj, S32 argc, ScriptStackValueRef argv[]); | |
/// Evaluate an arbitrary chunk of code. | |
/// | |
/// @param string Buffer containing code to execute. | |
/// @param echo Should we echo the string to the console? | |
/// @param fileName Indicate what file this code is coming from; used in error reporting and such. | |
virtual ScriptStackValueRef evaluate(const char* string, bool echo = false, const char *fileName = NULL); | |
/// Evaluate an arbitrary line of script. | |
/// | |
/// This wraps dVsprintf(), so you can substitute parameters into the code being executed. | |
virtual ScriptStackValueRef evaluatef(const char* string, ...); | |
// Compiles a file to a binary script | |
virtual bool compileFile(const char *filename) = 0; | |
// Executes a file to a binary script | |
virtual bool executeFile(const char *filename) = 0; | |
// TS Specific helpers | |
void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ); | |
inline ExprEvalState *getEvalState() { return &gEvalState; } | |
// Stuff from CodeBlock | |
public: | |
CodeBlock* smCodeBlockList; | |
CodeBlock* smCurrentCodeBlock; | |
public: | |
CodeBlock* getCurrentBlock() | |
{ | |
return smCurrentCodeBlock; | |
} | |
CodeBlock *getCodeBlockList() | |
{ | |
return smCodeBlockList; | |
} | |
StringTableEntry getCurrentCodeBlockName(); | |
StringTableEntry getCurrentCodeBlockFullPath(); | |
StringTableEntry getCurrentCodeBlockModName(); | |
CodeBlock *findCodeblock(StringTableEntry); | |
virtual void incCodeBlockRef(void *code); | |
virtual void decCodeBlockRef(void *code); | |
virtual void getFunctionArgs(char buffer[1024], void* code, U32 offset); | |
virtual const char *execCodeBlock(void* code, U32 offset, const char *functionName, Namespace *thisNamespace, U32 argc, ScriptStackValueRef argv[], bool noCalls, StringTableEntry packageName) { return ""; } | |
}; | |
#endif /* defined(__Torque2D__TSScriptEngine__) */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment