Improvements based on experience from implementing Python runtime

This commit is contained in:
Lukas Aldershaab 2023-06-02 16:36:04 +02:00
parent e28e24a802
commit 83ea6cd0df
6 changed files with 67 additions and 188 deletions

View file

@ -365,6 +365,18 @@ public:
return type >= ConsoleValueType::cvConsoleValueType; return type >= ConsoleValueType::cvConsoleValueType;
} }
TORQUE_FORCEINLINE ConsoleValueConsoleType* getConsoleType() const
{
if(type >= ConsoleValueType::cvConsoleValueType)
{
return ct;
}
else
{
return NULL;
}
}
TORQUE_FORCEINLINE void setFastFloat(F64 flt) TORQUE_FORCEINLINE void setFastFloat(F64 flt)
{ {
type = ConsoleValueType::cvFloat; type = ConsoleValueType::cvFloat;

View file

@ -181,13 +181,13 @@ void Dictionary::exportVariables(const char *varString, const char *fileName, bo
for (s = sortList.begin(); s != sortList.end(); s++) for (s = sortList.begin(); s != sortList.end(); s++)
{ {
switch ((*s)->type) switch ((*s)->value.getType())
{ {
case Entry::TypeInternalInt: case ConsoleValueType::cvInteger:
dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->ival, cat); dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->getIntValue(), cat);
break; break;
case Entry::TypeInternalFloat: case ConsoleValueType::cvFloat:
dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->fval, cat); dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->getFloatValue(), cat);
break; break;
default: default:
expandEscape(expandBuffer, (*s)->getStringValue()); expandEscape(expandBuffer, (*s)->getStringValue());
@ -241,7 +241,7 @@ void Dictionary::exportVariables(const char *varString, Vector<String> *names, V
if (values) if (values)
{ {
switch ((*s)->type) switch ((*s)->value.getType())
{ {
case ConsoleValueType::cvInteger: case ConsoleValueType::cvInteger:
case ConsoleValueType::cvFloat: case ConsoleValueType::cvFloat:
@ -461,19 +461,11 @@ const char *Dictionary::tabComplete(const char *prevText, S32 baseLen, bool fFor
Dictionary::Entry::Entry(StringTableEntry in_name) Dictionary::Entry::Entry(StringTableEntry in_name)
{ {
name = in_name; name = in_name;
type = TypeInternalString;
notify = NULL; notify = NULL;
nextEntry = NULL; nextEntry = NULL;
mUsage = NULL; mUsage = NULL;
mIsConstant = false; mIsConstant = false;
mNext = NULL; mNext = NULL;
ival = 0;
fval = 0;
sval = NULL;
bufferLen = 0;
dataPtr = NULL;
enumTable = NULL;
} }
Dictionary::Entry::~Entry() Dictionary::Entry::~Entry()
@ -484,73 +476,11 @@ Dictionary::Entry::~Entry()
void Dictionary::Entry::reset() void Dictionary::Entry::reset()
{ {
name = NULL; name = NULL;
if (type <= TypeInternalString && sval != NULL) value.reset();
dFree(sval);
if (notify) if (notify)
delete notify; delete notify;
} }
void Dictionary::Entry::setStringValue(const char* value)
{
if (mIsConstant)
{
Con::errorf("Cannot assign value to constant '%s'.", name);
return;
}
if (type <= TypeInternalString)
{
// Let's not remove empty-string-valued global vars from the dict.
// If we remove them, then they won't be exported, and sometimes
// it could be necessary to export such a global. There are very
// few empty-string global vars so there's no performance-related
// need to remove them from the dict.
/*
if(!value[0] && name[0] == '$')
{
gEvalState.globalVars.remove(this);
return;
}
*/
U32 stringLen = dStrlen(value);
// If it's longer than 256 bytes, it's certainly not a number.
//
// (This decision may come back to haunt you. Shame on you if it
// does.)
if (stringLen < 256)
{
fval = dAtof(value);
ival = dAtoi(value);
}
else
{
fval = 0.f;
ival = 0;
}
type = TypeInternalString;
// may as well pad to the next cache line
U32 newLen = ((stringLen + 1) + 15) & ~15;
if (sval == NULL)
sval = (char*)dMalloc(newLen);
else if (newLen > bufferLen)
sval = (char*)dRealloc(sval, newLen);
bufferLen = newLen;
dStrcpy(sval, value, newLen);
}
else
Con::setData(type, dataPtr, 0, 1, &value, enumTable);
// Fire off the notification if we have one.
if (notify)
notify->trigger();
}
const char *Dictionary::getVariable(StringTableEntry name, bool *entValid) const char *Dictionary::getVariable(StringTableEntry name, bool *entValid)
{ {
Entry *ent = lookup(name); Entry *ent = lookup(name);
@ -626,17 +556,12 @@ Dictionary::Entry* Dictionary::addVariable(const char *name,
Entry *ent = add(StringTable->insert(name)); Entry *ent = add(StringTable->insert(name));
if (ent->type <= Entry::TypeInternalString && ent->sval != NULL)
dFree(ent->sval);
ent->mUsage = usage; ent->mUsage = usage;
ent->type = type;
ent->dataPtr = dataPtr;
// Fetch enum table, if any. // Fetch enum table, if any.
ConsoleBaseType* conType = ConsoleBaseType::getType(type); ConsoleBaseType* conType = ConsoleBaseType::getType(type);
AssertFatal(conType, "Dictionary::addVariable - invalid console type"); AssertFatal(conType, "Dictionary::addVariable - invalid console type");
ent->enumTable = conType->getEnumTable(); ent->value.setConsoleData(type, dataPtr, conType->getEnumTable());
return ent; return ent;
} }

View file

@ -286,16 +286,8 @@ public:
{ {
friend class Dictionary; friend class Dictionary;
enum
{
TypeInternalInt = -3,
TypeInternalFloat = -2,
TypeInternalString = -1,
};
StringTableEntry name; StringTableEntry name;
Entry *nextEntry; Entry *nextEntry;
S32 type;
typedef Signal<void()> NotifySignal; typedef Signal<void()> NotifySignal;
@ -309,58 +301,17 @@ public:
/// Whether this is a constant that cannot be assigned to. /// Whether this is a constant that cannot be assigned to.
bool mIsConstant; bool mIsConstant;
protected: ConsoleValue value;
// NOTE: This is protected to ensure no one outside
// of this structure is messing with it.
#pragma warning( push )
#pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union
// An variable is either a real dynamic type or
// its one exposed from C++ using a data pointer.
//
// We use this nameless union and struct setup
// to optimize the memory usage.
union
{
struct
{
char* sval;
U32 ival; // doubles as strlen when type is TypeInternalString
F32 fval;
U32 bufferLen;
};
struct
{
/// The real data pointer.
void* dataPtr;
/// The enum lookup table for enumerated types.
const EnumTable* enumTable;
};
};
#pragma warning( pop ) // C4201
public: public:
Entry() { Entry() {
name = NULL; name = NULL;
type = TypeInternalString;
notify = NULL; notify = NULL;
nextEntry = NULL; nextEntry = NULL;
mUsage = NULL; mUsage = NULL;
mIsConstant = false; mIsConstant = false;
mNext = NULL; mNext = NULL;
ival = 0;
fval = 0;
sval = NULL;
bufferLen = 0;
dataPtr = NULL;
enumTable = NULL;
} }
Entry(StringTableEntry name); Entry(StringTableEntry name);
@ -370,32 +321,21 @@ public:
void reset(); void reset();
inline ConsoleValue getValue() { return std::move(value); }
inline U32 getIntValue() inline U32 getIntValue()
{ {
if (type <= TypeInternalString) return value.getInt();
return ival;
else
return dAtoi(Con::getData(type, dataPtr, 0, enumTable));
} }
inline F32 getFloatValue() inline F32 getFloatValue()
{ {
if (type <= TypeInternalString) return value.getFloat();
return fval;
else
return dAtof(Con::getData(type, dataPtr, 0, enumTable));
} }
inline const char *getStringValue() inline const char *getStringValue()
{ {
if (type == TypeInternalString) return value.getString();
return sval;
if (type == TypeInternalFloat)
return Con::getData(TypeF32, &fval, 0);
else if (type == TypeInternalInt)
return Con::getData(TypeS32, &ival, 0);
else
return Con::getData(type, dataPtr, 0, enumTable);
} }
void setIntValue(U32 val) void setIntValue(U32 val)
@ -406,21 +346,15 @@ public:
return; return;
} }
if (type <= TypeInternalString) if (value.isConsoleType())
{ {
fval = (F32)val; const char* dptr = Con::getData(TypeS32, &val, 0);
ival = val; ConsoleValueConsoleType* cvt = value.getConsoleType();
if (sval != NULL) Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &dptr, cvt->enumTable);
{
dFree(sval);
sval = NULL;
}
type = TypeInternalInt;
} }
else else
{ {
const char* dptr = Con::getData(TypeS32, &val, 0); value.setInt(val);
Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
} }
// Fire off the notification if we have one. // Fire off the notification if we have one.
@ -436,21 +370,15 @@ public:
return; return;
} }
if (type <= TypeInternalString) if (value.isConsoleType())
{ {
fval = val; const char* dptr = Con::getData(TypeF32, &val, 0);
ival = static_cast<U32>(val); ConsoleValueConsoleType* cvt = value.getConsoleType();
if (sval != NULL) Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &dptr, cvt->enumTable);
{
dFree(sval);
sval = NULL;
}
type = TypeInternalFloat;
} }
else else
{ {
const char* dptr = Con::getData(TypeF32, &val, 0); value.setFloat(val);
Con::setData(type, dataPtr, 0, 1, &dptr, enumTable);
} }
// Fire off the notification if we have one. // Fire off the notification if we have one.
@ -458,7 +386,29 @@ public:
notify->trigger(); notify->trigger();
} }
void setStringValue(const char* value);
void setStringValue(const char* val)
{
if (mIsConstant)
{
Con::errorf("Cannot assign value to constant '%s'.", name);
return;
}
if (value.isConsoleType())
{
ConsoleValueConsoleType* cvt = value.getConsoleType();
Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &val, cvt->enumTable);
}
else
{
value.setString(val);
}
// Fire off the notification if we have one.
if (notify)
notify->trigger();
}
}; };
struct HashTableData struct HashTableData

View file

@ -112,10 +112,10 @@ static void dumpVariable( Stream& stream,
const char* inClass = NULL ) const char* inClass = NULL )
{ {
// Skip variables defined in script. // Skip variables defined in script.
if( entry->type <= Dictionary::Entry::TypeInternalString ) if( entry->value.getType() <= ConsoleValueType::cvString )
return; return;
// Skip internals... don't export them. // Skip internals... don't export them.
if ( entry->mUsage && if ( entry->mUsage &&
( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) ) ( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) )
@ -145,10 +145,10 @@ static void dumpVariable( Stream& stream,
if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep ) if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep )
return; return;
} }
// Skip variables for which we can't decipher their type. // Skip variables for which we can't decipher their type.
ConsoleBaseType* type = ConsoleBaseType::getType( entry->type ); ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.getConsoleType()->consoleType );
if( !type ) if( !type )
{ {
Con::errorf( "Can't find type for variable '%s'", entry->name ); Con::errorf( "Can't find type for variable '%s'", entry->name );

View file

@ -4,6 +4,7 @@
#include "runtime.h" #include "runtime.h"
#include "core/stream/stream.h" #include "core/stream/stream.h"
#include "module.h" #include "module.h"
#include "core/util/tDictionary.h"
namespace Con namespace Con
{ {
@ -24,15 +25,10 @@ namespace Con
Module* getCurrentModule(); Module* getCurrentModule();
inline Vector<Runtime*> gRuntimes(32); inline HashMap<S32, Runtime*> gRuntimes;
inline Runtime* getRuntime(S32 pRuntimeId = 0) { return gRuntimes[pRuntimeId]; } inline Runtime* getRuntime(S32 pRuntimeId = 0) { return gRuntimes[pRuntimeId]; }
inline void registerRuntime(S32 pRuntimeId, Runtime* pRuntime) inline void registerRuntime(S32 pRuntimeId, Runtime* pRuntime)
{ {
if (gRuntimes.size() == 0)
{
gRuntimes.setSize(pRuntimeId + 1);
gRuntimes.fill(NULL);
}
AssertFatal(gRuntimes[pRuntimeId] == NULL, "A runtime with that ID already exists"); AssertFatal(gRuntimes[pRuntimeId] == NULL, "A runtime with that ID already exists");
gRuntimes[pRuntimeId] = pRuntime; gRuntimes[pRuntimeId] = pRuntime;
} }

View file

@ -95,16 +95,12 @@ namespace TorqueScript
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Con::EvalResult TorqueScriptRuntime::evaluatef(const char* string, ...) Con::EvalResult TorqueScriptRuntime::evaluatef(const char* string, ...)
{ {
ConsoleStackFrameSaver stackSaver;
stackSaver.save();
char buffer[4096]; char buffer[4096];
va_list args; va_list args;
va_start(args, string); va_start(args, string);
dVsprintf(buffer, sizeof(buffer), string, args); dVsprintf(buffer, sizeof(buffer), string, args);
va_end(args); va_end(args);
CodeBlock* newCodeBlock = new CodeBlock(); return evaluate(buffer);
return newCodeBlock->compileExec(NULL, buffer, false, 0);
} }
bool TorqueScriptRuntime::executeFile(const char* fileName, bool noCalls, bool journalScript) bool TorqueScriptRuntime::executeFile(const char* fileName, bool noCalls, bool journalScript)