diff --git a/src/dobject.cpp b/src/dobject.cpp index 10b37f636..63c7bb688 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -397,6 +397,23 @@ size_t DObject::PropagateMark() GC::Mark((DObject **)((BYTE *)this + *offsets)); offsets++; } + + offsets = info->ArrayPointers; + if (offsets == NULL) + { + const_cast(info)->BuildArrayPointers(); + offsets = info->ArrayPointers; + } + while (*offsets != ~(size_t)0) + { + auto aray = (TArray*)((BYTE *)this + *offsets); + for (auto &p : *aray) + { + GC::Mark(&p); + } + offsets++; + } + return info->Size; } return 0; @@ -427,6 +444,28 @@ size_t DObject::PointerSubstitution (DObject *old, DObject *notOld) } offsets++; } + + offsets = info->ArrayPointers; + if (offsets == NULL) + { + const_cast(info)->BuildArrayPointers(); + offsets = info->ArrayPointers; + } + while (*offsets != ~(size_t)0) + { + auto aray = (TArray*)((BYTE *)this + *offsets); + for (auto &p : *aray) + { + if (p == old) + { + p = notOld; + changed++; + } + } + offsets++; + } + + return changed; } diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 958cd4275..034510ff9 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -65,6 +65,7 @@ EXTERN_CVAR(Bool, strictdecorate); // PUBLIC DATA DEFINITIONS ------------------------------------------------- +FMemArena FlatpointerArena; // stores the flat pointers because freeing them individually is rather messy. FNamespaceManager Namespaces; FTypeTable TypeTable; @@ -99,7 +100,7 @@ PPointer *TypeVoidPtr; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -// A harmless non-NULL FlatPointer for classes without pointers. +// A harmless non-nullptr FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; // CODE -------------------------------------------------------------------- @@ -118,7 +119,7 @@ void DumpTypeTable() { int len = 0; Printf("%4zu:", i); - for (PType *ty = TypeTable.TypeHash[i]; ty != NULL; ty = ty->HashNext) + for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) { Printf(" -> %s", ty->DescriptiveName()); len++; @@ -161,7 +162,7 @@ IMPLEMENT_CLASS(PClassType, false, false) //========================================================================== PClassType::PClassType() -: TypeTableType(NULL) +: TypeTableType(nullptr) { } @@ -211,7 +212,7 @@ IMPLEMENT_POINTERS_END //========================================================================== PType::PType(unsigned int size, unsigned int align) -: Size(size), Align(align), HashNext(NULL) +: Size(size), Align(align), HashNext(nullptr) { mDescriptiveName = "Type"; loadOp = OP_NOP; @@ -286,6 +287,10 @@ void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) con { } +void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) const +{ +} + //========================================================================== // // PType :: InitializeValue @@ -831,7 +836,7 @@ PBool::PBool() MemberOnly = false; // Override the default max set by PInt's constructor PSymbolConstNumeric *maxsym = static_cast(Symbols.FindSymbol(NAME_Max, false)); - assert(maxsym != NULL && maxsym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric))); + assert(maxsym != nullptr && maxsym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric))); maxsym->Value = 1; } @@ -1147,7 +1152,7 @@ bool PString::ReadValue(FSerializer &ar, const char *key, void *addr) const void PString::SetDefaultValue(void *base, unsigned offset, TArray *special) const { if (base != nullptr) new((BYTE *)base + offset) FString; - if (special != NULL) + if (special != nullptr) { special->Push(std::make_pair(this, offset)); } @@ -1415,7 +1420,7 @@ IMPLEMENT_POINTERS_END //========================================================================== PPointer::PPointer() -: PBasicType(sizeof(void *), alignof(void *)), PointedType(NULL), IsConst(false) +: PBasicType(sizeof(void *), alignof(void *)), PointedType(nullptr), IsConst(false) { mDescriptiveName = "NullPointer"; SetOps(); @@ -1442,8 +1447,9 @@ PPointer::PPointer(PType *pointsat, bool isconst) void PPointer::SetOps() { - storeOp = OP_SP; 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; } @@ -1476,7 +1482,7 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const //========================================================================== // -// PPointer :: SetDefaultValue +// PPointer :: SetPointer // //========================================================================== @@ -1547,7 +1553,7 @@ 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 == NULL) + if (ptype == nullptr) { ptype = new PPointer(type, isconst); TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket); @@ -1606,18 +1612,6 @@ IMPLEMENT_POINTERS_START(PClassPointer) IMPLEMENT_POINTER(ClassRestriction) IMPLEMENT_POINTERS_END -//========================================================================== -// -// PClassPointer - Default Constructor -// -//========================================================================== - -PClassPointer::PClassPointer() -: PPointer(RUNTIME_CLASS(PClass)), ClassRestriction(NULL) -{ - mDescriptiveName = "ClassPointer"; -} - //========================================================================== // // PClassPointer - Parameterized Constructor @@ -1629,6 +1623,10 @@ PClassPointer::PClassPointer(PClass *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; } //========================================================================== @@ -1683,7 +1681,7 @@ 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 == NULL) + if (ptype == nullptr) { ptype = new PClassPointer(restrict); TypeTable.AddType(ptype, RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, bucket); @@ -1739,7 +1737,7 @@ 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 == NULL) + if (etype == nullptr) { etype = new PEnum(name, outer); TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket); @@ -1762,7 +1760,7 @@ IMPLEMENT_POINTERS_END //========================================================================== PArray::PArray() -: ElementType(NULL), ElementCount(0) +: ElementType(nullptr), ElementCount(0) { mDescriptiveName = "Array"; } @@ -1902,7 +1900,7 @@ PArray *NewArray(PType *type, unsigned int count) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket); - if (atype == NULL) + if (atype == nullptr) { atype = new PArray(type, count); TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket); @@ -1976,7 +1974,7 @@ PResizableArray *NewResizableArray(PType *type) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, &bucket); - if (atype == NULL) + if (atype == nullptr) { atype = new PResizableArray(type); TypeTable.AddType(atype, RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, bucket); @@ -1999,7 +1997,7 @@ IMPLEMENT_POINTERS_END //========================================================================== PDynArray::PDynArray() -: ElementType(NULL) +: ElementType(nullptr) { mDescriptiveName = "DynArray"; Size = sizeof(FArray); @@ -2046,6 +2044,152 @@ void PDynArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const 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 +{ + 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)) && 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 BYTE *addrb = (const BYTE *)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; + + BYTE *addrb = (BYTE *)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 @@ -2059,7 +2203,7 @@ PDynArray *NewDynArray(PType *type) { size_t bucket; PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); - if (atype == NULL) + if (atype == nullptr) { FString backingname; @@ -2109,7 +2253,7 @@ IMPLEMENT_POINTERS_END //========================================================================== PMap::PMap() -: KeyType(NULL), ValueType(NULL) +: KeyType(nullptr), ValueType(nullptr) { mDescriptiveName = "Map"; Size = sizeof(FMap); @@ -2169,7 +2313,7 @@ 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 == NULL) + if (maptype == nullptr) { maptype = new PMap(keytype, valuetype); TypeTable.AddType(maptype, RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, bucket); @@ -2308,7 +2452,7 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const foundsomething = true; const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); - if (sym == NULL) + if (sym == nullptr) { DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n", label, TypeName.GetChars()); @@ -2332,7 +2476,7 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const // PStruct :: AddField // // Appends a new field to the end of a struct. Returns either the new field -// or NULL if a symbol by that name already exists. +// or nullptr if a symbol by that name already exists. // //========================================================================== @@ -2350,10 +2494,10 @@ PField *PStruct::AddField(FName name, PType *type, DWORD flags) // its fields. Align = MAX(Align, type->Align); - if (Symbols.AddSymbol(field) == NULL) + if (Symbols.AddSymbol(field) == nullptr) { // name is already in use delete field; - return NULL; + return nullptr; } Fields.Push(field); @@ -2365,7 +2509,7 @@ PField *PStruct::AddField(FName name, PType *type, DWORD flags) // PStruct :: AddField // // Appends a new native field to the struct. Returns either the new field -// or NULL if a symbol by that name already exists. +// or nullptr if a symbol by that name already exists. // //========================================================================== @@ -2408,7 +2552,7 @@ 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 == NULL) + if (stype == nullptr) { stype = new PStruct(name, outer); TypeTable.AddType(stype, RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, bucket); @@ -2447,7 +2591,7 @@ 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 == NULL) + if (stype == nullptr) { stype = new PNativeStruct(name, outer); TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket); @@ -2466,7 +2610,7 @@ IMPLEMENT_CLASS(PField, false, false) //========================================================================== PField::PField() -: PSymbol(NAME_None), Offset(0), Type(NULL), Flags(0) +: PSymbol(NAME_None), Offset(0), Type(nullptr), Flags(0) { } @@ -2599,7 +2743,7 @@ PPrototype *NewPrototype(const TArray &rettypes, const TArray { size_t bucket; PType *proto = TypeTable.FindType(RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, &bucket); - if (proto == NULL) + if (proto == nullptr) { proto = new PPrototype(rettypes, argtypes); TypeTable.AddType(proto, RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, bucket); @@ -2689,7 +2833,7 @@ IMPLEMENT_POINTERS_END static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr) { - if (type != NULL) + if (type != nullptr) { RecurseWriteFields(type->ParentClass, ar, addr); // Don't write this part if it has no non-transient variables @@ -2773,7 +2917,7 @@ bool PClass::ReadAllFields(FSerializer &ar, void *addr) const { // Only read it if the type is related to this one. const PClass *parent; - for (parent = this; parent != NULL; parent = parent->ParentClass) + for (parent = this; parent != nullptr; parent = parent->ParentClass) { if (parent == type) { @@ -2835,7 +2979,7 @@ void PClass::StaticInit () FAutoSegIterator probe(CRegHead, CRegTail); - while (*++probe != NULL) + while (*++probe != nullptr) { ((ClassReg *)*probe)->RegisterClass (); } @@ -2859,16 +3003,12 @@ void PClass::StaticInit () // // PClass :: StaticShutdown STATIC // -// Frees FlatPointers belonging to all classes. Only really needed to avoid -// memory leak warnings at exit. +// Frees all static class data. // //========================================================================== void PClass::StaticShutdown () { - TArray uniqueFPs(64); - unsigned int i, j; - // delete all variables containing pointers to script functions. for (auto p : FunctionPtrList) { @@ -2876,7 +3016,8 @@ void PClass::StaticShutdown () } FunctionPtrList.Clear(); - // Make a full garbage collection here so that all destroyed but uncollected higher level objects that still exist can be properly taken down. + // 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. @@ -2886,33 +3027,7 @@ void PClass::StaticShutdown () // Unless something went wrong, anything left here should be class and type objects only, which do not own any scripts. TypeTable.Clear(); Namespaces.ReleaseSymbols(); - - for (i = 0; i < PClass::AllClasses.Size(); ++i) - { - PClass *type = PClass::AllClasses[i]; - PClass::AllClasses[i] = NULL; - if (type->FlatPointers != &TheEnd && type->FlatPointers != type->Pointers) - { - // FlatPointers are shared by many classes, so we must check for - // duplicates and only delete those that are unique. - for (j = 0; j < uniqueFPs.Size(); ++j) - { - if (type->FlatPointers == uniqueFPs[j]) - { - break; - } - } - if (j == uniqueFPs.Size()) - { - uniqueFPs.Push(const_cast(type->FlatPointers)); - } - } - type->Destroy(); - } - for (i = 0; i < uniqueFPs.Size(); ++i) - { - delete[] uniqueFPs[i]; - } + FlatpointerArena.FreeAllBlocks(); bShutdown = true; AllClasses.Clear(); @@ -2946,7 +3061,7 @@ void PClass::StaticBootstrap() PClassClass *cls = new PClassClass; PClass::RegistrationInfo.SetupClass(cls); - // The PClassClass constructor initialized these to NULL, because the + // 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. clscls->TypeTableType = cls; @@ -2971,6 +3086,7 @@ PClass::PClass() ParentClass = nullptr; Pointers = nullptr; FlatPointers = nullptr; + ArrayPointers = nullptr; HashNext = nullptr; Defaults = nullptr; bRuntimeClass = false; @@ -2990,10 +3106,10 @@ PClass::PClass() PClass::~PClass() { - if (Defaults != NULL) + if (Defaults != nullptr) { M_Free(Defaults); - Defaults = NULL; + Defaults = nullptr; } } @@ -3020,7 +3136,7 @@ PClass *ClassReg::RegisterClass() }; // Skip classes that have already been registered - if (MyClass != NULL) + if (MyClass != nullptr) { return MyClass; } @@ -3059,7 +3175,7 @@ PClass *ClassReg::RegisterClass() void ClassReg::SetupClass(PClass *cls) { - assert(MyClass == NULL); + assert(MyClass == nullptr); MyClass = cls; cls->TypeName = FName(Name+1); cls->Size = SizeOf; @@ -3082,7 +3198,7 @@ void PClass::InsertIntoHash () PType *found; found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket); - if (found != NULL) + if (found != nullptr) { // This type has already been inserted I_Error("Tried to register class '%s' more than once.\n", TypeName.GetChars()); } @@ -3102,14 +3218,14 @@ void PClass::InsertIntoHash () const PClass *PClass::FindParentClass(FName name) const { - for (const PClass *type = this; type != NULL; type = type->ParentClass) + for (const PClass *type = this; type != nullptr; type = type->ParentClass) { if (type->TypeName == name) { return type; } } - return NULL; + return nullptr; } //========================================================================== @@ -3124,9 +3240,9 @@ PClass *PClass::FindClass (FName zaname) { if (zaname == NAME_None) { - return NULL; + return nullptr; } - return static_cast(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, NULL)); + return static_cast(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, nullptr)); } //========================================================================== @@ -3140,10 +3256,10 @@ PClass *PClass::FindClass (FName zaname) DObject *PClass::CreateNew() const { BYTE *mem = (BYTE *)M_Malloc (Size); - assert (mem != NULL); + assert (mem != nullptr); // Set this object's defaults before constructing it. - if (Defaults != NULL) + if (Defaults != nullptr) memcpy (mem, Defaults, Size); else memset (mem, 0, Size); @@ -3170,7 +3286,7 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const { return; } - assert(ParentClass != NULL); + assert(ParentClass != nullptr); ParentClass->InitializeSpecials(addr, defaults); for (auto tao : SpecialInits) { @@ -3195,7 +3311,7 @@ void PClass::DestroySpecials(void *addr) const { return; } - assert(ParentClass != NULL); + assert(ParentClass != nullptr); ParentClass->DestroySpecials(addr); for (auto tao : SpecialInits) { @@ -3231,7 +3347,7 @@ void PClass::InitializeDefaults() { if (IsKindOf(RUNTIME_CLASS(PClassActor))) { - assert(Defaults == NULL); + assert(Defaults == nullptr); Defaults = (BYTE *)M_Malloc(Size); // run the constructor on the defaults to set the vtbl pointer which is needed to run class-aware functions on them. @@ -3248,7 +3364,7 @@ void PClass::InitializeDefaults() // Copy the defaults from the parent but leave the DObject part alone because it contains important data. - if (ParentClass->Defaults != NULL) + if (ParentClass->Defaults != nullptr) { memcpy(Defaults + sizeof(DObject), ParentClass->Defaults + sizeof(DObject), ParentClass->Size - sizeof(DObject)); if (Size > ParentClass->Size) @@ -3265,7 +3381,7 @@ void PClass::InitializeDefaults() if (bRuntimeClass) { // Copy parent values from the parent defaults. - assert(ParentClass != NULL); + assert(ParentClass != nullptr); ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); for (const PField *field : Fields) @@ -3404,14 +3520,14 @@ PClass *PClass::FindClassTentative(FName name) { if (name == NAME_None) { - return NULL; + return nullptr; } size_t bucket; PType *found = TypeTable.FindType(RUNTIME_CLASS(PClass), /*FIXME:Outer*/0, name, &bucket); - if (found != NULL) + if (found != nullptr) { return static_cast(found); } @@ -3484,14 +3600,14 @@ int PClass::FindVirtualIndex(FName name, PPrototype *proto) void PClass::BuildFlatPointers () { - if (FlatPointers != NULL) + if (FlatPointers != nullptr) { // Already built: Do nothing. return; } - else if (ParentClass == NULL) + else if (ParentClass == nullptr) { // No parent (i.e. DObject: FlatPointers is the same as Pointers. - if (Pointers == NULL) - { // No pointers: Make FlatPointers a harmless non-NULL. + if (Pointers == nullptr) + { // No pointers: Make FlatPointers a harmless non-nullptr. FlatPointers = &TheEnd; } else @@ -3536,7 +3652,7 @@ void PClass::BuildFlatPointers () { } // Concatenate them into a new array - size_t *flat = new size_t[numPointers + numSuperPointers + ScriptPointers.Size() + 1]; + size_t *flat = (size_t*)FlatpointerArena.Alloc(sizeof(size_t) * (numPointers + numSuperPointers + ScriptPointers.Size() + 1)); if (numSuperPointers > 0) { memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers); @@ -3555,6 +3671,68 @@ void PClass::BuildFlatPointers () } } +//========================================================================== +// +// 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*)FlatpointerArena.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 @@ -3604,18 +3782,18 @@ void PClass::FindFunction(VMFunction **pptr, FName clsname, FName funcname) 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 != NULL) + if (bucketnum != nullptr) { *bucketnum = bucket; } - for (PType *type = TypeHash[bucket]; type != NULL; type = type->HashNext) + for (PType *type = TypeHash[bucket]; type != nullptr; type = type->HashNext) { if (type->GetClass()->TypeTableType == metatype && type->IsMatch(parm1, parm2)) { return type; } } - return NULL; + return nullptr; } //========================================================================== @@ -3630,13 +3808,13 @@ PType *FTypeTable::FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, si void FTypeTable::ReplaceType(PType *newtype, PType *oldtype, size_t bucket) { - for (PType **type_p = &TypeHash[bucket]; *type_p != NULL; type_p = &(*type_p)->HashNext) + for (PType **type_p = &TypeHash[bucket]; *type_p != nullptr; type_p = &(*type_p)->HashNext) { PType *type = *type_p; if (type == oldtype) { newtype->HashNext = type->HashNext; - type->HashNext = NULL; + type->HashNext = nullptr; *type_p = newtype; break; } @@ -3654,7 +3832,7 @@ void FTypeTable::AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t #ifdef _DEBUG size_t bucketcheck; assert(metatype == type->GetClass()->TypeTableType && "Metatype does not match passed object"); - assert(FindType(metatype, parm1, parm2, &bucketcheck) == NULL && "Type must not be inserted more than once"); + assert(FindType(metatype, parm1, parm2, &bucketcheck) == nullptr && "Type must not be inserted more than once"); assert(bucketcheck == bucket && "Passed bucket was wrong"); #endif type->HashNext = TypeHash[bucket]; @@ -3677,7 +3855,7 @@ void FTypeTable::AddType(PType *type) metatype = type->GetClass()->TypeTableType; type->GetTypeIDs(parm1, parm2); bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; - assert(FindType(metatype, parm1, parm2, NULL) == NULL && "Type must not be inserted more than once"); + assert(FindType(metatype, parm1, parm2, nullptr) == nullptr && "Type must not be inserted more than once"); type->HashNext = TypeHash[bucket]; TypeHash[bucket] = type; @@ -3732,7 +3910,7 @@ void FTypeTable::Mark() { for (int i = HASH_SIZE - 1; i >= 0; --i) { - if (TypeHash[i] != NULL) + if (TypeHash[i] != nullptr) { GC::Mark(TypeHash[i]); } @@ -3790,7 +3968,7 @@ PSymbol::~PSymbol() } PSymbolTable::PSymbolTable() -: ParentSymbolTable(NULL) +: ParentSymbolTable(nullptr) { } @@ -3833,24 +4011,24 @@ void PSymbolTable::SetParentTable (PSymbolTable *parent) PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const { PSymbol * const *value = Symbols.CheckKey(symname); - if (value == NULL && searchparents && ParentSymbolTable != NULL) + if (value == nullptr && searchparents && ParentSymbolTable != nullptr) { return ParentSymbolTable->FindSymbol(symname, searchparents); } - return value != NULL ? *value : NULL; + return value != nullptr ? *value : nullptr; } PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable) { PSymbol * const *value = Symbols.CheckKey(symname); - if (value == NULL) + if (value == nullptr) { - if (ParentSymbolTable != NULL) + if (ParentSymbolTable != nullptr) { return ParentSymbolTable->FindSymbolInTable(symname, symtable); } - symtable = NULL; - return NULL; + symtable = nullptr; + return nullptr; } symtable = this; return *value; @@ -3859,9 +4037,9 @@ PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable) PSymbol *PSymbolTable::AddSymbol (PSymbol *sym) { // Symbols that already exist are not inserted. - if (Symbols.CheckKey(sym->SymbolName) != NULL) + if (Symbols.CheckKey(sym->SymbolName) != nullptr) { - return NULL; + return nullptr; } Symbols.Insert(sym->SymbolName, sym); return sym; @@ -3878,16 +4056,16 @@ PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym) { // If a symbol with a matching name exists, take its place and return it. PSymbol **symslot = Symbols.CheckKey(newsym->SymbolName); - if (symslot != NULL) + if (symslot != nullptr) { PSymbol *oldsym = *symslot; *symslot = newsym; return oldsym; } - // Else, just insert normally and return NULL since there was no + // Else, just insert normally and return nullptr since there was no // symbol to replace. Symbols.Insert(newsym->SymbolName, newsym); - return NULL; + return nullptr; } IMPLEMENT_CLASS(PNamespace, false, true) @@ -3969,7 +4147,7 @@ void RemoveUnusedSymbols() // We do not need any non-field and non-function symbols in structs and classes anymore. for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) { - for (PType *ty = TypeTable.TypeHash[i]; ty != NULL; ty = ty->HashNext) + for (PType *ty = TypeTable.TypeHash[i]; ty != nullptr; ty = ty->HashNext) { if (ty->IsKindOf(RUNTIME_CLASS(PStruct))) { diff --git a/src/dobjtype.h b/src/dobjtype.h index 60d410918..3e204da62 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -247,6 +247,7 @@ public: // object is destroyed. virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const; virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL) const; + virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const; // Initialize the value, if needed (e.g. strings) virtual void InitializeValue(void *addr, const void *def) const; @@ -534,7 +535,7 @@ class PClassPointer : public PPointer DECLARE_CLASS(PClassPointer, PPointer); HAS_OBJECT_POINTERS; public: - PClassPointer(class PClass *restrict); + PClassPointer(class PClass *restrict = nullptr); class PClass *ClassRestriction; @@ -542,8 +543,6 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; -protected: - PClassPointer(); }; // Struct/class fields ------------------------------------------------------ @@ -657,6 +656,14 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; + void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const override; + protected: PDynArray(); }; @@ -801,6 +808,7 @@ public: PClass *ParentClass; // the class this class derives from const size_t *Pointers; // object pointers defined by this class *only* const size_t *FlatPointers; // object pointers defined by this class and all its superclasses; not initialized by default + const size_t *ArrayPointers; // dynamic arrays containing object pointers. BYTE *Defaults; bool bRuntimeClass; // class was defined at run-time, not compile-time bool bExported; // This type has been declared in a script @@ -818,6 +826,7 @@ public: PField *AddField(FName name, PType *type, DWORD flags=0) override; void InitializeActorInfo(); void BuildFlatPointers(); + void BuildArrayPointers(); void DestroySpecials(void *addr) const; const PClass *NativeClass() const; diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 4d6376f3b..a3dc066e9 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -542,7 +542,7 @@ ExpEmit FxConstant::Emit(VMFunctionBuilder *build) { tag = ATAG_STATE; } - else if (value.Type->GetLoadOp() == OP_LO) + else if (value.Type->GetLoadOp() != OP_LP) { tag = ATAG_OBJECT; } @@ -6356,7 +6356,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build) ob.Free(build); ExpEmit meta(build, REGT_POINTER); build->Emit(OP_META, meta.RegNum, ob.RegNum); - build->Emit(OP_LO, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + build->Emit(OP_LOS, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); return meta; } @@ -8874,7 +8874,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build) op.Free(build); } ExpEmit to(build, REGT_POINTER); - build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass))); + build->Emit(OP_LOS, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, ParentClass))); return to; } @@ -8946,7 +8946,7 @@ ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build) build->Emit(OP_LKP, to.RegNum, op.RegNum); op = to; } - build->Emit(OP_LO, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + build->Emit(OP_LOS, to.RegNum, op.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); return to; } @@ -10683,8 +10683,7 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) case REGT_POINTER: { - bool isobject = ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass))); - build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), isobject ? ATAG_OBJECT : ATAG_GENERIC)); + build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ValueType->GetLoadOp() != OP_LP ? ATAG_OBJECT : ATAG_GENERIC)); break; } case REGT_STRING: @@ -10824,7 +10823,7 @@ ExpEmit FxStaticArray::Emit(VMFunctionBuilder *build) { TArray cvalues; for (auto v : values) cvalues.Push(static_cast(v)->GetValue().GetPointer()); - StackOffset = build->AllocConstantsAddress(cvalues.Size(), &cvalues[0], ElementType->GetLoadOp() == OP_LO ? ATAG_OBJECT : ATAG_GENERIC); + StackOffset = build->AllocConstantsAddress(cvalues.Size(), &cvalues[0], ElementType->GetLoadOp() != OP_LP ? ATAG_OBJECT : ATAG_GENERIC); break; } } diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 8786a31bf..97f6ae10b 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -1141,19 +1141,8 @@ DEFINE_ACTION_FUNCTION(FStringStruct, AppendFormat) DEFINE_ACTION_FUNCTION(FStringStruct, Mid) { PARAM_SELF_STRUCT_PROLOGUE(FString); - PARAM_INT(ipos); - PARAM_INT(ilen); - // validate. we don't want to crash if someone passes negative values. - // with size_t it's handled naturally I think, as it's unsigned, but not in ZScript. - if (ipos < 0) ipos = 0; - if (ilen < 0) ilen = 0; - // convert to size_t to prevent overflows here - size_t slen = self->Len(); - size_t pos = (size_t)ipos; - size_t len = (size_t)ilen; - if (pos > slen) pos = slen - 1; - if (pos + len > slen) - len = slen - pos; + PARAM_UINT(pos); + PARAM_UINT(len); FString s = self->Mid(pos, len); ACTION_RETURN_STRING(s); } @@ -1161,7 +1150,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid) DEFINE_ACTION_FUNCTION(FStringStruct, Len) { PARAM_SELF_STRUCT_PROLOGUE(FString); - ACTION_RETURN_INT(self->Len()); + ACTION_RETURN_INT((int)self->Len()); } // CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int. @@ -1169,7 +1158,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharAt) { PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_INT(pos); - int slen = self->Len(); + int slen = (int)self->Len(); if (pos < 0 || pos >= slen) ACTION_RETURN_STRING(""); ACTION_RETURN_STRING(FString((*self)[pos])); @@ -1179,7 +1168,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharCodeAt) { PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_INT(pos); - int slen = self->Len(); + int slen = (int)self->Len(); if (pos < 0 || pos >= slen) ACTION_RETURN_INT(0); ACTION_RETURN_INT((*self)[pos]); diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 1a5c10b4e..8b8e3c864 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -997,6 +997,7 @@ void NullParam(const char *varname); // For required parameters. #define PARAM_INT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); int x = param[p].i; +#define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); unsigned x = param[p].i; #define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); bool x = !!param[p].i; #define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FName x = ENamedName(param[p].i); #define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FSoundID x = param[p].i; @@ -1040,6 +1041,7 @@ void NullParam(const char *varname); #define PARAM_PROLOGUE int paramnum = -1; #define PARAM_INT(x) ++paramnum; PARAM_INT_AT(paramnum,x) +#define PARAM_UINT(x) ++paramnum; PARAM_UINT_AT(paramnum,x) #define PARAM_BOOL(x) ++paramnum; PARAM_BOOL_AT(paramnum,x) #define PARAM_NAME(x) ++paramnum; PARAM_NAME_AT(paramnum,x) #define PARAM_SOUND(x) ++paramnum; PARAM_SOUND_AT(paramnum,x) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 38dbbc411..b9f8e81ca 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -347,6 +347,17 @@ begin: GETADDR(PA,RC,X_WRITE_NIL); *(void **)ptr = reg.a[B]; NEXTOP; + OP(SO): + ASSERTA(a); ASSERTA(B); ASSERTKD(C); + GETADDR(PA,KC,X_WRITE_NIL); + *(void **)ptr = reg.a[B]; + GC::WriteBarrier((DObject*)*(void **)ptr); + NEXTOP; + OP(SO_R): + ASSERTA(a); ASSERTA(B); ASSERTD(C); + GETADDR(PA,RC,X_WRITE_NIL); + GC::WriteBarrier((DObject*)*(void **)ptr); + NEXTOP; OP(SV2): ASSERTA(a); ASSERTF(B+1); ASSERTKD(C); GETADDR(PA,KC,X_WRITE_NIL); diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 3fc3d7643..7128f1e43 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -70,6 +70,8 @@ xx(SS, ss, RPRSKI, SS_R, 4, REGT_INT), // store string xx(SS_R, ss, RPRSRI, NOP, 0, 0), xx(SP, sp, RPRPKI, SP_R, 4, REGT_INT), // store pointer xx(SP_R, sp, RPRPRI, NOP, 0, 0), +xx(SO, sp, RPRPKI, SO_R, 4, REGT_INT), // store object pointer with write barrier (only needed for non thinkers and non types +xx(SO_R, sp, RPRPRI, NOP, 0, 0), xx(SV2, sv2, RPRVKI, SV2_R, 4, REGT_INT), // store vector2 xx(SV2_R, sv2, RPRVRI, NOP, 0, 0), xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT), // store vector3