From 456ac64723945ddf91a39a78c926d605ac8c3427 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 17:58:55 +0100 Subject: [PATCH] - 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. --- src/dobjtype.cpp | 4 +- src/dobjtype.h | 1 + src/doomtype.h | 32 ++++ src/sc_man.h | 19 +-- src/sc_man_scanner.re | 44 ++--- src/scripting/backend/vmbuilder.cpp | 3 +- src/scripting/backend/vmbuilder.h | 3 +- src/scripting/decorate/olddecorations.cpp | 1 + src/scripting/decorate/thingdef_parse.cpp | 5 + src/scripting/decorate/thingdef_states.cpp | 2 +- src/scripting/symbols.h | 1 + src/scripting/thingdef.cpp | 4 +- src/scripting/thingdef.h | 3 +- src/scripting/thingdef_properties.cpp | 2 +- src/scripting/zscript/zcc-parse.lemon | 14 +- src/scripting/zscript/zcc_compile.cpp | 185 +++++++++++++-------- src/scripting/zscript/zcc_compile.h | 3 +- src/scripting/zscript/zcc_parser.cpp | 18 +- src/scripting/zscript/zcc_parser.h | 1 + 19 files changed, 220 insertions(+), 125 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 1a582d3662..13b1c7ed94 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -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(); diff --git a/src/dobjtype.h b/src/dobjtype.h index 1d134bd078..0ec96b304f 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -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); diff --git a/src/doomtype.h b/src/doomtype.h index c88ec842d9..fda7d00fa7 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -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 diff --git a/src/sc_man.h b/src/sc_man.h index b6be8df606..25565ad9af 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -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 diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index d8b81aef0c..1c98e92721 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -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); } diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index 3be1000c59..e3942c33ce 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -802,7 +802,7 @@ void VMFunctionBuilder::BackpatchListToHere(TArray &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; diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h index 300cfd843e..a5274794a7 100644 --- a/src/scripting/backend/vmbuilder.h +++ b/src/scripting/backend/vmbuilder.h @@ -146,13 +146,14 @@ class FFunctionBuildList int StateIndex; int StateCount; int Lump; + VersionInfo Version; bool FromDecorate; }; TArray 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(); }; diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index e656445fc0..e926fdbbd1 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -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 diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 72cac7cde7..05a9272886 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -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(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('{'); diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index b3d4c904a2..7bfd5c685f 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -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) diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h index 3c9d1f8f32..6f95190658 100644 --- a/src/scripting/symbols.h +++ b/src/scripting/symbols.h @@ -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; } diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 99353de6e7..e63f9aa712 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -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); } } diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 5558ffe3fc..3f42e8c7aa 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -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 *args, TArray *argflags, TArray *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); //========================================================================== // diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 6212f0ff1d..7d70cfd0f3 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -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); } //========================================================================== diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 6259301f22..c012d71e98 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -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 diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 49485da4e1..0695b20708 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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 &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 &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 &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(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(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(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); } } diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index dc5fea565c..3bc9aa1780 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -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 Structs; TArray Classes; TArray Properties; + VersionInfo mVersion; PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 9072a9d85c..521ac9f678 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -360,34 +360,34 @@ static void DoParse(int lumpnum) { char *endp; sc.MustGetString(); - state.ParseVersion.major = (int16_t)clamp(strtoll(sc.String, &endp, 10), -1, USHRT_MAX); + state.ParseVersion.major = (int16_t)clamp(strtoull(sc.String, &endp, 10), 0, USHRT_MAX); if (*endp != '.') { sc.ScriptError("Bad version directive"); } - state.ParseVersion.minor = (int16_t)clamp(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX); + state.ParseVersion.minor = (int16_t)clamp(strtoll(endp + 1, &endp, 10), 0, USHRT_MAX); if (*endp == '.') { - state.ParseVersion.revision = (int16_t)clamp(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX); + state.ParseVersion.revision = (int16_t)clamp(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) diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index ead59472ea..49384b522e 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -500,6 +500,7 @@ struct ZCC_Declarator : ZCC_TreeNode { ZCC_Type *Type; int Flags; + VersionInfo Version; }; // A variable in a class or struct.