diff --git a/src/common/engine/sc_man_scanner.re b/src/common/engine/sc_man_scanner.re index b5cce6d0944..dfc3f050f0e 100644 --- a/src/common/engine/sc_man_scanner.re +++ b/src/common/engine/sc_man_scanner.re @@ -204,6 +204,7 @@ std2: 'stop' { RET(TK_Stop); } 'null' { RET(TK_Null); } 'nullptr' { RET(ParseVersion >= MakeVersion(4, 9, 0)? TK_Null : TK_Identifier); } + 'sealed' { RET(ParseVersion >= MakeVersion(4, 12, 0)? TK_Sealed : TK_Identifier); } 'is' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Is : TK_Identifier); } 'replaces' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Replaces : TK_Identifier); } diff --git a/src/common/engine/sc_man_tokens.h b/src/common/engine/sc_man_tokens.h index d93e5be622e..8fb2f88e3ef 100644 --- a/src/common/engine/sc_man_tokens.h +++ b/src/common/engine/sc_man_tokens.h @@ -122,6 +122,7 @@ xx(TK_Null, "'null'") xx(TK_Global, "'global'") xx(TK_Stop, "'stop'") xx(TK_Include, "'include'") +xx(TK_Sealed, "'sealed'") xx(TK_Is, "'is'") xx(TK_Replaces, "'replaces'") diff --git a/src/common/objects/dobjtype.h b/src/common/objects/dobjtype.h index c8464a03012..81fbe3aac21 100644 --- a/src/common/objects/dobjtype.h +++ b/src/common/objects/dobjtype.h @@ -66,11 +66,14 @@ class PClass bool bRuntimeClass = false; // class was defined at run-time, not compile-time bool bDecorateClass = false; // may be subject to some idiosyncracies due to DECORATE backwards compatibility bool bAbstract = false; + bool bSealed = false; + bool bFinal = false; bool bOptional = false; TArray Virtuals; // virtual function table TArray MetaInits; TArray SpecialInits; TArray Fields; + TArray SealedRestriction; PClassType *VMType = nullptr; void (*ConstructNative)(void *); diff --git a/src/common/scripting/frontend/zcc-parse.lemon b/src/common/scripting/frontend/zcc-parse.lemon index 71385c58267..47c36c19ba1 100644 --- a/src/common/scripting/frontend/zcc-parse.lemon +++ b/src/common/scripting/frontend/zcc-parse.lemon @@ -78,6 +78,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line) struct ClassFlagsBlock { VM_UWORD Flags; ZCC_Identifier *Replaces; + ZCC_Identifier *Sealed; VersionInfo Version; }; @@ -242,6 +243,7 @@ class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C). head->ParentName = B; head->Flags = C.Flags; head->Replaces = C.Replaces; + head->Sealed = C.Sealed; head->Version = C.Version; head->Type = nullptr; head->Symbol = nullptr; @@ -253,13 +255,15 @@ class_ancestry(X) ::= . { X = NULL; } class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ } %type class_flags{ClassFlagsBlock} -class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; } -class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } -class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); } +class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; X.Sealed = NULL; } +class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) FINAL. { X.Flags = A.Flags | ZCC_Final; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed;} +class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; X.Version = A.Version; X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); X.Sealed = A.Sealed; } +class_flags(X) ::= class_flags(A) SEALED LPAREN states_opt(B) RPAREN. { X.Flags = A.Flags | ZCC_Sealed; X.Replaces = A.Replaces; X.Version = A.Version; X.Sealed = B; } /*----- Dottable Identifier -----*/ // This can be either a single identifier or two identifiers connected by a . diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index fb21526f1da..251737c63e6 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -792,8 +792,14 @@ void ZCCCompiler::CreateClassTypes() PClass *parent; auto ParentName = c->cls->ParentName; - if (ParentName != nullptr && ParentName->SiblingNext == ParentName) parent = PClass::FindClass(ParentName->Id); - else if (ParentName == nullptr) parent = RUNTIME_CLASS(DObject); + if (ParentName != nullptr && ParentName->SiblingNext == ParentName) + { + parent = PClass::FindClass(ParentName->Id); + } + else if (ParentName == nullptr) + { + parent = RUNTIME_CLASS(DObject); + } else { // The parent is a dotted name which the type system currently does not handle. @@ -813,6 +819,15 @@ void ZCCCompiler::CreateClassTypes() if (parent != nullptr && (parent->VMType != nullptr || c->NodeName() == NAME_Object)) { + if(parent->bFinal) + { + Error(c->cls, "Class '%s' cannot extend final class '%s'", FName(c->NodeName()).GetChars(), parent->TypeName.GetChars()); + } + else if(parent->bSealed && !parent->SealedRestriction.Contains(c->NodeName())) + { + Error(c->cls, "Class '%s' cannot extend sealed class '%s'", FName(c->NodeName()).GetChars(), parent->TypeName.GetChars()); + } + // The parent exists, we may create a type for this class if (c->cls->Flags & ZCC_Native) { @@ -874,6 +889,25 @@ void ZCCCompiler::CreateClassTypes() { c->Type()->mVersion = c->cls->Version; } + + + if (c->cls->Flags & ZCC_Final) + { + c->ClassType()->bFinal = true; + } + + if (c->cls->Flags & ZCC_Sealed) + { + PClass * ccls = c->ClassType(); + ccls->bSealed = true; + ZCC_Identifier * it = c->cls->Sealed; + if(it) do + { + ccls->SealedRestriction.Push(FName(it->Id)); + it = (ZCC_Identifier*) it->SiblingNext; + } + while(it != c->cls->Sealed); + } // if (mVersion >= MakeVersion(2, 4, 0)) { diff --git a/src/common/scripting/frontend/zcc_parser.cpp b/src/common/scripting/frontend/zcc_parser.cpp index 978006054ae..6b1de220b0a 100644 --- a/src/common/scripting/frontend/zcc_parser.cpp +++ b/src/common/scripting/frontend/zcc_parser.cpp @@ -232,6 +232,7 @@ static void InitTokenMap() TOKENDEF (TK_Out, ZCC_OUT); TOKENDEF (TK_Super, ZCC_SUPER); TOKENDEF (TK_Null, ZCC_NULLPTR); + TOKENDEF (TK_Sealed, ZCC_SEALED); TOKENDEF ('~', ZCC_TILDE); TOKENDEF ('!', ZCC_BANG); TOKENDEF (TK_SizeOf, ZCC_SIZEOF); diff --git a/src/common/scripting/frontend/zcc_parser.h b/src/common/scripting/frontend/zcc_parser.h index 25a60d9ad2f..e4e4ea36352 100644 --- a/src/common/scripting/frontend/zcc_parser.h +++ b/src/common/scripting/frontend/zcc_parser.h @@ -64,6 +64,7 @@ enum ZCC_VirtualScope = 1 << 20, ZCC_Version = 1 << 21, ZCC_Internal = 1 << 22, + ZCC_Sealed = 1 << 23, }; // Function parameter modifiers @@ -251,6 +252,7 @@ struct ZCC_Class : ZCC_Struct { ZCC_Identifier *ParentName; ZCC_Identifier *Replaces; + ZCC_Identifier *Sealed; PClass *CType() { return static_cast(Type)->Descriptor; } };