diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 1a582d366..13b1c7ed9 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 1d134bd07..0ec96b304 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 c88ec842d..fda7d00fa 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 b6be8df60..25565ad9a 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 d8b81aef0..1c98e9272 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 3be1000c5..e3942c33c 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 300cfd843..a5274794a 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 e656445fc..e926fdbbd 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 72cac7cde..05a927288 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 b3d4c904a..7bfd5c685 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 3c9d1f8f3..6f9519065 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 99353de6e..e63f9aa71 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 5558ffe3f..3f42e8c7a 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 6212f0ff1..7d70cfd0f 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 6259301f2..c012d71e9 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 49485da4e..0695b2070 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 dc5fea565..3bc9aa178 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 9072a9d85..521ac9f67 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 ead59472e..49384b522 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.