From d567bc97355a9d547d5b223c82937ebee983352f Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Sat, 3 Aug 2019 17:54:21 +0200 Subject: [PATCH] Improve Engine API export, robust Default Value logic and allow _ in arg --- Engine/source/console/engineFunctions.h | 33 ++++++---- Engine/source/console/engineXMLExport.cpp | 76 ++++++++++++++--------- Engine/source/console/fixedTuple.h | 20 +++++- 3 files changed, 86 insertions(+), 43 deletions(-) diff --git a/Engine/source/console/engineFunctions.h b/Engine/source/console/engineFunctions.h index 8c6127374..84bc298d4 100644 --- a/Engine/source/console/engineFunctions.h +++ b/Engine/source/console/engineFunctions.h @@ -67,18 +67,9 @@ struct EngineFunctionDefaultArguments /// @warn This is @b NOT the size of the memory block returned by getArgs() and also /// not the number of elements it contains. U32 mNumDefaultArgs; - - /// Return a pointer to the variable-sized array of default argument values. - /// - /// @warn The arguments must be stored @b IMMEDIATELY after #mNumDefaultArgs. - /// @warn This is a @b FULL frame and not just the default arguments, i.e. it starts with the - /// first argument that the function takes and ends with the last argument it takes. - /// @warn If the compiler's #pragma pack is buggy, the elements in this structure are allowed - /// to be 4-byte aligned rather than byte-aligned as they should be. - const U8* getArgs() const - { - return ( const U8* ) &( mNumDefaultArgs ) + sizeof( mNumDefaultArgs ); - } + + U32* mOffsets; + U8* mFirst; }; @@ -130,13 +121,29 @@ private: SelfType::template copyHelper(argsT, tailT, typename Gens::type()); return argsT; }; + + template + typename std::enable_if::type initOffsetsHelper() + { } + + template + typename std::enable_if < I < sizeof...(ArgTs)>::type initOffsetsHelper() + { + mOffsets[I] = fixed_tuple_offset(mArgs); + initOffsetsHelper(); + } public: template _EngineFunctionDefaultArguments(TailTs ...tail) - : EngineFunctionDefaultArguments({sizeof...(TailTs)}) + : EngineFunctionDefaultArguments() { std::tuple...> tmpTup = SelfType::tailInit(tail...); fixed_tuple_mutator...), void(DefVST...)>::copy(tmpTup, mArgs); + + mNumDefaultArgs = sizeof...(TailTs); + mOffsets = new U32[sizeof...(ArgTs)]; + initOffsetsHelper(); + mFirst = (U8*)& mArgs; } }; diff --git a/Engine/source/console/engineXMLExport.cpp b/Engine/source/console/engineXMLExport.cpp index ae1b182bb..2da81ff50 100644 --- a/Engine/source/console/engineXMLExport.cpp +++ b/Engine/source/console/engineXMLExport.cpp @@ -58,9 +58,9 @@ static const char* getDocString(const EngineExport* exportInfo) } template< typename T > -inline T getArgValue(const EngineFunctionDefaultArguments* defaultArgs, U32 offset) +inline T getArgValue(const EngineFunctionDefaultArguments* defaultArgs, U32 idx) { - return *reinterpret_cast< const T* >(defaultArgs->getArgs() + offset); + return *(const T*)(defaultArgs->mFirst + defaultArgs->mOffsets[idx]); } @@ -122,7 +122,7 @@ static Vector< String > parseFunctionArgumentNames(const EngineFunctionInfo* fun // Parse out name. const char* end = ptr + 1; - while (ptr > prototype && dIsalnum(*ptr)) + while (ptr > prototype && (dIsalnum(*ptr) || *ptr == '_')) ptr--; const char* start = ptr + 1; @@ -169,20 +169,19 @@ static Vector< String > parseFunctionArgumentNames(const EngineFunctionInfo* fun } //----------------------------------------------------------------------------- - -static String getDefaultArgumentValue(const EngineFunctionInfo* function, const EngineTypeInfo* type, U32 offset) +static String getValueForType(const EngineTypeInfo* type, void* addr) { String value; - const EngineFunctionDefaultArguments* defaultArgs = function->getDefaultArguments(); +#define ADDRESS_TO_TYPE(tp) *(const tp*)(addr); switch (type->getTypeKind()) { case EngineTypeKindPrimitive: { -#define PRIMTYPE( tp ) \ +#define PRIMTYPE( tp ) \ if( TYPE< tp >() == type ) \ { \ - tp val = getArgValue< tp >( defaultArgs, offset ); \ + tp val = ADDRESS_TO_TYPE(tp); \ value = String::ToString( val ); \ } @@ -195,9 +194,9 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const PRIMTYPE(F64); //TODO: for now we store string literals in ASCII; needs to be sorted out - if (TYPE< const char* >() == type) + if (TYPE< String >() == type || TYPE< const UTF8* >() == type) { - const char* val = reinterpret_cast(defaultArgs->getArgs() + offset); + const UTF8* val = *((const UTF8**)(addr)); value = val; } @@ -207,7 +206,7 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const case EngineTypeKindEnum: { - S32 val = getArgValue< S32 >(defaultArgs, offset); + S32 val = ADDRESS_TO_TYPE(S32); AssertFatal(type->getEnumTable(), "engineXMLExport - Enum type without table!"); const EngineEnumTable& table = *(type->getEnumTable()); @@ -225,7 +224,7 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const case EngineTypeKindBitfield: { - S32 val = getArgValue< S32 >(defaultArgs, offset); + S32 val = ADDRESS_TO_TYPE(S32); AssertFatal(type->getEnumTable(), "engineXMLExport - Bitfield type without table!"); const EngineEnumTable& table = *(type->getEnumTable()); @@ -247,7 +246,24 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const case EngineTypeKindStruct: { - //TODO: struct type default argument values + AssertFatal(type->getFieldTable(), "engineXMLExport - Struct type without table!"); + const EngineFieldTable* fieldTable = type->getFieldTable(); + U32 numFields = fieldTable->getNumFields(); + + + for (int i = 0; i < numFields; ++i) + { + const EngineTypeInfo* fieldType = (*fieldTable)[i].getType(); + U32 fieldOffset = (*fieldTable)[i].getOffset(); + AssertFatal((*fieldTable)[i].getNumElements() == 1, "engineXMLExport - numElements != 1 not supported currently."); + if (i == 0) { + value = getValueForType(fieldType, (void*)((size_t)addr + fieldOffset)); + } + else { + value += " " + getValueForType(fieldType, (void*)((size_t)addr + fieldOffset)); + } + } + break; } @@ -257,7 +273,7 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const // For these two kinds, we support "null" as the only valid // default value. - const void* ptr = getArgValue< const void* >(defaultArgs, offset); + const void* ptr = ADDRESS_TO_TYPE(void*); if (!ptr) value = "null"; break; @@ -267,11 +283,20 @@ static String getDefaultArgumentValue(const EngineFunctionInfo* function, const break; } +#undef ADDRESS_TO_TYPE return value; } //----------------------------------------------------------------------------- +static String getDefaultArgumentValue(const EngineFunctionInfo* function, const EngineTypeInfo* type, U32 idx) +{ + const EngineFunctionDefaultArguments* defaultArgs = function->getDefaultArguments(); + return getValueForType(type, (void*)(defaultArgs->mFirst + defaultArgs->mOffsets[idx])); +} + +//----------------------------------------------------------------------------- + static void exportFunction(const EngineFunctionInfo* function, SimXMLDocument* xml) { if (isExportFiltered(function)) @@ -295,9 +320,6 @@ static void exportFunction(const EngineFunctionInfo* function, SimXMLDocument* x Vector< String > argumentNames = parseFunctionArgumentNames(function); const U32 numArgumentNames = argumentNames.size(); - // Accumulated offset in function argument frame vector. - U32 argFrameOffset = 0; - for (U32 i = 0; i < numArguments; ++i) { xml->pushNewElement("EngineFunctionArgument"); @@ -313,21 +335,17 @@ static void exportFunction(const EngineFunctionInfo* function, SimXMLDocument* x if (i >= firstDefaultArg) { - String defaultValue = getDefaultArgumentValue(function, type, argFrameOffset); + String defaultValue = getDefaultArgumentValue(function, type, i); xml->setAttribute("defaultValue", defaultValue); } + // A bit hacky, default arguments have all offsets. + if (function->getDefaultArguments() != NULL) + { + xml->setAttribute("offset", String::ToString(function->getDefaultArguments()->mOffsets[i])); + } + xml->popElement(); - - if (type->getTypeKind() == EngineTypeKindStruct) - argFrameOffset += type->getInstanceSize(); - else - argFrameOffset += type->getValueSize(); - -#ifdef _PACK_BUG_WORKAROUNDS - if (argFrameOffset % 4 > 0) - argFrameOffset += 4 - (argFrameOffset % 4); -#endif } xml->popElement(); @@ -579,4 +597,4 @@ DefineEngineFunction(exportEngineAPIToXML, SimXMLDocument*, (), , exportScope(EngineExportScope::getGlobalScope(), xml, true); return xml; -} \ No newline at end of file +} diff --git a/Engine/source/console/fixedTuple.h b/Engine/source/console/fixedTuple.h index 871cd7011..60b43be1f 100644 --- a/Engine/source/console/fixedTuple.h +++ b/Engine/source/console/fixedTuple.h @@ -22,6 +22,9 @@ #ifndef _FIXEDTUPLE_H_ #define _FIXEDTUPLE_H_ + +#include "engineTypes.h" + /// @name Fixed-layout tuple definition /// These structs and templates serve as a way to pass arguments from external /// applications and into the T3D console system. @@ -113,6 +116,21 @@ struct fixed_tuple_accessor<0> } }; +#pragma warning( push ) +#pragma warning( disable : 4267 ) +template +static U32 fixed_tuple_offset(fixed_tuple& t) +{ + return (U32)((size_t)& fixed_tuple_accessor::get(t)) - ((size_t)& t); +} + +template +static U32 fixed_tuple_offset(const fixed_tuple& t) +{ + return (U32)((size_t)& fixed_tuple_accessor::get(t)) - ((size_t)& t); +} +#pragma warning(pop) + template< typename T1, typename T2 > struct fixed_tuple_mutator {}; @@ -150,4 +168,4 @@ struct fixed_tuple_mutator /// @} -#endif // !_FIXEDTUPLE_H_ \ No newline at end of file +#endif // !_FIXEDTUPLE_H_