- implemented the backend for dynamic arrays. Still needs thorough testing but it should be complete.

- use a memory arena to store flat pointers so that the messed up cleanup can be avoided by deallocating this in bulk.
- added a new SO opcode to the VM to execute a write barrier. This is necessary for all objects that are not linked into one global table, i.e. everything except thinkers and class types.
- always use the cheaper LOS opcode for reading pointers to classes and defaults because these cannot be destroyed during normal operation.
- removed the pointless validation from String.Mid. If the values are read as unsigned the internal validation of FString::Mid will automatically ensure proper results.
This commit is contained in:
Christoph Oelckers 2017-02-07 14:48:27 +01:00
parent 4ca69f10c7
commit 56024a1ebe
8 changed files with 376 additions and 147 deletions

View file

@ -397,6 +397,23 @@ size_t DObject::PropagateMark()
GC::Mark((DObject **)((BYTE *)this + *offsets));
offsets++;
}
offsets = info->ArrayPointers;
if (offsets == NULL)
{
const_cast<PClass *>(info)->BuildArrayPointers();
offsets = info->ArrayPointers;
}
while (*offsets != ~(size_t)0)
{
auto aray = (TArray<DObject*>*)((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<PClass *>(info)->BuildArrayPointers();
offsets = info->ArrayPointers;
}
while (*offsets != ~(size_t)0)
{
auto aray = (TArray<DObject*>*)((BYTE *)this + *offsets);
for (auto &p : *aray)
{
if (p == old)
{
p = notOld;
changed++;
}
}
offsets++;
}
return changed;
}

View file

@ -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<size_t> *stroffs) con
{
}
void PType::SetPointerArray(void *base, unsigned offset, TArray<size_t> *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<PSymbolConstNumeric *>(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<FTypeAndOffset> *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<PClass*>(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<FString>(*(TArray<FString>*)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<FString>*)addr)->~TArray<FString>();
}
}
aray->Count = aray->Most = 0;
aray->Array = nullptr;
}
//==========================================================================
//
// PDynArray :: SetDefaultValue
//
//==========================================================================
void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *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<size_t> *special) const
{
if (ElementType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(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; i<count; i++)
{
// Strings must be constructed first.
if (ElementType->GetRegType() == 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<PType *> &rettypes, const TArray<PType *>
{
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<size_t *> 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<size_t *>(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<PClass *>(TypeTable.FindType(RUNTIME_CLASS(PClass), 0, zaname, NULL));
return static_cast<PClass *>(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<PClass *>(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<size_t> 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)))
{

View file

@ -247,6 +247,7 @@ public:
// object is destroyed.
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const;
virtual void SetPointer(void *base, unsigned offset, TArray<size_t> *ptrofs = NULL) const;
virtual void SetPointerArray(void *base, unsigned offset, TArray<size_t> *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<FTypeAndOffset> *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<size_t> *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;

View file

@ -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<PPointer*>(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<void*> cvalues;
for (auto v : values) cvalues.Push(static_cast<FxConstant *>(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;
}
}

View file

@ -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]);

View file

@ -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)

View file

@ -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);

View file

@ -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