- implemented version checks for compile-time type determination and for class inheritance.

Note that in this system, DECORATE counts as version 2.0, so any data that should be available to DECORATE should not use version restriction.
This commit is contained in:
Christoph Oelckers 2017-03-05 17:58:55 +01:00
parent 7df698dad8
commit 456ac64723
19 changed files with 220 additions and 125 deletions

View file

@ -3146,6 +3146,9 @@ void PClass::Derive(PClass *newclass, FName name)
newclass->Symbols.SetParentTable(&this->Symbols);
newclass->TypeName = name;
newclass->mDescriptiveName.Format("Class<%s>", name.GetChars());
newclass->mVersion = mVersion;
newclass->MetaSize = MetaSize;
}
//==========================================================================
@ -3292,7 +3295,6 @@ PClass *PClass::CreateDerivedClass(FName name, unsigned int size)
type->bRuntimeClass = true;
Derive(type, name);
type->Size = size;
type->MetaSize = MetaSize;
if (size != TentativeClass)
{
type->InitializeDefaults();

View file

@ -91,6 +91,7 @@ public:
PSymbolTable Symbols;
bool MemberOnly = false; // type may only be used as a struct/class member but not as a local variable or function argument.
FString mDescriptiveName;
VersionInfo mVersion = { 0,0,0 };
BYTE loadOp, storeOp, moveOp, RegType, RegCount;
PType(unsigned int size = 1, unsigned int align = 1);

View file

@ -193,6 +193,38 @@ public:
};
struct VersionInfo
{
uint16_t major;
uint16_t minor;
uint32_t revision;
bool operator <=(const VersionInfo &o) const
{
return o.major > this->major || (o.major == this->major && o.minor > this->minor) || (o.major == this->major && o.minor == this->minor && o.revision >= this->revision);
}
bool operator >=(const VersionInfo &o) const
{
return o.major < this->major || (o.major == this->major && o.minor < this->minor) || (o.major == this->major && o.minor == this->minor && o.revision <= this->revision);
}
bool operator > (const VersionInfo &o) const
{
return o.major < this->major || (o.major == this->major && o.minor < this->minor) || (o.major == this->major && o.minor == this->minor && o.revision < this->revision);
}
bool operator < (const VersionInfo &o) const
{
return o.major > this->major || (o.major == this->major && o.minor > this->minor) || (o.major == this->major && o.minor == this->minor && o.revision > this->revision);
}
void operator=(const char *string);
};
// Cannot be a constructor because Lemon would puke on it.
inline VersionInfo MakeVersion(unsigned int ma, unsigned int mi, unsigned int re = 0)
{
return{ (uint16_t)ma, (uint16_t)mi, (uint32_t)re };
}
// Screenshot buffer image data types
enum ESSType

View file

@ -1,23 +1,6 @@
#ifndef __SC_MAN_H__
#define __SC_MAN_H__
struct VersionInfo
{
uint16_t major;
uint16_t minor;
uint32_t revision;
bool Check(unsigned int major, unsigned int minor)
{
return major < this->major || (major == this->major && minor <= this->minor);
}
bool Check(unsigned int major, unsigned int minor, unsigned int revision)
{
return major < this->major || (major == this->major && minor < this->minor) || (major == this->major && minor == this->minor && revision <= this->revision);
}
void operator=(const char *string);
};
class FScanner
{
public:
@ -123,7 +106,7 @@ protected:
BYTE StateMode;
bool StateOptions;
bool Escape;
VersionInfo ParseVersion = { 0, 0 }; // no ZScript extensions by default
VersionInfo ParseVersion = { 0, 0, 0 }; // no ZScript extensions by default
};
enum

View file

@ -146,35 +146,35 @@ std2:
'property' { RET(TK_Property); }
'native' { RET(TK_Native); }
'var' { RET(TK_Var); }
'out' { RET(ParseVersion.Check(1,0)? TK_Out : TK_Identifier); }
'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); }
'static' { RET(TK_Static); }
'transient' { RET(ParseVersion.Check(1,0)? TK_Transient : TK_Identifier); }
'final' { RET(ParseVersion.Check(1,0)? TK_Final : TK_Identifier); }
'extend' { RET(ParseVersion.Check(1,0)? TK_Extend : TK_Identifier); }
'protected' { RET(ParseVersion.Check(1,0)? TK_Protected : TK_Identifier); }
'private' { RET(ParseVersion.Check(1,0)? TK_Private : TK_Identifier); }
'transient' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Transient : TK_Identifier); }
'final' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Final : TK_Identifier); }
'extend' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Extend : TK_Identifier); }
'protected' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Protected : TK_Identifier); }
'private' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Private : TK_Identifier); }
'dot' { RET(TK_Dot); }
'cross' { RET(TK_Cross); }
'virtual' { RET(ParseVersion.Check(1,0)? TK_Virtual : TK_Identifier); }
'override' { RET(ParseVersion.Check(1,0)? TK_Override : TK_Identifier); }
'vararg' { RET(ParseVersion.Check(1,0)? TK_VarArg : TK_Identifier); }
'ui' { RET(ParseVersion.Check(2,4)? TK_UI : TK_Identifier); }
'play' { RET(ParseVersion.Check(2,4)? TK_Play : TK_Identifier); }
'clearscope' { RET(ParseVersion.Check(2,4)? TK_ClearScope : TK_Identifier); }
'virtualscope' { RET(ParseVersion.Check(2,4)? TK_VirtualScope : TK_Identifier); }
'super' { RET(ParseVersion.Check(1,0)? TK_Super : TK_Identifier); }
'virtual' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Virtual : TK_Identifier); }
'override' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Override : TK_Identifier); }
'vararg' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_VarArg : TK_Identifier); }
'ui' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_UI : TK_Identifier); }
'play' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Play : TK_Identifier); }
'clearscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_ClearScope : TK_Identifier); }
'virtualscope' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_VirtualScope : TK_Identifier); }
'super' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Super : TK_Identifier); }
'stop' { RET(TK_Stop); }
'null' { RET(TK_Null); }
'is' { RET(ParseVersion.Check(1,0)? TK_Is : TK_Identifier); }
'replaces' { RET(ParseVersion.Check(1,0)? TK_Replaces : TK_Identifier); }
'is' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Is : TK_Identifier); }
'replaces' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Replaces : TK_Identifier); }
'states' { RET(TK_States); }
'meta' { RET(ParseVersion.Check(1,0)? TK_Meta : TK_Identifier); }
'deprecated' { RET(ParseVersion.Check(1,0)? TK_Deprecated : TK_Identifier); }
'version' { RET(ParseVersion.Check(2,4)? TK_Version : TK_Identifier); }
'action' { RET(ParseVersion.Check(1,0)? TK_Action : TK_Identifier); }
'readonly' { RET(ParseVersion.Check(1,0)? TK_ReadOnly : TK_Identifier); }
'let' { RET(ParseVersion.Check(1,0)? TK_Let : TK_Identifier); }
'meta' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Meta : TK_Identifier); }
'deprecated' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Deprecated : TK_Identifier); }
'version' { RET(ParseVersion >= MakeVersion(2, 4, 0)? TK_Version : TK_Identifier); }
'action' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Action : TK_Identifier); }
'readonly' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_ReadOnly : TK_Identifier); }
'let' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Let : TK_Identifier); }
/* Actor state options */
'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); }

View file

@ -802,7 +802,7 @@ void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
//==========================================================================
FFunctionBuildList FunctionBuildList;
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, const VersionInfo &ver, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
{
auto func = code->GetDirectFunction();
if (func != nullptr)
@ -828,6 +828,7 @@ VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functy
it.StateIndex = stateindex;
it.StateCount = statecount;
it.Lump = lumpnum;
it.Version = ver;
assert(it.Func->Variants.Size() == 1);
it.Func->Variants[0].Implementation = it.Function;

View file

@ -146,13 +146,14 @@ class FFunctionBuildList
int StateIndex;
int StateCount;
int Lump;
VersionInfo Version;
bool FromDecorate;
};
TArray<Item> mItems;
public:
VMFunction *AddFunction(PNamespace *curglobals, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
VMFunction *AddFunction(PNamespace *curglobals, const VersionInfo &ver, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
void Build();
};

View file

@ -119,6 +119,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def, PNamespace *ns)
bag.Namespace = ns;
bag.Info = type;
bag.fromDecorate = true;
bag.Version = { 2, 0, 0 };
#ifdef _DEBUG
bag.ClassName = type->TypeName;
#endif

View file

@ -69,6 +69,10 @@ EXTERN_CVAR(Bool, strictdecorate);
PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName)
{
if (parent->mVersion > MakeVersion(2, 0))
{
sc.Message(MSG_ERROR, "Parent class %s of %s not accessible to DECORATE", parent->GetClass()->TypeName.GetChars(), typeName.GetChars());
}
PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size));
if (type == nullptr)
{
@ -1125,6 +1129,7 @@ static void ParseActor(FScanner &sc, PNamespace *ns)
Baggage bag;
bag.Namespace = ns;
bag.Version = { 2, 0, 0 };
bag.fromDecorate = true;
info = ParseActorHeader(sc, &bag);
sc.MustGetToken('{');

View file

@ -340,7 +340,7 @@ endofstate:
if (ScriptCode != nullptr)
{
auto funcsym = CreateAnonymousFunction(actor, nullptr, state.UseFlags);
state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
state.ActionFunc = FunctionBuildList.AddFunction(bag.Namespace, bag.Version, funcsym, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true, bag.statedef.GetStateCount(), int(statestring.Len()), sc.LumpNum);
}
int count = bag.statedef.AddStates(&state, statestring, scp);
if (count < 0)

View file

@ -26,6 +26,7 @@ class PSymbol : public DObject
DECLARE_ABSTRACT_CLASS(PSymbol, DObject);
public:
FName SymbolName;
VersionInfo mVersion = { 0,0,0 };
protected:
PSymbol(FName name) { SymbolName = name; }

View file

@ -228,7 +228,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam
//
//==========================================================================
void CreateDamageFunction(PNamespace *OutNamespace, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
void CreateDamageFunction(PNamespace *OutNamespace, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum)
{
if (id == nullptr)
{
@ -238,7 +238,7 @@ void CreateDamageFunction(PNamespace *OutNamespace, PClassActor *info, AActor *d
{
auto dmg = new FxReturnStatement(new FxIntCast(id, true), id->ScriptPosition);
auto funcsym = CreateAnonymousFunction(info, TypeSInt32, 0);
defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
defaults->DamageFunc = FunctionBuildList.AddFunction(OutNamespace, ver, funcsym, dmg, FStringf("%s.DamageFunction", info->TypeName.GetChars()), fromDecorate, -1, 0, lumpnum);
}
}

View file

@ -124,6 +124,7 @@ struct Baggage
bool fromDecorate;
int CurrentState;
int Lumpnum;
VersionInfo Version;
FStateDefinitions statedef;
FDropItem *DropItemList;
@ -161,7 +162,7 @@ FName CheckCastKludges(FName in);
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, TArray<FName> *argnames, PStruct *cls, DWORD funcflags, int useflags);
PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, int flags);
PFunction *FindClassMemberFunction(PStruct *cls, PStruct *funccls, FName name, FScriptPosition &sc, bool *error);
void CreateDamageFunction(PNamespace *ns, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
void CreateDamageFunction(PNamespace *ns, const VersionInfo &ver, PClassActor *info, AActor *defaults, FxExpression *id, bool fromDecorate, int lumpnum);
//==========================================================================
//

View file

@ -679,7 +679,7 @@ DEFINE_PROPERTY(damage, X, Actor)
defaults->DamageVal = dmgval;
// Only DECORATE can get here with a valid expression.
CreateDamageFunction(bag.Namespace, bag.Info, defaults, id, true, bag.Lumpnum);
CreateDamageFunction(bag.Namespace, bag.Version, bag.Info, defaults, id, true, bag.Lumpnum);
}
//==========================================================================

View file

@ -873,6 +873,9 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
decl->Name = C.FuncName;
decl->UseFlags = A == nullptr? nullptr : A->Id;
decl->Flags = (A == nullptr? 0 : A->Flags) | C.FuncFlags;
if (A == nullptr) decl->Version = {0,0,0};
else decl->Version = A->Version;
decl->Body = C.FuncBody;
X = decl;
}
@ -881,7 +884,16 @@ declarator(X) ::= decl_flags(A) type_list_or_void(B) variables_or_function(C).
NEW_AST_NODE(VarDeclarator, decl, A == nullptr? B->SourceLoc : A->SourceLoc);
decl->Type = B;
decl->Names = C.VarNames;
decl->Flags = (A == nullptr? 0 : A->Flags);
if (A == nullptr)
{
decl->Flags = 0;
decl->Version = {0,0,0};
}
else
{
decl->Flags = A->Flags;
decl->Version = A->Version;
}
X = decl;
}
else

View file

@ -275,8 +275,8 @@ void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZC
//
//==========================================================================
ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PNamespace *_outnamespc, int lumpnum)
: Outer(_outer), GlobalTreeNodes(&_symbols), OutNamespace(_outnamespc), AST(ast), Lump(lumpnum)
ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PNamespace *_outnamespc, int lumpnum, const VersionInfo &ver)
: Outer(_outer), GlobalTreeNodes(&_symbols), OutNamespace(_outnamespc), AST(ast), Lump(lumpnum), mVersion(ver)
{
FScriptPosition::ResetErrorCounter();
// Group top-level nodes by type
@ -495,20 +495,32 @@ void ZCCCompiler::CreateStructTypes()
{
s->strct->Type = NewStruct(s->NodeName(), outer);
}
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
if (s->strct->Flags & ZCC_Version)
{
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
s->strct->Type->mVersion = s->strct->Version;
}
if (outer != OutNamespace) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags));
else if (s->strct->Flags & ZCC_ClearScope) Warn(s->strct, "Useless 'ClearScope' on struct %s not inside a class", s->NodeName().GetChars());
if (s->strct->Flags & ZCC_UIFlag)
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_UI);
if (s->strct->Flags & ZCC_Play)
if (mVersion >= MakeVersion(2, 4, 0))
{
if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play))
{
Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars());
}
if (outer != OutNamespace) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags));
else if (s->strct->Flags & ZCC_ClearScope) Warn(s->strct, "Useless 'ClearScope' on struct %s not inside a class", s->NodeName().GetChars());
if (s->strct->Flags & ZCC_UIFlag)
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_UI);
if (s->strct->Flags & ZCC_Play)
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_Play);
if (s->strct->Flags & ZCC_ClearScope)
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_PlainData); // don't inherit the scope from the outer class
}
else
{
// old versions force 'play'.
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_Play);
if (s->strct->Flags & ZCC_ClearScope)
s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_PlainData); // don't inherit the scope from the outer class
}
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
syms->AddSymbol(s->strct->Symbol);
@ -597,6 +609,10 @@ void ZCCCompiler::CreateClassTypes()
// We will never get here if the name is a duplicate, so we can just do the assignment.
try
{
if (parent->mVersion > mVersion)
{
Error(c->cls, "Parent class %s of %s not accessible to ZScript version %d.%d.%d", parent->GetClass()->TypeName.GetChars(), c->NodeName().GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
}
c->cls->Type = parent->CreateDerivedClass(c->NodeName(), TentativeClass);
if (c->Type() == nullptr)
{
@ -614,28 +630,40 @@ void ZCCCompiler::CreateClassTypes()
if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName());
if (c->cls->Flags & ZCC_Abstract)
c->Type()->ObjectFlags |= OF_Abstract;
//
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
int incompatiblecnt = 0;
for (size_t k = 0; k < countof(incompatible); k++)
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
if (incompatiblecnt > 1)
if (c->cls->Flags & ZCC_Version)
{
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
c->Type()->mVersion = c->cls->Version;
}
if (c->cls->Flags & ZCC_UIFlag)
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
if (c->cls->Flags & ZCC_Play)
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play
//
if (mVersion >= MakeVersion(2, 4, 0))
{
if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play))
static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope };
int incompatiblecnt = 0;
for (size_t k = 0; k < countof(incompatible); k++)
if (incompatible[k] & c->cls->Flags) incompatiblecnt++;
if (incompatiblecnt > 1)
{
Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars());
Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars());
}
c->Type()->ObjectFlags = (c->Type()->ObjectFlags & ~(OF_UI | OF_Play)) | (parent->ObjectFlags & (OF_UI | OF_Play));
if (c->cls->Flags & ZCC_UIFlag)
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI;
if (c->cls->Flags & ZCC_Play)
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
if (parent->ObjectFlags & (OF_UI | OF_Play)) // parent is either ui or play
{
if (c->cls->Flags & (ZCC_UIFlag | ZCC_Play))
{
Error(c->cls, "Can't change class scope in class %s", c->NodeName().GetChars());
}
c->Type()->ObjectFlags = (c->Type()->ObjectFlags & ~(OF_UI | OF_Play)) | (parent->ObjectFlags & (OF_UI | OF_Play));
}
}
else
{
c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_UI) | OF_Play;
}
c->Type()->bExported = true; // this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.)
@ -1102,16 +1130,23 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
if (field->Flags & ZCC_Transient) varflags |= VARF_Transient;
if (type->ObjectFlags & OF_UI)
varflags |= VARF_UI;
if (type->ObjectFlags & OF_Play)
if (mVersion >= MakeVersion(2, 4, 0))
{
if (type->ObjectFlags & OF_UI)
varflags |= VARF_UI;
if (type->ObjectFlags & OF_Play)
varflags |= VARF_Play;
if (field->Flags & ZCC_UIFlag)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
if (field->Flags & ZCC_Play)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
if (field->Flags & ZCC_ClearScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData);
}
else
{
varflags |= VARF_Play;
if (field->Flags & ZCC_UIFlag)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
if (field->Flags & ZCC_Play)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
if (field->Flags & ZCC_ClearScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData);
}
if (field->Flags & ZCC_Native)
{
@ -1178,7 +1213,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
{
// for bit fields the type must point to the source variable.
if (fd->BitValue != 0) thisfieldtype = fd->FieldSize == 1 ? TypeUInt8 : fd->FieldSize == 2 ? TypeUInt16 : TypeUInt32;
type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
auto f = type->AddNativeField(name->Name, thisfieldtype, fd->FieldOffset, varflags, fd->BitValue);
if (field->Flags & (ZCC_Version | ZCC_Deprecated)) f->mVersion = field->Version;
}
}
}
@ -1188,7 +1224,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fiel
}
else
{
type->AddField(name->Name, thisfieldtype, varflags);
auto f = type->AddField(name->Name, thisfieldtype, varflags);
if (field->Flags & (ZCC_Version | ZCC_Deprecated)) f->mVersion = field->Version;
}
}
name = static_cast<ZCC_VarName*>(name->SiblingNext);
@ -1471,6 +1508,11 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
Error(field, "%s does not represent a class type", FName(ctype->Restriction->Id).GetChars());
return TypeError;
}
if (typesym->Type->mVersion > mVersion)
{
Error(field, "Class %s not accessible to ZScript version %d.%d.%d", FName(ctype->Restriction->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
return TypeError;
}
retval = NewClassPointer(static_cast<PClass *>(typesym->Type));
}
break;
@ -1504,6 +1546,12 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
{
auto ptype = static_cast<PSymbolType *>(sym)->Type;
if (ptype->mVersion > mVersion)
{
Error(type, "Type %s not accessible to ZScript version %d.%d.%d", FName(type->UserType->Id).GetChars(), mVersion.major, mVersion.minor, mVersion.revision);
return TypeError;
}
if (ptype->IsKindOf(RUNTIME_CLASS(PEnum)))
{
return TypeSInt32; // hack this to an integer until we can resolve the enum mess.
@ -1850,7 +1898,7 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro
if (namenode->Id == NAME_DamageFunction)
{
auto x = ConvertNode(prop->Values);
CreateDamageFunction(OutNamespace, cls, (AActor *)bag.Info->Defaults, x, false, Lump);
CreateDamageFunction(OutNamespace, mVersion, cls, (AActor *)bag.Info->Defaults, x, false, Lump);
((AActor *)bag.Info->Defaults)->DamageVal = -1;
return;
}
@ -2005,6 +2053,7 @@ void ZCCCompiler::InitDefaults()
#ifdef _DEBUG
bag.ClassName = c->Type()->TypeName;
#endif
bag.Version = mVersion;
bag.Namespace = OutNamespace;
bag.Info = ti;
bag.DropItemSet = false;
@ -2128,31 +2177,27 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
if (f->Flags & ZCC_VarArg) varflags |= VARF_VarArg;
if (f->Flags & ZCC_FuncConst) varflags |= VARF_ReadOnly; // FuncConst method is internally marked as VARF_ReadOnly
if (c->Type()->ObjectFlags & OF_UI)
varflags |= VARF_UI;
if (c->Type()->ObjectFlags & OF_Play)
varflags |= VARF_Play;
//if (f->Flags & ZCC_FuncConst)
// varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
if (f->Flags & ZCC_UIFlag)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
if (f->Flags & ZCC_Play)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
if (f->Flags & ZCC_ClearScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear);
if (f->Flags & ZCC_VirtualScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual);
// [ZZ] supporting const self for actors is quite a cumbersome task because there's no concept of a const pointer (?)
// either way, it doesn't make sense, because you can call any method on a readonly class instance.
// The above is nonsense. This needs to work and needs to be implemented properly.
/*
if ((f->Flags & ZCC_FuncConst) && (c->Type()->IsKindOf(RUNTIME_CLASS(PClass))))
if (mVersion >= MakeVersion(2, 4, 0))
{
Error(f, "'Const' on a method can only be used in structs");
if (c->Type()->ObjectFlags & OF_UI)
varflags |= VARF_UI;
if (c->Type()->ObjectFlags & OF_Play)
varflags |= VARF_Play;
//if (f->Flags & ZCC_FuncConst)
// varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag.
if (f->Flags & ZCC_UIFlag)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI);
if (f->Flags & ZCC_Play)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play);
if (f->Flags & ZCC_ClearScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear);
if (f->Flags & ZCC_VirtualScope)
varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual);
}
else
{
varflags |= VARF_Play;
}
*/
if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native))
{
@ -2390,6 +2435,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
if (imp != nullptr) vindex = imp->VirtualIndex;
else Error(f, "Virtual base function %s not found in %s", FName(f->Name).GetChars(), cls->ParentClass->TypeName.GetChars());
}
if (f->Flags & (ZCC_Version | ZCC_Deprecated))
{
sym->mVersion = f->Version;
if (varflags & VARF_Override)
{
Error(f, "Overridden function %s may not alter version restriction in %s", FName(f->Name).GetChars(), cls->ParentClass->TypeName.GetChars());
}
}
if (!(f->Flags & ZCC_Native))
{
@ -2403,7 +2456,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
auto code = ConvertAST(c->Type(), f->Body);
if (code != nullptr)
{
FunctionBuildList.AddFunction(OutNamespace, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
FunctionBuildList.AddFunction(OutNamespace, mVersion, sym, code, FStringf("%s.%s", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()), false, -1, 0, Lump);
}
}
}
@ -2739,7 +2792,7 @@ void ZCCCompiler::CompileStates()
if (code != nullptr)
{
auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, state.UseFlags);
state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
state.ActionFunc = FunctionBuildList.AddFunction(OutNamespace, mVersion, funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false, statedef.GetStateCount(), (int)sl->Frames->Len(), Lump);
}
}

View file

@ -86,7 +86,7 @@ struct ZCC_ConstantWork
class ZCCCompiler
{
public:
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum);
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver);
~ZCCCompiler();
int Compile();
@ -127,6 +127,7 @@ private:
TArray<ZCC_StructWork *> Structs;
TArray<ZCC_ClassWork *> Classes;
TArray<ZCC_PropertyWork *> Properties;
VersionInfo mVersion;
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);

View file

@ -360,34 +360,34 @@ static void DoParse(int lumpnum)
{
char *endp;
sc.MustGetString();
state.ParseVersion.major = (int16_t)clamp<long long>(strtoll(sc.String, &endp, 10), -1, USHRT_MAX);
state.ParseVersion.major = (int16_t)clamp<unsigned long long>(strtoull(sc.String, &endp, 10), 0, USHRT_MAX);
if (*endp != '.')
{
sc.ScriptError("Bad version directive");
}
state.ParseVersion.minor = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
state.ParseVersion.minor = (int16_t)clamp<unsigned long long>(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX);
if (*endp == '.')
{
state.ParseVersion.revision = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
state.ParseVersion.revision = (int16_t)clamp<unsigned long long>(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX);
}
else state.ParseVersion.revision = 0;
if (*endp != 0)
{
sc.ScriptError("Bad version directive");
}
if (state.ParseVersion.major < 0 || state.ParseVersion.minor < 0 || state.ParseVersion.revision < 0)
if (state.ParseVersion.major == USHRT_MAX || state.ParseVersion.minor == USHRT_MAX || state.ParseVersion.revision == USHRT_MAX)
{
sc.ScriptError("Bad version directive");
}
if (!state.ParseVersion.Check(VER_MAJOR, VER_MINOR, VER_REVISION))
if (state.ParseVersion > MakeVersion(VER_MAJOR, VER_MINOR, VER_REVISION))
{
sc.ScriptError("Version mismatch. %d.%d expected but only %d.%d supported", state.ParseVersion.major, state.ParseVersion.minor, VER_MAJOR, VER_MINOR);
sc.ScriptError("Version mismatch. %d.%d.%d expected but only %d.%d.%d supported", state.ParseVersion.major, state.ParseVersion.minor, state.ParseVersion.revision, VER_MAJOR, VER_MINOR, VER_REVISION);
}
}
else
{
state.ParseVersion.major = 1; // 0 excludes all ZSCRIPT keywords from the parser.
state.ParseVersion.minor = 0;
state.ParseVersion.major = 2; // 2.3 is the first version of ZScript.
state.ParseVersion.minor = 3;
sc.RestorePos(saved);
}
}
@ -452,7 +452,7 @@ static void DoParse(int lumpnum)
PSymbolTable symtable;
auto newns = Wads.GetLumpFile(lumpnum) == 0 ? Namespaces.GlobalNamespace : Namespaces.NewNamespace(Wads.GetLumpFile(lumpnum));
ZCCCompiler cc(state, NULL, symtable, newns, lumpnum);
ZCCCompiler cc(state, NULL, symtable, newns, lumpnum, state.ParseVersion);
cc.Compile();
if (FScriptPosition::ErrorCounter > 0)

View file

@ -500,6 +500,7 @@ struct ZCC_Declarator : ZCC_TreeNode
{
ZCC_Type *Type;
int Flags;
VersionInfo Version;
};
// A variable in a class or struct.