From 0a7aebae93589d7cdb2009724647b915e821c855 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 20:55:59 +0200 Subject: [PATCH 01/21] Fixed const method qualifier. Also, fixed: const is not clearscope, treat differently. --- src/scripting/zscript/zcc_compile.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index f8e9980a2..5519d2166 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2128,8 +2128,8 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool 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_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) @@ -2216,13 +2216,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "'VirtualScope' on a static method is not supported"); } - // you can't have a const function belonging to either ui or play. - // const is intended for plain data to signify that you can call a method on readonly variable. - if ((f->Flags & ZCC_FuncConst) && (f->Flags & (ZCC_UIFlag | ZCC_Play | ZCC_VirtualScope))) - { - Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play | ZCC_VirtualScope)).GetChars(), FName(f->Name).GetChars()); - } - static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope, ZCC_VirtualScope }; excludeflags = 0; fc = 0; @@ -2458,9 +2451,9 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars()); } // you can't change const qualifier for a virtual method - if (oldfunc->FuncConst != sym->Variants[0].Implementation->FuncConst) + if (sym->Variants[0].Implementation->FuncConst && !oldfunc->FuncConst) { - Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); + Error(f, "Attempt to add const qualifier to virtual function %s", FName(f->Name).GetChars()); } // inherit scope of original function if override not specified sym->Variants[0].Implementation->BarrierSide = oldfunc->BarrierSide; From 6c90d05407c1ab19899eb5f1b3bb23796703d9f1 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 21:30:38 +0200 Subject: [PATCH 02/21] By default, inner struct in class should take the scope of it's outer class --- src/scripting/backend/scopebarrier.cpp | 21 +++++++++++++++++++++ src/scripting/backend/scopebarrier.h | 3 +++ src/scripting/zscript/zcc_compile.cpp | 7 +++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp index 4d9d6d2fc..fda9de3c3 100644 --- a/src/scripting/backend/scopebarrier.cpp +++ b/src/scripting/backend/scopebarrier.cpp @@ -44,6 +44,19 @@ int FScopeBarrier::FlagsFromSide(int side) } } +int FScopeBarrier::ObjectFlagsFromSide(int side) +{ + switch (side) + { + case Side_Play: + return OF_Play; + case Side_UI: + return OF_UI; + default: + return 0; + } +} + // used for errors const char* FScopeBarrier::StringFromSide(int side) { @@ -72,6 +85,14 @@ int FScopeBarrier::ChangeSideInFlags(int flags, int side) return flags; } +// this modifies OF_ flags and sets the side properly. +int FScopeBarrier::ChangeSideInObjectFlags(int flags, int side) +{ + flags &= ~(OF_UI | OF_Play); + flags |= ObjectFlagsFromSide(side); + return flags; +} + FScopeBarrier::FScopeBarrier() { sidefrom = -1; diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h index ed4f25a10..514db7a97 100644 --- a/src/scripting/backend/scopebarrier.h +++ b/src/scripting/backend/scopebarrier.h @@ -35,12 +35,15 @@ struct FScopeBarrier // static int FlagsFromSide(int side); + static int ObjectFlagsFromSide(int side); // used for errors static const char* StringFromSide(int side); // this modifies VARF_ flags and sets the side properly. static int ChangeSideInFlags(int flags, int side); + // this modifies OF_ flags and sets the side properly. + static int ChangeSideInObjectFlags(int flags, int side); FScopeBarrier(); FScopeBarrier(int flags1, int flags2, const char* name); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 5519d2166..14eceb0cb 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -501,10 +501,13 @@ void ZCCCompiler::CreateStructTypes() Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars()); } + if (outer) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags)); if (s->strct->Flags & ZCC_UIFlag) - s->Type()->ObjectFlags |= OF_UI; + s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::Side_UI); if (s->strct->Flags & ZCC_Play) - s->Type()->ObjectFlags |= OF_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); From 86a494aacd1ec7f01c177107472ad6d4f4f6d621 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 21:32:15 +0200 Subject: [PATCH 03/21] Can use clearscope on struct to signify that it should be plain data even if defined inside a play/ui class --- src/scripting/zscript/zcc-parse.lemon | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index f80a09974..afb0ec987 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -330,6 +330,7 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body struct_flags(X) ::= . { X.Flags = 0; } struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; } struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; } +struct_flags(X) ::= struct_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; } struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; } opt_struct_body(X) ::= . { X = NULL; } From 6af5c1fa2b8f2d696742e44382f1a259f280cec0 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 21:36:27 +0200 Subject: [PATCH 04/21] Produce a warning if clearscope is used on non-inner structs. Also now checking for outer class presence correctly. --- src/scripting/zscript/zcc_compile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 14eceb0cb..8a73f891d 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -501,7 +501,8 @@ void ZCCCompiler::CreateStructTypes() Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars()); } - if (outer) s->Type()->ObjectFlags = FScopeBarrier::ChangeSideInObjectFlags(s->Type()->ObjectFlags, FScopeBarrier::SideFromObjectFlags(outer->ObjectFlags)); + 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) From a43bc279a3305dda00531c5cc59fa9e937ef77b6 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 00:38:00 +0200 Subject: [PATCH 05/21] Gave static methods in EventHandler/StaticEventHandler clearscope --- wadsrc/static/zscript/events.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 6452d19e0..c1f59a60e 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -283,12 +283,12 @@ class StaticEventHandler : Object native play { // static event handlers CAN register other static event handlers. // unlike EventHandler.Create that will not create them. - protected static native StaticEventHandler Create(class type); - protected static native StaticEventHandler CreateOnce(class type); - protected static native StaticEventHandler Find(Class type); // just for convenience. who knows. + clearscope protected static native StaticEventHandler Create(class type); + clearscope protected static native StaticEventHandler CreateOnce(class type); + clearscope protected static native StaticEventHandler Find(Class type); // just for convenience. who knows. - protected static native bool Register(StaticEventHandler handler); - protected static native bool Unregister(StaticEventHandler handler); + clearscope protected static native bool Register(StaticEventHandler handler); + clearscope protected static native bool Unregister(StaticEventHandler handler); // these are called when the handler gets registered or unregistered // you can set Order/IsUiProcessor here. @@ -337,12 +337,12 @@ class StaticEventHandler : Object native play class EventHandler : StaticEventHandler native { - static native StaticEventHandler Create(class type); - static native StaticEventHandler CreateOnce(class type); - static native StaticEventHandler Find(class type); + clearscope static native StaticEventHandler Create(class type); + clearscope static native StaticEventHandler CreateOnce(class type); + clearscope static native StaticEventHandler Find(class type); - static native bool Register(StaticEventHandler handler); - static native bool Unregister(StaticEventHandler handler); + clearscope static native bool Register(StaticEventHandler handler); + clearscope static native bool Unregister(StaticEventHandler handler); clearscope static native void SendNetworkEvent(String name, int arg1 = 0, int arg2 = 0, int arg3 = 0); } From 9d4179ca06bdf9c2a3015abc4bcb99f8fd08ea48 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 00:52:43 +0200 Subject: [PATCH 06/21] Protected on methods of StaticEventHandler is completely useless --- src/scripting/backend/codegen.cpp | 7 +++++++ src/scripting/thingdef.cpp | 7 +++++++ wadsrc/static/zscript/events.txt | 10 +++++----- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index eb92fe974..c9c391ac6 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6082,6 +6082,13 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct ScriptPosition.Message(MSG_ERROR, "Private member %s not accessible", vsym->SymbolName.GetChars()); return nullptr; } + PClass* cls_ctx = dyn_cast(classctx); + PClass* cls_target = dyn_cast(objtype); + if ((vsym->Flags & VARF_Protected) && (!cls_ctx || !cls_target || cls_ctx->IsDescendantOf(cls_target))) + { + ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); + return nullptr; + } auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition); object = nullptr; diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index c0e444893..b61c9f4ae 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -195,6 +195,8 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam if (symbol != nullptr) { + PClass* cls_ctx = dyn_cast(selfcls); + PClass* cls_target = dyn_cast(funccls); if (funcsym == nullptr) { sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); @@ -204,6 +206,11 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam // private access is only allowed if the symbol table belongs to the class in which the current function is being defined. sc.Message(MSG_ERROR, "%s is declared private and not accessible", symbol->SymbolName.GetChars()); } + else if ((funcsym->Variants[0].Flags & VARF_Protected) && (!cls_ctx || !cls_target || cls_ctx->IsDescendantOf((PClass*)cls_target))) + { + sc.Message(MSG_ERROR, "%s is declared protected and not accessible", symbol->SymbolName.GetChars()); + return nullptr; + } else if (funcsym->Variants[0].Flags & VARF_Deprecated) { sc.Message(MSG_WARNING, "Call to deprecated function %s", symbol->SymbolName.GetChars()); diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index c1f59a60e..03c2de90f 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -283,12 +283,12 @@ class StaticEventHandler : Object native play { // static event handlers CAN register other static event handlers. // unlike EventHandler.Create that will not create them. - clearscope protected static native StaticEventHandler Create(class type); - clearscope protected static native StaticEventHandler CreateOnce(class type); - clearscope protected static native StaticEventHandler Find(Class type); // just for convenience. who knows. + clearscope static native StaticEventHandler Create(class type); + clearscope static native StaticEventHandler CreateOnce(class type); + clearscope static native StaticEventHandler Find(Class type); // just for convenience. who knows. - clearscope protected static native bool Register(StaticEventHandler handler); - clearscope protected static native bool Unregister(StaticEventHandler handler); + clearscope static native bool Register(StaticEventHandler handler); + clearscope static native bool Unregister(StaticEventHandler handler); // these are called when the handler gets registered or unregistered // you can set Order/IsUiProcessor here. From b87f9b540ef160f93320d375859411727d2698b4 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 01:03:38 +0200 Subject: [PATCH 07/21] Implemented 'protected' on fields and methods --- src/scripting/backend/codegen.cpp | 2 +- src/scripting/thingdef.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index c9c391ac6..1fc243ac3 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6084,7 +6084,7 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct } PClass* cls_ctx = dyn_cast(classctx); PClass* cls_target = dyn_cast(objtype); - if ((vsym->Flags & VARF_Protected) && (!cls_ctx || !cls_target || cls_ctx->IsDescendantOf(cls_target))) + if ((vsym->Flags & VARF_Protected) && (!cls_ctx || !cls_target || !cls_ctx->IsDescendantOf(cls_target))) { ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); return nullptr; diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index b61c9f4ae..99353de6e 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -195,8 +195,8 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam if (symbol != nullptr) { - PClass* cls_ctx = dyn_cast(selfcls); - PClass* cls_target = dyn_cast(funccls); + PClass* cls_ctx = dyn_cast(funccls); + PClass* cls_target = funcsym?dyn_cast(funcsym->OwningClass):nullptr; if (funcsym == nullptr) { sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); @@ -206,7 +206,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam // private access is only allowed if the symbol table belongs to the class in which the current function is being defined. sc.Message(MSG_ERROR, "%s is declared private and not accessible", symbol->SymbolName.GetChars()); } - else if ((funcsym->Variants[0].Flags & VARF_Protected) && (!cls_ctx || !cls_target || cls_ctx->IsDescendantOf((PClass*)cls_target))) + else if ((funcsym->Variants[0].Flags & VARF_Protected) && (!cls_ctx || !cls_target || !cls_ctx->IsDescendantOf((PClass*)cls_target))) { sc.Message(MSG_ERROR, "%s is declared protected and not accessible", symbol->SymbolName.GetChars()); return nullptr; From 7cbabfb0d4f28ccbf97850bcfeb72cc35c980aaa Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 01:24:22 +0200 Subject: [PATCH 08/21] Implemented implicit 'protected' inheritance in virtual functions --- src/scripting/backend/codegen.cpp | 2 +- src/scripting/vm/vm.h | 15 +++++++-------- src/scripting/vm/vmexec.h | 8 ++++---- src/scripting/vm/vmframe.cpp | 3 +-- src/scripting/zscript/zcc_compile.cpp | 20 ++++++++++++-------- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1fc243ac3..9f7da15c4 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -8725,7 +8725,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) } VMFunction *vmfunc = Function->Variants[0].Implementation; - bool staticcall = (vmfunc->Final || vmfunc->VirtualIndex == ~0u || NoVirtual); + bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual); count = 0; // Emit code to pass implied parameters diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 06de1e2c1..3874df2a7 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -705,10 +705,8 @@ do_double: if (inexact) class VMFunction { public: - bool Native; - bool Final = false; // cannot be overridden - bool Unsafe = false; // Contains references to class fields that are unsafe for psp and item state calls. - bool FuncConst = false; // [ZZ] readonly function + bool Unsafe = false; + int VarFlags = 0; // [ZZ] this replaces 5+ bool fields int BarrierSide = 0; // [ZZ] FScopeBarrier::Side BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action unsigned VirtualIndex = ~0u; @@ -718,7 +716,7 @@ public: class PPrototype *Proto; - VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) + VMFunction(FName name = NAME_None) : ImplicitArgs(0), Name(name), Proto(NULL) { AllFunctions.Push(this); } @@ -942,9 +940,10 @@ class VMNativeFunction : public VMFunction public: typedef int (*NativeCallType)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); - VMNativeFunction() : NativeCall(NULL) { Native = true; } - VMNativeFunction(NativeCallType call) : NativeCall(call) { Native = true; } - VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { Native = true; } + // 8 is VARF_Native. + VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; } + VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; } + VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; } // Return value is the number of results. NativeCallType NativeCall; diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index cd3925adc..cdf598649 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -22,7 +22,7 @@ static int Exec(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) const FVoidObj *konsta; const VM_ATAG *konstatag; - if (f->Func != NULL && !f->Func->Native) + if (f->Func != NULL && !(f->Func->VarFlags & VARF_Native)) { sfunc = static_cast(f->Func); konstd = sfunc->KonstD; @@ -679,7 +679,7 @@ begin: #endif FillReturns(reg, f, returns, pc+1, C); - if (call->Native) + if (call->VarFlags & VARF_Native) { try { @@ -736,7 +736,7 @@ begin: { VMFunction *call = (VMFunction *)ptr; - if (call->Native) + if (call->VarFlags & VARF_Native) { try { @@ -1966,7 +1966,7 @@ static void SetReturn(const VMRegisters ®, VMFrame *frame, VMReturn *ret, VM_ const void *src; VMScriptFunction *func = static_cast(frame->Func); - assert(func != NULL && !func->Native); + assert(func != NULL && !(func->VarFlags & VARF_Native)); assert((regtype & ~REGT_KONST) == ret->RegType); switch (regtype & REGT_TYPE) diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 10fed9abb..44bf46f23 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -48,7 +48,6 @@ TArray VMFunction::AllFunctions; VMScriptFunction::VMScriptFunction(FName name) { - Native = false; Name = name; LineInfo = nullptr; Code = NULL; @@ -438,7 +437,7 @@ int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMRetur bool allocated = false; try { - if (func->Native) + if (func->VarFlags & VARF_Native) { return static_cast(func)->NativeCall(params, func->DefaultArgs, numparams, results, numresults); } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 8a73f891d..533104121 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2416,6 +2416,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::SideFromFlags(varflags); + sym->Variants[0].Implementation->VarFlags = sym->Variants[0].Flags; } PClass *clstype = static_cast(c->Type()); @@ -2426,11 +2427,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Virtual function %s.%s not present", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); return; } - - if (varflags & VARF_Final) - sym->Variants[0].Implementation->Final = true; - if (varflags & VARF_ReadOnly) - sym->Variants[0].Implementation->FuncConst = true; if (forclass) { @@ -2445,7 +2441,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool else { auto oldfunc = clstype->Virtuals[vindex]; - if (oldfunc->Final) + if (oldfunc->VarFlags & VARF_Final) { Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } @@ -2455,19 +2451,27 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars()); } // you can't change const qualifier for a virtual method - if (sym->Variants[0].Implementation->FuncConst && !oldfunc->FuncConst) + if ((sym->Variants[0].Implementation->VarFlags & VARF_ReadOnly) && !(oldfunc->VarFlags & VARF_ReadOnly)) { Error(f, "Attempt to add const qualifier to virtual function %s", FName(f->Name).GetChars()); } + // you can't change protected qualifier for a virtual method (i.e. putting private), because this cannot be reliably checked without runtime stuff + if (f->Flags & (ZCC_Private | ZCC_Protected)) + { + Error(f, "Attempt to change private/protected qualifiers for virtual function %s", FName(f->Name).GetChars()); + } // inherit scope of original function if override not specified sym->Variants[0].Implementation->BarrierSide = oldfunc->BarrierSide; sym->Variants[0].Flags = FScopeBarrier::ChangeSideInFlags(sym->Variants[0].Flags, oldfunc->BarrierSide); // inherit const from original function - if ((sym->Variants[0].Implementation->FuncConst = oldfunc->FuncConst)) + if (oldfunc->VarFlags & VARF_ReadOnly) sym->Variants[0].Flags |= VARF_ReadOnly; + if (oldfunc->VarFlags & VARF_Protected) + sym->Variants[0].Flags |= VARF_Protected; clstype->Virtuals[vindex] = sym->Variants[0].Implementation; sym->Variants[0].Implementation->VirtualIndex = vindex; + sym->Variants[0].Implementation->VarFlags = sym->Variants[0].Flags; } } else From 3eafc13b080636696ab4a291224dc2a5fd1b3311 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 01:31:23 +0200 Subject: [PATCH 09/21] Removed BarrierSide from VMFunction, since it's obsolete with addition of VarFlags --- src/scripting/vm/vm.h | 3 +-- src/scripting/zscript/zcc_compile.cpp | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 3874df2a7..6c66c7b85 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -707,7 +707,6 @@ class VMFunction public: bool Unsafe = false; int VarFlags = 0; // [ZZ] this replaces 5+ bool fields - int BarrierSide = 0; // [ZZ] FScopeBarrier::Side BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action unsigned VirtualIndex = ~0u; FName Name; @@ -940,7 +939,7 @@ class VMNativeFunction : public VMFunction public: typedef int (*NativeCallType)(VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret); - // 8 is VARF_Native. + // 8 is VARF_Native. I can't write VARF_Native because of circular references between this and dobject/dobjtype. VMNativeFunction() : NativeCall(NULL) { VarFlags = 8; } VMNativeFunction(NativeCallType call) : NativeCall(call) { VarFlags = 8; } VMNativeFunction(NativeCallType call, FName name) : VMFunction(name), NativeCall(call) { VarFlags = 8; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 533104121..49485da4e 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2415,7 +2415,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (sym->Variants[0].Implementation != nullptr) { // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::SideFromFlags(varflags); sym->Variants[0].Implementation->VarFlags = sym->Variants[0].Flags; } @@ -2461,8 +2460,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to change private/protected qualifiers for virtual function %s", FName(f->Name).GetChars()); } // inherit scope of original function if override not specified - sym->Variants[0].Implementation->BarrierSide = oldfunc->BarrierSide; - sym->Variants[0].Flags = FScopeBarrier::ChangeSideInFlags(sym->Variants[0].Flags, oldfunc->BarrierSide); + sym->Variants[0].Flags = FScopeBarrier::ChangeSideInFlags(sym->Variants[0].Flags, FScopeBarrier::SideFromFlags(oldfunc->VarFlags)); // inherit const from original function if (oldfunc->VarFlags & VARF_ReadOnly) sym->Variants[0].Flags |= VARF_ReadOnly; From 1832531726082e6680061ce1203ba8d0815f2115 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 01:47:01 +0200 Subject: [PATCH 10/21] Fixed: check protected fields against analog of OwningClass in functions, as opposed to the type that the field was accessed from. --- src/scripting/backend/codegen.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 9f7da15c4..859b5b4c1 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6084,10 +6084,34 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct } PClass* cls_ctx = dyn_cast(classctx); PClass* cls_target = dyn_cast(objtype); - if ((vsym->Flags & VARF_Protected) && (!cls_ctx || !cls_target || !cls_ctx->IsDescendantOf(cls_target))) + // [ZZ] neither PSymbol, PField or PSymbolTable have the necessary information. so we need to do the more complex check here. + if (vsym->Flags & VARF_Protected) { - ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); - return nullptr; + // early break. + if (!cls_ctx || !cls_target) + { + ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); + return nullptr; + } + + // find the class that declared this field. + PClass* p = cls_target; + while (p) + { + if (&p->Symbols == symtbl) + { + cls_target = p; + break; + } + + p = p->ParentClass; + } + + if (!cls_ctx->IsDescendantOf(cls_target)) + { + ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); + return nullptr; + } } auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition); From 38b86ad83c53994bedaea79fd1a571204f37344c Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 02:48:36 +0200 Subject: [PATCH 11/21] No need for explicit EFX_Self check in FxStructMember::RequestAddress if self is set to readonly elsewhere. --- src/scripting/backend/codegen.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 859b5b4c1..bcaa374c2 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6841,12 +6841,6 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) // [ZZ] original check. bool bWritable = (AddressWritable && !ctx.CheckWritable(membervar->Flags) && (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->IsConst)); - // [ZZ] self in a const function is not writable. - if (bWritable) // don't do complex checks on early fail - { - if ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly))) - bWritable = false; - } // [ZZ] implement write barrier between different scopes if (bWritable) { From 27376b169759006e112a2d412cb8c34d0c558990 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 5 Mar 2017 11:39:25 +0200 Subject: [PATCH 12/21] Fixed compilation of Debug configuration src/scripting/vm/vmexec.cpp:204:44: error: no member named 'Native' in 'VMScriptFunction' --- src/scripting/vm/vmexec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 1ad4e3256..f8fb4bfd2 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -201,7 +201,7 @@ void VMFillParams(VMValue *params, VMFrame *callee, int numparam) VMScriptFunction *calleefunc = static_cast(callee->Func); const VMRegisters calleereg(callee); - assert(calleefunc != NULL && !calleefunc->Native); + assert(calleefunc != NULL && !(calleefunc->VarFlags & VARF_Native)); assert(numparam == calleefunc->NumArgs || ((int)calleefunc->DefaultArgs.Size() == calleefunc->NumArgs)); assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3); From 5d3244c3a7bf482eb13930b0cca8c9ab621b9658 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 5 Mar 2017 13:07:51 +0200 Subject: [PATCH 13/21] Fixed minor issues in config file reader Removed hardcoded values from long lines handling and made sure that pointer to the last character is always initialized --- src/configfile.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/configfile.cpp b/src/configfile.cpp index d56e74e9a..3adbd3761 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -644,8 +644,12 @@ bool FConfigFile::ReadConfig (void *file) continue; } // Do not process tail of long line - const bool longline = 255 == strlen(readbuf) && '\n' != readbuf[254]; - if (!longline) + const bool longline = (READBUFFERSIZE - 1) == strlen(readbuf) && '\n' != readbuf[READBUFFERSIZE - 2]; + if (longline) + { + endpt = start + READBUFFERSIZE - 2; + } + else { // Remove white space at end of line endpt = start + strlen (start) - 1; From 7df698dad88541ecf05d0f39dc710b546a6ca229 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 14:03:27 +0100 Subject: [PATCH 14/21] - implemented the parser basics of a ZScript versioning system. Note that this completely disables the newly added keywords 'play' and 'ui' for unversioned code to allow using them as identifiers as I have found at least one mod that uses a variable named 'play' that would have been rendered broken otherwise. This also disables many ZScript only keywords for other parsing jobs. --- src/sc_man.cpp | 25 ++++++ src/sc_man.h | 22 +++++ src/sc_man_scanner.re | 56 +++++------- src/sc_man_tokens.h | 3 +- src/scripting/zscript/zcc-parse.lemon | 50 ++++++++++- src/scripting/zscript/zcc_parser.cpp | 89 +++++++++++++------ src/scripting/zscript/zcc_parser.h | 4 + src/version.h | 4 + wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/actor.txt | 54 +++++------ wadsrc/static/zscript/actor_checks.txt | 2 +- wadsrc/static/zscript/base.txt | 46 +++++----- wadsrc/static/zscript/compatibility.txt | 8 +- wadsrc/static/zscript/events.txt | 18 ++-- .../zscript/inventory/stateprovider.txt | 8 +- .../static/zscript/menu/conversationmenu.txt | 4 +- wadsrc/static/zscript/menu/menu.txt | 10 +-- wadsrc/static/zscript/menu/menuitembase.txt | 2 +- wadsrc/static/zscript/menu/optionmenu.txt | 2 +- wadsrc/static/zscript/statscreen/types.txt | 4 +- 20 files changed, 268 insertions(+), 144 deletions(-) diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 70bdabd76..577f7690e 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -42,6 +42,31 @@ // CODE -------------------------------------------------------------------- +void VersionInfo::operator=(const char *string) +{ + char *endp; + major = (int16_t)clamp(strtoull(string, &endp, 10), 0, USHRT_MAX); + if (*endp == '.') + { + minor = (int16_t)clamp(strtoull(endp + 1, &endp, 10), 0, USHRT_MAX); + if (*endp == '.') + { + revision = (int16_t)clamp(strtoull(endp + 1, &endp, 10), 0, USHRT_MAX); + if (*endp != 0) major = USHRT_MAX; + } + else if (*endp == 0) + { + revision = 0; + } + else major = USHRT_MAX; + } + else if (*endp == 0) + { + minor = revision = 0; + } + else major = USHRT_MAX; +} + //========================================================================== // // FScanner Constructor diff --git a/src/sc_man.h b/src/sc_man.h index 3347fe26a..b6be8df60 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -1,6 +1,23 @@ #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: @@ -24,6 +41,10 @@ public: void OpenString(const char *name, FString buffer); void OpenLumpNum(int lump); void Close(); + void SetParseVersion(VersionInfo ver) + { + ParseVersion = ver; + } void SetCMode(bool cmode); void SetEscape(bool esc); @@ -102,6 +123,7 @@ protected: BYTE StateMode; bool StateOptions; bool Escape; + VersionInfo ParseVersion = { 0, 0 }; // no ZScript extensions by default }; enum diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index ba839813f..d8b81aef0 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -143,50 +143,38 @@ std2: 'false' { RET(TK_False); } 'none' { RET(TK_None); } 'auto' { RET(TK_Auto); } - 'exec' { RET(TK_Exec); } 'property' { RET(TK_Property); } 'native' { RET(TK_Native); } 'var' { RET(TK_Var); } - 'out' { RET(TK_Out); } - 'ref' { RET(TK_Ref); } - 'event' { RET(TK_Event); } + 'out' { RET(ParseVersion.Check(1,0)? TK_Out : TK_Identifier); } 'static' { RET(TK_Static); } - 'transient' { RET(TK_Transient); } - 'final' { RET(TK_Final); } - 'throws' { RET(TK_Throws); } - 'extend' { RET(TK_Extend); } - 'public' { RET(TK_Public); } - 'protected' { RET(TK_Protected); } - 'private' { RET(TK_Private); } + '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); } 'dot' { RET(TK_Dot); } 'cross' { RET(TK_Cross); } - 'localized' { RET(TK_Localized); } - 'latent' { RET(TK_Latent); } - 'singular' { RET(TK_Singular); } - 'config' { RET(TK_Config); } - 'coerce' { RET(TK_Coerce); } - 'optional' { RET(TK_Optional); } - 'export' { RET(TK_Export); } - 'virtual' { RET(TK_Virtual); } - 'override' { RET(TK_Override); } - 'vararg' { RET(TK_VarArg); } - 'ui' { RET(TK_UI); } - 'play' { RET(TK_Play); } - 'clearscope' { RET(TK_ClearScope); } - 'virtualscope' { RET(TK_VirtualScope); } - 'super' { RET(TK_Super); } - 'global' { RET(TK_Global); } + '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); } 'stop' { RET(TK_Stop); } 'null' { RET(TK_Null); } - 'is' { RET(TK_Is); } - 'replaces' { RET(TK_Replaces); } + 'is' { RET(ParseVersion.Check(1,0)? TK_Is : TK_Identifier); } + 'replaces' { RET(ParseVersion.Check(1,0)? TK_Replaces : TK_Identifier); } 'states' { RET(TK_States); } - 'meta' { RET(TK_Meta); } - 'deprecated' { RET(TK_Deprecated); } - 'action' { RET(TK_Action); } - 'readonly' { RET(TK_ReadOnly); } - 'let' { RET(TK_Let); } + '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); } /* Actor state options */ 'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 320601414..b24cb369d 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -108,8 +108,6 @@ xx(TK_Singular, "'singular'") xx(TK_Config, "'config'") xx(TK_Coerce, "'coerce'") xx(TK_Iterator, "'iterator'") -xx(TK_Optional, "'optional'") -xx(TK_Export, "'expert'") xx(TK_Virtual, "'virtual'") xx(TK_VarArg, "'vararg'") xx(TK_UI, "'ui'") @@ -138,6 +136,7 @@ xx(TK_Fail, "'fail'") xx(TK_Wait, "'wait'") xx(TK_Meta, "'meta'") xx(TK_Deprecated, "'deprecated'") +xx(TK_Version, "'version'") xx(TK_ReadOnly, "'readonly'") xx(TK_CanRaise, "'canraise'") diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index afb0ec987..6259301f2 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -40,6 +40,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line) struct ClassFlagsBlock { VM_UWORD Flags; ZCC_Identifier *Replaces; + VersionInfo Version; }; struct StateOpts { @@ -188,6 +189,7 @@ class_head(X) ::= EXTEND CLASS(T) IDENTIFIER(A). head->ParentName = nullptr; head->Flags = ZCC_Extension; head->Replaces = nullptr; + head->Version = {0, 0}; head->Type = nullptr; head->Symbol = nullptr; X = head; @@ -200,6 +202,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->Version = C.Version; head->Type = nullptr; head->Symbol = nullptr; X = head; @@ -210,12 +213,13 @@ 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; } +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(); } /*----- Dottable Identifier -----*/ // This can be either a single identifier or two identifiers connected by a . @@ -322,16 +326,18 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body def->Body = B; def->Type = nullptr; def->Symbol = nullptr; + def->Version = S.Version; def->Flags = S.Flags; X = def; } %type struct_flags{ClassFlagsBlock} -struct_flags(X) ::= . { X.Flags = 0; } +struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; } struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; } struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; } struct_flags(X) ::= struct_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; } struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; } +struct_flags(X) ::= struct_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Version = C.String->GetChars(); } opt_struct_body(X) ::= . { X = NULL; } opt_struct_body(X) ::= struct_body(X). @@ -966,6 +972,7 @@ decl_flags(X) ::= decl_flags(F) decl_flag(A). X = nil_f; X->Id = nullptr; X->Flags = A.Int; + X->Version = { 0, 0 }; } else { @@ -982,6 +989,8 @@ decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A). NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc); X = nil_f; X->Flags = ZCC_Action; + X->Id = nullptr; + X->Version = { 0, 0 }; } else { @@ -991,6 +1000,42 @@ decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A). X->Id = A; } +decl_flags(X) ::= decl_flags(F) DEPRECATED(B) LPAREN STRCONST(A) RPAREN. +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc); + X = nil_f; + X->Flags = ZCC_Deprecated; + X->Id = nullptr; + X->Version = { 0, 0 }; + } + else + { + X = F; + X->Flags |= ZCC_Deprecated; + } + X->Version = A.String->GetChars(); +} + +decl_flags(X) ::= decl_flags(F) VERSION(B) LPAREN STRINGCONST(A) RPAREN. +{ + if (F == nullptr) + { + NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc); + X = nil_f; + X->Flags = ZCC_Version; + X->Id = nullptr; + X->Version = { 0, 0 }; + } + else + { + X = F; + X->Flags |= ZCC_Version; + } + X->Version = A.String->GetChars(); +} + decl_flag(X) ::= NATIVE(T). { X.Int = ZCC_Native; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= STATIC(T). { X.Int = ZCC_Static; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= PRIVATE(T). { X.Int = ZCC_Private; X.SourceLoc = T.SourceLoc; } @@ -1000,7 +1045,6 @@ decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= TRANSIENT(T). { X.Int = ZCC_Transient; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; } -decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= VARARG(T). { X.Int = ZCC_VarArg; X.SourceLoc = T.SourceLoc; } diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 761af9541..9072a9d85 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -41,6 +41,7 @@ #include "i_system.h" #include "m_argv.h" #include "v_text.h" +#include "version.h" #include "zcc_parser.h" #include "zcc_compile.h" @@ -145,6 +146,7 @@ static void InitTokenMap() TOKENDEF (TK_Final, ZCC_FINAL); TOKENDEF (TK_Meta, ZCC_META); TOKENDEF (TK_Deprecated, ZCC_DEPRECATED); + TOKENDEF (TK_Version, ZCC_VERSION); TOKENDEF (TK_ReadOnly, ZCC_READONLY); TOKENDEF ('{', ZCC_LBRACE); TOKENDEF ('}', ZCC_RBRACE); @@ -179,7 +181,6 @@ static void InitTokenMap() TOKENDEF (']', ZCC_RBRACKET); TOKENDEF (TK_In, ZCC_IN); TOKENDEF (TK_Out, ZCC_OUT); - TOKENDEF (TK_Optional, ZCC_OPTIONAL); TOKENDEF (TK_Super, ZCC_SUPER); TOKENDEF (TK_Null, ZCC_NULLPTR); TOKENDEF ('~', ZCC_TILDE); @@ -232,29 +233,36 @@ static void InitTokenMap() //**-------------------------------------------------------------------------- -static void ParseSingleFile(const char *filename, int lump, void *parser, ZCCParseState &state) +static void ParseSingleFile(FScanner *pSC, const char *filename, int lump, void *parser, ZCCParseState &state) { int tokentype; //bool failed; ZCCToken value; - FScanner sc; + FScanner lsc; - if (filename != nullptr) + if (pSC == nullptr) { - lump = Wads.CheckNumForFullName(filename, true); - if (lump >= 0) + if (filename != nullptr) { - sc.OpenLumpNum(lump); + lump = Wads.CheckNumForFullName(filename, true); + if (lump >= 0) + { + lsc.OpenLumpNum(lump); + } + else + { + Printf("Could not find script lump '%s'\n", filename); + return; + } } - else - { - Printf("Could not find script lump '%s'\n", filename); - return; - } - } - else sc.OpenLumpNum(lump); + else lsc.OpenLumpNum(lump); + pSC = &lsc; + } + FScanner &sc = *pSC; + sc.SetParseVersion(state.ParseVersion); state.sc = ≻ + while (sc.GetToken()) { value.SourceLoc = sc.GetMessageLine(); @@ -343,9 +351,48 @@ static void DoParse(int lumpnum) #endif sc.OpenLumpNum(lumpnum); + sc.SetParseVersion({ 2, 4 }); // To get 'version' we need parse version 2.4 for the initial test auto saved = sc.SavePos(); - ParseSingleFile(nullptr, lumpnum, parser, state); + if (sc.GetToken()) + { + if (sc.TokenType == TK_Version) + { + char *endp; + sc.MustGetString(); + state.ParseVersion.major = (int16_t)clamp(strtoll(sc.String, &endp, 10), -1, USHRT_MAX); + if (*endp != '.') + { + sc.ScriptError("Bad version directive"); + } + state.ParseVersion.minor = (int16_t)clamp(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX); + if (*endp == '.') + { + state.ParseVersion.revision = (int16_t)clamp(strtoll(endp + 1, &endp, 10), -1, 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) + { + sc.ScriptError("Bad version directive"); + } + if (!state.ParseVersion.Check(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); + } + } + else + { + state.ParseVersion.major = 1; // 0 excludes all ZSCRIPT keywords from the parser. + state.ParseVersion.minor = 0; + sc.RestorePos(saved); + } + } + + ParseSingleFile(&sc, nullptr, lumpnum, parser, state); for (unsigned i = 0; i < Includes.Size(); i++) { lumpnum = Wads.CheckNumForFullName(Includes[i], true); @@ -362,7 +409,7 @@ static void DoParse(int lumpnum) Wads.GetWadFullName(Wads.GetLumpFile(baselump)), Includes[i].GetChars()); } - ParseSingleFile(nullptr, lumpnum, parser, state); + ParseSingleFile(nullptr, nullptr, lumpnum, parser, state); } } Includes.Clear(); @@ -436,16 +483,6 @@ void ParseScripts() } } -/* -CCMD(parse) -{ - if (argv.argc() == 2) - { - DoParse(argv[1]); - } -} -*/ - static FString ZCCTokenName(int terminal) { if (terminal == ZCC_EOF) diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index c414c0e9c..ead59472e 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -41,6 +41,7 @@ enum ZCC_Play = 1 << 18, ZCC_ClearScope = 1 << 19, ZCC_VirtualScope = 1 << 20, + ZCC_Version = 1 << 21, }; // Function parameter modifiers @@ -194,6 +195,7 @@ struct ZCC_Struct : ZCC_NamedNode VM_UWORD Flags; ZCC_TreeNode *Body; PStruct *Type; + VersionInfo Version; }; struct ZCC_Property : ZCC_NamedNode @@ -483,6 +485,7 @@ struct ZCC_FuncParamDecl : ZCC_TreeNode struct ZCC_DeclFlags : ZCC_TreeNode { ZCC_Identifier *Id; + VersionInfo Version; int Flags; }; @@ -542,6 +545,7 @@ struct ZCC_AST FSharedStringArena Strings; FMemArena SyntaxArena; struct ZCC_TreeNode *TopNode; + VersionInfo ParseVersion; }; struct ZCCParseState : public ZCC_AST diff --git a/src/version.h b/src/version.h index d0910f8b4..4f8f76fab 100644 --- a/src/version.h +++ b/src/version.h @@ -53,6 +53,10 @@ const char *GetVersionString(); #define RC_FILEVERSION 2,3,9999,0 #define RC_PRODUCTVERSION 2,3,9999,0 #define RC_PRODUCTVERSION2 VERSIONSTR +// These are for content versioning. The current state is '2.4'. +#define VER_MAJOR 2 +#define VER_MINOR 4 +#define VER_REVISION 0 // Version identifier for network games. // Bump it every time you do a release unless you're certain you diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index bf7f75f18..84b0f9df5 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -1,3 +1,4 @@ +version "2.4" #include "zscript/base.txt" #include "zscript/sounddata.txt" #include "zscript/mapdata.txt" diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index ec5ce71d8..994fe6f4b 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -237,17 +237,17 @@ class Actor : Thinker native //int ConversationRoot; // THe root of the current dialogue // deprecated things. - native readonly deprecated double X; - native readonly deprecated double Y; - native readonly deprecated double Z; - native readonly deprecated double VelX; - native readonly deprecated double VelY; - native readonly deprecated double VelZ; - native readonly deprecated double MomX; - native readonly deprecated double MomY; - native readonly deprecated double MomZ; - native deprecated double ScaleX; - native deprecated double ScaleY; + native readonly deprecated("2.3") double X; + native readonly deprecated("2.3") double Y; + native readonly deprecated("2.3") double Z; + native readonly deprecated("2.3") double VelX; + native readonly deprecated("2.3") double VelY; + native readonly deprecated("2.3") double VelZ; + native readonly deprecated("2.3") double MomX; + native readonly deprecated("2.3") double MomY; + native readonly deprecated("2.3") double MomZ; + native deprecated("2.3") double ScaleX; + native deprecated("2.3") double ScaleY; //FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is used.; @@ -716,7 +716,7 @@ class Actor : Thinker native return true; } - deprecated void A_FaceConsolePlayer(double MaxTurnAngle = 0) {} + deprecated("2.3") void A_FaceConsolePlayer(double MaxTurnAngle = 0) {} void A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0) { @@ -867,18 +867,18 @@ class Actor : Thinker native } } - deprecated void A_MeleeAttack() + deprecated("2.3") void A_MeleeAttack() { DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0); } - deprecated void A_MissileAttack() + deprecated("2.3") void A_MissileAttack() { Class MissileType = MissileName; DoAttack(false, true, 0, 0, MissileType, MissileHeight); } - deprecated void A_ComboAttack() + deprecated("2.3") void A_ComboAttack() { Class MissileType = MissileName; DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); @@ -918,13 +918,13 @@ class Actor : Thinker native native void A_Wander(int flags = 0); native void A_Look2(); - deprecated native void A_BulletAttack(); + deprecated("2.3") native void A_BulletAttack(); native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", double snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, double runspeed = 160.0, class pufftype = "BulletPuff"); native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, double volume = 1.0, bool looping = false, double attenuation = ATTN_NORM, bool local = false); - deprecated void A_PlayWeaponSound(sound whattoplay) { A_PlaySound(whattoplay, CHAN_WEAPON); } + deprecated("2.3") void A_PlayWeaponSound(sound whattoplay) { A_PlaySound(whattoplay, CHAN_WEAPON); } native void A_StopSound(int slot = CHAN_VOICE); // Bad default but that's what is originally was... - deprecated native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0); - deprecated native void A_StopSoundEx(name slot); + deprecated("2.3") native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0); + deprecated("2.3") native void A_StopSoundEx(name slot); native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10); native action state A_Jump(int chance, statelabel label, ...); native Actor A_SpawnProjectile(class missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET); @@ -950,7 +950,7 @@ class Actor : Thinker native native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false); native void A_DropInventory(class itemtype, int amount = -1); native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0); - deprecated native void A_ChangeFlag(string flagname, bool value); + deprecated("2.3") native void A_ChangeFlag(string flagname, bool value); native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE); native void A_RaiseMaster(int flags = 0); native void A_RaiseChildren(int flags = 0); @@ -989,10 +989,10 @@ class Actor : Thinker native native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_ScaleVelocity(double scale, int ptr = AAPTR_DEFAULT); native void A_ChangeVelocity(double x = 0, double y = 0, double z = 0, int flags = 0, int ptr = AAPTR_DEFAULT); - deprecated native void A_SetUserVar(name varname, int value); - deprecated native void A_SetUserArray(name varname, int index, int value); - deprecated native void A_SetUserVarFloat(name varname, double value); - deprecated native void A_SetUserArrayFloat(name varname, int index, double value); + deprecated("2.3") native void A_SetUserVar(name varname, int value); + deprecated("2.3") native void A_SetUserArray(name varname, int index, int value); + deprecated("2.3") native void A_SetUserVarFloat(name varname, double value); + deprecated("2.3") native void A_SetUserArrayFloat(name varname, int index, double value); native void A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, double mulWaveX = 1, double mulWaveY = 1, double mulWaveZ = 1, int falloff = 0, int highpoint = 0, double rollIntensity = 0, double rollWave = 0); action native void A_SetTics(int tics); @@ -1090,7 +1090,7 @@ class Actor : Thinker native } // Internal functions - deprecated private native int __decorate_internal_int__(int i); - deprecated private native bool __decorate_internal_bool__(bool b); - deprecated private native double __decorate_internal_float__(double f); + deprecated("2.3") private native int __decorate_internal_int__(int i); + deprecated("2.3") private native bool __decorate_internal_bool__(bool b); + deprecated("2.3") private native double __decorate_internal_float__(double f); } diff --git a/wadsrc/static/zscript/actor_checks.txt b/wadsrc/static/zscript/actor_checks.txt index eea10b5ec..f07903305 100644 --- a/wadsrc/static/zscript/actor_checks.txt +++ b/wadsrc/static/zscript/actor_checks.txt @@ -170,7 +170,7 @@ extend class Actor private native bool CheckFlag(string flagname, int check_pointer = AAPTR_DEFAULT); - deprecated action state A_CheckFlag(string flagname, statelabel label, int check_pointer = AAPTR_DEFAULT) + deprecated("2.3") action state A_CheckFlag(string flagname, statelabel label, int check_pointer = AAPTR_DEFAULT) { return CheckFlag(flagname, check_pointer)? ResolveState(label) : null; } diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index a15b78de8..fa37a021c 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -1,4 +1,4 @@ -struct InputEventData native +struct InputEventData native version("2.4") { native uint8 type; native uint8 subtype; @@ -244,7 +244,7 @@ struct Font native native BrokenLines BreakLines(String text, int maxlen); } -struct Translation +struct Translation version("2.4") { Color colors[256]; @@ -290,7 +290,7 @@ struct CVar native native int ResetToDefault(); } -struct GIFont +struct GIFont version("2.4") { Name fontname; Name color; @@ -337,7 +337,7 @@ class Object native virtual virtualscope void OnDestroy() {} } -class BrokenLines : Object native +class BrokenLines : Object native version("2.4") { native int Count(); native int StringWidth(int line); @@ -458,23 +458,23 @@ struct LevelLocals native native int found_items; native int total_monsters; native int killed_monsters; - native double gravity; - native double aircontrol; - native double airfriction; - native int airsupply; - native double teamdamage; - native bool monsterstelefrag; - native bool actownspecial; - native bool sndseqtotalctrl; + native play double gravity; + native play double aircontrol; + native play double airfriction; + native play int airsupply; + native readonly double teamdamage; + native readonly bool monsterstelefrag; + native readonly bool actownspecial; + native readonly bool sndseqtotalctrl; native bool allmap; - native bool missilesactivateimpact; - native bool monsterfallingdamage; - native bool checkswitchrange; - native bool polygrind; - native bool nomonsters; + native readonly bool missilesactivateimpact; + native readonly bool monsterfallingdamage; + native readonly bool checkswitchrange; + native readonly bool polygrind; + native readonly bool nomonsters; native bool frozen; - native bool infinite_flight; - native bool no_dlg_freeze; + native readonly bool infinite_flight; + native readonly bool no_dlg_freeze; // level_info_t *info cannot be done yet. native String GetUDMFString(int type, int index, Name key); @@ -618,7 +618,7 @@ class Floor : Thinker native floorRaiseInstant, floorMoveToValue, floorRaiseToLowestCeiling, - floorRaiseuint8xture, + floorRaiseByTexture, floorLowerAndChange, floorRaiseAndChange, @@ -626,7 +626,7 @@ class Floor : Thinker native floorRaiseToLowest, floorRaiseToCeiling, floorLowerToLowestCeiling, - floorLoweruint8xture, + floorLowerByTexture, floorLowerToCeiling, donutRaise, @@ -669,8 +669,8 @@ class Ceiling : Thinker native ceilLowerToNearest, ceilRaiseToHighestFloor, ceilRaiseToFloor, - ceilRaiseuint8xture, - ceilLoweruint8xture, + ceilRaiseByTexture, + ceilLowerByTexture, genCeilingChg0, genCeilingChgT, diff --git a/wadsrc/static/zscript/compatibility.txt b/wadsrc/static/zscript/compatibility.txt index 1ae88ab85..3699a8f3a 100644 --- a/wadsrc/static/zscript/compatibility.txt +++ b/wadsrc/static/zscript/compatibility.txt @@ -2,12 +2,12 @@ extend class Object { - deprecated static int GameType() // deprecated for 2.4.x + deprecated("2.4") static int GameType() { return gameinfo.gametype; } - deprecated static void C_MidPrint(string fontname, string textlabel, bool bold = false) // deprecated for 2.4.x + deprecated("2.4") static void C_MidPrint(string fontname, string textlabel, bool bold = false) { let f = Font.GetFont(fontname); if (f == null) return; @@ -18,7 +18,7 @@ extend class Object extend class Actor { - deprecated void A_CustomMissile(class missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET) + deprecated("2.3") void A_CustomMissile(class missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET) { A_SpawnProjectile(missiletype, spawnheight, spawnofs_xy, angle, flags|CMF_BADPITCH, pitch, ptr); } @@ -26,7 +26,7 @@ extend class Actor extend class StateProvider { - deprecated action void A_FireCustomMissile(class missiletype, double angle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0) + deprecated("2.3") action void A_FireCustomMissile(class missiletype, double angle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0) { A_FireProjectile(missiletype, angle, useammo, spawnofs_xy, spawnheight, flags, -pitch); } diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 03c2de90f..e1c2b485a 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -1,6 +1,6 @@ -class BaseEvent native { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything. +class BaseEvent native version("2.4") { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything. -class RenderEvent : BaseEvent native ui +class RenderEvent : BaseEvent native ui version("2.4") { native readonly Vector3 ViewPos; native readonly double ViewAngle; @@ -10,7 +10,7 @@ class RenderEvent : BaseEvent native ui native readonly Actor Camera; } -class WorldEvent : BaseEvent native play +class WorldEvent : BaseEvent native play version("2.4") { // for loaded/unloaded native readonly bool IsSaveGame; @@ -28,7 +28,7 @@ class WorldEvent : BaseEvent native play native readonly double DamageAngle; } -class PlayerEvent : BaseEvent native play +class PlayerEvent : BaseEvent native play version("2.4") { // this is the player number that caused the event. // note: you can get player struct from this by using players[e.PlayerNumber] @@ -37,7 +37,7 @@ class PlayerEvent : BaseEvent native play native readonly bool IsReturn; } -class UiEvent : BaseEvent native ui +class UiEvent : BaseEvent native ui version("2.4") { // d_gui.h enum EGUIEvent @@ -121,7 +121,7 @@ class UiEvent : BaseEvent native ui native readonly bool IsAlt; } -class InputEvent : BaseEvent native play +class InputEvent : BaseEvent native play version("2.4") { enum EGenericEvent { @@ -270,7 +270,7 @@ class InputEvent : BaseEvent native play native readonly int MouseY; } -class ConsoleEvent : BaseEvent native +class ConsoleEvent : BaseEvent native version("2.4") { // for net events, this will be the activator. // for UI events, this is always -1, and you need to check if level is loaded and use players[consoleplayer]. @@ -279,7 +279,7 @@ class ConsoleEvent : BaseEvent native native readonly int Args[3]; } -class StaticEventHandler : Object native play +class StaticEventHandler : Object native play version("2.4") { // static event handlers CAN register other static event handlers. // unlike EventHandler.Create that will not create them. @@ -335,7 +335,7 @@ class StaticEventHandler : Object native play native bool RequireMouse; } -class EventHandler : StaticEventHandler native +class EventHandler : StaticEventHandler native version("2.4") { clearscope static native StaticEventHandler Create(class type); clearscope static native StaticEventHandler CreateOnce(class type); diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 043af1d50..4d842dc80 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -31,10 +31,10 @@ class CustomInventory : StateProvider //--------------------------------------------------------------------------- // This is only here, because these functions were originally exported on Inventory, despite only working for weapons, so this is here to satisfy some potential old mods having called it through CustomInventory. - deprecated action void A_GunFlash(statelabel flash = null, int flags = 0) {} - deprecated action void A_Lower() {} - deprecated action void A_Raise() {} - deprecated action void A_CheckReload() {} + deprecated("2.3") action void A_GunFlash(statelabel flash = null, int flags = 0) {} + deprecated("2.3") action void A_Lower() {} + deprecated("2.3") action void A_Raise() {} + deprecated("2.3") action void A_CheckReload() {} native bool CallStateChain (Actor actor, State state); //=========================================================================== diff --git a/wadsrc/static/zscript/menu/conversationmenu.txt b/wadsrc/static/zscript/menu/conversationmenu.txt index dba162ad7..9d1d9f494 100644 --- a/wadsrc/static/zscript/menu/conversationmenu.txt +++ b/wadsrc/static/zscript/menu/conversationmenu.txt @@ -32,7 +32,7 @@ ** */ -struct StrifeDialogueNode native +struct StrifeDialogueNode native version("2.4") { native Class DropType; native int ThisNodeNum; @@ -49,7 +49,7 @@ struct StrifeDialogueNode native } // FStrifeDialogueReply holds responses the player can give to the NPC -struct StrifeDialogueReply native +struct StrifeDialogueReply native version("2.4") { native StrifeDialogueReply Next; native Class GiveType; diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index 81727ad2b..e2861cdd3 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -1,5 +1,5 @@ -struct KeyBindings native +struct KeyBindings native version("2.4") { native static String NameKeys(int k1, int k2); @@ -8,7 +8,7 @@ struct KeyBindings native native void UnbindACommand (String str); } -struct OptionValues native +struct OptionValues native version("2.4") { native static int GetCount(Name group); native static String GetText(Name group, int index); @@ -16,7 +16,7 @@ struct OptionValues native native static String GetTextValue(Name group, int index); } -struct JoystickConfig native +struct JoystickConfig native version("2.4") { enum EJoyAxis { @@ -48,7 +48,7 @@ struct JoystickConfig native } -class Menu : Object native ui +class Menu : Object native ui version("2.4") { enum EMenuKey { @@ -287,7 +287,7 @@ class Menu : Object native ui } -class MenuDescriptor : Object native ui +class MenuDescriptor : Object native ui version("2.4") { native Name mMenuName; native String mNetgameMessage; diff --git a/wadsrc/static/zscript/menu/menuitembase.txt b/wadsrc/static/zscript/menu/menuitembase.txt index 9b758c117..598cf7f8a 100644 --- a/wadsrc/static/zscript/menu/menuitembase.txt +++ b/wadsrc/static/zscript/menu/menuitembase.txt @@ -4,7 +4,7 @@ // //============================================================================= -class MenuItemBase : Object native ui +class MenuItemBase : Object native ui version("2.4") { protected native double mXpos, mYpos; protected native Name mAction; diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index ed6189335..f0917654a 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -32,7 +32,7 @@ ** */ -struct FOptionMenuSettings +struct FOptionMenuSettings version("2.4") { int mTitleColor; int mFontColor; diff --git a/wadsrc/static/zscript/statscreen/types.txt b/wadsrc/static/zscript/statscreen/types.txt index 57080643e..63485e035 100644 --- a/wadsrc/static/zscript/statscreen/types.txt +++ b/wadsrc/static/zscript/statscreen/types.txt @@ -3,7 +3,7 @@ // INTERMISSION // Structure passed e.g. to WI_Start(wb) // -struct WBPlayerStruct native +struct WBPlayerStruct native version("2.4") { // Player stats, kills, collected items etc. native int skills; @@ -14,7 +14,7 @@ struct WBPlayerStruct native native int fragcount; // [RH] Cumulative frags for this player } -struct WBStartStruct native +struct WBStartStruct native version("2.4") { native int finished_ep; native int next_ep; From 456ac64723945ddf91a39a78c926d605ac8c3427 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 17:58:55 +0100 Subject: [PATCH 15/21] - 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 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. From 8400961a001d0d53bf522cea06302485e725fe64 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sun, 5 Mar 2017 16:05:05 +0200 Subject: [PATCH 16/21] Did some cleanup - since FScopeBarrier is not in codegen now, it's possible to include it properly from vm.h --- src/scripting/backend/codegen.cpp | 31 -------------------------- src/scripting/backend/scopebarrier.cpp | 26 ++++++++++++++++++++- src/scripting/backend/scopebarrier.h | 4 ++++ src/scripting/vm/vm.h | 5 +---- src/scripting/vm/vmexec.h | 4 ++-- 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index bcaa374c2..1b1672545 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -89,37 +89,6 @@ static const FLOP FxFlops[] = { NAME_TanH, FLOP_TANH, [](double v) { return g_tanh(v); } }, }; - -//========================================================================== -// -// [ZZ] Magic methods to be used in vmexec.h for runtime checking of scope -// -//========================================================================== - -// this can be imported in vmexec.h -void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc) -{ - int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual; - if (outerside == FScopeBarrier::Side_Virtual) - outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); - int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); - if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" - ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); -} -// this can be imported in vmexec.h -void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype) -{ - // [ZZ] anonymous blocks have 0 variants, so give them Side_Virtual. - int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual; - if (outerside == FScopeBarrier::Side_Virtual) - outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); - int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags); - if (innerside == FScopeBarrier::Side_Virtual) - innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); - if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) - ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->SymbolName.GetChars(), FScopeBarrier::StringFromSide(outerside)); -} - //========================================================================== // // FCompileContext diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp index fda9de3c3..ad104f317 100644 --- a/src/scripting/backend/scopebarrier.cpp +++ b/src/scripting/backend/scopebarrier.cpp @@ -1,5 +1,5 @@ -#include "scopebarrier.h" #include "dobject.h" +#include "scopebarrier.h" // Note: the same object can't be both UI and Play. This is checked explicitly in the field construction and will cause esoteric errors here if found. @@ -171,3 +171,27 @@ void FScopeBarrier::AddFlags(int flags1, int flags2, const char* name) if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } } + +// these are for vmexec.h +void FScopeBarrier::ValidateNew(PClass* cls, PFunction* callingfunc) +{ + int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual; + if (outerside == FScopeBarrier::Side_Virtual) + outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" + ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); +} +// this can be imported in vmexec.h +void FScopeBarrier::ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype) +{ + // [ZZ] anonymous blocks have 0 variants, so give them Side_Virtual. + int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual; + if (outerside == FScopeBarrier::Side_Virtual) + outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); + int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags); + if (innerside == FScopeBarrier::Side_Virtual) + innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); + if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) + ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->SymbolName.GetChars(), FScopeBarrier::StringFromSide(outerside)); +} \ No newline at end of file diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h index 514db7a97..db8a5044a 100644 --- a/src/scripting/backend/scopebarrier.h +++ b/src/scripting/backend/scopebarrier.h @@ -51,5 +51,9 @@ struct FScopeBarrier // This is used for comparing a.b.c.d access - if non-allowed field is seen anywhere in the chain, anything after it is non-allowed. // This struct is used so that the logic is in a single place. void AddFlags(int flags1, int flags2, const char* name); + + // this is called from vmexec.h + static void ValidateNew(PClass* cls, PFunction* callingfunc); + static void ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype); }; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 6c66c7b85..02f048a1c 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -7,11 +7,8 @@ #include "cmdlib.h" #include "doomerrors.h" #include "memarena.h" +#include "scripting/backend/scopebarrier.h" -// [ZZ] there are serious circular references between this and the rest of ZScript code, so it needs to be done like this -// these are used in vmexec.h -void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc); -void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype); class DObject; extern FMemArena ClassDataAllocator; diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index cdf598649..0281be3df 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -673,7 +673,7 @@ begin: PFunction* callingfunc = (PFunction*)(reg.param + f->NumParam - b)[1].a; DObject* dobj = (DObject*)(reg.param + f->NumParam - b)[2].a; // this is the self pointer. it should be in, since Side_Virtual functions are always non-static methods. PClass* selftype = dobj->GetClass(); - FScopeBarrier_ValidateCall(calledfunc, callingfunc, selftype); + FScopeBarrier::ValidateCall(calledfunc, callingfunc, selftype); b -= 2; } #endif @@ -821,7 +821,7 @@ begin: if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); // [ZZ] validate readonly and between scope construction if (callingfunc) - FScopeBarrier_ValidateNew(cls, callingfunc); + FScopeBarrier::ValidateNew(cls, callingfunc); reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP; From f9e543bed32994ec58de80d62981f2a184c2b476 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 5 Mar 2017 20:51:48 +0200 Subject: [PATCH 17/21] Fixed black target screen during wipe (legacy renderer only) Credits: NiTROACTiVE and Erick194 https://www.doomworld.com/vb/source-ports/93179-two-issues-im-having-with-gzdoom-v2-3-2/ --- src/gl/system/gl_wipe.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 51d3f9042..e0d944ad9 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -143,17 +143,26 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) glFinish(); wipestartscreen->Bind(0, false, false); + const auto copyPixels = [&viewport]() + { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); + }; + if (FGLRenderBuffers::IsEnabled()) { GLRenderer->mBuffers->BindCurrentFB(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); + copyPixels(); + } + else if (gl.legacyMode) + { + copyPixels(); } else { GLint readbuffer = 0; glGetIntegerv(GL_READ_BUFFER, &readbuffer); glReadBuffer(GL_FRONT); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); + copyPixels(); glReadBuffer(readbuffer); } From 8a5daf211cf488b3228bc988e1c265dcf4b6bc0d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 20:40:07 +0100 Subject: [PATCH 18/21] - added version check for member variables. --- src/dobjtype.cpp | 10 +++++ src/scripting/backend/codegen.cpp | 53 ++++++++++++++++++++++----- src/scripting/backend/codegen.h | 4 +- src/scripting/backend/vmbuilder.cpp | 2 +- src/scripting/symbols.h | 1 + src/scripting/zscript/zcc-parse.lemon | 2 +- src/scripting/zscript/zcc_parser.cpp | 3 +- 7 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 13b1c7ed9..a5dc66a5b 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1316,6 +1316,7 @@ PPointer::PPointer(PType *pointsat, bool isconst) : PBasicType(sizeof(void *), alignof(void *)), PointedType(pointsat), IsConst(isconst) { mDescriptiveName.Format("Pointer<%s%s>", pointsat->DescriptiveName(), isconst? "readonly " : ""); + mVersion = pointsat->mVersion; SetOps(); } @@ -1518,6 +1519,7 @@ PClassPointer::PClassPointer(PClass *restrict) // This means we can use the cheapoer non-barriered opcodes here. loadOp = OP_LOS; storeOp = OP_SP; + mVersion = restrict->mVersion; } //========================================================================== @@ -2522,6 +2524,14 @@ PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue else BitValue = -1; } +VersionInfo PField::GetVersion() +{ + VersionInfo Highest = { 0,0,0 }; + if (!(Flags & VARF_Deprecated)) Highest = mVersion; + if (Type->mVersion > Highest) Highest = Type->mVersion; + return Highest; +} + /* PProperty *****************************************************************/ IMPLEMENT_CLASS(PProperty, false, false) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1b1672545..db834d146 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -89,15 +89,25 @@ static const FLOP FxFlops[] = { NAME_TanH, FLOP_TANH, [](double v) { return g_tanh(v); } }, }; + //========================================================================== // // FCompileContext // //========================================================================== -FCompileContext::FCompileContext(PNamespace *cg, PFunction *fnc, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump) - : ReturnProto(ret), Function(fnc), Class(nullptr), FromDecorate(fromdecorate), StateIndex(stateindex), StateCount(statecount), Lump(lump), CurGlobals(cg) +FCompileContext::FCompileContext(PNamespace *cg, PFunction *fnc, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump, const VersionInfo &ver) + : ReturnProto(ret), Function(fnc), Class(nullptr), FromDecorate(fromdecorate), StateIndex(stateindex), StateCount(statecount), Lump(lump), CurGlobals(cg), Version(ver) { + if (Version >= MakeVersion(2, 3)) + { + VersionString.Format("ZScript version %d.%d.%d", Version.major, Version.minor, Version.revision); + } + else + { + VersionString = "DECORATE"; + } + if (fnc != nullptr) Class = fnc->OwningClass; } @@ -5955,6 +5965,15 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) } else if (sym->IsKindOf(RUNTIME_CLASS(PField))) { + PField *vsym = static_cast(sym); + + if (vsym->GetVersion() > ctx.Version) + { + ScriptPosition.Message(MSG_ERROR, "%s not accessible to %s", sym->SymbolName.GetChars(), ctx.VersionString.GetChars()); + delete this; + return nullptr; + } + // internally defined global variable ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as global variable\n", Identifier.GetChars()); newex = new FxGlobalVariable(static_cast(sym), ScriptPosition); @@ -6014,7 +6033,8 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct if (!objtype->IsKindOf(RUNTIME_CLASS(PClassActor))) { ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); - delete this; + delete object; + object = nullptr; return nullptr; } @@ -6036,19 +6056,28 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct else if (sym->IsKindOf(RUNTIME_CLASS(PField))) { PField *vsym = static_cast(sym); - - // We have 4 cases to consider here: - // 1. The symbol is a static/meta member (not implemented yet) which is always accessible. - // 2. This is a static function - // 3. This is an action function with a restricted self pointer - // 4. This is a normal member or unrestricted action function. - if (vsym->Flags & VARF_Deprecated && !ctx.FromDecorate) + if (vsym->GetVersion() > ctx.Version) + { + ScriptPosition.Message(MSG_ERROR, "%s not accessible to %s", sym->SymbolName.GetChars(), ctx.VersionString.GetChars()); + delete object; + object = nullptr; + return nullptr; + } + if ((vsym->Flags & VARF_Deprecated) && sym->mVersion >= ctx.Version) { ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s", vsym->SymbolName.GetChars()); } + + // We have 4 cases to consider here: + // 1. The symbol is a static/meta member which is always accessible. + // 2. This is a static function + // 3. This is an action function with a restricted self pointer + // 4. This is a normal member or unrestricted action function. if ((vsym->Flags & VARF_Private) && symtbl != &classctx->Symbols) { ScriptPosition.Message(MSG_ERROR, "Private member %s not accessible", vsym->SymbolName.GetChars()); + delete object; + object = nullptr; return nullptr; } PClass* cls_ctx = dyn_cast(classctx); @@ -6060,6 +6089,8 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct if (!cls_ctx || !cls_target) { ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); + delete object; + object = nullptr; return nullptr; } @@ -6079,6 +6110,8 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct if (!cls_ctx->IsDescendantOf(cls_target)) { ScriptPosition.Message(MSG_ERROR, "Protected member %s not accessible", vsym->SymbolName.GetChars()); + delete object; + object = nullptr; return nullptr; } } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 000688341..5c6fb8ddd 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -86,8 +86,10 @@ struct FCompileContext bool Unsafe = false; TDeletingArray FunctionArgs; PNamespace *CurGlobals; + VersionInfo Version; + FString VersionString; - FCompileContext(PNamespace *spc, PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump); + FCompileContext(PNamespace *spc, PFunction *func, PPrototype *ret, bool fromdecorate, int stateindex, int statecount, int lump, const VersionInfo &ver); FCompileContext(PNamespace *spc, PStruct *cls, bool fromdecorate); // only to be used to resolve constants! PSymbol *FindInClass(FName identifier, PSymbolTable *&symt); diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index e3942c33c..df53e51ed 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -857,7 +857,7 @@ void FFunctionBuildList::Build() assert(item.Code != NULL); // We don't know the return type in advance for anonymous functions. - FCompileContext ctx(item.CurGlobals, item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump); + FCompileContext ctx(item.CurGlobals, item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump, item.Version); // Allocate registers for the function's arguments and create local variable nodes before starting to resolve it. VMFunctionBuilder buildit(item.Func->GetImplicitArgs()); diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h index 6f9519065..648462168 100644 --- a/src/scripting/symbols.h +++ b/src/scripting/symbols.h @@ -77,6 +77,7 @@ class PField : public PSymbol HAS_OBJECT_POINTERS public: PField(FName name, PType *type, uint32_t flags = 0, size_t offset = 0, int bitvalue = 0); + VersionInfo GetVersion(); size_t Offset; PType *Type; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index c012d71e9..58d87557a 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -1030,7 +1030,7 @@ decl_flags(X) ::= decl_flags(F) DEPRECATED(B) LPAREN STRCONST(A) RPAREN. X->Version = A.String->GetChars(); } -decl_flags(X) ::= decl_flags(F) VERSION(B) LPAREN STRINGCONST(A) RPAREN. +decl_flags(X) ::= decl_flags(F) VERSION(B) LPAREN STRCONST(A) RPAREN. { if (F == nullptr) { diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 521ac9f67..6df435f6c 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -386,8 +386,7 @@ static void DoParse(int lumpnum) } else { - state.ParseVersion.major = 2; // 2.3 is the first version of ZScript. - state.ParseVersion.minor = 3; + state.ParseVersion = MakeVersion(2, 3); // 2.3 is the first version of ZScript. sc.RestorePos(saved); } } From 2b5fea4ea8f4d9efbec62323e26020e283ab42cc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 21:44:10 +0100 Subject: [PATCH 19/21] - added version checks for function calls and virtual overrides. - restricted the UI functions in inventory. --- src/scripting/backend/codegen.cpp | 45 ++++++++++++++++--- src/scripting/backend/codegen.h | 10 +++-- src/scripting/backend/vmbuilder.cpp | 2 +- src/scripting/zscript/zcc_compile.cpp | 7 ++- wadsrc/static/zscript/inventory/inventory.txt | 4 +- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index db834d146..06f8757c3 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -388,7 +388,7 @@ bool FxExpression::isConstant() const // //========================================================================== -VMFunction *FxExpression::GetDirectFunction() +VMFunction *FxExpression::GetDirectFunction(const VersionInfo &ver) { return nullptr; } @@ -6065,6 +6065,7 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct } if ((vsym->Flags & VARF_Deprecated) && sym->mVersion >= ctx.Version) { + ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s - deprecated since %d.%d.%d", sym->SymbolName.GetChars(), vsym->mVersion.major, vsym->mVersion.minor, vsym->mVersion.revision); ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s", vsym->SymbolName.GetChars()); } @@ -8495,19 +8496,44 @@ PPrototype *FxVMFunctionCall::ReturnProto() return Function->Variants[0].Proto; } + +bool FxVMFunctionCall::CheckAccessibility(const VersionInfo &ver) +{ + if (Function->mVersion > ver && !(Function->Variants[0].Flags & VARF_Deprecated)) + { + FString VersionString; + if (ver >= MakeVersion(2, 3)) + { + VersionString.Format("ZScript version %d.%d.%d", ver.major, ver.minor, ver.revision); + } + else + { + VersionString = "DECORATE"; + } + ScriptPosition.Message(MSG_ERROR, "%s not accessible to %s", Function->SymbolName.GetChars(), VersionString.GetChars()); + return false; + } + if ((Function->Variants[0].Flags & VARF_Deprecated) && Function->mVersion >= ver) + { + ScriptPosition.Message(MSG_WARNING, "Accessing deprecated function %s - deprecated since %d.%d.%d", Function->SymbolName.GetChars(), Function->mVersion.major, Function->mVersion.minor, Function->mVersion.revision); + return false; + } + return true; +} //========================================================================== // // // //========================================================================== -VMFunction *FxVMFunctionCall::GetDirectFunction() +VMFunction *FxVMFunctionCall::GetDirectFunction(const VersionInfo &ver) { // If this return statement calls a non-virtual function with no arguments, // then it can be a "direct" function. That is, the DECORATE // definition can call that function directly without wrapping // it inside VM code. - if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual)) + + if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual) && CheckAccessibility(ver)) { unsigned imp = Function->GetImplicitArgs(); if (Function->Variants[0].ArgFlags.Size() > imp && !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr; @@ -8536,6 +8562,11 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) int implicit = Function->GetImplicitArgs(); + if (!CheckAccessibility(ctx.Version)) + { + delete this; + return false; + } // This should never happen. if (Self == nullptr && (Function->Variants[0].Flags & VARF_Method)) { @@ -9372,11 +9403,11 @@ ExpEmit FxSequence::Emit(VMFunctionBuilder *build) // //========================================================================== -VMFunction *FxSequence::GetDirectFunction() +VMFunction *FxSequence::GetDirectFunction(const VersionInfo &ver) { if (Expressions.Size() == 1) { - return Expressions[0]->GetDirectFunction(); + return Expressions[0]->GetDirectFunction(ver); } return nullptr; } @@ -10358,11 +10389,11 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) return out; } -VMFunction *FxReturnStatement::GetDirectFunction() +VMFunction *FxReturnStatement::GetDirectFunction(const VersionInfo &ver) { if (Args.Size() == 1) { - return Args[0]->GetDirectFunction(); + return Args[0]->GetDirectFunction(ver); } return nullptr; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 5c6fb8ddd..0861ee4ed 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -324,7 +324,7 @@ public: virtual bool isConstant() const; virtual bool RequestAddress(FCompileContext &ctx, bool *writable); virtual PPrototype *ReturnProto(); - virtual VMFunction *GetDirectFunction(); + virtual VMFunction *GetDirectFunction(const VersionInfo &ver); virtual bool CheckReturn() { return false; } virtual int GetBitValue() { return -1; } bool IsNumeric() const { return ValueType->isNumeric(); } @@ -1714,12 +1714,14 @@ class FxVMFunctionCall : public FxExpression TArray ReturnRegs; PFunction *CallingFunction; + bool CheckAccessibility(const VersionInfo &ver); + public: FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual); ~FxVMFunctionCall(); FxExpression *Resolve(FCompileContext&); PPrototype *ReturnProto(); - VMFunction *GetDirectFunction(); + VMFunction *GetDirectFunction(const VersionInfo &ver); ExpEmit Emit(VMFunctionBuilder *build); bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit ®); TArray &GetReturnTypes() const @@ -1744,7 +1746,7 @@ public: FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); expr->NeedResult = false; } - VMFunction *GetDirectFunction(); + VMFunction *GetDirectFunction(const VersionInfo &ver); bool CheckReturn(); }; @@ -1951,7 +1953,7 @@ public: ~FxReturnStatement(); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); - VMFunction *GetDirectFunction(); + VMFunction *GetDirectFunction(const VersionInfo &ver); bool CheckReturn() { return true; } }; diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp index df53e51ed..31290cc48 100644 --- a/src/scripting/backend/vmbuilder.cpp +++ b/src/scripting/backend/vmbuilder.cpp @@ -804,7 +804,7 @@ FFunctionBuildList FunctionBuildList; 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(); + auto func = code->GetDirectFunction(ver); if (func != nullptr) { delete code; diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 0695b2070..fc8794aac 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -611,7 +611,7 @@ void ZCCCompiler::CreateClassTypes() { 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); + Error(c->cls, "Parent class %s of %s not accessible to ZScript version %d.%d.%d", parent->TypeName.GetChars(), c->NodeName().GetChars(), mVersion.major, mVersion.minor, mVersion.revision); } c->cls->Type = parent->CreateDerivedClass(c->NodeName(), TentativeClass); if (c->Type() == nullptr) @@ -2493,6 +2493,11 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool else { auto oldfunc = clstype->Virtuals[vindex]; + auto parentfunc = dyn_cast(clstype->ParentClass->Symbols.FindSymbol(sym->SymbolName, true)); + if (parentfunc && parentfunc->mVersion > mVersion) + { + Error(f, "Attempt to override function %s which is incompatible with version %d.%d.%d", FName(f->Name).GetChars(), mVersion.major, mVersion.minor, mVersion.revision); + } if (oldfunc->VarFlags & VARF_Final) { Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 16a83c7a5..722f3158b 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -751,7 +751,7 @@ class Inventory : Actor native virtual bool Use (bool pickup) { return false; } virtual double GetSpeedFactor() { return 1; } virtual bool GetNoTeleportFreeze() { return false; } - virtual ui void AlterWeaponSprite(VisStyle vis, in out int changed) {} + virtual version("2.4") ui void AlterWeaponSprite(VisStyle vis, in out int changed) {} virtual void OwnerDied() {} virtual Color GetBlend () { return 0; } @@ -818,7 +818,7 @@ class Inventory : Actor native // //=========================================================================== - virtual ui bool DrawPowerup(int x, int y) { return false; } + virtual ui version("2.4") bool DrawPowerup(int x, int y) { return false; } //=========================================================================== // From b4079b9915ef5ff56afad5d42e988e9cdfb82f04 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Sun, 5 Mar 2017 09:58:07 -0500 Subject: [PATCH 20/21] - implemented "hazardcolor" and "hazardflash" properties. This affects strife's sector damage type that passively builds up over time. setting "hazardcolor" changes the gradual blend that is used when palette flash is disabled. setting "hazardflash" changes the flashing blend that is used when palette flash is turned on. --- src/g_level.cpp | 2 ++ src/g_level.h | 2 ++ src/g_levellocals.h | 3 +++ src/g_mapinfo.cpp | 16 ++++++++++++++++ src/v_blend.cpp | 11 +++++++++-- 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index b0de51b43..427e17278 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1462,6 +1462,8 @@ void G_InitLevelLocals () level.NextMap = info->NextMap; level.NextSecretMap = info->NextSecretMap; level.F1Pic = info->F1Pic; + level.hazardcolor = info->hazardcolor; + level.hazardflash = info->hazardflash; compatflags.Callback(); compatflags2.Callback(); diff --git a/src/g_level.h b/src/g_level.h index fd66527a7..26da28b9a 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -315,6 +315,8 @@ struct level_info_t FName Intermission; FName deathsequence; FName slideshow; + DWORD hazardcolor; + DWORD hazardflash; // Redirection: If any player is carrying the specified item, then // you go to the RedirectMap instead of this one. diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 1b3d1dba2..9c77a64f9 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -43,6 +43,9 @@ struct FLevelLocals DWORD fadeto; // The color the palette fades to (usually black) DWORD outsidefog; // The fog for sectors with sky ceilings + DWORD hazardcolor; // what color strife hazard blends the screen color as + DWORD hazardflash; // what color strife hazard flashes the screen color as + FString Music; int musicorder; int cdtrack; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 44b21bfd8..69c90048f 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -273,6 +273,8 @@ void level_info_t::Reset() SndSeq = ""; BorderTexture = ""; teamdamage = 0.f; + hazardcolor = 0xff004200; + hazardflash = 0xff00ff00; specialactions.Clear(); DefaultEnvironment = 0; PrecacheSounds.Clear(); @@ -1200,6 +1202,20 @@ DEFINE_MAP_OPTION(defaultenvironment, false) info->DefaultEnvironment = id; } +DEFINE_MAP_OPTION(hazardcolor, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->hazardcolor = V_GetColor(NULL, parse.sc); +} + +DEFINE_MAP_OPTION(hazardflash, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->hazardflash = V_GetColor(NULL, parse.sc); +} + //========================================================================== // diff --git a/src/v_blend.cpp b/src/v_blend.cpp index 41e6fe7ff..874e81ca1 100644 --- a/src/v_blend.cpp +++ b/src/v_blend.cpp @@ -51,6 +51,7 @@ #include "colormatcher.h" #include "v_palette.h" #include "d_player.h" +#include "g_levellocals.h" CVAR( Float, blood_fade_scalar, 1.0f, CVAR_ARCHIVE ) // [SP] Pulled from Skulltag - changed default from 0.5 to 1.0 CVAR( Float, pickup_fade_scalar, 1.0f, CVAR_ARCHIVE ) // [SP] Uses same logic as blood_fade_scalar except for pickups @@ -168,13 +169,19 @@ void V_AddPlayerBlend (player_t *CPlayer, float blend[4], float maxinvalpha, int { if (CPlayer->hazardcount > 16*TICRATE || (CPlayer->hazardcount & 8)) { - V_AddBlend (0.f, 1.f, 0.f, 0.125f, blend); + float r = ((level.hazardflash & 0xff0000) >> 16) / 255.f; + float g = ((level.hazardflash & 0xff00) >> 8) / 255.f; + float b = ((level.hazardflash & 0xff)) / 255.f; + V_AddBlend (r, g, b, 0.125f, blend); } } else { cnt= MIN(CPlayer->hazardcount/8, 64); - V_AddBlend (0.f, 0.2571f, 0.f, cnt/93.2571428571f, blend); + float r = ((level.hazardcolor & 0xff0000) >> 16) / 255.f; + float g = ((level.hazardcolor & 0xff00) >> 8) / 255.f; + float b = ((level.hazardcolor & 0xff)) / 255.f; + V_AddBlend (r, g, b, cnt/93.2571428571f, blend); } } From ad89d3eea0ede0b96bf4432e24ca12c6a3c6eefe Mon Sep 17 00:00:00 2001 From: Gaerzi Date: Sun, 5 Mar 2017 21:03:36 +0100 Subject: [PATCH 21/21] Added SLADE3's colourise and tint translations. Includes support for ACS. Added tokens for '#' and '@' in sc_man_scanner.re. --- src/p_acs.cpp | 29 ++++++++++ src/p_acs.h | 2 + src/r_data/r_translate.cpp | 112 +++++++++++++++++++++++++++++++++---- src/r_data/r_translate.h | 2 + src/sc_man_scanner.re | 2 + 5 files changed, 135 insertions(+), 12 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 8c2859479..e2070083d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -9088,6 +9088,35 @@ scriptwait: } break; + case PCD_TRANSLATIONRANGE4: + { // Colourise translation + int start = STACK(5); + int end = STACK(4); + int r = STACK(3); + int g = STACK(2); + int b = STACK(1); + sp -= 5; + + if (translation != NULL) + translation->AddColourisation(start, end, r, g, b); + } + break; + + case PCD_TRANSLATIONRANGE5: + { // Tint translation + int start = STACK(6); + int end = STACK(5); + int a = STACK(4); + int r = STACK(3); + int g = STACK(2); + int b = STACK(1); + sp -= 6; + + if (translation != NULL) + translation->AddTint(start, end, r, g, b, a); + } + break; + case PCD_ENDTRANSLATION: if (translation != NULL) { diff --git a/src/p_acs.h b/src/p_acs.h index 09d2ca686..dae4103ca 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -779,6 +779,8 @@ public: /*380*/ PCD_STRCPYTOSCRIPTCHRANGE, PCD_LSPEC5EX, PCD_LSPEC5EXRESULT, + PCD_TRANSLATIONRANGE4, + PCD_TRANSLATIONRANGE5, /*381*/ PCODE_COMMAND_COUNT }; diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index fd1bf5d21..e8ffda7ef 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -487,6 +487,56 @@ void FRemapTable::AddDesaturation(int start, int end, double r1, double g1, doub // //---------------------------------------------------------------------------- +void FRemapTable::AddColourisation(int start, int end, int r, int g, int b) +{ + for (int i = start; i < end; ++i) + { + float br = GPalette.BaseColors[i].r; + float bg = GPalette.BaseColors[i].g; + float bb = GPalette.BaseColors[i].b; + float grey = (br * 0.299 + bg * 0.587 + bb * 0.114) / 255.0f; + if (grey > 1.0) grey = 1.0; + br = r * grey; + bg = g * grey; + bb = b * grey; + + int j = GPalette.Remap[i]; + Palette[j] = PalEntry(j == 0 ? 0 : 255, int(br), int(bg), int(bb)); + Remap[j] = ColorMatcher.Pick(Palette[j]); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void FRemapTable::AddTint(int start, int end, int r, int g, int b, int amount) +{ + for (int i = start; i < end; ++i) + { + float br = GPalette.BaseColors[i].r; + float bg = GPalette.BaseColors[i].g; + float bb = GPalette.BaseColors[i].b; + float a = amount * 0.01f; + float ia = 1.0f - a; + br = br * ia + r * a; + bg = bg * ia + g * a; + bb = bb * ia + b * a; + + int j = GPalette.Remap[i]; + Palette[j] = PalEntry(j == 0 ? 0 : 255, int(br), int(bg), int(bb)); + Remap[j] = ColorMatcher.Pick(Palette[j]); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + void FRemapTable::AddToTranslation(const char *range) { int start,end; @@ -511,19 +561,9 @@ void FRemapTable::AddToTranslation(const char *range) } sc.MustGetAnyToken(); + Printf(0, "token type: %d", sc.TokenType); - if (sc.TokenType != '[' && sc.TokenType != '%') - { - int pal1,pal2; - - sc.TokenMustBe(TK_IntConst); - pal1 = sc.Number; - sc.MustGetToken(':'); - sc.MustGetToken(TK_IntConst); - pal2 = sc.Number; - AddIndexRange(start, end, pal1, pal2); - } - else if (sc.TokenType == '[') + if (sc.TokenType == '[') { // translation using RGB values int r1,g1,b1,r2,g2,b2; @@ -596,6 +636,54 @@ void FRemapTable::AddToTranslation(const char *range) AddDesaturation(start, end, r1, g1, b1, r2, g2, b2); } + else if (sc.TokenType == '#') + { + // Colourise translation + int r, g, b; + sc.MustGetToken('['); + sc.MustGetToken(TK_IntConst); + r = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + g = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + b = sc.Number; + sc.MustGetToken(']'); + + AddColourisation(start, end, r, g, b); + } + else if (sc.TokenType == '@') + { + // Tint translation + int a, r, g, b; + + sc.MustGetToken(TK_IntConst); + a = sc.Number; + sc.MustGetToken('['); + sc.MustGetToken(TK_IntConst); + r = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + g = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + b = sc.Number; + sc.MustGetToken(']'); + + AddTint(start, end, r, g, b, a); + } + else + { + int pal1, pal2; + + sc.TokenMustBe(TK_IntConst); + pal1 = sc.Number; + sc.MustGetToken(':'); + sc.MustGetToken(TK_IntConst); + pal2 = sc.Number; + AddIndexRange(start, end, pal1, pal2); + } } catch (CRecoverableError &err) { diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index 1680f4df0..6c0fe8c77 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -42,6 +42,8 @@ struct FRemapTable void AddIndexRange(int start, int end, int pal1, int pal2); void AddColorRange(int start, int end, int r1,int g1, int b1, int r2, int g2, int b2); void AddDesaturation(int start, int end, double r1, double g1, double b1, double r2, double g2, double b2); + void AddColourisation(int start, int end, int r, int g, int b); + void AddTint(int start, int end, int r, int g, int b, int amount); void AddToTranslation(const char * range); int StoreTranslation(int slot); diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 1c98e9272..0c3da78bf 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -255,6 +255,8 @@ std2: "^" { RET('^'); } "|" { RET('|'); } "?" { RET('?'); } + "#" { RET('#'); } + "@" { RET('@'); } [ \t\v\f\r]+ { goto std1; } "\n" { goto newline; }