- 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)); GC::Mark((DObject **)((BYTE *)this + *offsets));
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 info->Size;
} }
return 0; return 0;
@ -427,6 +444,28 @@ size_t DObject::PointerSubstitution (DObject *old, DObject *notOld)
} }
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)
{
if (p == old)
{
p = notOld;
changed++;
}
}
offsets++;
}
return changed; return changed;
} }

View file

@ -65,6 +65,7 @@ EXTERN_CVAR(Bool, strictdecorate);
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
FMemArena FlatpointerArena; // stores the flat pointers because freeing them individually is rather messy.
FNamespaceManager Namespaces; FNamespaceManager Namespaces;
FTypeTable TypeTable; FTypeTable TypeTable;
@ -99,7 +100,7 @@ PPointer *TypeVoidPtr;
// PRIVATE DATA DEFINITIONS ------------------------------------------------ // 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; static const size_t TheEnd = ~(size_t)0;
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
@ -118,7 +119,7 @@ void DumpTypeTable()
{ {
int len = 0; int len = 0;
Printf("%4zu:", i); 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()); Printf(" -> %s", ty->DescriptiveName());
len++; len++;
@ -161,7 +162,7 @@ IMPLEMENT_CLASS(PClassType, false, false)
//========================================================================== //==========================================================================
PClassType::PClassType() PClassType::PClassType()
: TypeTableType(NULL) : TypeTableType(nullptr)
{ {
} }
@ -211,7 +212,7 @@ IMPLEMENT_POINTERS_END
//========================================================================== //==========================================================================
PType::PType(unsigned int size, unsigned int align) PType::PType(unsigned int size, unsigned int align)
: Size(size), Align(align), HashNext(NULL) : Size(size), Align(align), HashNext(nullptr)
{ {
mDescriptiveName = "Type"; mDescriptiveName = "Type";
loadOp = OP_NOP; 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 // PType :: InitializeValue
@ -831,7 +836,7 @@ PBool::PBool()
MemberOnly = false; MemberOnly = false;
// Override the default max set by PInt's constructor // Override the default max set by PInt's constructor
PSymbolConstNumeric *maxsym = static_cast<PSymbolConstNumeric *>(Symbols.FindSymbol(NAME_Max, false)); 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; 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 void PString::SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special) const
{ {
if (base != nullptr) new((BYTE *)base + offset) FString; if (base != nullptr) new((BYTE *)base + offset) FString;
if (special != NULL) if (special != nullptr)
{ {
special->Push(std::make_pair(this, offset)); special->Push(std::make_pair(this, offset));
} }
@ -1415,7 +1420,7 @@ IMPLEMENT_POINTERS_END
//========================================================================== //==========================================================================
PPointer::PPointer() PPointer::PPointer()
: PBasicType(sizeof(void *), alignof(void *)), PointedType(NULL), IsConst(false) : PBasicType(sizeof(void *), alignof(void *)), PointedType(nullptr), IsConst(false)
{ {
mDescriptiveName = "NullPointer"; mDescriptiveName = "NullPointer";
SetOps(); SetOps();
@ -1442,8 +1447,9 @@ PPointer::PPointer(PType *pointsat, bool isconst)
void PPointer::SetOps() void PPointer::SetOps()
{ {
storeOp = OP_SP;
loadOp = (PointedType && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) ? OP_LO : OP_LP; 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; moveOp = OP_MOVEA;
RegType = REGT_POINTER; 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; size_t bucket;
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, &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); ptype = new PPointer(type, isconst);
TypeTable.AddType(ptype, RUNTIME_CLASS(PPointer), (intptr_t)type, isconst ? 1 : 0, bucket); 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_POINTER(ClassRestriction)
IMPLEMENT_POINTERS_END IMPLEMENT_POINTERS_END
//==========================================================================
//
// PClassPointer - Default Constructor
//
//==========================================================================
PClassPointer::PClassPointer()
: PPointer(RUNTIME_CLASS(PClass)), ClassRestriction(NULL)
{
mDescriptiveName = "ClassPointer";
}
//========================================================================== //==========================================================================
// //
// PClassPointer - Parameterized Constructor // PClassPointer - Parameterized Constructor
@ -1629,6 +1623,10 @@ PClassPointer::PClassPointer(PClass *restrict)
{ {
if (restrict) mDescriptiveName.Format("ClassPointer<%s>", restrict->TypeName.GetChars()); if (restrict) mDescriptiveName.Format("ClassPointer<%s>", restrict->TypeName.GetChars());
else mDescriptiveName = "ClassPointer"; 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; size_t bucket;
PType *ptype = TypeTable.FindType(RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, &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); ptype = new PClassPointer(restrict);
TypeTable.AddType(ptype, RUNTIME_CLASS(PClassPointer), (intptr_t)RUNTIME_CLASS(PClass), (intptr_t)restrict, bucket); 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; size_t bucket;
if (outer == nullptr) outer = Namespaces.GlobalNamespace; if (outer == nullptr) outer = Namespaces.GlobalNamespace;
PType *etype = TypeTable.FindType(RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, &bucket); 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); etype = new PEnum(name, outer);
TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket); TypeTable.AddType(etype, RUNTIME_CLASS(PEnum), (intptr_t)outer, (intptr_t)name, bucket);
@ -1762,7 +1760,7 @@ IMPLEMENT_POINTERS_END
//========================================================================== //==========================================================================
PArray::PArray() PArray::PArray()
: ElementType(NULL), ElementCount(0) : ElementType(nullptr), ElementCount(0)
{ {
mDescriptiveName = "Array"; mDescriptiveName = "Array";
} }
@ -1902,7 +1900,7 @@ PArray *NewArray(PType *type, unsigned int count)
{ {
size_t bucket; size_t bucket;
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket); PType *atype = TypeTable.FindType(RUNTIME_CLASS(PArray), (intptr_t)type, count, &bucket);
if (atype == NULL) if (atype == nullptr)
{ {
atype = new PArray(type, count); atype = new PArray(type, count);
TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket); TypeTable.AddType(atype, RUNTIME_CLASS(PArray), (intptr_t)type, count, bucket);
@ -1976,7 +1974,7 @@ PResizableArray *NewResizableArray(PType *type)
{ {
size_t bucket; size_t bucket;
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, &bucket); PType *atype = TypeTable.FindType(RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, &bucket);
if (atype == NULL) if (atype == nullptr)
{ {
atype = new PResizableArray(type); atype = new PResizableArray(type);
TypeTable.AddType(atype, RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, bucket); TypeTable.AddType(atype, RUNTIME_CLASS(PResizableArray), (intptr_t)type, 0, bucket);
@ -1999,7 +1997,7 @@ IMPLEMENT_POINTERS_END
//========================================================================== //==========================================================================
PDynArray::PDynArray() PDynArray::PDynArray()
: ElementType(NULL) : ElementType(nullptr)
{ {
mDescriptiveName = "DynArray"; mDescriptiveName = "DynArray";
Size = sizeof(FArray); Size = sizeof(FArray);
@ -2046,6 +2044,152 @@ void PDynArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const
id2 = 0; 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 // NewDynArray
@ -2059,7 +2203,7 @@ PDynArray *NewDynArray(PType *type)
{ {
size_t bucket; size_t bucket;
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
if (atype == NULL) if (atype == nullptr)
{ {
FString backingname; FString backingname;
@ -2109,7 +2253,7 @@ IMPLEMENT_POINTERS_END
//========================================================================== //==========================================================================
PMap::PMap() PMap::PMap()
: KeyType(NULL), ValueType(NULL) : KeyType(nullptr), ValueType(nullptr)
{ {
mDescriptiveName = "Map"; mDescriptiveName = "Map";
Size = sizeof(FMap); Size = sizeof(FMap);
@ -2169,7 +2313,7 @@ PMap *NewMap(PType *keytype, PType *valuetype)
{ {
size_t bucket; size_t bucket;
PType *maptype = TypeTable.FindType(RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, &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); maptype = new PMap(keytype, valuetype);
TypeTable.AddType(maptype, RUNTIME_CLASS(PMap), (intptr_t)keytype, (intptr_t)valuetype, bucket); 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; foundsomething = true;
const PSymbol *sym = Symbols.FindSymbol(FName(label, true), 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", DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n",
label, TypeName.GetChars()); label, TypeName.GetChars());
@ -2332,7 +2476,7 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const
// PStruct :: AddField // PStruct :: AddField
// //
// Appends a new field to the end of a struct. Returns either the new field // 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. // its fields.
Align = MAX(Align, type->Align); Align = MAX(Align, type->Align);
if (Symbols.AddSymbol(field) == NULL) if (Symbols.AddSymbol(field) == nullptr)
{ // name is already in use { // name is already in use
delete field; delete field;
return NULL; return nullptr;
} }
Fields.Push(field); Fields.Push(field);
@ -2365,7 +2509,7 @@ PField *PStruct::AddField(FName name, PType *type, DWORD flags)
// PStruct :: AddField // PStruct :: AddField
// //
// Appends a new native field to the struct. Returns either the new field // 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; size_t bucket;
if (outer == nullptr) outer = Namespaces.GlobalNamespace; if (outer == nullptr) outer = Namespaces.GlobalNamespace;
PType *stype = TypeTable.FindType(RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, &bucket); 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); stype = new PStruct(name, outer);
TypeTable.AddType(stype, RUNTIME_CLASS(PStruct), (intptr_t)outer, (intptr_t)name, bucket); 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; size_t bucket;
if (outer == nullptr) outer = Namespaces.GlobalNamespace; if (outer == nullptr) outer = Namespaces.GlobalNamespace;
PType *stype = TypeTable.FindType(RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, &bucket); 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); stype = new PNativeStruct(name, outer);
TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket); TypeTable.AddType(stype, RUNTIME_CLASS(PNativeStruct), (intptr_t)outer, (intptr_t)name, bucket);
@ -2466,7 +2610,7 @@ IMPLEMENT_CLASS(PField, false, false)
//========================================================================== //==========================================================================
PField::PField() 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; size_t bucket;
PType *proto = TypeTable.FindType(RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, &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); proto = new PPrototype(rettypes, argtypes);
TypeTable.AddType(proto, RUNTIME_CLASS(PPrototype), (intptr_t)&argtypes, (intptr_t)&rettypes, bucket); 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) static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr)
{ {
if (type != NULL) if (type != nullptr)
{ {
RecurseWriteFields(type->ParentClass, ar, addr); RecurseWriteFields(type->ParentClass, ar, addr);
// Don't write this part if it has no non-transient variables // 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. // Only read it if the type is related to this one.
const PClass *parent; const PClass *parent;
for (parent = this; parent != NULL; parent = parent->ParentClass) for (parent = this; parent != nullptr; parent = parent->ParentClass)
{ {
if (parent == type) if (parent == type)
{ {
@ -2835,7 +2979,7 @@ void PClass::StaticInit ()
FAutoSegIterator probe(CRegHead, CRegTail); FAutoSegIterator probe(CRegHead, CRegTail);
while (*++probe != NULL) while (*++probe != nullptr)
{ {
((ClassReg *)*probe)->RegisterClass (); ((ClassReg *)*probe)->RegisterClass ();
} }
@ -2859,16 +3003,12 @@ void PClass::StaticInit ()
// //
// PClass :: StaticShutdown STATIC // PClass :: StaticShutdown STATIC
// //
// Frees FlatPointers belonging to all classes. Only really needed to avoid // Frees all static class data.
// memory leak warnings at exit.
// //
//========================================================================== //==========================================================================
void PClass::StaticShutdown () void PClass::StaticShutdown ()
{ {
TArray<size_t *> uniqueFPs(64);
unsigned int i, j;
// delete all variables containing pointers to script functions. // delete all variables containing pointers to script functions.
for (auto p : FunctionPtrList) for (auto p : FunctionPtrList)
{ {
@ -2876,7 +3016,8 @@ void PClass::StaticShutdown ()
} }
FunctionPtrList.Clear(); 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(); GC::FullGC();
// From this point onward no scripts may be called anymore because the data needed by the VM is getting deleted now. // 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. // Unless something went wrong, anything left here should be class and type objects only, which do not own any scripts.
TypeTable.Clear(); TypeTable.Clear();
Namespaces.ReleaseSymbols(); Namespaces.ReleaseSymbols();
FlatpointerArena.FreeAllBlocks();
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];
}
bShutdown = true; bShutdown = true;
AllClasses.Clear(); AllClasses.Clear();
@ -2946,7 +3061,7 @@ void PClass::StaticBootstrap()
PClassClass *cls = new PClassClass; PClassClass *cls = new PClassClass;
PClass::RegistrationInfo.SetupClass(cls); 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 // 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. // they should be and can insert them into the type table successfully.
clscls->TypeTableType = cls; clscls->TypeTableType = cls;
@ -2971,6 +3086,7 @@ PClass::PClass()
ParentClass = nullptr; ParentClass = nullptr;
Pointers = nullptr; Pointers = nullptr;
FlatPointers = nullptr; FlatPointers = nullptr;
ArrayPointers = nullptr;
HashNext = nullptr; HashNext = nullptr;
Defaults = nullptr; Defaults = nullptr;
bRuntimeClass = false; bRuntimeClass = false;
@ -2990,10 +3106,10 @@ PClass::PClass()
PClass::~PClass() PClass::~PClass()
{ {
if (Defaults != NULL) if (Defaults != nullptr)
{ {
M_Free(Defaults); M_Free(Defaults);
Defaults = NULL; Defaults = nullptr;
} }
} }
@ -3020,7 +3136,7 @@ PClass *ClassReg::RegisterClass()
}; };
// Skip classes that have already been registered // Skip classes that have already been registered
if (MyClass != NULL) if (MyClass != nullptr)
{ {
return MyClass; return MyClass;
} }
@ -3059,7 +3175,7 @@ PClass *ClassReg::RegisterClass()
void ClassReg::SetupClass(PClass *cls) void ClassReg::SetupClass(PClass *cls)
{ {
assert(MyClass == NULL); assert(MyClass == nullptr);
MyClass = cls; MyClass = cls;
cls->TypeName = FName(Name+1); cls->TypeName = FName(Name+1);
cls->Size = SizeOf; cls->Size = SizeOf;
@ -3082,7 +3198,7 @@ void PClass::InsertIntoHash ()
PType *found; PType *found;
found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket); found = TypeTable.FindType(RUNTIME_CLASS(PClass), 0, TypeName, &bucket);
if (found != NULL) if (found != nullptr)
{ // This type has already been inserted { // This type has already been inserted
I_Error("Tried to register class '%s' more than once.\n", TypeName.GetChars()); 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 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) if (type->TypeName == name)
{ {
return type; return type;
} }
} }
return NULL; return nullptr;
} }
//========================================================================== //==========================================================================
@ -3124,9 +3240,9 @@ PClass *PClass::FindClass (FName zaname)
{ {
if (zaname == NAME_None) 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 DObject *PClass::CreateNew() const
{ {
BYTE *mem = (BYTE *)M_Malloc (Size); BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != NULL); assert (mem != nullptr);
// Set this object's defaults before constructing it. // Set this object's defaults before constructing it.
if (Defaults != NULL) if (Defaults != nullptr)
memcpy (mem, Defaults, Size); memcpy (mem, Defaults, Size);
else else
memset (mem, 0, Size); memset (mem, 0, Size);
@ -3170,7 +3286,7 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const
{ {
return; return;
} }
assert(ParentClass != NULL); assert(ParentClass != nullptr);
ParentClass->InitializeSpecials(addr, defaults); ParentClass->InitializeSpecials(addr, defaults);
for (auto tao : SpecialInits) for (auto tao : SpecialInits)
{ {
@ -3195,7 +3311,7 @@ void PClass::DestroySpecials(void *addr) const
{ {
return; return;
} }
assert(ParentClass != NULL); assert(ParentClass != nullptr);
ParentClass->DestroySpecials(addr); ParentClass->DestroySpecials(addr);
for (auto tao : SpecialInits) for (auto tao : SpecialInits)
{ {
@ -3231,7 +3347,7 @@ void PClass::InitializeDefaults()
{ {
if (IsKindOf(RUNTIME_CLASS(PClassActor))) if (IsKindOf(RUNTIME_CLASS(PClassActor)))
{ {
assert(Defaults == NULL); assert(Defaults == nullptr);
Defaults = (BYTE *)M_Malloc(Size); 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. // 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. // 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)); memcpy(Defaults + sizeof(DObject), ParentClass->Defaults + sizeof(DObject), ParentClass->Size - sizeof(DObject));
if (Size > ParentClass->Size) if (Size > ParentClass->Size)
@ -3265,7 +3381,7 @@ void PClass::InitializeDefaults()
if (bRuntimeClass) if (bRuntimeClass)
{ {
// Copy parent values from the parent defaults. // Copy parent values from the parent defaults.
assert(ParentClass != NULL); assert(ParentClass != nullptr);
ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults);
for (const PField *field : Fields) for (const PField *field : Fields)
@ -3404,14 +3520,14 @@ PClass *PClass::FindClassTentative(FName name)
{ {
if (name == NAME_None) if (name == NAME_None)
{ {
return NULL; return nullptr;
} }
size_t bucket; size_t bucket;
PType *found = TypeTable.FindType(RUNTIME_CLASS(PClass), PType *found = TypeTable.FindType(RUNTIME_CLASS(PClass),
/*FIXME:Outer*/0, name, &bucket); /*FIXME:Outer*/0, name, &bucket);
if (found != NULL) if (found != nullptr)
{ {
return static_cast<PClass *>(found); return static_cast<PClass *>(found);
} }
@ -3484,14 +3600,14 @@ int PClass::FindVirtualIndex(FName name, PPrototype *proto)
void PClass::BuildFlatPointers () void PClass::BuildFlatPointers ()
{ {
if (FlatPointers != NULL) if (FlatPointers != nullptr)
{ // Already built: Do nothing. { // Already built: Do nothing.
return; return;
} }
else if (ParentClass == NULL) else if (ParentClass == nullptr)
{ // No parent (i.e. DObject: FlatPointers is the same as Pointers. { // No parent (i.e. DObject: FlatPointers is the same as Pointers.
if (Pointers == NULL) if (Pointers == nullptr)
{ // No pointers: Make FlatPointers a harmless non-NULL. { // No pointers: Make FlatPointers a harmless non-nullptr.
FlatPointers = &TheEnd; FlatPointers = &TheEnd;
} }
else else
@ -3536,7 +3652,7 @@ void PClass::BuildFlatPointers ()
{ } { }
// Concatenate them into a new array // 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) if (numSuperPointers > 0)
{ {
memcpy (flat, ParentClass->FlatPointers, sizeof(size_t)*numSuperPointers); 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 // 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) PType *FTypeTable::FindType(PClass *metatype, intptr_t parm1, intptr_t parm2, size_t *bucketnum)
{ {
size_t bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; size_t bucket = Hash(metatype, parm1, parm2) % HASH_SIZE;
if (bucketnum != NULL) if (bucketnum != nullptr)
{ {
*bucketnum = bucket; *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)) if (type->GetClass()->TypeTableType == metatype && type->IsMatch(parm1, parm2))
{ {
return type; 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) 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; PType *type = *type_p;
if (type == oldtype) if (type == oldtype)
{ {
newtype->HashNext = type->HashNext; newtype->HashNext = type->HashNext;
type->HashNext = NULL; type->HashNext = nullptr;
*type_p = newtype; *type_p = newtype;
break; break;
} }
@ -3654,7 +3832,7 @@ void FTypeTable::AddType(PType *type, PClass *metatype, intptr_t parm1, intptr_t
#ifdef _DEBUG #ifdef _DEBUG
size_t bucketcheck; size_t bucketcheck;
assert(metatype == type->GetClass()->TypeTableType && "Metatype does not match passed object"); 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"); assert(bucketcheck == bucket && "Passed bucket was wrong");
#endif #endif
type->HashNext = TypeHash[bucket]; type->HashNext = TypeHash[bucket];
@ -3677,7 +3855,7 @@ void FTypeTable::AddType(PType *type)
metatype = type->GetClass()->TypeTableType; metatype = type->GetClass()->TypeTableType;
type->GetTypeIDs(parm1, parm2); type->GetTypeIDs(parm1, parm2);
bucket = Hash(metatype, parm1, parm2) % HASH_SIZE; 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]; type->HashNext = TypeHash[bucket];
TypeHash[bucket] = type; TypeHash[bucket] = type;
@ -3732,7 +3910,7 @@ void FTypeTable::Mark()
{ {
for (int i = HASH_SIZE - 1; i >= 0; --i) for (int i = HASH_SIZE - 1; i >= 0; --i)
{ {
if (TypeHash[i] != NULL) if (TypeHash[i] != nullptr)
{ {
GC::Mark(TypeHash[i]); GC::Mark(TypeHash[i]);
} }
@ -3790,7 +3968,7 @@ PSymbol::~PSymbol()
} }
PSymbolTable::PSymbolTable() PSymbolTable::PSymbolTable()
: ParentSymbolTable(NULL) : ParentSymbolTable(nullptr)
{ {
} }
@ -3833,24 +4011,24 @@ void PSymbolTable::SetParentTable (PSymbolTable *parent)
PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const PSymbol *PSymbolTable::FindSymbol (FName symname, bool searchparents) const
{ {
PSymbol * const *value = Symbols.CheckKey(symname); PSymbol * const *value = Symbols.CheckKey(symname);
if (value == NULL && searchparents && ParentSymbolTable != NULL) if (value == nullptr && searchparents && ParentSymbolTable != nullptr)
{ {
return ParentSymbolTable->FindSymbol(symname, searchparents); return ParentSymbolTable->FindSymbol(symname, searchparents);
} }
return value != NULL ? *value : NULL; return value != nullptr ? *value : nullptr;
} }
PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable) PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable)
{ {
PSymbol * const *value = Symbols.CheckKey(symname); PSymbol * const *value = Symbols.CheckKey(symname);
if (value == NULL) if (value == nullptr)
{ {
if (ParentSymbolTable != NULL) if (ParentSymbolTable != nullptr)
{ {
return ParentSymbolTable->FindSymbolInTable(symname, symtable); return ParentSymbolTable->FindSymbolInTable(symname, symtable);
} }
symtable = NULL; symtable = nullptr;
return NULL; return nullptr;
} }
symtable = this; symtable = this;
return *value; return *value;
@ -3859,9 +4037,9 @@ PSymbol *PSymbolTable::FindSymbolInTable(FName symname, PSymbolTable *&symtable)
PSymbol *PSymbolTable::AddSymbol (PSymbol *sym) PSymbol *PSymbolTable::AddSymbol (PSymbol *sym)
{ {
// Symbols that already exist are not inserted. // 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); Symbols.Insert(sym->SymbolName, sym);
return 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. // If a symbol with a matching name exists, take its place and return it.
PSymbol **symslot = Symbols.CheckKey(newsym->SymbolName); PSymbol **symslot = Symbols.CheckKey(newsym->SymbolName);
if (symslot != NULL) if (symslot != nullptr)
{ {
PSymbol *oldsym = *symslot; PSymbol *oldsym = *symslot;
*symslot = newsym; *symslot = newsym;
return oldsym; 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. // symbol to replace.
Symbols.Insert(newsym->SymbolName, newsym); Symbols.Insert(newsym->SymbolName, newsym);
return NULL; return nullptr;
} }
IMPLEMENT_CLASS(PNamespace, false, true) 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. // 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 (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))) if (ty->IsKindOf(RUNTIME_CLASS(PStruct)))
{ {

View file

@ -247,6 +247,7 @@ public:
// object is destroyed. // object is destroyed.
virtual void SetDefaultValue(void *base, unsigned offset, TArray<FTypeAndOffset> *special=NULL) const; 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 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) // Initialize the value, if needed (e.g. strings)
virtual void InitializeValue(void *addr, const void *def) const; virtual void InitializeValue(void *addr, const void *def) const;
@ -534,7 +535,7 @@ class PClassPointer : public PPointer
DECLARE_CLASS(PClassPointer, PPointer); DECLARE_CLASS(PClassPointer, PPointer);
HAS_OBJECT_POINTERS; HAS_OBJECT_POINTERS;
public: public:
PClassPointer(class PClass *restrict); PClassPointer(class PClass *restrict = nullptr);
class PClass *ClassRestriction; class PClass *ClassRestriction;
@ -542,8 +543,6 @@ public:
virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
protected:
PClassPointer();
}; };
// Struct/class fields ------------------------------------------------------ // Struct/class fields ------------------------------------------------------
@ -657,6 +656,14 @@ public:
virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(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: protected:
PDynArray(); PDynArray();
}; };
@ -801,6 +808,7 @@ public:
PClass *ParentClass; // the class this class derives from PClass *ParentClass; // the class this class derives from
const size_t *Pointers; // object pointers defined by this class *only* 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 *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; BYTE *Defaults;
bool bRuntimeClass; // class was defined at run-time, not compile-time bool bRuntimeClass; // class was defined at run-time, not compile-time
bool bExported; // This type has been declared in a script 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; PField *AddField(FName name, PType *type, DWORD flags=0) override;
void InitializeActorInfo(); void InitializeActorInfo();
void BuildFlatPointers(); void BuildFlatPointers();
void BuildArrayPointers();
void DestroySpecials(void *addr) const; void DestroySpecials(void *addr) const;
const PClass *NativeClass() const; const PClass *NativeClass() const;

View file

@ -542,7 +542,7 @@ ExpEmit FxConstant::Emit(VMFunctionBuilder *build)
{ {
tag = ATAG_STATE; tag = ATAG_STATE;
} }
else if (value.Type->GetLoadOp() == OP_LO) else if (value.Type->GetLoadOp() != OP_LP)
{ {
tag = ATAG_OBJECT; tag = ATAG_OBJECT;
} }
@ -6356,7 +6356,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
ob.Free(build); ob.Free(build);
ExpEmit meta(build, REGT_POINTER); ExpEmit meta(build, REGT_POINTER);
build->Emit(OP_META, meta.RegNum, ob.RegNum); 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; return meta;
} }
@ -8874,7 +8874,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build)
op.Free(build); op.Free(build);
} }
ExpEmit to(build, REGT_POINTER); 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; return to;
} }
@ -8946,7 +8946,7 @@ ExpEmit FxGetDefaultByType::Emit(VMFunctionBuilder *build)
build->Emit(OP_LKP, to.RegNum, op.RegNum); build->Emit(OP_LKP, to.RegNum, op.RegNum);
op = to; 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; return to;
} }
@ -10683,8 +10683,7 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
case REGT_POINTER: 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(), ValueType->GetLoadOp() != OP_LP ? ATAG_OBJECT : ATAG_GENERIC));
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), isobject ? ATAG_OBJECT : ATAG_GENERIC));
break; break;
} }
case REGT_STRING: case REGT_STRING:
@ -10824,7 +10823,7 @@ ExpEmit FxStaticArray::Emit(VMFunctionBuilder *build)
{ {
TArray<void*> cvalues; TArray<void*> cvalues;
for (auto v : values) cvalues.Push(static_cast<FxConstant *>(v)->GetValue().GetPointer()); 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; break;
} }
} }

View file

@ -1141,19 +1141,8 @@ DEFINE_ACTION_FUNCTION(FStringStruct, AppendFormat)
DEFINE_ACTION_FUNCTION(FStringStruct, Mid) DEFINE_ACTION_FUNCTION(FStringStruct, Mid)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_INT(ipos); PARAM_UINT(pos);
PARAM_INT(ilen); PARAM_UINT(len);
// 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;
FString s = self->Mid(pos, len); FString s = self->Mid(pos, len);
ACTION_RETURN_STRING(s); ACTION_RETURN_STRING(s);
} }
@ -1161,7 +1150,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid)
DEFINE_ACTION_FUNCTION(FStringStruct, Len) DEFINE_ACTION_FUNCTION(FStringStruct, Len)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FString); 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. // 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_SELF_STRUCT_PROLOGUE(FString);
PARAM_INT(pos); PARAM_INT(pos);
int slen = self->Len(); int slen = (int)self->Len();
if (pos < 0 || pos >= slen) if (pos < 0 || pos >= slen)
ACTION_RETURN_STRING(""); ACTION_RETURN_STRING("");
ACTION_RETURN_STRING(FString((*self)[pos])); ACTION_RETURN_STRING(FString((*self)[pos]));
@ -1179,7 +1168,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharCodeAt)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_INT(pos); PARAM_INT(pos);
int slen = self->Len(); int slen = (int)self->Len();
if (pos < 0 || pos >= slen) if (pos < 0 || pos >= slen)
ACTION_RETURN_INT(0); ACTION_RETURN_INT(0);
ACTION_RETURN_INT((*self)[pos]); ACTION_RETURN_INT((*self)[pos]);

View file

@ -997,6 +997,7 @@ void NullParam(const char *varname);
// For required parameters. // 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_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_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_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; #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_PROLOGUE int paramnum = -1;
#define PARAM_INT(x) ++paramnum; PARAM_INT_AT(paramnum,x) #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_BOOL(x) ++paramnum; PARAM_BOOL_AT(paramnum,x)
#define PARAM_NAME(x) ++paramnum; PARAM_NAME_AT(paramnum,x) #define PARAM_NAME(x) ++paramnum; PARAM_NAME_AT(paramnum,x)
#define PARAM_SOUND(x) ++paramnum; PARAM_SOUND_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); GETADDR(PA,RC,X_WRITE_NIL);
*(void **)ptr = reg.a[B]; *(void **)ptr = reg.a[B];
NEXTOP; 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): OP(SV2):
ASSERTA(a); ASSERTF(B+1); ASSERTKD(C); ASSERTA(a); ASSERTF(B+1); ASSERTKD(C);
GETADDR(PA,KC,X_WRITE_NIL); 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(SS_R, ss, RPRSRI, NOP, 0, 0),
xx(SP, sp, RPRPKI, SP_R, 4, REGT_INT), // store pointer xx(SP, sp, RPRPKI, SP_R, 4, REGT_INT), // store pointer
xx(SP_R, sp, RPRPRI, NOP, 0, 0), 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, sv2, RPRVKI, SV2_R, 4, REGT_INT), // store vector2
xx(SV2_R, sv2, RPRVRI, NOP, 0, 0), xx(SV2_R, sv2, RPRVRI, NOP, 0, 0),
xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT), // store vector3 xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT), // store vector3