/* ** dobjtype.cpp ** Implements the type information class ** **--------------------------------------------------------------------------- ** Copyright 1998-2010 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ // HEADER FILES ------------------------------------------------------------ #include #include #include "dobject.h" #include "i_system.h" #include "serializer.h" #include "actor.h" #include "templates.h" #include "autosegs.h" #include "v_text.h" #include "a_pickups.h" #include "d_player.h" #include "doomerrors.h" #include "fragglescript/t_fs.h" #include "a_keys.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- EXTERN_CVAR(Bool, strictdecorate); // PUBLIC DATA DEFINITIONS ------------------------------------------------- FMemArena ClassDataAllocator(32768); // use this for all static class data that can be released in bulk when the type system is shut down. FTypeTable TypeTable; TArray PClass::AllClasses; TArray PClass::FunctionPtrList; bool PClass::bShutdown; bool PClass::bVMOperational; PErrorType *TypeError; PErrorType *TypeAuto; PVoidType *TypeVoid; PInt *TypeSInt8, *TypeUInt8; PInt *TypeSInt16, *TypeUInt16; PInt *TypeSInt32, *TypeUInt32; PBool *TypeBool; PFloat *TypeFloat32, *TypeFloat64; PString *TypeString; PName *TypeName; PSound *TypeSound; PColor *TypeColor; PTextureID *TypeTextureID; PSpriteID *TypeSpriteID; PStatePointer *TypeState; PPointer *TypeFont; PStateLabel *TypeStateLabel; PStruct *TypeVector2; PStruct *TypeVector3; PStruct *TypeColorStruct; PStruct *TypeStringStruct; PPointer *TypeNullPtr; PPointer *TypeVoidPtr; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // A harmless non-nullptr FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; // CODE -------------------------------------------------------------------- IMPLEMENT_CLASS(PErrorType, false, false) IMPLEMENT_CLASS(PVoidType, false, false) void DumpTypeTable() { int used = 0; int min = INT_MAX; int max = 0; int all = 0; int lens[10] = {0}; for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) { int len = 0; Printf("%4zu:", i); for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) { Printf(" -> %s", ty->DescriptiveName()); len++; all++; } if (len != 0) { used++; if (len < min) min = len; if (len > max) max = len; } if (len < (int)countof(lens)) { lens[len]++; } Printf("\n"); } Printf("Used buckets: %d/%lu (%.2f%%) for %d entries\n", used, countof(TypeTable.TypeHash), double(used)/countof(TypeTable.TypeHash)*100, all); Printf("Min bucket size: %d\n", min); Printf("Max bucket size: %d\n", max); Printf("Avg bucket size: %.2f\n", double(all) / used); int j,k; for (k = countof(lens)-1; k > 0; --k) if (lens[k]) break; for (j = 0; j <= k; ++j) Printf("Buckets of len %d: %d (%.2f%%)\n", j, lens[j], j!=0?double(lens[j])/used*100:-1.0); } /* PType ******************************************************************/ IMPLEMENT_CLASS(PType, true, false) //========================================================================== // // PType Parameterized Constructor // //========================================================================== PType::PType(unsigned int size, unsigned int align) : Size(size), Align(align), HashNext(nullptr) { mDescriptiveName = "Type"; loadOp = OP_NOP; storeOp = OP_NOP; moveOp = OP_NOP; RegType = REGT_NIL; RegCount = 1; } //========================================================================== // // PType Destructor // //========================================================================== PType::~PType() { } //========================================================================== // // PType :: WriteValue // //========================================================================== void PType::WriteValue(FSerializer &ar, const char *key,const void *addr) const { assert(0 && "Cannot write value for this type"); } //========================================================================== // // PType :: ReadValue // //========================================================================== bool PType::ReadValue(FSerializer &ar, const char *key, void *addr) const { assert(0 && "Cannot read value for this type"); return false; } //========================================================================== // // PType :: SetDefaultValue // //========================================================================== void PType::SetDefaultValue(void *base, unsigned offset, TArray *stroffs) const { } //========================================================================== // // PType :: SetDefaultValue // //========================================================================== void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) const { } void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) const { } //========================================================================== // // PType :: InitializeValue // //========================================================================== void PType::InitializeValue(void *addr, const void *def) const { } //========================================================================== // // PType :: DestroyValue // //========================================================================== void PType::DestroyValue(void *addr) const { } //========================================================================== // // PType :: SetValue // //========================================================================== void PType::SetValue(void *addr, int val) { assert(0 && "Cannot set int value for this type"); } void PType::SetValue(void *addr, double val) { assert(0 && "Cannot set float value for this type"); } //========================================================================== // // PType :: GetValue // //========================================================================== int PType::GetValueInt(void *addr) const { assert(0 && "Cannot get value for this type"); return 0; } double PType::GetValueFloat(void *addr) const { assert(0 && "Cannot get value for this type"); return 0; } //========================================================================== // // PType :: IsMatch // //========================================================================== bool PType::IsMatch(intptr_t id1, intptr_t id2) const { return false; } //========================================================================== // // PType :: GetTypeIDs // //========================================================================== void PType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = 0; id2 = 0; } //========================================================================== // // PType :: GetTypeIDs // //========================================================================== const char *PType::DescriptiveName() const { return mDescriptiveName.GetChars(); } //========================================================================== // // PType :: StaticInit STATIC // //========================================================================== void PType::StaticInit() { // Create types and add them type the type table. TypeTable.AddType(TypeError = new PErrorType); TypeTable.AddType(TypeAuto = new PErrorType(2)); TypeTable.AddType(TypeVoid = new PVoidType); TypeTable.AddType(TypeSInt8 = new PInt(1, false)); TypeTable.AddType(TypeUInt8 = new PInt(1, true)); TypeTable.AddType(TypeSInt16 = new PInt(2, false)); TypeTable.AddType(TypeUInt16 = new PInt(2, true)); TypeTable.AddType(TypeSInt32 = new PInt(4, false)); TypeTable.AddType(TypeUInt32 = new PInt(4, true)); TypeTable.AddType(TypeBool = new PBool); TypeTable.AddType(TypeFloat32 = new PFloat(4)); TypeTable.AddType(TypeFloat64 = new PFloat(8)); TypeTable.AddType(TypeString = new PString); TypeTable.AddType(TypeName = new PName); TypeTable.AddType(TypeSound = new PSound); TypeTable.AddType(TypeColor = new PColor); TypeTable.AddType(TypeState = new PStatePointer); TypeTable.AddType(TypeStateLabel = new PStateLabel); TypeTable.AddType(TypeNullPtr = new PPointer); TypeTable.AddType(TypeSpriteID = new PSpriteID); TypeTable.AddType(TypeTextureID = new PTextureID); TypeVoidPtr = NewPointer(TypeVoid, false); TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value. TypeStringStruct = NewNativeStruct("Stringstruct", nullptr); TypeFont = NewPointer(NewNativeStruct("Font", nullptr)); #ifdef __BIG_ENDIAN__ TypeColorStruct->AddField(NAME_a, TypeUInt8); TypeColorStruct->AddField(NAME_r, TypeUInt8); TypeColorStruct->AddField(NAME_g, TypeUInt8); TypeColorStruct->AddField(NAME_b, TypeUInt8); #else TypeColorStruct->AddField(NAME_b, TypeUInt8); TypeColorStruct->AddField(NAME_g, TypeUInt8); TypeColorStruct->AddField(NAME_r, TypeUInt8); TypeColorStruct->AddField(NAME_a, TypeUInt8); #endif TypeVector2 = new PStruct(NAME_Vector2, nullptr); TypeVector2->AddField(NAME_X, TypeFloat64); TypeVector2->AddField(NAME_Y, TypeFloat64); TypeTable.AddType(TypeVector2); TypeVector2->loadOp = OP_LV2; TypeVector2->storeOp = OP_SV2; TypeVector2->moveOp = OP_MOVEV2; TypeVector2->RegType = REGT_FLOAT; TypeVector2->RegCount = 2; TypeVector3 = new PStruct(NAME_Vector3, nullptr); TypeVector3->AddField(NAME_X, TypeFloat64); TypeVector3->AddField(NAME_Y, TypeFloat64); TypeVector3->AddField(NAME_Z, TypeFloat64); // allow accessing xy as a vector2. This is not supposed to be serialized so it's marked transient TypeVector3->Symbols.AddSymbol(new PField(NAME_XY, TypeVector2, VARF_Transient, 0)); TypeTable.AddType(TypeVector3); TypeVector3->loadOp = OP_LV3; TypeVector3->storeOp = OP_SV3; TypeVector3->moveOp = OP_MOVEV3; TypeVector3->RegType = REGT_FLOAT; TypeVector3->RegCount = 3; Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_sByte, TypeSInt8)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Byte, TypeUInt8)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Short, TypeSInt16)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uShort, TypeUInt16)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Int, TypeSInt32)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_uInt, TypeUInt32)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Bool, TypeBool)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float, TypeFloat64)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Double, TypeFloat64)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float32, TypeFloat32)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Float64, TypeFloat64)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_String, TypeString)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Name, TypeName)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Sound, TypeSound)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Color, TypeColor)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_State, TypeState)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector2, TypeVector2)); Namespaces.GlobalNamespace->Symbols.AddSymbol(new PSymbolType(NAME_Vector3, TypeVector3)); } /* PBasicType *************************************************************/ IMPLEMENT_CLASS(PBasicType, true, false) //========================================================================== // // PBasicType Default Constructor // //========================================================================== PBasicType::PBasicType() { } //========================================================================== // // PBasicType Parameterized Constructor // //========================================================================== PBasicType::PBasicType(unsigned int size, unsigned int align) : PType(size, align) { mDescriptiveName = "BasicType"; } /* PCompoundType **********************************************************/ IMPLEMENT_CLASS(PCompoundType, true, false) /* PNamedType *************************************************************/ IMPLEMENT_CLASS(PNamedType, true, false) //========================================================================== // // PNamedType :: IsMatch // //========================================================================== bool PNamedType::IsMatch(intptr_t id1, intptr_t id2) const { const DObject *outer = (const DObject *)id1; FName name = (ENamedName)(intptr_t)id2; return Outer == outer && TypeName == name; } //========================================================================== // // PNamedType :: GetTypeIDs // //========================================================================== void PNamedType::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)Outer; id2 = TypeName; } /* PInt *******************************************************************/ IMPLEMENT_CLASS(PInt, false, false) //========================================================================== // // PInt Default Constructor // //========================================================================== PInt::PInt() : PBasicType(4, 4), Unsigned(false), IntCompatible(true) { mDescriptiveName = "SInt32"; Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, -0x7FFFFFFF - 1)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, 0x7FFFFFFF)); SetOps(); } //========================================================================== // // PInt Parameterized Constructor // //========================================================================== PInt::PInt(unsigned int size, bool unsign, bool compatible) : PBasicType(size, size), Unsigned(unsign), IntCompatible(compatible) { mDescriptiveName.Format("%cInt%d", unsign? 'U':'S', size); MemberOnly = (size < 4); if (!unsign) { int maxval = (1u << ((8 * size) - 1)) - 1; // compute as unsigned to prevent overflow before -1 int minval = -maxval - 1; Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, minval)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, maxval)); } else { Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, 0u)); Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Max, this, (1u << ((8 * size) - 1)))); } SetOps(); } void PInt::SetOps() { moveOp = OP_MOVE; RegType = REGT_INT; if (Size == 4) { storeOp = OP_SW; loadOp = OP_LW; } else if (Size == 1) { storeOp = OP_SB; loadOp = Unsigned ? OP_LBU : OP_LB; } else if (Size == 2) { storeOp = OP_SH; loadOp = Unsigned ? OP_LHU : OP_LH; } else { assert(0 && "Unhandled integer size"); storeOp = OP_NOP; } } //========================================================================== // // PInt :: WriteValue // //========================================================================== void PInt::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (Size == 8 && Unsigned) { // this is a special case that cannot be represented by an int64_t. uint64_t val = *(uint64_t*)addr; ar(key, val); } else { int64_t val; switch (Size) { case 1: val = Unsigned ? *(uint8_t*)addr : *(int8_t*)addr; break; case 2: val = Unsigned ? *(uint16_t*)addr : *(int16_t*)addr; break; case 4: val = Unsigned ? *(uint32_t*)addr : *(int32_t*)addr; break; case 8: val = *(int64_t*)addr; break; default: return; // something invalid } ar(key, val); } } //========================================================================== // // PInt :: ReadValue // //========================================================================== bool PInt::ReadValue(FSerializer &ar, const char *key, void *addr) const { NumericValue val; ar(key, val); if (val.type == NumericValue::NM_invalid) return false; // not found or usable if (val.type == NumericValue::NM_float) val.signedval = (int64_t)val.floatval; // No need to check the unsigned state here. Downcasting to smaller types will yield the same result for both. switch (Size) { case 1: *(uint8_t*)addr = (uint8_t)val.signedval; break; case 2: *(uint16_t*)addr = (uint16_t)val.signedval; break; case 4: *(uint32_t*)addr = (uint32_t)val.signedval; break; case 8: *(uint64_t*)addr = (uint64_t)val.signedval; break; default: return false; // something invalid } return true; } //========================================================================== // // PInt :: SetValue // //========================================================================== void PInt::SetValue(void *addr, int val) { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) { *(int *)addr = val; } else if (Size == 1) { *(uint8_t *)addr = val; } else if (Size == 2) { *(uint16_t *)addr = val; } else if (Size == 8) { *(QWORD *)addr = val; } else { assert(0 && "Unhandled integer size"); } } void PInt::SetValue(void *addr, double val) { SetValue(addr, (int)val); } //========================================================================== // // PInt :: GetValueInt // //========================================================================== int PInt::GetValueInt(void *addr) const { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) { return *(int *)addr; } else if (Size == 1) { return Unsigned ? *(uint8_t *)addr : *(SBYTE *)addr; } else if (Size == 2) { return Unsigned ? *(uint16_t *)addr : *(SWORD *)addr; } else if (Size == 8) { // truncated output return (int)*(QWORD *)addr; } else { assert(0 && "Unhandled integer size"); return 0; } } //========================================================================== // // PInt :: GetValueFloat // //========================================================================== double PInt::GetValueFloat(void *addr) const { return GetValueInt(addr); } //========================================================================== // // PInt :: GetStoreOp // //========================================================================== /* PBool ******************************************************************/ IMPLEMENT_CLASS(PBool, false, false) //========================================================================== // // PBool Default Constructor // //========================================================================== PBool::PBool() : PInt(sizeof(bool), true) { mDescriptiveName = "Bool"; MemberOnly = false; } /* PFloat *****************************************************************/ IMPLEMENT_CLASS(PFloat, false, false) //========================================================================== // // PFloat Default Constructor // //========================================================================== PFloat::PFloat() : PBasicType(8, 8) { mDescriptiveName = "Float"; SetDoubleSymbols(); SetOps(); } //========================================================================== // // PFloat Parameterized Constructor // //========================================================================== PFloat::PFloat(unsigned int size) : PBasicType(size, size) { mDescriptiveName.Format("Float%d", size); if (size == 8) { SetDoubleSymbols(); } else { assert(size == 4); MemberOnly = true; SetSingleSymbols(); } SetOps(); } //========================================================================== // // PFloat :: SetDoubleSymbols // // Setup constant values for 64-bit floats. // //========================================================================== void PFloat::SetDoubleSymbols() { static const SymbolInitF symf[] = { { NAME_Min_Normal, DBL_MIN }, { NAME_Max, DBL_MAX }, { NAME_Epsilon, DBL_EPSILON }, { NAME_NaN, std::numeric_limits::quiet_NaN() }, { NAME_Infinity, std::numeric_limits::infinity() }, { NAME_Min_Denormal, std::numeric_limits::denorm_min() } }; static const SymbolInitI symi[] = { { NAME_Dig, DBL_DIG }, { NAME_Min_Exp, DBL_MIN_EXP }, { NAME_Max_Exp, DBL_MAX_EXP }, { NAME_Mant_Dig, DBL_MANT_DIG }, { NAME_Min_10_Exp, DBL_MIN_10_EXP }, { NAME_Max_10_Exp, DBL_MAX_10_EXP } }; SetSymbols(symf, countof(symf)); SetSymbols(symi, countof(symi)); } //========================================================================== // // PFloat :: SetSingleSymbols // // Setup constant values for 32-bit floats. // //========================================================================== void PFloat::SetSingleSymbols() { static const SymbolInitF symf[] = { { NAME_Min_Normal, FLT_MIN }, { NAME_Max, FLT_MAX }, { NAME_Epsilon, FLT_EPSILON }, { NAME_NaN, std::numeric_limits::quiet_NaN() }, { NAME_Infinity, std::numeric_limits::infinity() }, { NAME_Min_Denormal, std::numeric_limits::denorm_min() } }; static const SymbolInitI symi[] = { { NAME_Dig, FLT_DIG }, { NAME_Min_Exp, FLT_MIN_EXP }, { NAME_Max_Exp, FLT_MAX_EXP }, { NAME_Mant_Dig, FLT_MANT_DIG }, { NAME_Min_10_Exp, FLT_MIN_10_EXP }, { NAME_Max_10_Exp, FLT_MAX_10_EXP } }; SetSymbols(symf, countof(symf)); SetSymbols(symi, countof(symi)); } //========================================================================== // // PFloat :: SetSymbols // //========================================================================== void PFloat::SetSymbols(const PFloat::SymbolInitF *sym, size_t count) { for (size_t i = 0; i < count; ++i) { Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); } } void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) { for (size_t i = 0; i < count; ++i) { Symbols.AddSymbol(new PSymbolConstNumeric(sym[i].Name, this, sym[i].Value)); } } //========================================================================== // // PFloat :: WriteValue // //========================================================================== void PFloat::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (Size == 8) { ar(key, *(double*)addr); } else { ar(key, *(float*)addr); } } //========================================================================== // // PFloat :: ReadValue // //========================================================================== bool PFloat::ReadValue(FSerializer &ar, const char *key, void *addr) const { NumericValue val; ar(key, val); if (val.type == NumericValue::NM_invalid) return false; // not found or usable else if (val.type == NumericValue::NM_signed) val.floatval = (double)val.signedval; else if (val.type == NumericValue::NM_unsigned) val.floatval = (double)val.unsignedval; if (Size == 8) { *(double*)addr = val.floatval; } else { *(float*)addr = (float)val.floatval; } return true; } //========================================================================== // // PFloat :: SetValue // //========================================================================== void PFloat::SetValue(void *addr, int val) { return SetValue(addr, (double)val); } void PFloat::SetValue(void *addr, double val) { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) { *(float *)addr = (float)val; } else { assert(Size == 8); *(double *)addr = val; } } //========================================================================== // // PFloat :: GetValueInt // //========================================================================== int PFloat::GetValueInt(void *addr) const { return xs_ToInt(GetValueFloat(addr)); } //========================================================================== // // PFloat :: GetValueFloat // //========================================================================== double PFloat::GetValueFloat(void *addr) const { assert(((intptr_t)addr & (Align - 1)) == 0 && "unaligned address"); if (Size == 4) { return *(float *)addr; } else { assert(Size == 8); return *(double *)addr; } } //========================================================================== // // PFloat :: GetStoreOp // //========================================================================== void PFloat::SetOps() { if (Size == 4) { storeOp = OP_SSP; loadOp = OP_LSP; } else { assert(Size == 8); storeOp = OP_SDP; loadOp = OP_LDP; } moveOp = OP_MOVEF; RegType = REGT_FLOAT; } /* PString ****************************************************************/ IMPLEMENT_CLASS(PString, false, false) //========================================================================== // // PString Default Constructor // //========================================================================== PString::PString() : PBasicType(sizeof(FString), alignof(FString)) { mDescriptiveName = "String"; storeOp = OP_SS; loadOp = OP_LS; moveOp = OP_MOVES; RegType = REGT_STRING; } //========================================================================== // // PString :: WriteValue // //========================================================================== void PString::WriteValue(FSerializer &ar, const char *key,const void *addr) const { ar(key, *(FString*)addr); } //========================================================================== // // PString :: ReadValue // //========================================================================== bool PString::ReadValue(FSerializer &ar, const char *key, void *addr) const { const char *cptr; ar.StringPtr(key, cptr); if (cptr == nullptr) { return false; } else { *(FString*)addr = cptr; return true; } } //========================================================================== // // PString :: SetDefaultValue // //========================================================================== void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) const { if (base != nullptr) new((uint8_t *)base + offset) FString; if (special != nullptr) { special->Push(std::make_pair(this, offset)); } } //========================================================================== // // PString :: InitializeValue // //========================================================================== void PString::InitializeValue(void *addr, const void *def) const { if (def != nullptr) { new(addr) FString(*(FString *)def); } else { new(addr) FString; } } //========================================================================== // // PString :: DestroyValue // //========================================================================== void PString::DestroyValue(void *addr) const { ((FString *)addr)->~FString(); } /* PName ******************************************************************/ IMPLEMENT_CLASS(PName, false, false) //========================================================================== // // PName Default Constructor // //========================================================================== PName::PName() : PInt(sizeof(FName), true, false) { mDescriptiveName = "Name"; assert(sizeof(FName) == alignof(FName)); } //========================================================================== // // PName :: WriteValue // //========================================================================== void PName::WriteValue(FSerializer &ar, const char *key,const void *addr) const { const char *cptr = ((const FName*)addr)->GetChars(); ar.StringPtr(key, cptr); } //========================================================================== // // PName :: ReadValue // //========================================================================== bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const { const char *cptr; ar.StringPtr(key, cptr); if (cptr == nullptr) { return false; } else { *(FName*)addr = FName(cptr); return true; } } /* PSpriteID ******************************************************************/ IMPLEMENT_CLASS(PSpriteID, false, false) //========================================================================== // // PName Default Constructor // //========================================================================== PSpriteID::PSpriteID() : PInt(sizeof(int), true, true) { mDescriptiveName = "SpriteID"; } //========================================================================== // // PName :: WriteValue // //========================================================================== void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const { int32_t val = *(int*)addr; ar.Sprite(key, val, nullptr); } //========================================================================== // // PName :: ReadValue // //========================================================================== bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const { int32_t val; ar.Sprite(key, val, nullptr); *(int*)addr = val; return true; } /* PTextureID ******************************************************************/ IMPLEMENT_CLASS(PTextureID, false, false) //========================================================================== // // PTextureID Default Constructor // //========================================================================== PTextureID::PTextureID() : PInt(sizeof(FTextureID), true, false) { mDescriptiveName = "TextureID"; assert(sizeof(FTextureID) == alignof(FTextureID)); } //========================================================================== // // PTextureID :: WriteValue // //========================================================================== void PTextureID::WriteValue(FSerializer &ar, const char *key, const void *addr) const { FTextureID val = *(FTextureID*)addr; ar(key, val); } //========================================================================== // // PTextureID :: ReadValue // //========================================================================== bool PTextureID::ReadValue(FSerializer &ar, const char *key, void *addr) const { FTextureID val; ar(key, val); *(FTextureID*)addr = val; return true; } /* PSound *****************************************************************/ IMPLEMENT_CLASS(PSound, false, false) //========================================================================== // // PSound Default Constructor // //========================================================================== PSound::PSound() : PInt(sizeof(FSoundID), true) { mDescriptiveName = "Sound"; assert(sizeof(FSoundID) == alignof(FSoundID)); } //========================================================================== // // PSound :: WriteValue // //========================================================================== void PSound::WriteValue(FSerializer &ar, const char *key,const void *addr) const { const char *cptr = *(const FSoundID *)addr; ar.StringPtr(key, cptr); } //========================================================================== // // PSound :: ReadValue // //========================================================================== bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const { const char *cptr; ar.StringPtr(key, cptr); if (cptr == nullptr) { return false; } else { *(FSoundID *)addr = FSoundID(cptr); return true; } } /* PColor *****************************************************************/ IMPLEMENT_CLASS(PColor, false, false) //========================================================================== // // PColor Default Constructor // //========================================================================== PColor::PColor() : PInt(sizeof(PalEntry), true) { mDescriptiveName = "Color"; assert(sizeof(PalEntry) == alignof(PalEntry)); } /* PStateLabel *****************************************************************/ IMPLEMENT_CLASS(PStateLabel, false, false) //========================================================================== // // PStateLabel Default Constructor // //========================================================================== PStateLabel::PStateLabel() : PInt(sizeof(int), false, false) { mDescriptiveName = "StateLabel"; } /* PPointer ***************************************************************/ IMPLEMENT_CLASS(PPointer, false, false) //========================================================================== // // PPointer - Default Constructor // //========================================================================== PPointer::PPointer() : PBasicType(sizeof(void *), alignof(void *)), PointedType(nullptr), IsConst(false) { mDescriptiveName = "NullPointer"; SetOps(); } //========================================================================== // // PPointer - Parameterized Constructor // //========================================================================== PPointer::PPointer(PType *pointsat, bool isconst) : PBasicType(sizeof(void *), alignof(void *)), PointedType(pointsat), IsConst(isconst) { mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst? "readonly " : ""); mVersion = pointsat->mVersion; SetOps(); } //========================================================================== // // PPointer :: GetStoreOp // //========================================================================== void PPointer::SetOps() { loadOp = (PointedType && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) ? OP_LO : OP_LP; // Non-destroyed thinkers are always guaranteed to be linked into the thinker chain so we don't need the write barrier for them. storeOp = (loadOp == OP_LO && !static_cast(PointedType)->IsDescendantOf(RUNTIME_CLASS(DThinker))) ? OP_SO : OP_SP; moveOp = OP_MOVEA; RegType = REGT_POINTER; } //========================================================================== // // PPointer :: IsMatch // //========================================================================== bool PPointer::IsMatch(intptr_t id1, intptr_t id2) const { assert(id2 == 0 || id2 == 1); PType *pointat = (PType *)id1; return pointat == PointedType && (!!id2) == IsConst; } //========================================================================== // // PPointer :: GetTypeIDs // //========================================================================== void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)PointedType; id2 = 0; } //========================================================================== // // PPointer :: SetPointer // //========================================================================== void PPointer::SetPointer(void *base, unsigned offset, TArray *special) const { if (PointedType != nullptr && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { // Add to the list of pointers for this class. special->Push(offset); } } //========================================================================== // // PPointer :: WriteValue // //========================================================================== void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { auto pt = static_cast(PointedType); if (pt->IsDescendantOf(RUNTIME_CLASS(PClass))) { ar(key, *(PClass **)addr); } else { ar(key, *(DObject **)addr); } } else if (writer != nullptr) { writer(ar, key, addr); } else { I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName()); } } //========================================================================== // // PPointer :: ReadValue // //========================================================================== bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const { if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { auto pt = static_cast(PointedType); bool res = true; if (pt->IsDescendantOf(RUNTIME_CLASS(PClass))) { ::Serialize(ar, key, *(PClass **)addr, (PClass**)nullptr); } else { ::Serialize(ar, key, *(DObject **)addr, nullptr, &res); } return res; } else if (reader != nullptr) { return reader(ar, key, addr); } return false; } //========================================================================== // // NewPointer // // Returns a PPointer to an object of the specified type // //========================================================================== PPointer *NewPointer(PType *type, bool isconst) { size_t bucket; PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &bucket); if (ptype == nullptr) { ptype = new PPointer(type, isconst); TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket); } return static_cast(ptype); } /* PStatePointer **********************************************************/ IMPLEMENT_CLASS(PStatePointer, false, false) //========================================================================== // // PStatePointer Default Constructor // //========================================================================== PStatePointer::PStatePointer() { mDescriptiveName = "Pointer"; PointedType = NewNativeStruct(NAME_State, nullptr); IsConst = true; } //========================================================================== // // PStatePointer :: WriteValue // //========================================================================== void PStatePointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const { ar(key, *(FState **)addr); } //========================================================================== // // PStatePointer :: ReadValue // //========================================================================== bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const { bool res = false; ::Serialize(ar, key, *(FState **)addr, nullptr, &res); return res; } /* PClassPointer **********************************************************/ IMPLEMENT_CLASS(PClassPointer,false, false) //========================================================================== // // PClassPointer - Parameterized Constructor // //========================================================================== PClassPointer::PClassPointer(PClass *restrict) : PPointer(RUNTIME_CLASS(PClass)), ClassRestriction(restrict) { if (restrict) mDescriptiveName.Format("ClassPointer<%s>", restrict->TypeName.GetChars()); else mDescriptiveName = "ClassPointer"; // class pointers do not need write barriers because all classes are stored in the global type table and won't get collected. // This means we can use the cheapoer non-barriered opcodes here. loadOp = OP_LOS; storeOp = OP_SP; mVersion = restrict->mVersion; } //========================================================================== // // PClassPointer - isCompatible // //========================================================================== bool PClassPointer::isCompatible(PType *type) { auto other = dyn_cast(type); return (other != nullptr && other->ClassRestriction->IsDescendantOf(ClassRestriction)); } //========================================================================== // // PClassPointer :: SetPointer // //========================================================================== void PClassPointer::SetPointer(void *base, unsigned offset, TArray *special) const { // Class pointers do not get added to FlatPointers because they are released from the GC. } //========================================================================== // // PClassPointer :: IsMatch // //========================================================================== bool PClassPointer::IsMatch(intptr_t id1, intptr_t id2) const { const PType *pointat = (const PType *)id1; const PClass *classat = (const PClass *)id2; assert(pointat->IsKindOf(RUNTIME_CLASS(PClass))); return classat == ClassRestriction; } //========================================================================== // // PClassPointer :: GetTypeIDs // //========================================================================== void PClassPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { assert(PointedType == RUNTIME_CLASS(PClass)); id1 = (intptr_t)PointedType; id2 = (intptr_t)ClassRestriction; } //========================================================================== // // NewClassPointer // // Returns a PClassPointer for the restricted type. // //========================================================================== PClassPointer *NewClassPointer(PClass *restrict) { size_t bucket; PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, &bucket); if (ptype == nullptr) { ptype = new PClassPointer(restrict); TypeTable.AddType(ptype, RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, bucket); } return static_cast(ptype); } /* PEnum ******************************************************************/ IMPLEMENT_CLASS(PEnum, false, false) //========================================================================== // // PEnum - Default Constructor // //========================================================================== PEnum::PEnum() : PInt(4, false) { mDescriptiveName = "Enum"; } //========================================================================== // // PEnum - Parameterized Constructor // //========================================================================== PEnum::PEnum(FName name, PTypeBase *outer) : PInt(4, false) { EnumName = name; Outer = outer; mDescriptiveName.Format("Enum<%s>", name.GetChars()); } //========================================================================== // // NewEnum // // Returns a PEnum for the given name and container, making sure not to // create duplicates. // //========================================================================== PEnum *NewEnum(FName name, PTypeBase *outer) { size_t bucket; if (outer == nullptr) outer = Namespaces.GlobalNamespace; PType *etype = TypeTable.FindType(RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, &bucket); if (etype == nullptr) { etype = new PEnum(name, outer); TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket); } return static_cast(etype); } /* PArray *****************************************************************/ IMPLEMENT_CLASS(PArray, false, false) //========================================================================== // // PArray - Default Constructor // //========================================================================== PArray::PArray() : ElementType(nullptr), ElementCount(0) { mDescriptiveName = "Array"; } //========================================================================== // // PArray - Parameterized Constructor // //========================================================================== PArray::PArray(PType *etype, unsigned int ecount) : ElementType(etype), ElementCount(ecount) { mDescriptiveName.Format("Array<%s>[%d]", etype->DescriptiveName(), ecount); Align = etype->Align; // Since we are concatenating elements together, the element size should // also be padded to the nearest alignment. ElementSize = (etype->Size + (etype->Align - 1)) & ~(etype->Align - 1); Size = ElementSize * ecount; } //========================================================================== // // PArray :: IsMatch // //========================================================================== bool PArray::IsMatch(intptr_t id1, intptr_t id2) const { const PType *elemtype = (const PType *)id1; unsigned int count = (unsigned int)(intptr_t)id2; return elemtype == ElementType && count == ElementCount; } //========================================================================== // // PArray :: GetTypeIDs // //========================================================================== void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)ElementType; id2 = ElementCount; } //========================================================================== // // PArray :: WriteValue // //========================================================================== void PArray::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (ar.BeginArray(key)) { const uint8_t *addrb = (const uint8_t *)addr; for (unsigned i = 0; i < ElementCount; ++i) { ElementType->WriteValue(ar, nullptr, addrb); addrb += ElementSize; } ar.EndArray(); } } //========================================================================== // // PArray :: ReadValue // //========================================================================== bool PArray::ReadValue(FSerializer &ar, const char *key, void *addr) const { if (ar.BeginArray(key)) { bool readsomething = false; unsigned count = ar.ArraySize(); unsigned loop = MIN(count, ElementCount); uint8_t *addrb = (uint8_t *)addr; for(unsigned i=0;iReadValue(ar, nullptr, addrb); addrb += ElementSize; } if (loop < count) { DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", count, ElementCount); } ar.EndArray(); return readsomething; } return false; } //========================================================================== // // PArray :: SetDefaultValue // //========================================================================== void PArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const { for (unsigned i = 0; i < ElementCount; ++i) { ElementType->SetDefaultValue(base, offset + i*ElementSize, special); } } //========================================================================== // // PArray :: SetDefaultValue // //========================================================================== void PArray::SetPointer(void *base, unsigned offset, TArray *special) const { for (unsigned i = 0; i < ElementCount; ++i) { ElementType->SetPointer(base, offset + i*ElementSize, special); } } //========================================================================== // // NewArray // // Returns a PArray for the given type and size, making sure not to create // duplicates. // //========================================================================== PArray *NewArray(PType *type, unsigned int count) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket); if (atype == nullptr) { atype = new PArray(type, count); TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket); } return (PArray *)atype; } /* PArray *****************************************************************/ IMPLEMENT_CLASS(PResizableArray, false, false) //========================================================================== // // PArray - Default Constructor // //========================================================================== PResizableArray::PResizableArray() { mDescriptiveName = "ResizableArray"; } //========================================================================== // // PArray - Parameterized Constructor // //========================================================================== PResizableArray::PResizableArray(PType *etype) : PArray(etype, 0) { mDescriptiveName.Format("ResizableArray<%s>", etype->DescriptiveName()); } //========================================================================== // // PArray :: IsMatch // //========================================================================== bool PResizableArray::IsMatch(intptr_t id1, intptr_t id2) const { const PType *elemtype = (const PType *)id1; unsigned int count = (unsigned int)(intptr_t)id2; return elemtype == ElementType && count == 0; } //========================================================================== // // PArray :: GetTypeIDs // //========================================================================== void PResizableArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)ElementType; id2 = 0; } //========================================================================== // // NewResizableArray // // Returns a PArray for the given type and size, making sure not to create // duplicates. // //========================================================================== PResizableArray *NewResizableArray(PType *type) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, &bucket); if (atype == nullptr) { atype = new PResizableArray(type); TypeTable.AddType(atype, RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, bucket); } return (PResizableArray *)atype; } /* PDynArray **************************************************************/ IMPLEMENT_CLASS(PDynArray, false, false) //========================================================================== // // PDynArray - Default Constructor // //========================================================================== PDynArray::PDynArray() : ElementType(nullptr) { mDescriptiveName = "DynArray"; Size = sizeof(FArray); Align = alignof(FArray); } //========================================================================== // // PDynArray - Parameterized Constructor // //========================================================================== PDynArray::PDynArray(PType *etype,PStruct *backing) : ElementType(etype), BackingType(backing) { mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName()); Size = sizeof(FArray); Align = alignof(FArray); } //========================================================================== // // PDynArray :: IsMatch // //========================================================================== bool PDynArray::IsMatch(intptr_t id1, intptr_t id2) const { assert(id2 == 0); const PType *elemtype = (const PType *)id1; return elemtype == ElementType; } //========================================================================== // // PDynArray :: GetTypeIDs // //========================================================================== void PDynArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)ElementType; id2 = 0; } //========================================================================== // // PDynArray :: InitializeValue // //========================================================================== void PDynArray::InitializeValue(void *addr, const void *deff) const { const FArray *def = (const FArray*)deff; FArray *aray = (FArray*)addr; if (def == nullptr || def->Count == 0) { // Empty arrays do not need construction. *aray = { nullptr, 0, 0 }; } else if (ElementType->GetRegType() != REGT_STRING) { // These are just integral values which can be done without any constructor hackery. size_t blocksize = ElementType->Size * def->Count; aray->Array = M_Malloc(blocksize); memcpy(aray->Array, def->Array, blocksize); aray->Most = aray->Count = def->Count; } else { // non-empty string arrays require explicit construction. new(addr) TArray(*(TArray*)def); } } //========================================================================== // // PDynArray :: DestroyValue // //========================================================================== void PDynArray::DestroyValue(void *addr) const { FArray *aray = (FArray*)addr; if (aray->Array != nullptr) { if (ElementType->GetRegType() != REGT_STRING) { M_Free(aray->Array); } else { // Damn those cursed strings again. :( ((TArray*)addr)->~TArray(); } } aray->Count = aray->Most = 0; aray->Array = nullptr; } //========================================================================== // // PDynArray :: SetDefaultValue // //========================================================================== void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const { if (base != nullptr) memset((char*)base + offset, 0, sizeof(FArray)); // same as constructing an empty array. if (special != nullptr) { special->Push(std::make_pair(this, offset)); } } //========================================================================== // // PDynArray :: SetPointer // //========================================================================== void PDynArray::SetPointerArray(void *base, unsigned offset, TArray *special) const { if (ElementType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ElementType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && static_cast(ElementType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { // Add to the list of pointer arrays for this class. special->Push(offset); } } //========================================================================== // // PDynArray :: WriteValue // //========================================================================== void PDynArray::WriteValue(FSerializer &ar, const char *key, const void *addr) const { FArray *aray = (FArray*)addr; if (aray->Count > 0) { if (ar.BeginArray(key)) { const uint8_t *addrb = (const uint8_t *)aray->Array; for (unsigned i = 0; i < aray->Count; ++i) { ElementType->WriteValue(ar, nullptr, addrb); addrb += ElementType->Size; } ar.EndArray(); } } } //========================================================================== // // PDynArray :: ReadValue // //========================================================================== bool PDynArray::ReadValue(FSerializer &ar, const char *key, void *addr) const { FArray *aray = (FArray*)addr; DestroyValue(addr); // note that even after calling this we still got a validly constructed empty array. if (ar.BeginArray(key)) { bool readsomething = false; unsigned count = ar.ArraySize(); size_t blocksize = ElementType->Size * count; aray->Array = M_Malloc(blocksize); memset(aray->Array, 0, blocksize); aray->Most = aray->Count = count; uint8_t *addrb = (uint8_t *)aray->Array; for (unsigned i = 0; iGetRegType() == REGT_STRING) new(addrb) FString; readsomething |= ElementType->ReadValue(ar, nullptr, addrb); addrb += ElementType->Size; } ar.EndArray(); return readsomething; } return false; } //========================================================================== // // NewDynArray // // Creates a new DynArray of the given type, making sure not to create a // duplicate. // //========================================================================== PDynArray *NewDynArray(PType *type) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); if (atype == nullptr) { FString backingname; switch (type->GetRegType()) { case REGT_INT: backingname.Format("DynArray_I%d", type->Size * 8); break; case REGT_FLOAT: backingname.Format("DynArray_F%d", type->Size * 8); break; case REGT_STRING: backingname = "DynArray_String"; break; case REGT_POINTER: backingname = "DynArray_Ptr"; break; default: I_Error("Unsupported dynamic array requested"); break; } auto backing = NewNativeStruct(backingname, nullptr); atype = new PDynArray(type, backing); TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket); } return (PDynArray *)atype; } /* PMap *******************************************************************/ IMPLEMENT_CLASS(PMap, false, false) //========================================================================== // // PMap - Default Constructor // //========================================================================== PMap::PMap() : KeyType(nullptr), ValueType(nullptr) { mDescriptiveName = "Map"; Size = sizeof(FMap); Align = alignof(FMap); } //========================================================================== // // PMap - Parameterized Constructor // //========================================================================== PMap::PMap(PType *keytype, PType *valtype) : KeyType(keytype), ValueType(valtype) { mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); Size = sizeof(FMap); Align = alignof(FMap); } //========================================================================== // // PMap :: IsMatch // //========================================================================== bool PMap::IsMatch(intptr_t id1, intptr_t id2) const { const PType *keyty = (const PType *)id1; const PType *valty = (const PType *)id2; return keyty == KeyType && valty == ValueType; } //========================================================================== // // PMap :: GetTypeIDs // //========================================================================== void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)KeyType; id2 = (intptr_t)ValueType; } //========================================================================== // // NewMap // // Returns a PMap for the given key and value types, ensuring not to create // duplicates. // //========================================================================== PMap *NewMap(PType *keytype, PType *valuetype) { size_t bucket; PType *maptype = TypeTable.FindType(RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, &bucket); if (maptype == nullptr) { maptype = new PMap(keytype, valuetype); TypeTable.AddType(maptype, RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, bucket); } return (PMap *)maptype; } /* PStruct ****************************************************************/ IMPLEMENT_CLASS(PStruct, false, false) //========================================================================== // // PStruct - Default Constructor // //========================================================================== PStruct::PStruct() { mDescriptiveName = "Struct"; Size = 0; } //========================================================================== // // PStruct - Parameterized Constructor // //========================================================================== PStruct::PStruct(FName name, PTypeBase *outer) : PNamedType(name, outer) { mDescriptiveName.Format("Struct<%s>", name.GetChars()); Size = 0; HasNativeFields = false; } //========================================================================== // // PStruct :: SetDefaultValue // //========================================================================== void PStruct::SetDefaultValue(void *base, unsigned offset, TArray *special) const { for (const PField *field : Fields) { if (!(field->Flags & VARF_Transient)) { field->Type->SetDefaultValue(base, unsigned(offset + field->Offset), special); } } } //========================================================================== // // PStruct :: SetPointer // //========================================================================== void PStruct::SetPointer(void *base, unsigned offset, TArray *special) const { for (const PField *field : Fields) { if (!(field->Flags & VARF_Transient)) { field->Type->SetPointer(base, unsigned(offset + field->Offset), special); } } } //========================================================================== // // PStruct :: WriteValue // //========================================================================== void PStruct::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (ar.BeginObject(key)) { WriteFields(ar, addr, Fields); ar.EndObject(); } } //========================================================================== // // PStruct :: ReadValue // //========================================================================== bool PStruct::ReadValue(FSerializer &ar, const char *key, void *addr) const { if (ar.BeginObject(key)) { bool ret = ReadFields(ar, addr); ar.EndObject(); return ret; } return false; } //========================================================================== // // PStruct :: WriteFields STATIC // //========================================================================== void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray &fields) { for (unsigned i = 0; i < fields.Size(); ++i) { const PField *field = fields[i]; // Skip fields without or with native serialization if (!(field->Flags & (VARF_Transient|VARF_Meta))) { field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const uint8_t *)addr + field->Offset); } } } //========================================================================== // // PStruct :: ReadFields // //========================================================================== bool PStruct::ReadFields(FSerializer &ar, void *addr) const { bool readsomething = false; bool foundsomething = false; const char *label; while ((label = ar.GetKey())) { foundsomething = true; const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); if (sym == nullptr) { DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n", label, TypeName.GetChars()); } else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) { DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n", label, TypeName.GetChars()); } else if ((static_cast(sym)->Flags & (VARF_Transient | VARF_Meta))) { DPrintf(DMSG_ERROR, "Symbol %s in %s is not a serializable field\n", label, TypeName.GetChars()); } else { readsomething |= static_cast(sym)->Type->ReadValue(ar, nullptr, (uint8_t *)addr + static_cast(sym)->Offset); } } return readsomething || !foundsomething; } //========================================================================== // // PStruct :: AddField // // Appends a new field to the end of a struct. Returns either the new field // or nullptr if a symbol by that name already exists. // //========================================================================== PField *PStruct::AddField(FName name, PType *type, DWORD flags) { PField *field = new PField(name, type, flags); // The new field is added to the end of this struct, alignment permitting. field->Offset = (Size + (type->Align - 1)) & ~(type->Align - 1); // Enlarge this struct to enclose the new field. Size = unsigned(field->Offset + type->Size); // This struct's alignment is the same as the largest alignment of any of // its fields. Align = MAX(Align, type->Align); if (Symbols.AddSymbol(field) == nullptr) { // name is already in use field->Destroy(); return nullptr; } Fields.Push(field); return field; } //========================================================================== // // PStruct :: AddField // // Appends a new native field to the struct. Returns either the new field // or nullptr if a symbol by that name already exists. // //========================================================================== PField *PStruct::AddNativeField(FName name, PType *type, size_t address, DWORD flags, int bitvalue) { PField *field = new PField(name, type, flags|VARF_Native|VARF_Transient, address, bitvalue); if (Symbols.AddSymbol(field) == nullptr) { // name is already in use field->Destroy(); return nullptr; } Fields.Push(field); HasNativeFields = true; return field; } //========================================================================== // // NewStruct // Returns a PStruct for the given name and container, making sure not to // create duplicates. // //========================================================================== PStruct *NewStruct(FName name, PTypeBase *outer) { size_t bucket; if (outer == nullptr) outer = Namespaces.GlobalNamespace; PType *stype = TypeTable.FindType(RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, &bucket); if (stype == nullptr) { stype = new PStruct(name, outer); TypeTable.AddType(stype, RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, bucket); } return static_cast(stype); } /* PNativeStruct ****************************************************************/ IMPLEMENT_CLASS(PNativeStruct, false, false) //========================================================================== // // PNativeStruct - Parameterized Constructor // //========================================================================== PNativeStruct::PNativeStruct(FName name, PTypeBase *outer) : PStruct(name, outer) { mDescriptiveName.Format("NativeStruct<%s>", name.GetChars()); Size = 0; HasNativeFields = true; } //========================================================================== // // NewNativeStruct // Returns a PNativeStruct for the given name and container, making sure not to // create duplicates. // //========================================================================== PNativeStruct *NewNativeStruct(FName name, PTypeBase *outer) { size_t bucket; if (outer == nullptr) outer = Namespaces.GlobalNamespace; PType *stype = TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, &bucket); if (stype == nullptr) { stype = new PNativeStruct(name, outer); TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket); } return static_cast(stype); } /* PField *****************************************************************/ IMPLEMENT_CLASS(PField, false, false) //========================================================================== // // PField - Default Constructor // //========================================================================== PField::PField() : PSymbol(NAME_None), Offset(0), Type(nullptr), Flags(0) { } PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue) : PSymbol(name), Offset(offset), Type(type), Flags(flags) { if (bitvalue != 0) { BitValue = 0; unsigned val = bitvalue; while ((val >>= 1)) BitValue++; if (type->IsA(RUNTIME_CLASS(PInt)) && unsigned(BitValue) < 8u * type->Size) { // map to the single bytes in the actual variable. The internal bit instructions operate on 8 bit values. #ifndef __BIG_ENDIAN__ Offset += BitValue / 8; #else Offset += type->Size - 1 - BitValue / 8; #endif BitValue &= 7; Type = TypeBool; } else { // Just abort. Bit fields should only be defined internally. I_Error("Trying to create an invalid bit field element: %s", name.GetChars()); } } else BitValue = -1; } VersionInfo PField::GetVersion() { VersionInfo Highest = { 0,0,0 }; if (!(Flags & VARF_Deprecated)) Highest = mVersion; if (Type->mVersion > Highest) Highest = Type->mVersion; return Highest; } /* PProperty *****************************************************************/ IMPLEMENT_CLASS(PProperty, false, false) //========================================================================== // // PField - Default Constructor // //========================================================================== PProperty::PProperty() : PSymbol(NAME_None) { } PProperty::PProperty(FName name, TArray &fields) : PSymbol(name) { Variables = std::move(fields); } /* PPrototype *************************************************************/ IMPLEMENT_CLASS(PPrototype, false, false) //========================================================================== // // PPrototype - Default Constructor // //========================================================================== PPrototype::PPrototype() { } //========================================================================== // // PPrototype - Parameterized Constructor // //========================================================================== PPrototype::PPrototype(const TArray &rettypes, const TArray &argtypes) : ArgumentTypes(argtypes), ReturnTypes(rettypes) { } //========================================================================== // // PPrototype :: IsMatch // //========================================================================== bool PPrototype::IsMatch(intptr_t id1, intptr_t id2) const { const TArray *args = (const TArray *)id1; const TArray *rets = (const TArray *)id2; return *args == ArgumentTypes && *rets == ReturnTypes; } //========================================================================== // // PPrototype :: GetTypeIDs // //========================================================================== void PPrototype::GetTypeIDs(intptr_t &id1, intptr_t &id2) const { id1 = (intptr_t)&ArgumentTypes; id2 = (intptr_t)&ReturnTypes; } //========================================================================== // // PPrototype :: PropagateMark // //========================================================================== size_t PPrototype::PropagateMark() { GC::MarkArray(ArgumentTypes); GC::MarkArray(ReturnTypes); return (ArgumentTypes.Size() + ReturnTypes.Size()) * sizeof(void*) + Super::PropagateMark(); } //========================================================================== // // NewPrototype // // Returns a PPrototype for the given return and argument types, making sure // not to create duplicates. // //========================================================================== PPrototype *NewPrototype(const TArray &rettypes, const TArray &argtypes) { size_t bucket; PType *proto = TypeTable.FindType(RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, &bucket); if (proto == nullptr) { proto = new PPrototype(rettypes, argtypes); TypeTable.AddType(proto, RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, bucket); } return static_cast(proto); } /* PClass *****************************************************************/ IMPLEMENT_CLASS(PClass, false, false) //========================================================================== // // PClass :: WriteValue // // Similar to PStruct's version, except it also needs to traverse parent // classes. // //========================================================================== static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr) { if (type != nullptr) { RecurseWriteFields(type->ParentClass, ar, addr); // Don't write this part if it has no non-transient variables for (unsigned i = 0; i < type->Fields.Size(); ++i) { if (!(type->Fields[i]->Flags & (VARF_Transient|VARF_Meta))) { // Tag this section with the class it came from in case // a more-derived class has variables that shadow a less- // derived class. Whether or not that is a language feature // that will actually be allowed remains to be seen. FString key; key.Format("class:%s", type->TypeName.GetChars()); if (ar.BeginObject(key.GetChars())) { PStruct::WriteFields(ar, addr, type->Fields); ar.EndObject(); } break; } } } } void PClass::WriteValue(FSerializer &ar, const char *key,const void *addr) const { if (ar.BeginObject(key)) { RecurseWriteFields(this, ar, addr); ar.EndObject(); } } // Same as WriteValue, but does not create a new object in the serializer // This is so that user variables do not contain unnecessary subblocks. void PClass::WriteAllFields(FSerializer &ar, const void *addr) const { RecurseWriteFields(this, ar, addr); } //========================================================================== // // PClass :: ReadValue // //========================================================================== bool PClass::ReadValue(FSerializer &ar, const char *key, void *addr) const { if (ar.BeginObject(key)) { bool ret = ReadAllFields(ar, addr); ar.EndObject(); return ret; } return true; } bool PClass::ReadAllFields(FSerializer &ar, void *addr) const { bool readsomething = false; bool foundsomething = false; const char *key; key = ar.GetKey(); if (strcmp(key, "classtype")) { // this does not represent a DObject Printf(TEXTCOLOR_RED "trying to read user variables but got a non-object (first key is '%s')", key); ar.mErrors++; return false; } while ((key = ar.GetKey())) { if (strncmp(key, "class:", 6)) { // We have read all user variable blocks. break; } foundsomething = true; PClass *type = PClass::FindClass(key + 6); if (type != nullptr) { // Only read it if the type is related to this one. const PClass *parent; for (parent = this; parent != nullptr; parent = parent->ParentClass) { if (parent == type) { break; } } if (parent != nullptr) { if (ar.BeginObject(nullptr)) { readsomething |= type->ReadFields(ar, addr); ar.EndObject(); } } else { DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", type->TypeName.GetChars(), TypeName.GetChars()); } } else { DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", key+6, TypeName.GetChars()); } } return readsomething || !foundsomething; } //========================================================================== // // cregcmp // // Sorter to keep built-in types in a deterministic order. (Needed?) // //========================================================================== static int cregcmp (const void *a, const void *b) NO_SANITIZE { const PClass *class1 = *(const PClass **)a; const PClass *class2 = *(const PClass **)b; return strcmp(class1->TypeName, class2->TypeName); } //========================================================================== // // PClass :: StaticInit STATIC // // Creates class metadata for all built-in types. // //========================================================================== void PClass::StaticInit () { atterm (StaticShutdown); StaticBootstrap(); Namespaces.GlobalNamespace = Namespaces.NewNamespace(0); FAutoSegIterator probe(CRegHead, CRegTail); while (*++probe != nullptr) { ((ClassReg *)*probe)->RegisterClass (); } // Keep built-in classes in consistant order. I did this before, though // I'm not sure if this is really necessary to maintain any sort of sync. qsort(&AllClasses[0], AllClasses.Size(), sizeof(AllClasses[0]), cregcmp); // Set all symbol table relations here so that they are valid right from the start. for (auto c : AllClasses) { if (c->ParentClass != nullptr) { c->Symbols.SetParentTable(&c->ParentClass->Symbols); } } } //========================================================================== // // PClass :: StaticShutdown STATIC // // Frees all static class data. // //========================================================================== void PClass::StaticShutdown () { // delete all variables containing pointers to script functions. for (auto p : FunctionPtrList) { *p = nullptr; } FunctionPtrList.Clear(); VMFunction::DeleteAll(); // Make a full garbage collection here so that all destroyed but uncollected higher level objects // that still exist are properly taken down before the low level data is deleted. GC::FullGC(); // From this point onward no scripts may be called anymore because the data needed by the VM is getting deleted now. // This flags DObject::Destroy not to call any scripted OnDestroy methods anymore. bVMOperational = false; // PendingWeapon must be cleared manually because it is not subjected to the GC if it contains WP_NOCHANGE, which is just RUNTIME_CLASS(AWWeapon). // But that will get cleared here, confusing the GC if the value is left in. for (auto &p : players) { p.PendingWeapon = nullptr; } // Unless something went wrong, anything left here should be class and type objects only, which do not own any scripts. bShutdown = true; TypeTable.Clear(); Namespaces.ReleaseSymbols(); ClassDataAllocator.FreeAllBlocks(); AllClasses.Clear(); PClassActor::AllActorClasses.Clear(); FAutoSegIterator probe(CRegHead, CRegTail); while (*++probe != nullptr) { auto cr = ((ClassReg *)*probe); cr->MyClass = nullptr; } } //========================================================================== // // PClass :: StaticBootstrap STATIC // //========================================================================== void PClass::StaticBootstrap() { PClass *cls = new PClass; PClass::RegistrationInfo.SetupClass(cls); // The PClassClass constructor initialized these to nullptr, because the // PClass metadata had not been created yet. Now it has, so we know what // they should be and can insert them into the type table successfully. cls->InsertIntoHash(); // Create parent objects before we go so that these definitions are complete. cls->ParentClass = PClass::RegistrationInfo.ParentType->RegisterClass(); } //========================================================================== // // PClass Constructor // //========================================================================== PClass::PClass() { Size = sizeof(DObject); ParentClass = nullptr; Pointers = nullptr; FlatPointers = nullptr; ArrayPointers = nullptr; HashNext = nullptr; Defaults = nullptr; bRuntimeClass = false; bExported = false; bDecorateClass = false; ConstructNative = nullptr; Meta = nullptr; mDescriptiveName = "Class"; PClass::AllClasses.Push(this); } //========================================================================== // // PClass Destructor // //========================================================================== PClass::~PClass() { if (Defaults != nullptr) { M_Free(Defaults); Defaults = nullptr; } if (Meta != nullptr) { M_Free(Meta); Meta = nullptr; } } //========================================================================== // // ClassReg :: RegisterClass // // Create metadata describing the built-in class this struct is intended // for. // //========================================================================== PClass *ClassReg::RegisterClass() { static ClassReg *const metaclasses[] = { &PClass::RegistrationInfo, &PClassActor::RegistrationInfo, }; // Skip classes that have already been registered if (MyClass != nullptr) { return MyClass; } // Add type to list PClass *cls; if (MetaClassNum >= countof(metaclasses)) { assert(0 && "Class registry has an invalid meta class identifier"); } if (metaclasses[MetaClassNum]->MyClass == nullptr) { // Make sure the meta class is already registered before registering this one metaclasses[MetaClassNum]->RegisterClass(); } cls = static_cast(metaclasses[MetaClassNum]->MyClass->CreateNew()); SetupClass(cls); cls->InsertIntoHash(); if (ParentType != nullptr) { cls->ParentClass = ParentType->RegisterClass(); } return cls; } //========================================================================== // // ClassReg :: SetupClass // // Copies the class-defining parameters from a ClassReg to the Class object // created for it. // //========================================================================== void ClassReg::SetupClass(PClass *cls) { assert(MyClass == nullptr); MyClass = cls; cls->TypeName = FName(Name+1); cls->Size = SizeOf; cls->Pointers = Pointers; cls->ConstructNative = ConstructNative; cls->mDescriptiveName.Format("Class<%s>", cls->TypeName.GetChars()); } //========================================================================== // // PClass :: InsertIntoHash // // Add class to the type table. // //========================================================================== void PClass::InsertIntoHash () { size_t bucket; PType *found; found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket); if (found != nullptr) { // This type has already been inserted I_Error("Tried to register class '%s' more than once.\n", TypeName.GetChars()); } else { TypeTable.AddType(this, RUNTIME_CLASS(PClass), 0, TypeName, bucket); } } //========================================================================== // // PClass :: FindParentClass // // Finds a parent class that matches the given name, including itself. // //========================================================================== const PClass *PClass::FindParentClass(FName name) const { for (const PClass *type = this; type != nullptr; type = type->ParentClass) { if (type->TypeName == name) { return type; } } return nullptr; } //========================================================================== // // PClass :: FindClass // // Find a type, passed the name as a name. // //========================================================================== PClass *PClass::FindClass (FName zaname) { if (zaname == NAME_None) { return nullptr; } return static_cast(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, nullptr)); } //========================================================================== // // PClass :: CreateNew // // Create a new object that this class represents // //========================================================================== DObject *PClass::CreateNew() { uint8_t *mem = (uint8_t *)M_Malloc (Size); assert (mem != nullptr); // Set this object's defaults before constructing it. if (Defaults != nullptr) memcpy (mem, Defaults, Size); else memset (mem, 0, Size); if (ConstructNative == nullptr) { I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars()); } ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); InitializeSpecials(mem, Defaults, &PClass::SpecialInits); return (DObject *)mem; } //========================================================================== // // PClass :: InitializeSpecials // // Initialize special fields (e.g. strings) of a newly-created instance. // //========================================================================== void PClass::InitializeSpecials(void *addr, void *defaults, TArray PClass::*Inits) { // Once we reach a native class, we can stop going up the family tree, // since native classes handle initialization natively. if ((!bRuntimeClass && Inits == &PClass::SpecialInits) || ParentClass == nullptr) { return; } ParentClass->InitializeSpecials(addr, defaults, Inits); for (auto tao : (this->*Inits)) { tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second); } } //========================================================================== // // PClass :: DestroySpecials // // Destroy special fields (e.g. strings) of an instance that is about to be // deleted. // //========================================================================== void PClass::DestroySpecials(void *addr) { // Once we reach a native class, we can stop going up the family tree, // since native classes handle deinitialization natively. if (!bRuntimeClass) { return; } assert(ParentClass != nullptr); ParentClass->DestroySpecials(addr); for (auto tao : SpecialInits) { tao.first->DestroyValue((uint8_t *)addr + tao.second); } } //========================================================================== // // PClass :: Derive // // Copies inheritable values into the derived class and other miscellaneous setup. // //========================================================================== void PClass::Derive(PClass *newclass, FName name) { newclass->bRuntimeClass = true; newclass->ParentClass = this; newclass->ConstructNative = ConstructNative; newclass->Symbols.SetParentTable(&this->Symbols); newclass->TypeName = name; newclass->mDescriptiveName.Format("Class<%s>", name.GetChars()); newclass->mVersion = mVersion; newclass->MetaSize = MetaSize; } //========================================================================== // // PClassActor :: InitializeNativeDefaults // //========================================================================== void PClass::InitializeDefaults() { if (IsKindOf(RUNTIME_CLASS(PClassActor))) { assert(Defaults == nullptr); Defaults = (uint8_t *)M_Malloc(Size); // run the constructor on the defaults to set the vtbl pointer which is needed to run class-aware functions on them. // Temporarily setting bSerialOverride prevents linking into the thinker chains. auto s = DThinker::bSerialOverride; DThinker::bSerialOverride = true; ConstructNative(Defaults); DThinker::bSerialOverride = s; // We must unlink the defaults from the class list because it's just a static block of data to the engine. DObject *optr = (DObject*)Defaults; GC::Root = optr->ObjNext; optr->ObjNext = nullptr; optr->SetClass(this); // Copy the defaults from the parent but leave the DObject part alone because it contains important data. if (ParentClass->Defaults != nullptr) { memcpy(Defaults + sizeof(DObject), ParentClass->Defaults + sizeof(DObject), ParentClass->Size - sizeof(DObject)); if (Size > ParentClass->Size) { memset(Defaults + ParentClass->Size, 0, Size - ParentClass->Size); } } else { memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject)); } assert(MetaSize >= ParentClass->MetaSize); if (MetaSize != 0) { Meta = (uint8_t*)M_Malloc(MetaSize); // Copy the defaults from the parent but leave the DObject part alone because it contains important data. if (ParentClass->Meta != nullptr) { memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize); if (MetaSize > ParentClass->MetaSize) { memset(Meta + ParentClass->MetaSize, 0, MetaSize - ParentClass->MetaSize); } } else { memset(Meta, 0, MetaSize); } if (MetaSize > 0) memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize); else memset(Meta, 0, MetaSize); } } if (bRuntimeClass) { // Copy parent values from the parent defaults. assert(ParentClass != nullptr); if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits); if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits); for (const PField *field : Fields) { if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta)) { field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits); } } } if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits); for (const PField *field : Fields) { if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta)) { field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits); } } } //========================================================================== // // PClass :: DeriveData // // Copies inheritable data to the child class. // //========================================================================== void PClass::DeriveData(PClass *newclass) { } //========================================================================== // // PClass :: CreateDerivedClass // // Create a new class based on an existing class // //========================================================================== PClass *PClass::CreateDerivedClass(FName name, unsigned int size) { assert(size >= Size); PClass *type; bool notnew; const PClass *existclass = FindClass(name); if (existclass != nullptr) { // This is a placeholder so fill it in if (existclass->Size == TentativeClass) { type = const_cast(existclass); if (!IsDescendantOf(type->ParentClass)) { I_Error("%s must inherit from %s but doesn't.", name.GetChars(), type->ParentClass->TypeName.GetChars()); } DPrintf(DMSG_SPAMMY, "Defining placeholder class %s\n", name.GetChars()); notnew = true; } else { // a different class with the same name already exists. Let the calling code deal with this. return nullptr; } } else { type = static_cast(GetClass()->CreateNew()); notnew = false; } type->TypeName = name; type->bRuntimeClass = true; Derive(type, name); type->Size = size; if (size != TentativeClass) { type->InitializeDefaults(); type->Virtuals = Virtuals; DeriveData(type); } else type->ObjectFlags &= OF_Transient; if (!notnew) { type->InsertIntoHash(); } return type; } //========================================================================== // // PStruct :: AddField // // Appends a new metadata field to the end of a struct. Returns either the new field // or nullptr if a symbol by that name already exists. // //========================================================================== PField *PClass::AddMetaField(FName name, PType *type, DWORD flags) { PField *field = new PField(name, type, flags); // The new field is added to the end of this struct, alignment permitting. field->Offset = (MetaSize + (type->Align - 1)) & ~(type->Align - 1); // Enlarge this struct to enclose the new field. MetaSize = unsigned(field->Offset + type->Size); // This struct's alignment is the same as the largest alignment of any of // its fields. Align = MAX(Align, type->Align); if (Symbols.AddSymbol(field) == nullptr) { // name is already in use field->Destroy(); return nullptr; } Fields.Push(field); return field; } //========================================================================== // // PClass :: AddField // //========================================================================== PField *PClass::AddField(FName name, PType *type, DWORD flags) { if (!(flags & VARF_Meta)) { unsigned oldsize = Size; PField *field = Super::AddField(name, type, flags); // Only initialize the defaults if they have already been created. // For ZScript this is not the case, it will first define all fields before // setting up any defaults for any class. if (field != nullptr && !(flags & VARF_Native) && Defaults != nullptr) { Defaults = (uint8_t *)M_Realloc(Defaults, Size); memset(Defaults + oldsize, 0, Size - oldsize); } return field; } else { unsigned oldsize = MetaSize; PField *field = AddMetaField(name, type, flags); // Only initialize the defaults if they have already been created. // For ZScript this is not the case, it will first define all fields before // setting up any defaults for any class. if (field != nullptr && !(flags & VARF_Native) && Meta != nullptr) { Meta = (uint8_t *)M_Realloc(Meta, MetaSize); memset(Meta + oldsize, 0, MetaSize - oldsize); } return field; } } //========================================================================== // // PClass :: FindClassTentative // // Like FindClass but creates a placeholder if no class is found. // This will be filled in when the actual class is constructed. // //========================================================================== PClass *PClass::FindClassTentative(FName name) { if (name == NAME_None) { return nullptr; } size_t bucket; PType *found = TypeTable.FindType(RUNTIME_CLASS(PClass), /*FIXME:Outer*/0, name, &bucket); if (found != nullptr) { return static_cast(found); } PClass *type = static_cast(GetClass()->CreateNew()); DPrintf(DMSG_SPAMMY, "Creating placeholder class %s : %s\n", name.GetChars(), TypeName.GetChars()); Derive(type, name); type->Size = TentativeClass; TypeTable.AddType(type, RUNTIME_CLASS(PClass), 0, name, bucket); return type; } //========================================================================== // // PClass :: FindVirtualIndex // // Compares a prototype with the existing list of virtual functions // and returns an index if something matching is found. // //========================================================================== int PClass::FindVirtualIndex(FName name, PPrototype *proto) { for (unsigned i = 0; i < Virtuals.Size(); i++) { if (Virtuals[i]->Name == name) { auto vproto = Virtuals[i]->Proto; if (vproto->ReturnTypes.Size() != proto->ReturnTypes.Size() || vproto->ArgumentTypes.Size() != proto->ArgumentTypes.Size()) { continue; // number of parameters does not match, so it's incompatible } bool fail = false; // The first argument is self and will mismatch so just skip it. for (unsigned a = 1; a < proto->ArgumentTypes.Size(); a++) { if (proto->ArgumentTypes[a] != vproto->ArgumentTypes[a]) { fail = true; break; } } if (fail) continue; for (unsigned a = 0; a < proto->ReturnTypes.Size(); a++) { if (proto->ReturnTypes[a] != vproto->ReturnTypes[a]) { fail = true; break; } } if (!fail) return i; } } return -1; } //========================================================================== // // PClass :: BuildFlatPointers // // Create the FlatPointers array, if it doesn't exist already. // It comprises all the Pointers from superclasses plus this class's own // Pointers. If this class does not define any new Pointers, then // FlatPointers will be set to the same array as the super class. // //========================================================================== void PClass::BuildFlatPointers () { if (FlatPointers != nullptr) { // Already built: Do nothing. return; } else if (ParentClass == nullptr) { // No parent (i.e. DObject: FlatPointers is the same as Pointers. if (Pointers == nullptr) { // No pointers: Make FlatPointers a harmless non-nullptr. FlatPointers = &TheEnd; } else { FlatPointers = Pointers; } } else { ParentClass->BuildFlatPointers (); TArray ScriptPointers; // Collect all pointers in scripted fields. These are not part of the Pointers list. for (auto field : Fields) { if (!(field->Flags & VARF_Native)) { field->Type->SetPointer(Defaults, unsigned(field->Offset), &ScriptPointers); } } if (Pointers == nullptr && ScriptPointers.Size() == 0) { // No new pointers: Just use the same FlatPointers as the parent. FlatPointers = ParentClass->FlatPointers; } else { // New pointers: Create a new FlatPointers array and add them. int numPointers, numSuperPointers; if (Pointers != nullptr) { // Count pointers defined by this class. for (numPointers = 0; Pointers[numPointers] != ~(size_t)0; numPointers++) { } } else numPointers = 0; // Count pointers defined by superclasses. for (numSuperPointers = 0; ParentClass->FlatPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++) { } // Concatenate them into a new array size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numPointers + numSuperPointers + ScriptPointers.Size() + 1)); if (numSuperPointers > 0) { memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers); } if (numPointers > 0) { memcpy(flat + numSuperPointers, Pointers, sizeof(size_t)*numPointers); } if (ScriptPointers.Size() > 0) { memcpy(flat + numSuperPointers + numPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size()); } flat[numSuperPointers + numPointers + ScriptPointers.Size()] = ~(size_t)0; FlatPointers = flat; } } } //========================================================================== // // PClass :: BuildArrayPointers // // same as above, but creates a list to dynamic object arrays // //========================================================================== void PClass::BuildArrayPointers() { if (ArrayPointers != nullptr) { // Already built: Do nothing. return; } else if (ParentClass == nullptr) { // No parent (i.e. DObject: FlatPointers is the same as Pointers. ArrayPointers = &TheEnd; } else { ParentClass->BuildArrayPointers(); TArray ScriptPointers; // Collect all arrays to pointers in scripted fields. for (auto field : Fields) { if (!(field->Flags & VARF_Native)) { field->Type->SetPointerArray(Defaults, unsigned(field->Offset), &ScriptPointers); } } if (ScriptPointers.Size() == 0) { // No new pointers: Just use the same ArrayPointers as the parent. ArrayPointers = ParentClass->ArrayPointers; } else { // New pointers: Create a new FlatPointers array and add them. int numSuperPointers; // Count pointers defined by superclasses. for (numSuperPointers = 0; ParentClass->ArrayPointers[numSuperPointers] != ~(size_t)0; numSuperPointers++) { } // Concatenate them into a new array size_t *flat = (size_t*)ClassDataAllocator.Alloc(sizeof(size_t) * (numSuperPointers + ScriptPointers.Size() + 1)); if (numSuperPointers > 0) { memcpy(flat, ParentClass->ArrayPointers, sizeof(size_t)*numSuperPointers); } if (ScriptPointers.Size() > 0) { memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(size_t) * ScriptPointers.Size()); } flat[numSuperPointers + ScriptPointers.Size()] = ~(size_t)0; ArrayPointers = flat; } } } //========================================================================== // // PClass :: NativeClass // // Finds the native type underlying this class. // //========================================================================== const PClass *PClass::NativeClass() const { const PClass *cls = this; while (cls && cls->bRuntimeClass) cls = cls->ParentClass; return cls; } VMFunction *PClass::FindFunction(FName clsname, FName funcname) { auto cls = PClass::FindClass(clsname); if (!cls) return nullptr; auto func = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); if (!func) return nullptr; return func->Variants[0].Implementation; } void PClass::FindFunction(VMFunction **pptr, FName clsname, FName funcname) { auto cls = PClass::FindClass(clsname); if (!cls) return; auto func = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); if (!func) return; *pptr = func->Variants[0].Implementation; FunctionPtrList.Push(pptr); } /* FTypeTable **************************************************************/ //========================================================================== // // FTypeTable :: FindType // //========================================================================== PType *FTypeTable::FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum) { size_t bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; if (bucketnum != nullptr) { *bucketnum = bucket; } for (PType *type = TypeHash[bucket]; type != nullptr; type = type->HashNext) { if (type->TypeTableType == metatype && type->IsMatch(parm1, parm2)) { return type; } } return nullptr; } //========================================================================== // // FTypeTable :: AddType - Fully Parameterized Version // //========================================================================== void FTypeTable::AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t parm2, size_t bucket) { #ifdef _DEBUG size_t bucketcheck; assert(FindType(metatype, parm1, parm2, &bucketcheck) == nullptr && "Type must not be inserted more than once"); assert(bucketcheck == bucket && "Passed bucket was wrong"); #endif type->TypeTableType = metatype; type->HashNext = TypeHash[bucket]; TypeHash[bucket] = type; type->Release(); } //========================================================================== // // FTypeTable :: AddType - Simple Version // //========================================================================== void FTypeTable::AddType(PType *type) { intptr_t parm1, parm2; size_t bucket; // Type table stuff id only needed to let all classes hash to the same group. For all other types this is pointless. type->TypeTableType = type->GetClass(); PClass *metatype = type->TypeTableType; type->GetTypeIDs(parm1, parm2); bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; assert(FindType(metatype, parm1, parm2, nullptr) == nullptr && "Type must not be inserted more than once"); type->HashNext = TypeHash[bucket]; TypeHash[bucket] = type; type->Release(); } //========================================================================== // // FTypeTable :: Hash STATIC // //========================================================================== size_t FTypeTable::Hash(const PClass *p1, intptr_t p2, intptr_t p3) { size_t i1 = (size_t)p1; // Swap the high and low halves of i1. The compiler should be smart enough // to transform this into a ROR or ROL. i1 = (i1 >> (sizeof(size_t)*4)) | (i1 << (sizeof(size_t)*4)); if (p1 != RUNTIME_CLASS(PPrototype)) { size_t i2 = (size_t)p2; size_t i3 = (size_t)p3; return (~i1 ^ i2) + i3 * 961748927; // i3 is multiplied by a prime } else { // Prototypes need to hash the TArrays at p2 and p3 const TArray *a2 = (const TArray *)p2; const TArray *a3 = (const TArray *)p3; for (unsigned i = 0; i < a2->Size(); ++i) { i1 = (i1 * 961748927) + (size_t)((*a2)[i]); } for (unsigned i = 0; i < a3->Size(); ++i) { i1 = (i1 * 961748927) + (size_t)((*a3)[i]); } return i1; } } //========================================================================== // // FTypeTable :: Clear // //========================================================================== void FTypeTable::Clear() { for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) { for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr;) { auto next = ty->HashNext; delete ty; ty = next; } } memset(TypeHash, 0, sizeof(TypeHash)); } #include "c_dispatch.h" CCMD(typetable) { DumpTypeTable(); }