- implemented the basics of a working metadata system.

This will store class meta properties in a separate memory block so that it won't have to muck around with PClass - which made the implementation from the scripting branch relatively useless because extending the data wasn't particularly easy and also not well implemented. This can now be handled just like the defaults.
This commit is contained in:
Christoph Oelckers 2017-02-27 23:28:19 +01:00
parent d5d383ee93
commit f343d36ea9
9 changed files with 128 additions and 41 deletions

View file

@ -2910,6 +2910,11 @@ PClass::~PClass()
M_Free(Defaults); M_Free(Defaults);
Defaults = nullptr; Defaults = nullptr;
} }
if (Meta != nullptr)
{
M_Free(Meta);
Meta = nullptr;
}
} }
//========================================================================== //==========================================================================
@ -3047,7 +3052,7 @@ PClass *PClass::FindClass (FName zaname)
// //
//========================================================================== //==========================================================================
DObject *PClass::CreateNew() const DObject *PClass::CreateNew()
{ {
BYTE *mem = (BYTE *)M_Malloc (Size); BYTE *mem = (BYTE *)M_Malloc (Size);
assert (mem != nullptr); assert (mem != nullptr);
@ -3064,7 +3069,7 @@ DObject *PClass::CreateNew() const
} }
ConstructNative (mem); ConstructNative (mem);
((DObject *)mem)->SetClass (const_cast<PClass *>(this)); ((DObject *)mem)->SetClass (const_cast<PClass *>(this));
InitializeSpecials(mem, Defaults); InitializeSpecials(mem, Defaults, &PClass::SpecialInits);
return (DObject *)mem; return (DObject *)mem;
} }
@ -3076,7 +3081,7 @@ DObject *PClass::CreateNew() const
// //
//========================================================================== //==========================================================================
void PClass::InitializeSpecials(void *addr, void *defaults) const void PClass::InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits)
{ {
// Once we reach a native class, we can stop going up the family tree, // Once we reach a native class, we can stop going up the family tree,
// since native classes handle initialization natively. // since native classes handle initialization natively.
@ -3085,8 +3090,8 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const
return; return;
} }
assert(ParentClass != nullptr); assert(ParentClass != nullptr);
ParentClass->InitializeSpecials(addr, defaults); ParentClass->InitializeSpecials(addr, defaults, Inits);
for (auto tao : SpecialInits) for (auto tao : (this->*Inits))
{ {
tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second); tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second);
} }
@ -3101,7 +3106,7 @@ void PClass::InitializeSpecials(void *addr, void *defaults) const
// //
//========================================================================== //==========================================================================
void PClass::DestroySpecials(void *addr) const void PClass::DestroySpecials(void *addr)
{ {
// Once we reach a native class, we can stop going up the family tree, // Once we reach a native class, we can stop going up the family tree,
// since native classes handle deinitialization natively. // since native classes handle deinitialization natively.
@ -3160,7 +3165,6 @@ void PClass::InitializeDefaults()
optr->ObjNext = nullptr; optr->ObjNext = nullptr;
optr->SetClass(this); optr->SetClass(this);
// 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 != nullptr) if (ParentClass->Defaults != nullptr)
{ {
@ -3174,19 +3178,31 @@ void PClass::InitializeDefaults()
{ {
memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject)); memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject));
} }
if (MetaSize != 0)
{
Meta = (BYTE*)M_Malloc(MetaSize);
memset(Meta, 0, MetaSize);
if (ParentClass->MetaSize > 0) memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize);
}
} }
if (bRuntimeClass) if (bRuntimeClass)
{ {
// Copy parent values from the parent defaults. // Copy parent values from the parent defaults.
assert(ParentClass != nullptr); assert(ParentClass != nullptr);
if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults, &PClass::SpecialInits);
if (Meta != nullptr) ParentClass->InitializeSpecials(Meta, ParentClass->Meta, &PClass::MetaInits);
for (const PField *field : Fields) for (const PField *field : Fields)
{ {
if (!(field->Flags & VARF_Native)) if (!(field->Flags & VARF_Native) && !(field->Flags & VARF_Meta))
{ {
field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits); field->Type->SetDefaultValue(Defaults, unsigned(field->Offset), &SpecialInits);
} }
if (!(field->Flags & VARF_Native) && (field->Flags & VARF_Meta))
{
field->Type->SetDefaultValue(Meta, unsigned(field->Offset), &MetaInits);
}
} }
} }
} }
@ -3264,6 +3280,39 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
return type; return type;
} }
//==========================================================================
//
// PStruct :: AddField
//
// Appends a new metadata field to the end of a struct. Returns either the new field
// or nullptr if a symbol by that name already exists.
//
//==========================================================================
PField *PClass::AddMetaField(FName name, PType *type, DWORD flags)
{
PField *field = new PField(name, type, flags);
// The new field is added to the end of this struct, alignment permitting.
field->Offset = (MetaSize + (type->Align - 1)) & ~(type->Align - 1);
// Enlarge this struct to enclose the new field.
MetaSize = unsigned(field->Offset + type->Size);
// This struct's alignment is the same as the largest alignment of any of
// its fields.
Align = MAX(Align, type->Align);
if (Symbols.AddSymbol(field) == nullptr)
{ // name is already in use
field->Destroy();
return nullptr;
}
Fields.Push(field);
return field;
}
//========================================================================== //==========================================================================
// //
// PClass :: AddField // PClass :: AddField
@ -3272,6 +3321,8 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
PField *PClass::AddField(FName name, PType *type, DWORD flags) PField *PClass::AddField(FName name, PType *type, DWORD flags)
{ {
if (!(flags & VARF_Meta))
{
unsigned oldsize = Size; unsigned oldsize = Size;
PField *field = Super::AddField(name, type, flags); PField *field = Super::AddField(name, type, flags);
@ -3284,6 +3335,22 @@ PField *PClass::AddField(FName name, PType *type, DWORD flags)
memset(Defaults + oldsize, 0, Size - oldsize); memset(Defaults + oldsize, 0, Size - oldsize);
} }
return field; return field;
}
else
{
unsigned oldsize = MetaSize;
PField *field = AddMetaField(name, type, flags);
// Only initialize the defaults if they have already been created.
// For ZScript this is not the case, it will first define all fields before
// setting up any defaults for any class.
if (field != nullptr && !(flags & VARF_Native) && Meta != nullptr)
{
Meta = (BYTE *)M_Realloc(Meta, MetaSize);
memset(Meta + oldsize, 0, MetaSize - oldsize);
}
return field;
}
} }
//========================================================================== //==========================================================================

View file

@ -557,12 +557,13 @@ enum
class PClass : public PNativeStruct class PClass : public PNativeStruct
{ {
DECLARE_CLASS(PClass, PNativeStruct); DECLARE_CLASS(PClass, PNativeStruct);
protected:
// We unravel _WITH_META here just as we did for PType. // We unravel _WITH_META here just as we did for PType.
TArray<FTypeAndOffset> SpecialInits; protected:
TArray<FTypeAndOffset> MetaInits;
void Derive(PClass *newclass, FName name); void Derive(PClass *newclass, FName name);
void InitializeSpecials(void *addr, void *defaults) const; void InitializeSpecials(void *addr, void *defaults, TArray<FTypeAndOffset> PClass::*Inits);
void SetSuper(); void SetSuper();
PField *AddMetaField(FName name, PType *type, DWORD flags);
public: public:
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
void WriteAllFields(FSerializer &ar, const void *addr) const; void WriteAllFields(FSerializer &ar, const void *addr) const;
@ -577,11 +578,14 @@ public:
static void StaticBootstrap(); static void StaticBootstrap();
// Per-class information ------------------------------------- // Per-class information -------------------------------------
TArray<FTypeAndOffset> SpecialInits;
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. const size_t *ArrayPointers; // dynamic arrays containing object pointers.
BYTE *Defaults; BYTE *Defaults;
BYTE *Meta; // Per-class static script data
unsigned MetaSize;
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
bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility bool bDecorateClass; // may be subject to some idiosyncracies due to DECORATE backwards compatibility
@ -593,13 +597,13 @@ public:
PClass(); PClass();
~PClass(); ~PClass();
void InsertIntoHash(); void InsertIntoHash();
DObject *CreateNew() const; DObject *CreateNew();
PClass *CreateDerivedClass(FName name, unsigned int size); PClass *CreateDerivedClass(FName name, unsigned int size);
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 BuildArrayPointers();
void DestroySpecials(void *addr) const; void DestroySpecials(void *addr);
const PClass *NativeClass() const; const PClass *NativeClass() const;
// Returns true if this type is an ancestor of (or same as) the passed type. // Returns true if this type is an ancestor of (or same as) the passed type.

View file

@ -58,15 +58,15 @@ enum EScrollDir
}; };
// actions that don't create objects // actions that don't create objects
#define WIPER_ID ((const PClass*)intptr_t(-1)) #define WIPER_ID ((PClass*)intptr_t(-1))
#define TITLE_ID ((const PClass*)intptr_t(-2)) #define TITLE_ID ((PClass*)intptr_t(-2))
//========================================================================== //==========================================================================
struct FIntermissionAction struct FIntermissionAction
{ {
int mSize; int mSize;
const PClass *mClass; PClass *mClass;
FString mMusic; FString mMusic;
int mMusicOrder; int mMusicOrder;
int mCdTrack; int mCdTrack;

View file

@ -472,7 +472,7 @@ void M_SetMenu(FName menu, int param)
} }
else else
{ {
const PClass *menuclass = PClass::FindClass(menu); PClass *menuclass = PClass::FindClass(menu);
if (menuclass != nullptr) if (menuclass != nullptr)
{ {
if (menuclass->IsDescendantOf("GenericMenu")) if (menuclass->IsDescendantOf("GenericMenu"))

View file

@ -6371,7 +6371,7 @@ ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build)
ExpEmit ob = obj->Emit(build); ExpEmit ob = obj->Emit(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_CLSS, meta.RegNum, ob.RegNum);
build->Emit(OP_LOS, 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;
@ -6805,7 +6805,7 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build)
{ {
obj.Free(build); obj.Free(build);
ExpEmit meta(build, REGT_POINTER); ExpEmit meta(build, REGT_POINTER);
build->Emit(OP_META, meta.RegNum, obj.RegNum); build->Emit(membervar->Flags & VARF_Native? OP_CLSS : OP_META, meta.RegNum, obj.RegNum);
obj = meta; obj = meta;
} }
@ -8832,7 +8832,7 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
ExpEmit op = Self->Emit(build); ExpEmit op = Self->Emit(build);
op.Free(build); op.Free(build);
ExpEmit to(build, REGT_POINTER); ExpEmit to(build, REGT_POINTER);
build->Emit(OP_META, to.RegNum, op.RegNum); build->Emit(OP_CLSS, to.RegNum, op.RegNum);
return to; return to;
} }
@ -8873,7 +8873,7 @@ ExpEmit FxGetParentClass::Emit(VMFunctionBuilder *build)
if (Self->IsObject()) if (Self->IsObject())
{ {
ExpEmit to(build, REGT_POINTER); ExpEmit to(build, REGT_POINTER);
build->Emit(OP_META, to.RegNum, op.RegNum); build->Emit(OP_CLSS, to.RegNum, op.RegNum);
op = to; op = to;
op.Free(build); op.Free(build);
} }

View file

@ -826,10 +826,17 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul
if (i > 0) sc.MustGetStringName(","); if (i > 0) sc.MustGetStringName(",");
if (f->Flags & VARF_Meta) if (f->Flags & VARF_Meta)
{
if (f->Flags & VARF_Native)
{ {
addr = ((char*)bag.Info) + f->Offset; addr = ((char*)bag.Info) + f->Offset;
} }
else else
{
addr = ((char*)bag.Info->Meta) + f->Offset;
}
}
else
{ {
addr = ((char*)defaults) + f->Offset; addr = ((char*)defaults) + f->Offset;
} }

View file

@ -109,12 +109,18 @@ begin:
reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts. reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts.
NEXTOP; NEXTOP;
OP(META): OP(CLSS):
ASSERTA(a); ASSERTO(B); ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer... reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT; reg.atag[a] = ATAG_OBJECT;
NEXTOP; NEXTOP;
OP(META):
ASSERTA(a); ASSERTO(B);
reg.a[a] = ((DObject*)reg.a[B])->GetClass()->Meta; // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer...
reg.atag[a] = ATAG_OBJECT;
NEXTOP;
OP(LB): OP(LB):
ASSERTD(a); ASSERTA(B); ASSERTKD(C); ASSERTD(a); ASSERTA(B); ASSERTKD(C);
GETADDR(PB,KC,X_READ_NIL); GETADDR(PB,KC,X_READ_NIL);

View file

@ -23,7 +23,8 @@ xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed
xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed
xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed
xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer
xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta data address
xx(CLSS, clss, RPRP, NOP, 0, 0), // load a class's descriptor address
// Load from memory. rA = *(rB + rkC) // Load from memory. rA = *(rB + rkC)
xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte

View file

@ -1070,11 +1070,6 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Meta) if (field->Flags & ZCC_Meta)
{ {
varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly
if (!(field->Flags & ZCC_Native))
{
// Non-native meta data is not implemented yet and requires some groundwork in the class copy code.
Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars());
}
} }
if (field->Type->ArraySize != nullptr) if (field->Type->ArraySize != nullptr)
@ -1691,10 +1686,17 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop
void *addr; void *addr;
if (f->Flags & VARF_Meta) if (f->Flags & VARF_Meta)
{
if (f->Flags & VARF_Native)
{ {
addr = ((char*)bag.Info) + f->Offset; addr = ((char*)bag.Info) + f->Offset;
} }
else else
{
addr = ((char*)bag.Info->Meta) + f->Offset;
}
}
else
{ {
addr = ((char*)defaults) + f->Offset; addr = ((char*)defaults) + f->Offset;
} }