From 3577e4eddcaf4708734b9e857a5580a0e72aa157 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 02:14:49 +0200 Subject: [PATCH 01/73] Fixed possible nullptr dereferencing on c->Type() == nullptr --- src/scripting/zscript/zcc_compile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index a240c1973..bd73fa06c 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -598,11 +598,11 @@ void ZCCCompiler::CreateClassTypes() c->cls->Type = nullptr; } } + if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); if (c->cls->Flags & ZCC_Abstract) { c->Type()->ObjectFlags |= OF_Abstract; } - if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); 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.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); From 363990a105ca7088a257b4563fdd2442b952305b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 02:31:20 +0200 Subject: [PATCH 02/73] Class abstractness should be inherited --- src/scripting/thingdef.cpp | 2 +- src/scripting/zscript/zcc_compile.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d0b7df449..5b842ce07 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -194,7 +194,7 @@ PFunction *FindClassMemberFunction(PStruct *selfcls, PStruct *funccls, FName nam { sc.Message(MSG_ERROR, "%s is not a member function of %s", name.GetChars(), selfcls->TypeName.GetChars()); } - else if (funcsym->Variants[0].Flags & VARF_Private && symtable != &funccls->Symbols) + else if ((funcsym->Variants[0].Flags & VARF_Private) && symtable != &funccls->Symbols) { // 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()); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index bd73fa06c..187e2f341 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -599,10 +599,9 @@ void ZCCCompiler::CreateClassTypes() } } if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); - if (c->cls->Flags & ZCC_Abstract) - { + // [ZZ] if parent class is abstract, this one should be abstract as well - otherwise we can subclass Actor and be able to new() our subclass + if ((c->cls->Flags & ZCC_Abstract) || (parent && parent->ObjectFlags & OF_Abstract)) c->Type()->ObjectFlags |= OF_Abstract; - } 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.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); From 7d3663500f5f9f47aebacb3dc3be274a99263bc3 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 02:39:49 +0200 Subject: [PATCH 03/73] Disallow creation of abstract classes outside of their own class (this is so that modders can create their own factory methods, not just for native) --- src/dobject.h | 2 +- src/scripting/backend/codegen.cpp | 17 +++++++++++++++-- src/scripting/backend/codegen.h | 1 + src/scripting/vm/vmexec.h | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index ce4714010..01ee76fb2 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -205,7 +205,7 @@ enum EObjectFlags OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function - OF_Abstract = 1 << 14, // Marks a class that cannot be created with CreateNew + OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function }; template class TObjPtr; diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index db7d7425f..551378d8b 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5023,6 +5023,7 @@ FxNew::FxNew(FxExpression *v) { val = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), v, false); ValueType = NewPointer(RUNTIME_CLASS(DObject)); + CallingClass = nullptr; } //========================================================================== @@ -5047,6 +5048,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) CHECKRESOLVED(); SAFE_RESOLVE(val, ctx); + CallingClass = (PClass*)ctx.Class; if (!val->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { ScriptPosition.Message(MSG_ERROR, "Class type expected"); @@ -5058,6 +5060,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) auto cls = static_cast(static_cast(val)->GetValue().GetPointer()); ValueType = NewPointer(cls); } + return this; } @@ -5072,7 +5075,7 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build) ExpEmit from = val->Emit(build); from.Free(build); ExpEmit to(build, REGT_POINTER); - build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum); + build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum, build->GetConstantAddress(CallingClass, ATAG_OBJECT)); return to; } @@ -7527,8 +7530,18 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) break; case NAME_New: - if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition)) + if (CheckArgSize(MethodName, ArgList, 0, 1, ScriptPosition)) { + // [ZZ] allow implicit new() call to mean "create current class instance" + if (!ArgList.Size() && !ctx.Class->IsKindOf(RUNTIME_CLASS(PClass))) + { + ScriptPosition.Message(MSG_ERROR, "Cannot use implicit new() in a struct"); + delete this; + return nullptr; + } + else if (!ArgList.Size()) + ArgList.Push(new FxConstant((PClass*)ctx.Class, NewClassPointer((PClass*)ctx.Class), ScriptPosition)); + func = new FxNew(ArgList[0]); ArgList[0] = nullptr; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index abbcbd54e..90c474170 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -1208,6 +1208,7 @@ private: class FxNew : public FxExpression { FxExpression *val; + PClass *CallingClass; public: diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 8da2c4d8d..9f93af802 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -787,7 +787,8 @@ begin: { b = B; PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); - if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + PClass *callingcls = (PClass*)konsta[C].o; // [ZZ] due to how this is set, it's always const + if ((cls->ObjectFlags & OF_Abstract) && callingcls != cls) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP; From 0819dd8d89d2120a70894f27476c9c0105159d28 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 04:14:21 +0200 Subject: [PATCH 04/73] Actually pass PFunction to OP_NEW --- src/scripting/backend/codegen.cpp | 6 +++--- src/scripting/backend/codegen.h | 2 +- src/scripting/vm/vmexec.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 551378d8b..77ac9498c 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5023,7 +5023,7 @@ FxNew::FxNew(FxExpression *v) { val = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), v, false); ValueType = NewPointer(RUNTIME_CLASS(DObject)); - CallingClass = nullptr; + CallingFunction = nullptr; } //========================================================================== @@ -5048,7 +5048,7 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) CHECKRESOLVED(); SAFE_RESOLVE(val, ctx); - CallingClass = (PClass*)ctx.Class; + CallingFunction = ctx.Function; if (!val->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { ScriptPosition.Message(MSG_ERROR, "Class type expected"); @@ -5075,7 +5075,7 @@ ExpEmit FxNew::Emit(VMFunctionBuilder *build) ExpEmit from = val->Emit(build); from.Free(build); ExpEmit to(build, REGT_POINTER); - build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum, build->GetConstantAddress(CallingClass, ATAG_OBJECT)); + build->Emit(from.Konst ? OP_NEW_K : OP_NEW, to.RegNum, from.RegNum, build->GetConstantAddress(CallingFunction, ATAG_OBJECT)); return to; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 90c474170..3c4377716 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -1208,7 +1208,7 @@ private: class FxNew : public FxExpression { FxExpression *val; - PClass *CallingClass; + PFunction *CallingFunction; public: diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 9f93af802..01ad0111a 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -787,8 +787,8 @@ begin: { b = B; PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); - PClass *callingcls = (PClass*)konsta[C].o; // [ZZ] due to how this is set, it's always const - if ((cls->ObjectFlags & OF_Abstract) && callingcls != cls) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); + PFunction *callingfunc = (PFunction*)konsta[C].o; // [ZZ] due to how this is set, it's always const + if ((cls->ObjectFlags & OF_Abstract) && (!callingfunc || callingfunc->OwningClass != cls)) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP; From 0803faf5961b49be168c933d64089bffaa4d488f Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 04:22:14 +0200 Subject: [PATCH 05/73] Compile time check for abstract class instantiation to reduce programming errors ASAP --- src/scripting/backend/codegen.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 77ac9498c..1f2b038f1 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5058,6 +5058,13 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) if (val->isConstant()) { auto cls = static_cast(static_cast(val)->GetValue().GetPointer()); + if ((cls->ObjectFlags & OF_Abstract) && cls != ctx.Class) + { + ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); + delete this; + return nullptr; + } + ValueType = NewPointer(cls); } From 5e5d0d3e57e661786db3e7e8390926f82a17ca5c Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Thu, 16 Feb 2017 12:40:09 +0200 Subject: [PATCH 06/73] Using separate keyword 'nonew' as reverse abstract; nonew is inherited, and nonew class can only be created from the first nonew class in the hierarchy --- src/dobject.h | 3 ++- src/sc_man_scanner.re | 1 + src/sc_man_tokens.h | 1 + src/scripting/backend/codegen.cpp | 17 +++++++++++++++-- src/scripting/vm/vmexec.h | 14 +++++++++++++- src/scripting/zscript/zcc-parse.lemon | 1 + src/scripting/zscript/zcc_compile.cpp | 6 ++++-- src/scripting/zscript/zcc_parser.cpp | 1 + src/scripting/zscript/zcc_parser.h | 3 ++- 9 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index 01ee76fb2..6605cea5a 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -205,7 +205,8 @@ enum EObjectFlags OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function - OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function + OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all + OF_NoNew = 1 << 15, // Marks a class that can only be created with new() in the exact class that has this keyword }; template class TObjPtr; diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index d4a1254d6..57a3940ef 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -170,6 +170,7 @@ std2: 'virtual' { RET(TK_Virtual); } 'override' { RET(TK_Override); } 'vararg' { RET(TK_VarArg); } + 'nonew' { RET(TK_NoNew); } 'super' { RET(TK_Super); } 'global' { RET(TK_Global); } 'stop' { RET(TK_Stop); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 0227dffa9..b278de38e 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -112,6 +112,7 @@ xx(TK_Optional, "'optional'") xx(TK_Export, "'expert'") xx(TK_Virtual, "'virtual'") xx(TK_VarArg, "'vararg'") +xx(TK_NoNew, "'nonew'") xx(TK_Override, "'override'") xx(TK_Super, "'super'") xx(TK_Null, "'null'") diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1f2b038f1..fc18f481b 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5058,13 +5058,26 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) if (val->isConstant()) { auto cls = static_cast(static_cast(val)->GetValue().GetPointer()); - if ((cls->ObjectFlags & OF_Abstract) && cls != ctx.Class) + if (cls->ObjectFlags & OF_Abstract) { - ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); delete this; return nullptr; } + if (cls->ObjectFlags & OF_NoNew) + { + PClass* pcls = cls; + while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew)) + pcls = pcls->ParentClass; + if (pcls != ctx.Class) + { + ScriptPosition.Message(MSG_ERROR, "Cannot instantiate class %s directly", cls->TypeName.GetChars()); + delete this; + return nullptr; + } + } + ValueType = NewPointer(cls); } diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 01ad0111a..6c2eef8a1 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -788,7 +788,19 @@ begin: b = B; PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); PFunction *callingfunc = (PFunction*)konsta[C].o; // [ZZ] due to how this is set, it's always const - if ((cls->ObjectFlags & OF_Abstract) && (!callingfunc || callingfunc->OwningClass != cls)) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s outside of that class", cls->TypeName.GetChars()); + if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + if (cls->ObjectFlags & OF_NoNew) + { + // trace to the first nonew class in the hierarchy. + // compare that class to the context class. + // if not matching, disallow creation. + // this ensures that only the root class can have a static factory method that can also create instances of subclasses via new(). + PClass* pcls = cls; + while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew)) + pcls = pcls->ParentClass; + if (!callingfunc || pcls != callingfunc->OwningClass) + ThrowAbortException(X_OTHER, "Cannot instantiate class %s directly", cls->TypeName.GetChars()); + } reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 6d3e8a856..f158bff2d 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -212,6 +212,7 @@ 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) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } +class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; 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) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 187e2f341..cb93a5965 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -599,9 +599,11 @@ void ZCCCompiler::CreateClassTypes() } } if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); - // [ZZ] if parent class is abstract, this one should be abstract as well - otherwise we can subclass Actor and be able to new() our subclass - if ((c->cls->Flags & ZCC_Abstract) || (parent && parent->ObjectFlags & OF_Abstract)) + if (c->cls->Flags & ZCC_Abstract) c->Type()->ObjectFlags |= OF_Abstract; + // [ZZ] inherit nonew keyword + if (c->cls->Flags & ZCC_NoNew || (parent && parent->ObjectFlags & OF_NoNew)) + c->Type()->ObjectFlags |= OF_NoNew; 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.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 501a7b2ea..4bfb8d0f2 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -137,6 +137,7 @@ static void InitTokenMap() TOKENDEF (TK_Latent, ZCC_LATENT); TOKENDEF (TK_Virtual, ZCC_VIRTUAL); TOKENDEF (TK_VarArg, ZCC_VARARG); + TOKENDEF (TK_NoNew, ZCC_NONEW); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); TOKENDEF (TK_Meta, ZCC_META); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 95bafd54f..12a5be06b 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -36,7 +36,8 @@ enum ZCC_Virtual = 1 << 13, ZCC_Override = 1 << 14, ZCC_Transient = 1 << 15, - ZCC_VarArg = 1 << 16 + ZCC_VarArg = 1 << 16, + ZCC_NoNew = 1 << 17, }; // Function parameter modifiers From 4fe9c7d8c874fa506714c3e45aa523e3e3b682ce Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 17:58:16 +0200 Subject: [PATCH 07/73] Added parsing of ui, play, allowui and const qualifiers --- src/dobject.h | 2 + src/dobjtype.h | 2 + src/sc_man_scanner.re | 3 + src/sc_man_tokens.h | 3 + src/scripting/backend/codegen.h | 124 +++++++++++++++++++++ src/scripting/vm/vm.h | 3 + src/scripting/zscript/zcc-parse.lemon | 11 +- src/scripting/zscript/zcc_compile.cpp | 152 +++++++++++++++++++++++--- src/scripting/zscript/zcc_parser.cpp | 3 + src/scripting/zscript/zcc_parser.h | 3 + 10 files changed, 287 insertions(+), 19 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index 6605cea5a..b5f7ef8c4 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -207,6 +207,8 @@ enum EObjectFlags OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all OF_NoNew = 1 << 15, // Marks a class that can only be created with new() in the exact class that has this keyword + OF_UI = 1 << 16, // Marks a class that defaults to VARF_UI for it's fields/methods + OF_Play = 1 << 17, // Marks a class that defaults to VARF_Play for it's fields/methods }; template class TObjPtr; diff --git a/src/dobjtype.h b/src/dobjtype.h index 0151e6c11..cc023c66f 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -37,6 +37,8 @@ enum VARF_Transient = (1<<17), // don't auto serialize field. VARF_Meta = (1<<18), // static class data (by necessity read only.) VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature + VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim) + VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) }; // An action function ------------------------------------------------------- diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 57a3940ef..69dcd4397 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -171,6 +171,9 @@ std2: 'override' { RET(TK_Override); } 'vararg' { RET(TK_VarArg); } 'nonew' { RET(TK_NoNew); } + 'ui' { RET(TK_UI); } + 'play' { RET(TK_Play); } + 'allowui' { RET(TK_AllowUI); } 'super' { RET(TK_Super); } 'global' { RET(TK_Global); } 'stop' { RET(TK_Stop); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index b278de38e..1dc205504 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -113,6 +113,9 @@ xx(TK_Export, "'expert'") xx(TK_Virtual, "'virtual'") xx(TK_VarArg, "'vararg'") xx(TK_NoNew, "'nonew'") +xx(TK_UI, "'ui'") +xx(TK_Play, "'play'") +xx(TK_AllowUI, "'allowui'") xx(TK_Override, "'override'") xx(TK_Super, "'super'") xx(TK_Null, "'null'") diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 3c4377716..429cfd186 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -70,6 +70,130 @@ class FxCompoundStatement; class FxLocalVariableDeclaration; typedef TDeletingArray FArgumentList; +// [ZZ] this is kind of related to compile context as well +struct FPlayUIBarrier +{ + bool callable; + bool readable; + bool writable; + + // this is the error message + FString callerror; + FString readerror; + FString writeerror; + + // this is used to make the error message. + enum Side + { + Side_PlainData = 0, + Side_UI, + Side_Play + }; + int sidefrom; + int sidelast; + + // 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. + int SideFromFlags(int flags) + { + if (flags & VARF_UI) + return Side_UI; + if (flags & VARF_Play) + return Side_Play; + return Side_PlainData; + } + + // used for errors + const char* StringFromSide(int side) + { + switch (side) + { + case Side_PlainData: + return "data"; + case Side_UI: + return "ui"; + case Side_Play: + return "play"; + default: + return "unknown"; + } + } + + FPlayUIBarrier() + { + sidefrom = -1; + sidelast = -1; + callable = true; + readable = true; + writable = true; + } + + FPlayUIBarrier(int flags1, int flags2, const char* name) + { + sidefrom = -1; + sidelast = -1; + callable = true; + readable = true; + writable = true; + + AddFlags(flags1, flags2, name); + } + + // AddFlags modifies ALLOWED actions by flags1->flags2. + // 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) + { + // note: if it's already non-readable, don't even try advancing + if (!readable) + return; + + // we aren't interested in any other flags + flags1 &= VARF_UI | VARF_Play; + flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly; + + if (sidefrom < 0) sidefrom = SideFromFlags(flags1); + if (sidelast < 0) sidelast = sidefrom; + + // flags1 = what's trying to access + // flags2 = what's being accessed + + int sideto = SideFromFlags(flags2); + + // plain data inherits whatever scope modifiers that context or field container has. + // i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well. + if (sideto != Side_PlainData) + sidelast = sideto; + else sideto = sidelast; + + if ((sideto == Side_UI) != (sidefrom == Side_UI)) // only ui -> ui is readable + { + readable = false; + readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), StringFromSide(sidefrom)); + } + + if (!readable) + { + writable = false; + callable = false; + writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + return; + } + + if (writable && (sidefrom != sideto)) // only matching types are writable (plain data implicitly takes context type by default, unless overridden) + { + writable = false; + writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } + + if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context. + { + callable = false; + callerror.Format("Can't call %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } + } +}; + struct FCompileContext { FxExpression *ControlStmt = nullptr; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index aa191c34e..bcdf8d17b 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -702,6 +702,9 @@ 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 ScopeUI = false; // [ZZ] 'ui' method + bool ScopePlay = false; // [ZZ] 'play' method + bool FuncConst = false; // [ZZ] const qualifier for methods - these can be called on readonly BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action unsigned VirtualIndex = ~0u; FName Name; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index f158bff2d..c801950b5 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -214,6 +214,9 @@ class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; } class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; 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) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } /*----- Dottable Identifier -----*/ @@ -327,7 +330,10 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body %type struct_flags{ClassFlagsBlock} struct_flags(X) ::= . { X.Flags = 0; } -struct_flags(X) ::= NATIVE. { X.Flags = ZCC_Native; } +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) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; } +struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; } opt_struct_body(X) ::= . { X = NULL; } opt_struct_body(X) ::= struct_body(X). @@ -1000,6 +1006,9 @@ decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.Sourc 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; } +decl_flag(X) ::= UI(T). { X.Int = ZCC_UIFlag; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= ALLOWUI(T). { X.Int = ZCC_AllowUI; X.SourceLoc = T.SourceLoc; } func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); } func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index cb93a5965..81764a2df 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -496,6 +496,16 @@ void ZCCCompiler::CreateStructTypes() { s->strct->Type = NewStruct(s->NodeName(), outer); } + + if ((s->strct->Flags & (ZCC_UIFlag | ZCC_Play)) == (ZCC_UIFlag | ZCC_Play)) + { + Error(s->strct, "Struct %s has incompatible flags", s->NodeName().GetChars()); + } + + if (s->strct->Flags & ZCC_UIFlag) + s->Type()->ObjectFlags |= OF_UI; + if (s->strct->Flags & ZCC_Play) + s->Type()->ObjectFlags |= OF_Play; s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type()); syms->AddSymbol(s->strct->Symbol); @@ -602,8 +612,24 @@ void ZCCCompiler::CreateClassTypes() if (c->cls->Flags & ZCC_Abstract) c->Type()->ObjectFlags |= OF_Abstract; // [ZZ] inherit nonew keyword - if (c->cls->Flags & ZCC_NoNew || (parent && parent->ObjectFlags & OF_NoNew)) + if (c->cls->Flags & ZCC_NoNew || (parent->ObjectFlags & OF_NoNew)) c->Type()->ObjectFlags |= OF_NoNew; + // + static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + int incompatiblecnt = 0; + for (int k = 0; k < countof(incompatible); k++) + if (incompatible[k] & c->cls->Flags) incompatiblecnt++; + + if (incompatiblecnt > 1) + { + Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars()); + } + + if (c->cls->Flags & ZCC_UIFlag || ((parent->ObjectFlags & OF_UI) && !(c->cls->Flags & ZCC_AllowUI))) + c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI; + if (c->cls->Flags & ZCC_Play || ((parent->ObjectFlags & OF_Play) && !(c->cls->Flags & ZCC_AllowUI))) + 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.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); OutNamespace->Symbols.AddSymbol(c->cls->Symbol); @@ -1063,12 +1089,39 @@ 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) + varflags |= VARF_Play; + if (field->Flags & ZCC_UIFlag) + varflags = (varflags&~VARF_Play) | VARF_UI; + if (field->Flags & ZCC_Play) + varflags = (varflags&~VARF_UI) | VARF_Play; + if (field->Flags & ZCC_AllowUI) + varflags = (varflags&~(VARF_UI | VARF_Play)); if (field->Flags & ZCC_Native) { varflags |= VARF_Native | VARF_Transient; } + static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + int excludeflags = 0; + int fc = 0; + for (int i = 0; i < countof(excludescope); i++) + { + if (field->Flags & excludescope[i]) + { + fc++; + excludeflags |= excludescope[i]; + } + } + if (fc > 1) + { + Error(field, "Invalid combination of scope qualifiers %s on field %s", FlagsToString(excludeflags).GetChars(), FName(field->Names->Name).GetChars()); + varflags &= ~(VARF_UI | VARF_Play); // make plain data + } + if (field->Flags & ZCC_Meta) { varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly @@ -1101,11 +1154,11 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel fd = FindField(querytype, FName(name->Name).GetChars()); if (fd == nullptr) { - Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars()); + Error(field, "The member variable '%s.%s' has not been exported from the executable", type->TypeName.GetChars(), FName(name->Name).GetChars()); } else if (thisfieldtype->Size != fd->FieldSize && fd->BitValue == 0) { - Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration. (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); + Error(field, "The member variable '%s.%s' has mismatching sizes in internal and external declaration (Internal = %d, External = %d)", type->TypeName.GetChars(), FName(name->Name).GetChars(), fd->FieldSize, thisfieldtype->Size); } // Q: Should we check alignment, too? A mismatch may be an indicator for bad assumptions. else @@ -1117,7 +1170,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel } else if (hasnativechildren) { - Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change.", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars()); + Error(field, "Cannot add field %s to %s. %s has native children which means it size may not change", FName(name->Name).GetChars(), type->TypeName.GetChars(), type->TypeName.GetChars()); } else { @@ -1212,7 +1265,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper FString ZCCCompiler::FlagsToString(uint32_t flags) { - const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract", "extension", "virtual", "override", "transient", "vararg" }; + const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "allowui" }; FString build; for (size_t i = 0; i < countof(flagnames); i++) @@ -2014,7 +2067,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } while (t != f->Type); } - int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract; + int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract; if (f->Flags & notallowed) { @@ -2061,6 +2114,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual; 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 = (varflags&~(VARF_Play | VARF_UI)); // const implies allowui. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag. + if (f->Flags & ZCC_UIFlag) + varflags = (varflags&~VARF_Play) | VARF_UI; + if (f->Flags & ZCC_Play) + varflags = (varflags&~VARF_UI) | VARF_Play; + if (f->Flags & ZCC_AllowUI) + varflags = (varflags&~(VARF_Play | VARF_UI)); + if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native)) { Error(f, "'VarArg' can only be used with native methods"); @@ -2086,36 +2153,57 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final, implicitargs = 0; // Static implies Final. - if (varflags & VARF_Override) varflags &= ~VARF_Virtual; // allow 'virtual override'. // Only one of these flags may be used. static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static }; - static const char * print[] = { "virtual", "override", "action", "static" }; + int excludeflags = 0; int fc = 0; - FString build; - for (int i = 0; i < 4; i++) + for (int i = 0; i < countof(exclude); i++) { if (f->Flags & exclude[i]) { fc++; - if (build.Len() > 0) build += ", "; - build += print[i]; + excludeflags |= exclude[i]; } } if (fc > 1) { - Error(f, "Invalid combination of qualifiers %s on function %s.", FName(f->Name).GetChars(), build.GetChars()); + Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars()); varflags |= VARF_Method; } if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well. + // 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))) + { + Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play)).GetChars(), FName(f->Name).GetChars()); + } + + static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + excludeflags = 0; + fc = 0; + for (int i = 0; i < countof(excludescope); i++) + { + if (f->Flags & excludescope[i]) + { + fc++; + excludeflags |= excludescope[i]; + } + } + if (fc > 1) + { + Error(f, "Invalid combination of scope qualifiers %s on function %s", FlagsToString(excludeflags).GetChars(), FName(f->Name).GetChars()); + varflags &= ~(VARF_UI | VARF_Play); // make plain data + } + if (f->Flags & ZCC_Native) { varflags |= VARF_Native; afd = FindFunction(c->Type(), FName(f->Name).GetChars()); if (afd == nullptr) { - Error(f, "The function '%s.%s' has not been exported from the executable.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); + Error(f, "The function '%s.%s' has not been exported from the executable", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); } else { @@ -2229,7 +2317,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } else if (hasoptionals) { - Error(p, "All arguments after the first optional one need also be optional."); + Error(p, "All arguments after the first optional one need also be optional"); } // TBD: disallow certain types? For now, let everything pass that isn't an array. args.Push(type); @@ -2289,13 +2377,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { if (sym->Variants[0].Implementation == nullptr) { - Error(f, "Virtual function %s.%s not present.", c->Type()->TypeName.GetChars(), FName(f->Name).GetChars()); + 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; - } + // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. + if (f->Flags & ZCC_UIFlag) // only direct specification here (varflags can also have owning class scope applied, we don't want that) + sym->Variants[0].Implementation->ScopeUI = true; + if (f->Flags & ZCC_Play) // only direct specification here + sym->Variants[0].Implementation->ScopePlay = true; + if (varflags & VARF_ReadOnly) + sym->Variants[0].Implementation->FuncConst = true; + if (forclass) { int vindex = clstype->FindVirtualIndex(sym->SymbolName, sym->Variants[0].Proto); @@ -2313,6 +2408,27 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } + // you can't change ui/play/allowui for a virtual method. + if ((oldfunc->ScopePlay != sym->Variants[0].Implementation->ScopePlay) || + (oldfunc->ScopeUI != sym->Variants[0].Implementation->ScopeUI)) + { + 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 != (varflags & VARF_ReadOnly)) + { + Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); + } + // inherit scope of original function + if (sym->Variants[0].Implementation->ScopeUI = oldfunc->ScopeUI) + sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_Play)) | VARF_UI; + else if (sym->Variants[0].Implementation->ScopePlay = oldfunc->ScopePlay) + sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI)) | VARF_Play; + else sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI | VARF_Play)); + // inherit const from original function + if (sym->Variants[0].Implementation->FuncConst = oldfunc->FuncConst) + sym->Variants[0].Flags |= VARF_ReadOnly; + clstype->Virtuals[vindex] = sym->Variants[0].Implementation; sym->Variants[0].Implementation->VirtualIndex = vindex; } diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 4bfb8d0f2..2586cb8c5 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -137,6 +137,9 @@ static void InitTokenMap() TOKENDEF (TK_Latent, ZCC_LATENT); TOKENDEF (TK_Virtual, ZCC_VIRTUAL); TOKENDEF (TK_VarArg, ZCC_VARARG); + TOKENDEF (TK_UI, ZCC_UI); + TOKENDEF (TK_Play, ZCC_PLAY); + TOKENDEF (TK_AllowUI, ZCC_ALLOWUI); TOKENDEF (TK_NoNew, ZCC_NONEW); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 12a5be06b..72f81c1cc 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -38,6 +38,9 @@ enum ZCC_Transient = 1 << 15, ZCC_VarArg = 1 << 16, ZCC_NoNew = 1 << 17, + ZCC_UIFlag = 1 << 18, // there's also token called ZCC_UI + ZCC_Play = 1 << 19, + ZCC_AllowUI = 1 << 20, }; // Function parameter modifiers From 2b20abff028b330a99131998969f21b76d343c63 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 18:01:39 +0200 Subject: [PATCH 08/73] Fixed consistency of checks --- src/scripting/zscript/zcc_compile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 81764a2df..1418377c0 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2415,7 +2415,7 @@ 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 != (varflags & VARF_ReadOnly)) + if (oldfunc->FuncConst != sym->Variants[0].Implementation->FuncConst) { Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); } From 0f031c5f22a95dfc3bbbe56d63b2a21d5ba9ccfd Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 18:24:01 +0200 Subject: [PATCH 09/73] Renamed 'allowui' to 'clearscope'. 'allowui' doesn't reflect the real meaning of the keyword which clears implicit 'play' or 'ui' inherited from parent class (for classes) or owning type (for methods/fields) --- src/sc_man_scanner.re | 2 +- src/sc_man_tokens.h | 2 +- src/scripting/backend/codegen.h | 6 +++--- src/scripting/zscript/zcc-parse.lemon | 6 +++--- src/scripting/zscript/zcc_compile.cpp | 20 ++++++++++---------- src/scripting/zscript/zcc_parser.cpp | 2 +- src/scripting/zscript/zcc_parser.h | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 69dcd4397..560c2b077 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -173,7 +173,7 @@ std2: 'nonew' { RET(TK_NoNew); } 'ui' { RET(TK_UI); } 'play' { RET(TK_Play); } - 'allowui' { RET(TK_AllowUI); } + 'clearscope' { RET(TK_ClearScope); } 'super' { RET(TK_Super); } 'global' { RET(TK_Global); } 'stop' { RET(TK_Stop); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 1dc205504..b98c1108b 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -115,7 +115,7 @@ xx(TK_VarArg, "'vararg'") xx(TK_NoNew, "'nonew'") xx(TK_UI, "'ui'") xx(TK_Play, "'play'") -xx(TK_AllowUI, "'allowui'") +xx(TK_ClearScope, "'clearscope'") xx(TK_Override, "'override'") xx(TK_Super, "'super'") xx(TK_Null, "'null'") diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 429cfd186..ecdeaec15 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -71,7 +71,7 @@ class FxLocalVariableDeclaration; typedef TDeletingArray FArgumentList; // [ZZ] this is kind of related to compile context as well -struct FPlayUIBarrier +struct FScopeBarrier { bool callable; bool readable; @@ -118,7 +118,7 @@ struct FPlayUIBarrier } } - FPlayUIBarrier() + FScopeBarrier() { sidefrom = -1; sidelast = -1; @@ -127,7 +127,7 @@ struct FPlayUIBarrier writable = true; } - FPlayUIBarrier(int flags1, int flags2, const char* name) + FScopeBarrier(int flags1, int flags2, const char* name) { sidefrom = -1; sidelast = -1; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index c801950b5..59924b634 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -216,7 +216,7 @@ class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; X. 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) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; X.Replaces = A.Replaces; } +class_flags(X) ::= class_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } /*----- Dottable Identifier -----*/ @@ -332,7 +332,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) ALLOWUI. { X.Flags = A.Flags | ZCC_AllowUI; } +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; } @@ -1008,7 +1008,7 @@ 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; } decl_flag(X) ::= UI(T). { X.Int = ZCC_UIFlag; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; } -decl_flag(X) ::= ALLOWUI(T). { X.Int = ZCC_AllowUI; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= CLEARSCOPE(T). { X.Int = ZCC_ClearScope; X.SourceLoc = T.SourceLoc; } func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); } func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1418377c0..2d2f84492 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -615,7 +615,7 @@ void ZCCCompiler::CreateClassTypes() if (c->cls->Flags & ZCC_NoNew || (parent->ObjectFlags & OF_NoNew)) c->Type()->ObjectFlags |= OF_NoNew; // - static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; int incompatiblecnt = 0; for (int k = 0; k < countof(incompatible); k++) if (incompatible[k] & c->cls->Flags) incompatiblecnt++; @@ -625,9 +625,9 @@ void ZCCCompiler::CreateClassTypes() Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars()); } - if (c->cls->Flags & ZCC_UIFlag || ((parent->ObjectFlags & OF_UI) && !(c->cls->Flags & ZCC_AllowUI))) + if (c->cls->Flags & ZCC_UIFlag || ((parent->ObjectFlags & OF_UI) && !(c->cls->Flags & ZCC_ClearScope))) c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI; - if (c->cls->Flags & ZCC_Play || ((parent->ObjectFlags & OF_Play) && !(c->cls->Flags & ZCC_AllowUI))) + if (c->cls->Flags & ZCC_Play || ((parent->ObjectFlags & OF_Play) && !(c->cls->Flags & ZCC_ClearScope))) 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.) @@ -1097,7 +1097,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel varflags = (varflags&~VARF_Play) | VARF_UI; if (field->Flags & ZCC_Play) varflags = (varflags&~VARF_UI) | VARF_Play; - if (field->Flags & ZCC_AllowUI) + if (field->Flags & ZCC_ClearScope) varflags = (varflags&~(VARF_UI | VARF_Play)); if (field->Flags & ZCC_Native) @@ -1105,7 +1105,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel varflags |= VARF_Native | VARF_Transient; } - static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; int excludeflags = 0; int fc = 0; for (int i = 0; i < countof(excludescope); i++) @@ -1265,7 +1265,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper FString ZCCCompiler::FlagsToString(uint32_t flags) { - const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "allowui" }; + const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "clearscope" }; FString build; for (size_t i = 0; i < countof(flagnames); i++) @@ -2120,12 +2120,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (c->Type()->ObjectFlags & OF_Play) varflags |= VARF_Play; if (f->Flags & ZCC_FuncConst) - varflags = (varflags&~(VARF_Play | VARF_UI)); // const implies allowui. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag. + varflags = (varflags&~(VARF_Play | VARF_UI)); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag. if (f->Flags & ZCC_UIFlag) varflags = (varflags&~VARF_Play) | VARF_UI; if (f->Flags & ZCC_Play) varflags = (varflags&~VARF_UI) | VARF_Play; - if (f->Flags & ZCC_AllowUI) + if (f->Flags & ZCC_ClearScope) varflags = (varflags&~(VARF_Play | VARF_UI)); if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native)) @@ -2180,7 +2180,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play)).GetChars(), FName(f->Name).GetChars()); } - static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_AllowUI }; + static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; excludeflags = 0; fc = 0; for (int i = 0; i < countof(excludescope); i++) @@ -2408,7 +2408,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } - // you can't change ui/play/allowui for a virtual method. + // you can't change ui/play/clearscope for a virtual method. if ((oldfunc->ScopePlay != sym->Variants[0].Implementation->ScopePlay) || (oldfunc->ScopeUI != sym->Variants[0].Implementation->ScopeUI)) { diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 2586cb8c5..d77028d60 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -139,7 +139,7 @@ static void InitTokenMap() TOKENDEF (TK_VarArg, ZCC_VARARG); TOKENDEF (TK_UI, ZCC_UI); TOKENDEF (TK_Play, ZCC_PLAY); - TOKENDEF (TK_AllowUI, ZCC_ALLOWUI); + TOKENDEF (TK_ClearScope, ZCC_CLEARSCOPE); TOKENDEF (TK_NoNew, ZCC_NONEW); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 72f81c1cc..20ebcabae 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -40,7 +40,7 @@ enum ZCC_NoNew = 1 << 17, ZCC_UIFlag = 1 << 18, // there's also token called ZCC_UI ZCC_Play = 1 << 19, - ZCC_AllowUI = 1 << 20, + ZCC_ClearScope = 1 << 20, }; // Function parameter modifiers From 338e676e73910cf82f3f995972e9a5a098103f8b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 19:25:29 +0200 Subject: [PATCH 10/73] Allow calling const methods on readonly structs --- src/scripting/backend/codegen.cpp | 40 +++++++++++++++++-------------- src/scripting/backend/codegen.h | 2 +- wadsrc/static/zscript/base.txt | 8 +++---- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index fc18f481b..1ea2565cb 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -167,7 +167,8 @@ void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos) } } -bool FCompileContext::CheckReadOnly(int flags) +// [ZZ] I find it really dumb that something called CheckReadOnly returns false for readonly. renamed. +bool FCompileContext::CheckWritable(int flags) { if (!(flags & VARF_ReadOnly)) return false; if (!(flags & VARF_InternalAccess)) return true; @@ -6202,7 +6203,7 @@ FxExpression *FxLocalVariable::Resolve(FCompileContext &ctx) bool FxLocalVariable::RequestAddress(FCompileContext &ctx, bool *writable) { AddressRequested = true; - if (writable != nullptr) *writable = !ctx.CheckReadOnly(Variable->VarFlags); + if (writable != nullptr) *writable = !ctx.CheckWritable(Variable->VarFlags); return true; } @@ -6420,7 +6421,7 @@ FxGlobalVariable::FxGlobalVariable(PField* mem, const FScriptPosition &pos) bool FxGlobalVariable::RequestAddress(FCompileContext &ctx, bool *writable) { AddressRequested = true; - if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags); + if (writable != nullptr) *writable = AddressWritable && !ctx.CheckWritable(membervar->Flags); return true; } @@ -6610,7 +6611,7 @@ FxStackVariable::~FxStackVariable() bool FxStackVariable::RequestAddress(FCompileContext &ctx, bool *writable) { AddressRequested = true; - if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags); + if (writable != nullptr) *writable = AddressWritable && !ctx.CheckWritable(membervar->Flags); return true; } @@ -6708,7 +6709,7 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) return false; } AddressRequested = true; - if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckReadOnly(membervar->Flags) && + if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckWritable(membervar->Flags) && (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->IsConst)); return true; } @@ -7618,6 +7619,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) PStruct *cls; bool staticonly = false; bool novirtual = false; + bool isreadonly = false; PStruct *ccls = nullptr; @@ -7912,7 +7914,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) auto x = new FxGetParentClass(Self); return x->Resolve(ctx); } - + if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !Self->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { auto ptype = static_cast(Self->ValueType)->PointedType; @@ -7943,18 +7945,11 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) else if (Self->ValueType->IsKindOf(RUNTIME_CLASS(PStruct))) { bool writable; - if (Self->RequestAddress(ctx, &writable) && writable) - { - cls = static_cast(Self->ValueType); - Self->ValueType = NewPointer(Self->ValueType); - } - else - { - // Cannot be made writable so we cannot use its methods. - ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars()); - delete this; - return nullptr; - } + + // [ZZ] allow const method to be called on a readonly struct + isreadonly = !(Self->RequestAddress(ctx, &writable) && writable); + cls = static_cast(Self->ValueType); + Self->ValueType = NewPointer(Self->ValueType); } else { @@ -7981,6 +7976,15 @@ isresolved: return nullptr; } + if (isreadonly && !(afd->Variants[0].Flags & VARF_ReadOnly)) + { + // Cannot be made writable so we cannot use its methods. + // [ZZ] Why this esoteric message? + ScriptPosition.Message(MSG_ERROR, "Readonly struct on left hand side of %s not allowed\n", MethodName.GetChars()); + delete this; + return nullptr; + } + if (staticonly && (afd->Variants[0].Flags & VARF_Method)) { if (!novirtual || !(afd->Variants[0].Flags & VARF_Virtual)) diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index ecdeaec15..c8120f4d1 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -219,7 +219,7 @@ struct FCompileContext void HandleJumps(int token, FxExpression *handler); void CheckReturn(PPrototype *proto, FScriptPosition &pos); - bool CheckReadOnly(int flags); + bool CheckWritable(int flags); FxLocalVariableDeclaration *FindLocalVariable(FName name); }; diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index bffee91f0..208023b54 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -579,10 +579,10 @@ struct StringStruct native native vararg void AppendFormat(String fmt, ...); native void Replace(String pattern, String replacement); - native String Mid(int pos = 0, int len = 2147483647); - native int Len(); - native String CharAt(int pos); - native int CharCodeAt(int pos); + native String Mid(int pos = 0, int len = 2147483647) const; + native int Len() const; + native String CharAt(int pos) const; + native int CharCodeAt(int pos) const; } class Floor : Thinker native From 195ae24dcb23c1d432838dde3fc32d05e7059cff Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 19:54:59 +0200 Subject: [PATCH 11/73] const method cannot write to self --- src/scripting/backend/codegen.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1ea2565cb..f69205997 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6709,8 +6709,14 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) return false; } AddressRequested = true; - if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckWritable(membervar->Flags) && - (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->IsConst)); + if (writable != nullptr) + { + *writable = (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 ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly))) + *writable = false; + } return true; } From 496b2a74ce324405f1e96eb9c1d32565dcfee4ad Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 20:04:40 +0200 Subject: [PATCH 12/73] Disallow const qualifier for classes --- src/scripting/zscript/zcc_compile.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 2d2f84492..0c77b46f8 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2128,6 +2128,13 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (f->Flags & ZCC_ClearScope) varflags = (varflags&~(VARF_Play | VARF_UI)); + // [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. + if ((f->Flags & ZCC_FuncConst) && !(c->Type()->IsKindOf(RUNTIME_CLASS(PStruct)))) + { + Error(f, "'Const' on a method can only be used in structs"); + } + if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native)) { Error(f, "'VarArg' can only be used with native methods"); @@ -2173,6 +2180,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } if (varflags & VARF_Override) varflags |= VARF_Virtual; // Now that the flags are checked, make all override functions virtual as well. + // [ZZ] this doesn't make sense either. + if ((varflags&(VARF_ReadOnly | VARF_Method)) == VARF_ReadOnly) // non-method const function + { + Error(f, "'Const' 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))) From ecd4f5a32d9a585f95dd8012ae93726806d364ac Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 20:25:11 +0200 Subject: [PATCH 13/73] Removed supposedly debug value added in commit b0f3121bec0e9b265b11f002f93a18f8ffa829d2 --- src/scripting/backend/codegen.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index f69205997..4d30e8459 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6120,11 +6120,6 @@ FxExpression *FxMemberIdentifier::Resolve(FCompileContext& ctx) SAFE_RESOLVE(Object, ctx); - if (Identifier == FName("allmap")) - { - int a = 2; - } - // check for class or struct constants if the left side is a type name. if (Object->ValueType == TypeError) { From 8b0dee6f66a6a5e15630c44b9b8ac30a4aeb9f64 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 21:59:03 +0200 Subject: [PATCH 14/73] My am dumb: PStruct is a base for PClass --- src/scripting/zscript/zcc_compile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 0c77b46f8..3afb80ff3 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2130,7 +2130,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool // [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. - if ((f->Flags & ZCC_FuncConst) && !(c->Type()->IsKindOf(RUNTIME_CLASS(PStruct)))) + if ((f->Flags & ZCC_FuncConst) && (c->Type()->IsKindOf(RUNTIME_CLASS(PClass)))) { Error(f, "'Const' on a method can only be used in structs"); } From a7a4406bb14ec2b24c753ae38472bdd933faee95 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 22:41:04 +0200 Subject: [PATCH 15/73] Supposedly implemented a write check between ui/play/data --- src/scripting/backend/codegen.cpp | 32 ++++++++++++++++++++++++++++--- src/scripting/backend/codegen.h | 19 ++++++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 4d30e8459..635488f26 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6706,11 +6706,28 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) AddressRequested = true; if (writable != nullptr) { - *writable = (AddressWritable && !ctx.CheckWritable(membervar->Flags) && + // [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 ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly))) - *writable = false; + 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] plain data "inherits" scope of whatever it was defined in. + if (bWritable) // don't do complex checks on early fail + { + if (ctx.Function && FString(ctx.Function->SymbolName) == FString("DrawPowerup")) + Printf("field type = %d\n", BarrierSide); + int outerflags = 0; + if (ctx.Function) + outerflags = ctx.Function->Variants[0].Flags; + FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), membervar->SymbolName.GetChars()); + if (!scopeBarrier.writable) + bWritable = false; + } + *writable = bWritable; } return true; } @@ -6741,6 +6758,15 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) return x->Resolve(ctx); } + // [ZZ] support magic + BarrierSide = FScopeBarrier::SideFromFlags(membervar->Flags); + if (classx->ExprType == EFX_StructMember || classx->ExprType == EFX_ClassMember) + { + FxStructMember* pmember = (FxStructMember*)classx; + if (BarrierSide == FScopeBarrier::Side_PlainData && pmember) + BarrierSide = pmember->BarrierSide; + } + if (classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer))) { PPointer *ptrtype = dyn_cast(classx->ValueType); diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index c8120f4d1..ee43a0052 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -93,7 +93,7 @@ struct FScopeBarrier int sidelast; // 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. - int SideFromFlags(int flags) + static int SideFromFlags(int flags) { if (flags & VARF_UI) return Side_UI; @@ -102,8 +102,22 @@ struct FScopeBarrier return Side_PlainData; } + // + static int FlagsFromSide(int side) + { + switch (side) + { + case Side_Play: + return VARF_Play; + case Side_UI: + return VARF_UI; + default: + return 0; + } + } + // used for errors - const char* StringFromSide(int side) + static const char* StringFromSide(int side) { switch (side) { @@ -1451,6 +1465,7 @@ public: PField *membervar; bool AddressRequested = false; bool AddressWritable = true; + int BarrierSide = -1; // [ZZ] some magic FxMemberBase(EFxType type, PField *f, const FScriptPosition &p); }; From a2f3d8511de0120cd569ee17c5163c0c2fbe6564 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 22:46:18 +0200 Subject: [PATCH 16/73] Implemented a read check between ui/play/data fields with a meaningful error --- src/scripting/backend/codegen.cpp | 21 +++++++++++++++------ src/scripting/backend/codegen.h | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 635488f26..f53612b41 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6718,8 +6718,6 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) // [ZZ] plain data "inherits" scope of whatever it was defined in. if (bWritable) // don't do complex checks on early fail { - if (ctx.Function && FString(ctx.Function->SymbolName) == FString("DrawPowerup")) - Printf("field type = %d\n", BarrierSide); int outerflags = 0; if (ctx.Function) outerflags = ctx.Function->Variants[0].Flags; @@ -6748,7 +6746,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) if (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(AActor))) { - ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type."); + ScriptPosition.Message(MSG_ERROR, "'Default' requires an actor type"); delete this; return nullptr; } @@ -6759,7 +6757,18 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) } // [ZZ] support magic - BarrierSide = FScopeBarrier::SideFromFlags(membervar->Flags); + int outerflags = 0; + if (ctx.Function) + outerflags = ctx.Function->Variants[0].Flags; + FScopeBarrier scopeBarrier(outerflags, membervar->Flags, membervar->SymbolName.GetChars()); + if (!scopeBarrier.readable) + { + ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.readerror.GetChars()); + delete this; + return nullptr; + } + + BarrierSide = scopeBarrier.sidelast; if (classx->ExprType == EFX_StructMember || classx->ExprType == EFX_ClassMember) { FxStructMember* pmember = (FxStructMember*)classx; @@ -6772,7 +6781,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) PPointer *ptrtype = dyn_cast(classx->ValueType); if (ptrtype == nullptr || !ptrtype->PointedType->IsKindOf(RUNTIME_CLASS(PStruct))) { - ScriptPosition.Message(MSG_ERROR, "Member variable requires a struct or class object."); + ScriptPosition.Message(MSG_ERROR, "Member variable requires a struct or class object"); delete this; return nullptr; } @@ -6826,7 +6835,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) { if (!(classx->RequestAddress(ctx, &AddressWritable))) { - ScriptPosition.Message(MSG_ERROR, "unable to dereference left side of %s", membervar->SymbolName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Unable to dereference left side of %s", membervar->SymbolName.GetChars()); delete this; return nullptr; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index ee43a0052..f9291274d 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -182,7 +182,7 @@ struct FScopeBarrier if ((sideto == Side_UI) != (sidefrom == Side_UI)) // only ui -> ui is readable { readable = false; - readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), StringFromSide(sidefrom)); + readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } if (!readable) From 3056570ea9b5e0dbccc66d009b5ba3d3f34719d2 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 23:02:43 +0200 Subject: [PATCH 17/73] Implemented a call check between ui/play/data --- src/scripting/backend/codegen.cpp | 46 ++++++++++++++++++++++--------- src/scripting/backend/codegen.h | 10 ++++++- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index f53612b41..d452fedd9 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -7662,7 +7662,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (ctx.Class == nullptr) { // There's no way that a member function call can resolve to a constant so abort right away. - ScriptPosition.Message(MSG_ERROR, "Expression is not constant."); + ScriptPosition.Message(MSG_ERROR, "Expression is not constant"); delete this; return nullptr; } @@ -7671,7 +7671,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (a == nullptr) { - ScriptPosition.Message(MSG_ERROR, "Empty function argument."); + ScriptPosition.Message(MSG_ERROR, "Empty function argument"); delete this; return nullptr; } @@ -7762,7 +7762,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ArgList.Size() > 0) { - ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars()); delete this; return nullptr; } @@ -7806,7 +7806,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ArgList.Size() > 0) { - ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars()); delete this; return nullptr; } @@ -7900,7 +7900,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ArgList.Size() > 0) { - ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars()); delete this; return nullptr; } @@ -7960,7 +7960,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ArgList.Size() > 0) { - ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars()); delete this; return nullptr; } @@ -7973,7 +7973,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } else { - ScriptPosition.Message(MSG_ERROR, "Left hand side of %s must point to a class object\n", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Left hand side of %s must point to a class object", MethodName.GetChars()); delete this; return nullptr; } @@ -7989,7 +7989,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } else { - ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s\n", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s", MethodName.GetChars()); delete this; return nullptr; } @@ -8007,7 +8007,7 @@ isresolved: if (afd == nullptr) { - ScriptPosition.Message(MSG_ERROR, "Unknown function %s\n", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Unknown function %s", MethodName.GetChars()); delete this; return nullptr; } @@ -8016,11 +8016,31 @@ isresolved: { // Cannot be made writable so we cannot use its methods. // [ZZ] Why this esoteric message? - ScriptPosition.Message(MSG_ERROR, "Readonly struct on left hand side of %s not allowed\n", MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Readonly struct on left hand side of %s not allowed", MethodName.GetChars()); delete this; return nullptr; } + // [ZZ] if self is a struct or a class member, check if it's valid to call this function at all. + // implement more magic + if (Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StructMember) + { + FxStructMember* pmember = (FxStructMember*)Self; + int outerflags = 0; + if (ctx.Function) + outerflags = ctx.Function->Variants[0].Flags; + int innerflags = afd->Variants[0].Flags; + if (FScopeBarrier::SideFromFlags(innerflags) == FScopeBarrier::Side_PlainData) + innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); + FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); + if (!scopeBarrier.callable) + { + ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); + delete this; + return nullptr; + } + } + if (staticonly && (afd->Variants[0].Flags & VARF_Method)) { if (!novirtual || !(afd->Variants[0].Flags & VARF_Virtual)) @@ -8029,14 +8049,14 @@ isresolved: auto ccls = dyn_cast(cls); if (clstype == nullptr || ccls == nullptr || !clstype->IsDescendantOf(ccls)) { - ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here\n", cls->TypeName.GetChars(), MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Cannot call non-static function %s::%s from here", cls->TypeName.GetChars(), MethodName.GetChars()); delete this; return nullptr; } else { // Todo: If this is a qualified call to a parent class function, let it through (but this needs to disable virtual calls later.) - ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented\n", cls->TypeName.GetChars(), MethodName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Qualified member call to parent class %s::%s is not yet implemented", cls->TypeName.GetChars(), MethodName.GetChars()); delete this; return nullptr; } @@ -8064,7 +8084,7 @@ isresolved: // Functions with no Actor usage may not be called through a pointer because they will lose their context. if (!(afd->Variants[0].UseFlags & SUF_ACTOR)) { - ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object\n", afd->SymbolName.GetChars()); + ScriptPosition.Message(MSG_ERROR, "Function %s cannot be used with a non-self object", afd->SymbolName.GetChars()); delete this; return nullptr; } diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index f9291274d..39c893e33 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -132,6 +132,14 @@ struct FScopeBarrier } } + // this modifies VARF_ flags and sets the side properly. + static int ChangeSideInFlags(int flags, int side) + { + flags &= ~(VARF_UI | VARF_Play); + flags |= FlagsFromSide(side); + return flags; + } + FScopeBarrier() { sidefrom = -1; @@ -203,7 +211,7 @@ struct FScopeBarrier if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context. { callable = false; - callerror.Format("Can't call %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } } }; From 014db18f2a509e8d8d2ec93498f1ebabc7d83bd8 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 17 Feb 2017 23:36:16 +0200 Subject: [PATCH 18/73] Fixed some things around --- src/scripting/backend/codegen.cpp | 36 ++++++++++++++------------- src/scripting/backend/codegen.h | 2 +- src/scripting/zscript/zcc_compile.cpp | 9 +++---- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index d452fedd9..5a77b4a01 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6715,16 +6715,17 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) if ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly))) bWritable = false; } - // [ZZ] plain data "inherits" scope of whatever it was defined in. - if (bWritable) // don't do complex checks on early fail + // [ZZ] implement write barrier between different scopes + if (bWritable) { int outerflags = 0; if (ctx.Function) outerflags = ctx.Function->Variants[0].Flags; - FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), membervar->SymbolName.GetChars()); + FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), ""); if (!scopeBarrier.writable) bWritable = false; } + *writable = bWritable; } return true; @@ -6769,7 +6770,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) } BarrierSide = scopeBarrier.sidelast; - if (classx->ExprType == EFX_StructMember || classx->ExprType == EFX_ClassMember) + if (classx->ExprType == EFX_StructMember) // note: only do this for structs now { FxStructMember* pmember = (FxStructMember*)classx; if (BarrierSide == FScopeBarrier::Side_PlainData && pmember) @@ -6793,7 +6794,8 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) { auto parentfield = static_cast(classx)->membervar; // PFields are garbage collected so this will be automatically taken care of later. - auto newfield = new PField(NAME_None, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset); + // [ZZ] call ChangeSideInFlags to ensure that we don't get ui+play + auto newfield = new PField(NAME_None, membervar->Type, FScopeBarrier::ChangeSideInFlags(membervar->Flags | parentfield->Flags, BarrierSide), membervar->Offset + parentfield->Offset); newfield->BitValue = membervar->BitValue; static_cast(classx)->membervar = newfield; classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. @@ -8023,22 +8025,22 @@ isresolved: // [ZZ] if self is a struct or a class member, check if it's valid to call this function at all. // implement more magic - if (Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StructMember) + int outerflags = 0; + if (ctx.Function) + outerflags = ctx.Function->Variants[0].Flags; + int innerflags = afd->Variants[0].Flags; + if (Self->ExprType == EFX_StructMember) { FxStructMember* pmember = (FxStructMember*)Self; - int outerflags = 0; - if (ctx.Function) - outerflags = ctx.Function->Variants[0].Flags; - int innerflags = afd->Variants[0].Flags; if (FScopeBarrier::SideFromFlags(innerflags) == FScopeBarrier::Side_PlainData) innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); - FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); - if (!scopeBarrier.callable) - { - ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); - delete this; - return nullptr; - } + } + FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); + if (!scopeBarrier.callable) + { + ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); + delete this; + return nullptr; } if (staticonly && (afd->Variants[0].Flags & VARF_Method)) diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 39c893e33..cbf02f954 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -187,7 +187,7 @@ struct FScopeBarrier sidelast = sideto; else sideto = sidelast; - if ((sideto == Side_UI) != (sidefrom == Side_UI)) // only ui -> ui is readable + if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable { readable = false; readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 3afb80ff3..3eb36624a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2397,9 +2397,9 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (varflags & VARF_Final) sym->Variants[0].Implementation->Final = true; // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. - if (f->Flags & ZCC_UIFlag) // only direct specification here (varflags can also have owning class scope applied, we don't want that) + if (varflags & VARF_UI) sym->Variants[0].Implementation->ScopeUI = true; - if (f->Flags & ZCC_Play) // only direct specification here + if (varflags & VARF_Play) sym->Variants[0].Implementation->ScopePlay = true; if (varflags & VARF_ReadOnly) sym->Variants[0].Implementation->FuncConst = true; @@ -2422,8 +2422,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } // you can't change ui/play/clearscope for a virtual method. - if ((oldfunc->ScopePlay != sym->Variants[0].Implementation->ScopePlay) || - (oldfunc->ScopeUI != sym->Variants[0].Implementation->ScopeUI)) + if (f->Flags & (ZCC_UIFlag|ZCC_Play|ZCC_ClearScope)) { Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars()); } @@ -2432,7 +2431,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); } - // inherit scope of original function + // inherit scope of original function if override not specified if (sym->Variants[0].Implementation->ScopeUI = oldfunc->ScopeUI) sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_Play)) | VARF_UI; else if (sym->Variants[0].Implementation->ScopePlay = oldfunc->ScopePlay) From 12aa18a92b03c6a151a8caacf1fde5b4048f1f11 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 00:34:24 +0200 Subject: [PATCH 19/73] Preparing to do class-based method scopes: can't change class scope once defined (play stays play, ui stays ui) --- src/scripting/zscript/zcc-parse.lemon | 2 -- src/scripting/zscript/zcc_compile.cpp | 12 ++++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 59924b634..930f2a9ca 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -216,7 +216,6 @@ class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; X. 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) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } /*----- Dottable Identifier -----*/ @@ -332,7 +331,6 @@ 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; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 3eb36624a..52899e44f 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -625,10 +625,18 @@ void ZCCCompiler::CreateClassTypes() Error(c->cls, "Class %s has incompatible flags", c->NodeName().GetChars()); } - if (c->cls->Flags & ZCC_UIFlag || ((parent->ObjectFlags & OF_UI) && !(c->cls->Flags & ZCC_ClearScope))) + if (c->cls->Flags & ZCC_UIFlag) c->Type()->ObjectFlags = (c->Type()->ObjectFlags&~OF_Play) | OF_UI; - if (c->cls->Flags & ZCC_Play || ((parent->ObjectFlags & OF_Play) && !(c->cls->Flags & ZCC_ClearScope))) + 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)); + } 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.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); From b5ab011bb987e6961d5ec84ba4616127602e9431 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 04:07:12 +0200 Subject: [PATCH 20/73] Static virtualscope checking. This is possible, because virtualscope can't produce false positives (data readable for everyone), only false negatives (which are handled at runtime later) --- src/dobjtype.h | 1 + src/sc_man_scanner.re | 1 + src/sc_man_tokens.h | 1 + src/scripting/backend/codegen.cpp | 11 +++++-- src/scripting/backend/codegen.h | 20 ++++++++++++- src/scripting/vm/vm.h | 5 ++-- src/scripting/zscript/zcc-parse.lemon | 1 + src/scripting/zscript/zcc_compile.cpp | 41 ++++++++++++++++----------- src/scripting/zscript/zcc_parser.cpp | 1 + src/scripting/zscript/zcc_parser.h | 1 + wadsrc/static/zscript/base.txt | 2 +- 11 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/dobjtype.h b/src/dobjtype.h index cc023c66f..776d66ce3 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -39,6 +39,7 @@ enum VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim) VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) + VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only) }; // An action function ------------------------------------------------------- diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 560c2b077..f4aac0cb5 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -174,6 +174,7 @@ std2: 'ui' { RET(TK_UI); } 'play' { RET(TK_Play); } 'clearscope' { RET(TK_ClearScope); } + 'virtualscope' { RET(TK_VirtualScope); } 'super' { RET(TK_Super); } 'global' { RET(TK_Global); } 'stop' { RET(TK_Stop); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index b98c1108b..fdcb72229 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -116,6 +116,7 @@ xx(TK_NoNew, "'nonew'") xx(TK_UI, "'ui'") xx(TK_Play, "'play'") xx(TK_ClearScope, "'clearscope'") +xx(TK_VirtualScope, "'virtualscope'") xx(TK_Override, "'override'") xx(TK_Super, "'super'") xx(TK_Null, "'null'") diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 5a77b4a01..09c8d195e 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -7684,6 +7684,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) auto id = static_cast(Self)->Identifier; // If the left side is a class name for a static member function call it needs to be resolved manually // because the resulting value type would cause problems in nearly every other place where identifiers are being used. + // [ZZ] substitute ccls for String internal type. if (id == NAME_String) ccls = TypeStringStruct; else ccls = FindStructType(id, ctx); if (ccls != nullptr) static_cast(Self)->noglobal = true; @@ -7695,7 +7696,6 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) { if (ccls != nullptr) { - // [ZZ] substitute ccls for String internal type. if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast(ccls)->bExported) { cls = ccls; @@ -8029,10 +8029,17 @@ isresolved: if (ctx.Function) outerflags = ctx.Function->Variants[0].Flags; int innerflags = afd->Variants[0].Flags; + int innerside = FScopeBarrier::SideFromFlags(innerflags); + // [ZZ] check this at compile time. this would work for most legit cases. + if (innerside == FScopeBarrier::Side_Virtual) + { + innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + innerflags = FScopeBarrier::FlagsFromSide(innerside); + } if (Self->ExprType == EFX_StructMember) { FxStructMember* pmember = (FxStructMember*)Self; - if (FScopeBarrier::SideFromFlags(innerflags) == FScopeBarrier::Side_PlainData) + if (innerside == FScopeBarrier::Side_PlainData) innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); } FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index cbf02f954..5dc23dcf7 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -87,7 +87,8 @@ struct FScopeBarrier { Side_PlainData = 0, Side_UI, - Side_Play + Side_Play, + Side_Virtual }; int sidefrom; int sidelast; @@ -99,6 +100,18 @@ struct FScopeBarrier return Side_UI; if (flags & VARF_Play) return Side_Play; + if (flags & VARF_VirtualScope) + return Side_Virtual; + return Side_PlainData; + } + + // same as above, but from object flags + static int SideFromObjectFlags(int flags) + { + if (flags & OF_UI) + return Side_UI; + if (flags & OF_Play) + return Side_Play; return Side_PlainData; } @@ -111,6 +124,8 @@ struct FScopeBarrier return VARF_Play; case Side_UI: return VARF_UI; + case Side_Virtual: + return VARF_VirtualScope; default: return 0; } @@ -127,6 +142,8 @@ struct FScopeBarrier return "ui"; case Side_Play: return "play"; + case Side_Virtual: + return "virtual"; // should not happen! default: return "unknown"; } @@ -170,6 +187,7 @@ struct FScopeBarrier return; // we aren't interested in any other flags + // - update: including VARF_VirtualScope. inside the function itself, we treat it as if it's PlainData. flags1 &= VARF_UI | VARF_Play; flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index bcdf8d17b..36b25a40c 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -702,9 +702,8 @@ 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 ScopeUI = false; // [ZZ] 'ui' method - bool ScopePlay = false; // [ZZ] 'play' method - bool FuncConst = false; // [ZZ] const qualifier for methods - these can be called on readonly + bool FuncConst = false; // [ZZ] readonly function + int BarrierSide = -1; // [ZZ] FScopeBarrier::Side BYTE ImplicitArgs = 0; // either 0 for static, 1 for method or 3 for action unsigned VirtualIndex = ~0u; FName Name; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 930f2a9ca..2f412f5cf 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -1007,6 +1007,7 @@ decl_flag(X) ::= VARARG(T). { X.Int = ZCC_VarArg; X.SourceLoc = T.SourceLoc; decl_flag(X) ::= UI(T). { X.Int = ZCC_UIFlag; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= PLAY(T). { X.Int = ZCC_Play; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= CLEARSCOPE(T). { X.Int = ZCC_ClearScope; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= VIRTUALSCOPE(T). { X.Int = ZCC_VirtualScope; X.SourceLoc = T.SourceLoc; } func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); } func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 52899e44f..e6be47f81 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1273,7 +1273,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper FString ZCCCompiler::FlagsToString(uint32_t flags) { - const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "clearscope" }; + const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "clearscope", "virtualscope" }; FString build; for (size_t i = 0; i < countof(flagnames); i++) @@ -2130,11 +2130,13 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (f->Flags & ZCC_FuncConst) varflags = (varflags&~(VARF_Play | VARF_UI)); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag. if (f->Flags & ZCC_UIFlag) - varflags = (varflags&~VARF_Play) | VARF_UI; + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI); if (f->Flags & ZCC_Play) - varflags = (varflags&~VARF_UI) | VARF_Play; + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play); if (f->Flags & ZCC_ClearScope) - varflags = (varflags&~(VARF_Play | VARF_UI)); + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); + 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. @@ -2194,14 +2196,20 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "'Const' 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))) + // [ZZ] neither this + if ((varflags&(VARF_VirtualScope | VARF_Method)) == VARF_VirtualScope) // non-method virtualscope function { - Error(f, "Invalid combination of qualifiers %s on function %s", FlagsToString(f->Flags&(ZCC_FuncConst | ZCC_UIFlag | ZCC_Play)).GetChars(), FName(f->Name).GetChars()); + Error(f, "'VirtualScope' on a static method is not supported"); } - static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; + // 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; for (int i = 0; i < countof(excludescope); i++) @@ -2406,9 +2414,11 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool sym->Variants[0].Implementation->Final = true; // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. if (varflags & VARF_UI) - sym->Variants[0].Implementation->ScopeUI = true; + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI; if (varflags & VARF_Play) - sym->Variants[0].Implementation->ScopePlay = true; + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play; + if (varflags & VARF_VirtualScope) + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual; if (varflags & VARF_ReadOnly) sym->Variants[0].Implementation->FuncConst = true; @@ -2430,7 +2440,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } // you can't change ui/play/clearscope for a virtual method. - if (f->Flags & (ZCC_UIFlag|ZCC_Play|ZCC_ClearScope)) + if (f->Flags & (ZCC_UIFlag|ZCC_Play|ZCC_ClearScope|ZCC_VirtualScope)) { Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars()); } @@ -2440,11 +2450,8 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); } // inherit scope of original function if override not specified - if (sym->Variants[0].Implementation->ScopeUI = oldfunc->ScopeUI) - sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_Play)) | VARF_UI; - else if (sym->Variants[0].Implementation->ScopePlay = oldfunc->ScopePlay) - sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI)) | VARF_Play; - else sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_UI | VARF_Play)); + 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) sym->Variants[0].Flags |= VARF_ReadOnly; diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index d77028d60..9f1117193 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -140,6 +140,7 @@ static void InitTokenMap() TOKENDEF (TK_UI, ZCC_UI); TOKENDEF (TK_Play, ZCC_PLAY); TOKENDEF (TK_ClearScope, ZCC_CLEARSCOPE); + TOKENDEF (TK_VirtualScope, ZCC_VIRTUALSCOPE); TOKENDEF (TK_NoNew, ZCC_NONEW); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 20ebcabae..7cc41318d 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -41,6 +41,7 @@ enum ZCC_UIFlag = 1 << 18, // there's also token called ZCC_UI ZCC_Play = 1 << 19, ZCC_ClearScope = 1 << 20, + ZCC_VirtualScope = 1 << 21, }; // Function parameter modifiers diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 208023b54..394450f22 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -329,7 +329,7 @@ class Object native native static uint MSTime(); native Name GetClassName(); - native void Destroy(); + native virtualscope void Destroy(); // This does not call into the native method of the same name to avoid problems with objects that get garbage collected late on shutdown. virtual void OnDestroy() {} From e0ae0fdb2e0294119c8dfc5af58a3e458f12ebac Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 06:27:28 +0200 Subject: [PATCH 21/73] Dynamic virtualscope checking. May yet be buggy. --- src/scripting/backend/codegen.cpp | 39 +++++++++++++++++++++++++++ src/scripting/backend/codegen.h | 25 ++++++++++------- src/scripting/thingdef.cpp | 4 +++ src/scripting/vm/vm.h | 7 ++++- src/scripting/vm/vmexec.h | 19 +++++++++++-- src/scripting/zscript/zcc_compile.cpp | 18 ++++++++----- 6 files changed, 92 insertions(+), 20 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 09c8d195e..4bd3218df 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -89,6 +89,34 @@ 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 = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags); + 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) +{ + int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags); + if (outerside == FScopeBarrier::Side_Virtual) + outerside = FScopeBarrier::Side_PlainData; + 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 @@ -8300,6 +8328,7 @@ FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumen ArgList = std::move(args); EmitTail = false; NoVirtual = novirtual; + CallingFunction = nullptr; } //========================================================================== @@ -8373,6 +8402,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) return nullptr; } + CallingFunction = ctx.Function; if (ArgList.Size() > 0) { bool foundvarargs = false; @@ -8580,6 +8610,15 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) { + // [ZZ] + if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual) + { + // pass this even before Self, because otherwise we can't silently advance the arguments. + // this is not even implicit arguments. + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(Function, ATAG_OBJECT)); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT)); + count += 2; + } assert(Self != nullptr); selfemit = Self->Emit(build); assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target)); diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 5dc23dcf7..c9e23c980 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -70,7 +70,8 @@ class FxCompoundStatement; class FxLocalVariableDeclaration; typedef TDeletingArray FArgumentList; -// [ZZ] this is kind of related to compile context as well +// +// [ZZ] this really should be in codegen.h, but vmexec needs to access it struct FScopeBarrier { bool callable; @@ -86,9 +87,9 @@ struct FScopeBarrier enum Side { Side_PlainData = 0, - Side_UI, - Side_Play, - Side_Virtual + Side_UI = 1, + Side_Play = 2, + Side_Virtual = 3 // do NOT change the value }; int sidefrom; int sidelast; @@ -152,7 +153,7 @@ struct FScopeBarrier // this modifies VARF_ flags and sets the side properly. static int ChangeSideInFlags(int flags, int side) { - flags &= ~(VARF_UI | VARF_Play); + flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope); flags |= FlagsFromSide(side); return flags; } @@ -208,28 +209,31 @@ struct FScopeBarrier if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable { readable = false; - readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + if (name) readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } if (!readable) { writable = false; callable = false; - writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); - callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + if (name) + { + writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } return; } if (writable && (sidefrom != sideto)) // only matching types are writable (plain data implicitly takes context type by default, unless overridden) { writable = false; - writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + if (name) writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context. { callable = false; - callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); } } }; @@ -1873,6 +1877,7 @@ class FxVMFunctionCall : public FxExpression // for multi assignment int AssignCount = 0; TArray ReturnRegs; + PFunction *CallingFunction; public: FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 5b842ce07..d9eca5fca 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -163,6 +163,10 @@ PFunction *CreateAnonymousFunction(PClass *containingclass, PType *returntype, i // Functions that only get flagged for actors do not need the additional two context parameters. int fflags = (flags& (SUF_OVERLAY | SUF_WEAPON | SUF_ITEM)) ? VARF_Action | VARF_Method : VARF_Method; + // [ZZ] give anonymous functions the scope of their class + // (just give them VARF_Play, whatever) + fflags |= VARF_Play; + rets[0] = returntype != nullptr? returntype : TypeError; // Use TypeError as placeholder if we do not know the return type yet. SetImplicitArgs(&args, &argflags, &argnames, containingclass, fflags, flags); diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 36b25a40c..ba1db7aa9 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -8,6 +8,11 @@ #include "doomerrors.h" #include "memarena.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); + extern FMemArena ClassDataAllocator; #define MAX_RETURNS 8 // Maximum number of results a function called by script code can return @@ -712,7 +717,7 @@ public: class PPrototype *Proto; - VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) + VMFunction(FName name = NAME_None) : Native(false), ImplicitArgs(0), Name(name), Proto(NULL) { AllFunctions.Push(this); } diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 6c2eef8a1..d47a3bf48 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -648,13 +648,25 @@ begin: VMReturn returns[MAX_RETURNS]; int numret; + // [ZZ] hax! + b = B; + if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments. + { + PFunction* calledfunc = (PFunction*)(reg.param + f->NumParam - b)[0].a; + 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); + b -= 2; + } + FillReturns(reg, f, returns, pc+1, C); if (call->Native) { try { VMCycles[0].Unclock(); - numret = static_cast(call)->NativeCall(reg.param + f->NumParam - B, call->DefaultArgs, B, returns, C); + numret = static_cast(call)->NativeCall(reg.param + f->NumParam - b, call->DefaultArgs, b, returns, C); VMCycles[0].Clock(); } catch (CVMAbortException &err) @@ -670,7 +682,7 @@ begin: VMCalls[0]++; VMScriptFunction *script = static_cast(call); VMFrame *newf = stack->AllocFrame(script); - VMFillParams(reg.param + f->NumParam - B, newf, B); + VMFillParams(reg.param + f->NumParam - b, newf, b); try { numret = Exec(stack, script->Code, returns, C); @@ -801,6 +813,9 @@ begin: if (!callingfunc || pcls != callingfunc->OwningClass) ThrowAbortException(X_OTHER, "Cannot instantiate class %s directly", cls->TypeName.GetChars()); } + // [ZZ] validate readonly and between scope construction + if (callingfunc) + FScopeBarrier_ValidateNew(cls, callingfunc); reg.a[a] = cls->CreateNew(); reg.atag[a] = ATAG_OBJECT; NEXTOP; diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index e6be47f81..c19d14b1e 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2401,6 +2401,17 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool sym->Variants[0].Implementation->DefaultArgs = std::move(argdefaults); } + if (sym->Variants[0].Implementation != nullptr) + { + // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. + if (varflags & VARF_UI) + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI; + if (varflags & VARF_Play) + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play; + if (varflags & VARF_VirtualScope) + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual; + } + PClass *clstype = static_cast(c->Type()); if (varflags & VARF_Virtual) { @@ -2412,13 +2423,6 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (varflags & VARF_Final) sym->Variants[0].Implementation->Final = true; - // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. - if (varflags & VARF_UI) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI; - if (varflags & VARF_Play) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play; - if (varflags & VARF_VirtualScope) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual; if (varflags & VARF_ReadOnly) sym->Variants[0].Implementation->FuncConst = true; From afc9050a43ae43d89b91e0d4f0d23f1d9fc277b0 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 06:56:00 +0200 Subject: [PATCH 22/73] Marked Thinker play. Marked OnDestroy and Destroy virtualscope. It compiles :D --- src/scripting/backend/codegen.cpp | 14 +++++++++++++- wadsrc/static/zscript/base.txt | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 4bd3218df..99b83ceae 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -109,7 +109,7 @@ void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, P { int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags); if (outerside == FScopeBarrier::Side_Virtual) - outerside = FScopeBarrier::Side_PlainData; + outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags); if (innerside == FScopeBarrier::Side_Virtual) innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); @@ -6748,7 +6748,11 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) { int outerflags = 0; if (ctx.Function) + { outerflags = ctx.Function->Variants[0].Flags; + if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); + } FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), ""); if (!scopeBarrier.writable) bWritable = false; @@ -6788,7 +6792,11 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) // [ZZ] support magic int outerflags = 0; if (ctx.Function) + { outerflags = ctx.Function->Variants[0].Flags; + if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); + } FScopeBarrier scopeBarrier(outerflags, membervar->Flags, membervar->SymbolName.GetChars()); if (!scopeBarrier.readable) { @@ -8055,7 +8063,11 @@ isresolved: // implement more magic int outerflags = 0; if (ctx.Function) + { outerflags = ctx.Function->Variants[0].Flags; + if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); + } int innerflags = afd->Variants[0].Flags; int innerside = FScopeBarrier::SideFromFlags(innerflags); // [ZZ] check this at compile time. this would work for most legit cases. diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 394450f22..14aec9b1d 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -332,10 +332,10 @@ class Object native native virtualscope void Destroy(); // This does not call into the native method of the same name to avoid problems with objects that get garbage collected late on shutdown. - virtual void OnDestroy() {} + virtual virtualscope void OnDestroy() {} } -class Thinker : Object native +class Thinker : Object play native { enum EStatnums { From dbc595f886b833609125c2ca958639f959c3050b Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 07:30:08 +0200 Subject: [PATCH 23/73] Marked DrawPowerup as ui, still compiles and somewhat works :D --- src/scripting/backend/codegen.cpp | 6 +++--- src/scripting/vm/vm.h | 2 +- src/scripting/zscript/zcc_compile.cpp | 12 ++++++------ wadsrc/static/zscript/inventory/inventory.txt | 2 +- wadsrc/static/zscript/inventory/powerups.txt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 99b83ceae..0ec7c1381 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6750,7 +6750,7 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) if (ctx.Function) { outerflags = ctx.Function->Variants[0].Flags; - if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); } FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), ""); @@ -6794,7 +6794,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) if (ctx.Function) { outerflags = ctx.Function->Variants[0].Flags; - if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); } FScopeBarrier scopeBarrier(outerflags, membervar->Flags, membervar->SymbolName.GetChars()); @@ -8065,7 +8065,7 @@ isresolved: if (ctx.Function) { outerflags = ctx.Function->Variants[0].Flags; - if ((outerflags & (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); } int innerflags = afd->Variants[0].Flags; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index ba1db7aa9..b13f17777 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -708,7 +708,7 @@ public: 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 - int BarrierSide = -1; // [ZZ] FScopeBarrier::Side + 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; diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index c19d14b1e..0cfd2d66a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1081,8 +1081,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel // For structs only allow 'deprecated', for classes exclude function qualifiers. int notallowed = forstruct? - ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension : - ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension; + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension | ZCC_VirtualScope : + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension | ZCC_VirtualScope; if (field->Flags & notallowed) { @@ -1102,11 +1102,11 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (type->ObjectFlags & OF_Play) varflags |= VARF_Play; if (field->Flags & ZCC_UIFlag) - varflags = (varflags&~VARF_Play) | VARF_UI; + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_UI); if (field->Flags & ZCC_Play) - varflags = (varflags&~VARF_UI) | VARF_Play; + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play); if (field->Flags & ZCC_ClearScope) - varflags = (varflags&~(VARF_UI | VARF_Play)); + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); if (field->Flags & ZCC_Native) { @@ -2128,7 +2128,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (c->Type()->ObjectFlags & OF_Play) varflags |= VARF_Play; if (f->Flags & ZCC_FuncConst) - varflags = (varflags&~(VARF_Play | VARF_UI)); // const implies clearscope. this is checked a bit later to also not have ZCC_Play/ZCC_UIFlag. + 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) diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 28b20cdd5..f24a2de09 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -813,7 +813,7 @@ class Inventory : Actor native // //=========================================================================== - virtual bool DrawPowerup(int x, int y) { return false; } + virtual ui bool DrawPowerup(int x, int y) { return false; } //=========================================================================== // diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index d355a5e14..df0812355 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -934,7 +934,7 @@ class PowerFlight : Powerup +INVENTORY.HUBPOWER } - bool HitCenterFrame; + ui bool HitCenterFrame; //=========================================================================== // From d7f5d8a403bfb76b58d44cc2d60fc7ad1e47533f Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 07:31:07 +0200 Subject: [PATCH 24/73] ClearScope for fields essentially means 'world-writable'. We don't want to support that - disallowed --- src/scripting/zscript/zcc_compile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 0cfd2d66a..68b22a05e 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1081,8 +1081,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel // For structs only allow 'deprecated', for classes exclude function qualifiers. int notallowed = forstruct? - ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension | ZCC_VirtualScope : - ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension | ZCC_VirtualScope; + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Meta | ZCC_Extension | ZCC_VirtualScope | ZCC_ClearScope : + ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract | ZCC_Virtual | ZCC_Override | ZCC_Extension | ZCC_VirtualScope | ZCC_ClearScope; if (field->Flags & notallowed) { From 463620c3b8405fa0d44caace0921a2f0fcfe3d50 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 07:33:27 +0200 Subject: [PATCH 25/73] ClearScope is not needed for methods either. Disabled, but not removed - reenable if needed later --- src/scripting/zscript/zcc_compile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 68b22a05e..1db3fbdda 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2075,7 +2075,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } while (t != f->Type); } - int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract; + int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract | ZCC_ClearScope; if (f->Flags & notallowed) { From ab07b30d5e0158696fe8cab33dbbbd7e329048c4 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 07:48:28 +0200 Subject: [PATCH 26/73] Fixed: implicit method call without specifying self should be checked as well --- src/scripting/backend/codegen.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 0ec7c1381..f0011de81 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -7403,6 +7403,32 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) return nullptr; } + // [ZZ] validate call + PClass* cls = (PClass*)ctx.Class; + int outerflags = 0; + if (ctx.Function) + { + outerflags = ctx.Function->Variants[0].Flags; + if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) + outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); + } + int innerflags = afd->Variants[0].Flags; + int innerside = FScopeBarrier::SideFromFlags(innerflags); + // [ZZ] check this at compile time. this would work for most legit cases. + if (innerside == FScopeBarrier::Side_Virtual) + { + innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + innerflags = FScopeBarrier::FlagsFromSide(innerside); + } + FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); + if (!scopeBarrier.callable) + { + ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); + delete this; + return nullptr; + } + + // [ZZ] this is only checked for VARF_Methods in the other place. bug? if (!CheckFunctionCompatiblity(ScriptPosition, ctx.Function, afd)) { delete this; From fb9b8c8870e6505310361964757e78038d6b178d Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 07:51:29 +0200 Subject: [PATCH 27/73] Re-enabled clearscope for methods; made isBlinking() clearscope --- src/scripting/zscript/zcc_compile.cpp | 2 +- wadsrc/static/zscript/inventory/powerups.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1db3fbdda..68b22a05e 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2075,7 +2075,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } while (t != f->Type); } - int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract | ZCC_ClearScope; + int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_Abstract; if (f->Flags & notallowed) { diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index df0812355..5885100e6 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -285,7 +285,7 @@ class Powerup : Inventory // //=========================================================================== - virtual bool isBlinking() + virtual clearscope bool isBlinking() { return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink); } From 6ec7d9af59cf3ab72ec032a0445fc54631a1c3be Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 18 Feb 2017 08:02:38 +0200 Subject: [PATCH 28/73] No more crashing in anonymous functions during runtime check of virtualscope --- src/scripting/backend/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index f0011de81..fe8243d28 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -107,7 +107,8 @@ void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc) // this can be imported in vmexec.h void FScopeBarrier_ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype) { - int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags); + // [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); From fd20e1d78ff1d6d048cc903535fe82270c972020 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 3 Mar 2017 20:11:13 +0100 Subject: [PATCH 29/73] - allow hooking custom serializers into PPointer so that serializable types can be handled without having to create new type classes which would be a bit unwieldy thanks to how the type system works. --- src/dobjtype.cpp | 8 ++++ src/dobjtype.h | 13 ++++++ src/scripting/thingdef_data.cpp | 80 +++++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 98a893391..1a582d366 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1396,6 +1396,10 @@ void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) con ar(key, *(DObject **)addr); } } + else if (writer != nullptr) + { + writer(ar, key, addr); + } else { I_Error("Attempt to save pointer to unhandled type %s", PointedType->DescriptiveName()); @@ -1425,6 +1429,10 @@ bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const } return res; } + else if (reader != nullptr) + { + return reader(ar, key, addr); + } return false; } diff --git a/src/dobjtype.h b/src/dobjtype.h index f589b4e48..1a30b42ed 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -362,13 +362,26 @@ public: class PPointer : public PBasicType { DECLARE_CLASS(PPointer, PBasicType); + public: + typedef void(*WriteHandler)(FSerializer &ar, const char *key, const void *addr); + typedef bool(*ReadHandler)(FSerializer &ar, const char *key, void *addr); + PPointer(); PPointer(PType *pointsat, bool isconst = false); PType *PointedType; bool IsConst; + WriteHandler writer = nullptr; + ReadHandler reader = nullptr; + + void InstallHandlers(WriteHandler w, ReadHandler r) + { + writer = w; + reader = r; + } + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; void SetPointer(void *base, unsigned offset, TArray *special = NULL) const override; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 4b65e6413..c50b7f65b 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -59,6 +59,7 @@ #include "menu/menu.h" #include "teaminfo.h" #include "r_data/sprites.h" +#include "serializer.h" static TArray properties; static TArray AFTable; @@ -741,7 +742,7 @@ static int fieldcmp(const void * a, const void * b) void InitThingdef() { // Create all global variables here because this cannot be done on the script side and really isn't worth adding support for. - // Also create all special fields here that cannot be declared by script syntax. + // Also create all special fields here that cannot be declared by script syntax plus the pointer serializers. Doing all these with class overrides would be a bit messy. auto secplanestruct = NewNativeStruct("Secplane", nullptr); secplanestruct->Size = sizeof(secplane_t); @@ -750,18 +751,62 @@ void InitThingdef() auto sectorstruct = NewNativeStruct("Sector", nullptr); sectorstruct->Size = sizeof(sector_t); sectorstruct->Align = alignof(sector_t); + NewPointer(sectorstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(sector_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(sector_t **)addr, nullptr); + return true; + } + ); auto linestruct = NewNativeStruct("Line", nullptr); linestruct->Size = sizeof(line_t); linestruct->Align = alignof(line_t); + NewPointer(linestruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(line_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(line_t **)addr, nullptr); + return true; + } + ); auto sidestruct = NewNativeStruct("Side", nullptr); sidestruct->Size = sizeof(side_t); sidestruct->Align = alignof(side_t); + NewPointer(sidestruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(side_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(side_t **)addr, nullptr); + return true; + } + ); auto vertstruct = NewNativeStruct("Vertex", nullptr); vertstruct->Size = sizeof(vertex_t); vertstruct->Align = alignof(vertex_t); + NewPointer(vertstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(vertex_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(vertex_t **)addr, nullptr); + return true; + } + ); auto sectorportalstruct = NewNativeStruct("SectorPortal", nullptr); sectorportalstruct->Size = sizeof(FSectorPortal); @@ -779,6 +824,36 @@ void InitThingdef() teamstruct->Size = sizeof(FTeam); teamstruct->Align = alignof(FTeam); + PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); + pstruct->Size = sizeof(player_t); + pstruct->Align = alignof(player_t); + NewPointer(pstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(player_t **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(player_t **)addr, nullptr); + return true; + } + ); + + auto fontstruct = NewNativeStruct("FFont", nullptr); + fontstruct->Size = sizeof(FFont); + fontstruct->Align = alignof(FFont); + NewPointer(fontstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(FFont **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(FFont **)addr, nullptr); + return true; + } + ); + // set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well... // As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up. sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native); @@ -843,9 +918,6 @@ void InitThingdef() Namespaces.GlobalNamespace->Symbols.AddSymbol(gi); // set up a variable for the global players array. - PStruct *pstruct = NewNativeStruct("PlayerInfo", nullptr); - pstruct->Size = sizeof(player_t); - pstruct->Align = alignof(player_t); PArray *parray = NewArray(pstruct, MAXPLAYERS); PField *fieldptr = new PField("players", parray, VARF_Native | VARF_Static, (intptr_t)&players); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); From 78533cc316d545d6cad0cb98667fcb4ece81db4d Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 22:38:33 +0200 Subject: [PATCH 30/73] Fixed: the name of accessed field is now properly retrieved in FxStructMember::RequestAddress --- src/scripting/backend/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index fe8243d28..edc5066fb 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6754,7 +6754,7 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) if (((outerflags & (VARF_VirtualScope | VARF_Virtual)) == (VARF_VirtualScope | VARF_Virtual)) && ctx.Class) outerflags = FScopeBarrier::FlagsFromSide(FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags)); } - FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), ""); + FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), membervar->SymbolName.GetChars()); if (!scopeBarrier.writable) bWritable = false; } From 3a57a9809f997001f745cc2169ab76dc05a83f7a Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 22:42:12 +0200 Subject: [PATCH 31/73] Removed nonew class modifier --- src/dobject.h | 5 ++--- src/sc_man_scanner.re | 1 - src/sc_man_tokens.h | 1 - src/scripting/backend/codegen.cpp | 13 ------------- src/scripting/vm/vmexec.h | 12 ------------ src/scripting/zscript/zcc-parse.lemon | 1 - src/scripting/zscript/zcc_compile.cpp | 5 +---- src/scripting/zscript/zcc_parser.cpp | 1 - src/scripting/zscript/zcc_parser.h | 9 ++++----- 9 files changed, 7 insertions(+), 41 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index b5f7ef8c4..5bcb81c50 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -206,9 +206,8 @@ enum EObjectFlags OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning) OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function OF_Abstract = 1 << 14, // Marks a class that cannot be created with new() function at all - OF_NoNew = 1 << 15, // Marks a class that can only be created with new() in the exact class that has this keyword - OF_UI = 1 << 16, // Marks a class that defaults to VARF_UI for it's fields/methods - OF_Play = 1 << 17, // Marks a class that defaults to VARF_Play for it's fields/methods + OF_UI = 1 << 15, // Marks a class that defaults to VARF_UI for it's fields/methods + OF_Play = 1 << 16, // Marks a class that defaults to VARF_Play for it's fields/methods }; template class TObjPtr; diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index f4aac0cb5..ba839813f 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -170,7 +170,6 @@ std2: 'virtual' { RET(TK_Virtual); } 'override' { RET(TK_Override); } 'vararg' { RET(TK_VarArg); } - 'nonew' { RET(TK_NoNew); } 'ui' { RET(TK_UI); } 'play' { RET(TK_Play); } 'clearscope' { RET(TK_ClearScope); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index fdcb72229..320601414 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -112,7 +112,6 @@ xx(TK_Optional, "'optional'") xx(TK_Export, "'expert'") xx(TK_Virtual, "'virtual'") xx(TK_VarArg, "'vararg'") -xx(TK_NoNew, "'nonew'") xx(TK_UI, "'ui'") xx(TK_Play, "'play'") xx(TK_ClearScope, "'clearscope'") diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index edc5066fb..1700dd3e6 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -5095,19 +5095,6 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) return nullptr; } - if (cls->ObjectFlags & OF_NoNew) - { - PClass* pcls = cls; - while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew)) - pcls = pcls->ParentClass; - if (pcls != ctx.Class) - { - ScriptPosition.Message(MSG_ERROR, "Cannot instantiate class %s directly", cls->TypeName.GetChars()); - delete this; - return nullptr; - } - } - ValueType = NewPointer(cls); } diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index d47a3bf48..64ea0b7c3 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -801,18 +801,6 @@ begin: PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); PFunction *callingfunc = (PFunction*)konsta[C].o; // [ZZ] due to how this is set, it's always const if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); - if (cls->ObjectFlags & OF_NoNew) - { - // trace to the first nonew class in the hierarchy. - // compare that class to the context class. - // if not matching, disallow creation. - // this ensures that only the root class can have a static factory method that can also create instances of subclasses via new(). - PClass* pcls = cls; - while (pcls && pcls->ParentClass && (pcls->ParentClass->ObjectFlags & OF_NoNew)) - pcls = pcls->ParentClass; - if (!callingfunc || pcls != callingfunc->OwningClass) - ThrowAbortException(X_OTHER, "Cannot instantiate class %s directly", cls->TypeName.GetChars()); - } // [ZZ] validate readonly and between scope construction if (callingfunc) FScopeBarrier_ValidateNew(cls, callingfunc); diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 2f412f5cf..f80a09974 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -212,7 +212,6 @@ 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) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) NONEW. { X.Flags = A.Flags | ZCC_NoNew; 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; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 68b22a05e..923b52cfd 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -611,9 +611,6 @@ 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; - // [ZZ] inherit nonew keyword - if (c->cls->Flags & ZCC_NoNew || (parent->ObjectFlags & OF_NoNew)) - c->Type()->ObjectFlags |= OF_NoNew; // static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; int incompatiblecnt = 0; @@ -1273,7 +1270,7 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper FString ZCCCompiler::FlagsToString(uint32_t flags) { - const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "nonew", "ui", "play", "clearscope", "virtualscope" }; + const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "const", "abstract", "extend", "virtual", "override", "transient", "vararg", "ui", "play", "clearscope", "virtualscope" }; FString build; for (size_t i = 0; i < countof(flagnames); i++) diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 9f1117193..e48033bcc 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -141,7 +141,6 @@ static void InitTokenMap() TOKENDEF (TK_Play, ZCC_PLAY); TOKENDEF (TK_ClearScope, ZCC_CLEARSCOPE); TOKENDEF (TK_VirtualScope, ZCC_VIRTUALSCOPE); - TOKENDEF (TK_NoNew, ZCC_NONEW); TOKENDEF (TK_Override, ZCC_OVERRIDE); TOKENDEF (TK_Final, ZCC_FINAL); TOKENDEF (TK_Meta, ZCC_META); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 7cc41318d..c414c0e9c 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -37,11 +37,10 @@ enum ZCC_Override = 1 << 14, ZCC_Transient = 1 << 15, ZCC_VarArg = 1 << 16, - ZCC_NoNew = 1 << 17, - ZCC_UIFlag = 1 << 18, // there's also token called ZCC_UI - ZCC_Play = 1 << 19, - ZCC_ClearScope = 1 << 20, - ZCC_VirtualScope = 1 << 21, + ZCC_UIFlag = 1 << 17, // there's also token called ZCC_UI + ZCC_Play = 1 << 18, + ZCC_ClearScope = 1 << 19, + ZCC_VirtualScope = 1 << 20, }; // Function parameter modifiers From 421f78c771c12e2e030b70197fa08953c6ba245e Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 22:52:35 +0200 Subject: [PATCH 32/73] Fixed potential crash with new() in anonymous functions (is that even allowed?); Added compile-time check for disallowed new() --- src/scripting/backend/codegen.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1700dd3e6..dca4fdbf1 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -99,7 +99,9 @@ static const FLOP FxFlops[] = // this can be imported in vmexec.h void FScopeBarrier_ValidateNew(PClass* cls, PFunction* callingfunc) { - int outerside = FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags); + 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)); @@ -5095,6 +5097,18 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) return nullptr; } + // + int outerside = ctx.Function && ctx.Function->Variants.Size() ? FScopeBarrier::SideFromFlags(ctx.Function->Variants[0].Flags) : FScopeBarrier::Side_Virtual; + if (outerside == FScopeBarrier::Side_Virtual) + outerside = FScopeBarrier::SideFromObjectFlags(ctx.Class->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); + if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" + { + ScriptPosition.Message(MSG_ERROR, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); + delete this; + return nullptr; + } + ValueType = NewPointer(cls); } From f4a546aee6554368e18a8da5dac9cabc3d39106d Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 23:15:18 +0200 Subject: [PATCH 33/73] Moved menus to ui side, moved AlterWeaponSprite to ui side, moved StaticEventHandler to play side (except ui virtuals) --- wadsrc/static/zscript/events.txt | 8 ++++---- wadsrc/static/zscript/inventory/inventory.txt | 2 +- wadsrc/static/zscript/menu/menu.txt | 2 +- wadsrc/static/zscript/menu/menuitembase.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 24707055f..0e1237dfe 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -279,7 +279,7 @@ class ConsoleEvent : BaseEvent native native readonly int Args[3]; } -class StaticEventHandler : Object native +class StaticEventHandler : Object native play { // static event handlers CAN register other static event handlers. // unlike EventHandler.Create that will not create them. @@ -307,8 +307,8 @@ class StaticEventHandler : Object native virtual native void WorldTick(WorldEvent e); // - virtual native void RenderFrame(RenderEvent e); - virtual native void RenderOverlay(RenderEvent e); + virtual native ui void RenderFrame(RenderEvent e); + virtual native ui void RenderOverlay(RenderEvent e); // virtual native void PlayerEntered(PlayerEvent e); @@ -317,7 +317,7 @@ class StaticEventHandler : Object native virtual native void PlayerDisconnected(PlayerEvent e); // - virtual native bool UiProcess(UiEvent e); + virtual native ui bool UiProcess(UiEvent e); virtual native bool InputProcess(InputEvent e); // diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index f24a2de09..8b142c9a5 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -746,7 +746,7 @@ class Inventory : Actor native virtual bool Use (bool pickup) { return false; } virtual double GetSpeedFactor() { return 1; } virtual bool GetNoTeleportFreeze() { return false; } - virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {} + virtual ui void AlterWeaponSprite(VisStyle vis, in out int changed) {} virtual void OwnerDied() {} virtual Color GetBlend () { return 0; } diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index 1640e9d35..db105fa32 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -48,7 +48,7 @@ struct JoystickConfig native } -class Menu : Object native +class Menu : Object native ui { enum EMenuKey { diff --git a/wadsrc/static/zscript/menu/menuitembase.txt b/wadsrc/static/zscript/menu/menuitembase.txt index de5f05a42..901de9d4f 100644 --- a/wadsrc/static/zscript/menu/menuitembase.txt +++ b/wadsrc/static/zscript/menu/menuitembase.txt @@ -4,7 +4,7 @@ // //============================================================================= -class MenuItemBase : Object native +class MenuItemBase : Object native ui { protected native int mXpos, mYpos; protected native Name mAction; From 43d3e3e5c3cae98a65422c28f08eacf546f5f790 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 23:17:21 +0200 Subject: [PATCH 34/73] Assigned barrier sides to Event structures, added clearscope to ConsoleProcess because it handles both sides --- wadsrc/static/zscript/events.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 0e1237dfe..664438f0d 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 RenderEvent : BaseEvent native +class RenderEvent : BaseEvent native ui { native readonly Vector3 ViewPos; native readonly double ViewAngle; @@ -10,7 +10,7 @@ class RenderEvent : BaseEvent native native readonly Actor Camera; } -class WorldEvent : BaseEvent native +class WorldEvent : BaseEvent native play { // for loaded/unloaded native readonly bool IsSaveGame; @@ -28,7 +28,7 @@ class WorldEvent : BaseEvent native native readonly double DamageAngle; } -class PlayerEvent : BaseEvent native +class PlayerEvent : BaseEvent native play { // 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 native readonly bool IsReturn; } -class UiEvent : BaseEvent native +class UiEvent : BaseEvent native ui { // d_gui.h enum EGUIEvent @@ -121,7 +121,7 @@ class UiEvent : BaseEvent native native readonly bool IsAlt; } -class InputEvent : BaseEvent native +class InputEvent : BaseEvent native play { enum EGenericEvent { @@ -321,7 +321,7 @@ class StaticEventHandler : Object native play virtual native bool InputProcess(InputEvent e); // - virtual native void ConsoleProcess(ConsoleEvent e); + virtual native clearscope void ConsoleProcess(ConsoleEvent e); // this value will be queried on Register() to decide the relative order of this handler to every other. // this is most useful in UI systems. From 4b63092c7a20a42e361a90b57f66d5643c8998e6 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Fri, 3 Mar 2017 12:48:56 -0500 Subject: [PATCH 35/73] - disable size checking in windowed mode for vid_setmode, allowing it to set arbitrary sizes. --- src/v_video.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/v_video.cpp b/src/v_video.cpp index aca00cfb3..d993204a2 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -72,6 +72,7 @@ FRenderer *Renderer; IMPLEMENT_CLASS(DCanvas, true, false) IMPLEMENT_CLASS(DFrameBuffer, true, false) +EXTERN_CVAR (Bool, fullscreen) #if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) #define DBGBREAK { __asm int 3 } @@ -1489,6 +1490,9 @@ CCMD (vid_setmode) goodmode = true; } + if (!fullscreen) + goodmode = true; + if (goodmode) { // The actual change of resolution will take place From a924564bf34ecd3eb1a35197000237ff477b72a8 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 23:21:12 +0200 Subject: [PATCH 36/73] Implemented hard separation between playsim ConsoleEvent and networked ConsoleEvent (ConsoleProcess, NetworkProcess) --- src/events.cpp | 50 +++++++++++++++++++++++--------- wadsrc/static/zscript/events.txt | 3 +- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/events.cpp b/src/events.cpp index 4522f1989..bb7b34f66 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -630,6 +630,7 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, UiProcess); DEFINE_EMPTY_HANDLER(DStaticEventHandler, InputProcess); DEFINE_EMPTY_HANDLER(DStaticEventHandler, ConsoleProcess); +DEFINE_EMPTY_HANDLER(DStaticEventHandler, NetworkProcess); // =========================================== // @@ -1040,22 +1041,45 @@ static DConsoleEvent* E_SetupConsoleEvent() void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3) { - IFVIRTUAL(DStaticEventHandler, ConsoleProcess) + if (player < 0) { - // don't create excessive DObjects if not going to be processed anyway - if (func == DStaticEventHandler_ConsoleProcess_VMPtr) - return; - DConsoleEvent* e = E_SetupConsoleEvent(); + IFVIRTUAL(DStaticEventHandler, ConsoleProcess) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_ConsoleProcess_VMPtr) + return; + DConsoleEvent* e = E_SetupConsoleEvent(); - // - e->Player = player; - e->Name = name; - e->Args[0] = arg1; - e->Args[1] = arg2; - e->Args[2] = arg3; + // + e->Player = player; + e->Name = name; + e->Args[0] = arg1; + e->Args[1] = arg2; + e->Args[2] = arg3; - VMValue params[2] = { (DStaticEventHandler*)this, e }; - GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + VMValue params[2] = { (DStaticEventHandler*)this, e }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } + } + else + { + IFVIRTUAL(DStaticEventHandler, NetworkProcess) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_NetworkProcess_VMPtr) + return; + DConsoleEvent* e = E_SetupConsoleEvent(); + + // + e->Player = player; + e->Name = name; + e->Args[0] = arg1; + e->Args[1] = arg2; + e->Args[2] = arg3; + + VMValue params[2] = { (DStaticEventHandler*)this, e }; + GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); + } } } diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 664438f0d..5b5e067f4 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -321,7 +321,8 @@ class StaticEventHandler : Object native play virtual native bool InputProcess(InputEvent e); // - virtual native clearscope void ConsoleProcess(ConsoleEvent e); + virtual native ui void ConsoleProcess(ConsoleEvent e); + virtual native void NetworkProcess(ConsoleEvent e); // this value will be queried on Register() to decide the relative order of this handler to every other. // this is most useful in UI systems. From 1433b783019d37af0ff1223f031d10fc737d8e0d Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Fri, 3 Mar 2017 23:26:06 +0200 Subject: [PATCH 37/73] Assigned all map data to play --- wadsrc/static/zscript/mapdata.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wadsrc/static/zscript/mapdata.txt b/wadsrc/static/zscript/mapdata.txt index 5a216ea5e..4bbfb7097 100644 --- a/wadsrc/static/zscript/mapdata.txt +++ b/wadsrc/static/zscript/mapdata.txt @@ -1,5 +1,5 @@ -struct SectorPortal native +struct SectorPortal native play { enum EType { @@ -31,12 +31,12 @@ struct SectorPortal native }; -struct Vertex native +struct Vertex native play { native readonly Vector2 p; } -struct Side native +struct Side native play { enum ETexpart { @@ -100,7 +100,7 @@ struct Side native }; -struct Line native +struct Line native play { enum ELineFlags { @@ -171,7 +171,7 @@ struct Line native } } -struct SecPlane native +struct SecPlane native play { native Vector3 Normal; native double D; @@ -189,7 +189,7 @@ struct SecPlane native } // This encapsulates all info Doom's original 'special' field contained - for saving and transferring. -struct SecSpecial +struct SecSpecial play { Name damagetype; int damageamount; @@ -199,7 +199,7 @@ struct SecSpecial int Flags; } -struct Sector native +struct Sector native play { //secplane_t floorplane, ceilingplane; // defined internally //FDynamicColormap *ColorMap; From c9a994a885c8ccbad15b3464bb974f1600f34341 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 00:04:19 +0200 Subject: [PATCH 38/73] Fixed: clearscope should also clear the inherited scope (through struct member access chain); Fixed: struct member access chain should ONLY work for structs (forgot that FxClassMember inherits FxStructMember) --- src/dobjtype.h | 1 + src/scripting/backend/codegen.cpp | 13 ++++++---- src/scripting/backend/codegen.h | 13 +++++++--- src/scripting/zscript/zcc_compile.cpp | 9 ++----- wadsrc/static/zscript/actor.txt | 8 +++---- wadsrc/static/zscript/menu/menu.txt | 2 +- wadsrc/static/zscript/shared/player.txt | 32 ++++++++++++------------- 7 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/dobjtype.h b/src/dobjtype.h index de3b9c916..1d134bd07 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -40,6 +40,7 @@ enum VARF_UI = (1<<20), // [ZZ] ui: object is ui-scope only (can't modify playsim) VARF_Play = (1<<21), // [ZZ] play: object is playsim-scope only (can't access ui) VARF_VirtualScope = (1<<22), // [ZZ] virtualscope: object should use the scope of the particular class it's being used with (methods only) + VARF_ClearScope = (1<<23), // [ZZ] clearscope: this method ignores the member access chain that leads to it and is always plain data. }; // An action function ------------------------------------------------------- diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1359df59a..ad3ed7664 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6879,7 +6879,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) } BarrierSide = scopeBarrier.sidelast; - if (classx->ExprType == EFX_StructMember) // note: only do this for structs now + if (classx->ExprType == EFX_StructMember && ExprType == EFX_StructMember) // note: only do this for structs now { FxStructMember* pmember = (FxStructMember*)classx; if (BarrierSide == FScopeBarrier::Side_PlainData && pmember) @@ -8175,11 +8175,14 @@ isresolved: innerside = FScopeBarrier::SideFromObjectFlags(cls->ObjectFlags); innerflags = FScopeBarrier::FlagsFromSide(innerside); } - if (Self->ExprType == EFX_StructMember) + else if (innerside != FScopeBarrier::Side_Clear) { - FxStructMember* pmember = (FxStructMember*)Self; - if (innerside == FScopeBarrier::Side_PlainData) - innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); + if (Self->ExprType == EFX_StructMember) + { + FxStructMember* pmember = (FxStructMember*)Self; + if (innerside == FScopeBarrier::Side_PlainData) + innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); + } } FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); if (!scopeBarrier.callable) diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index c9e23c980..07425bd90 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -89,7 +89,8 @@ struct FScopeBarrier Side_PlainData = 0, Side_UI = 1, Side_Play = 2, - Side_Virtual = 3 // do NOT change the value + Side_Virtual = 3, // do NOT change the value + Side_Clear = 4 }; int sidefrom; int sidelast; @@ -103,6 +104,8 @@ struct FScopeBarrier return Side_Play; if (flags & VARF_VirtualScope) return Side_Virtual; + if (flags & VARF_ClearScope) + return Side_Clear; return Side_PlainData; } @@ -127,6 +130,8 @@ struct FScopeBarrier return VARF_UI; case Side_Virtual: return VARF_VirtualScope; + case Side_Clear: + return VARF_ClearScope; default: return 0; } @@ -144,7 +149,9 @@ struct FScopeBarrier case Side_Play: return "play"; case Side_Virtual: - return "virtual"; // should not happen! + return "virtualscope"; // should not happen! + case Side_Clear: + return "clearscope"; // should not happen! default: return "unknown"; } @@ -153,7 +160,7 @@ struct FScopeBarrier // this modifies VARF_ flags and sets the side properly. static int ChangeSideInFlags(int flags, int side) { - flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope); + flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope | VARF_ClearScope); flags |= FlagsFromSide(side); return flags; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index e28f5d271..1f7e66442 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2135,7 +2135,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (f->Flags & ZCC_Play) varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Play); if (f->Flags & ZCC_ClearScope) - varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_PlainData); + varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Clear); if (f->Flags & ZCC_VirtualScope) varflags = FScopeBarrier::ChangeSideInFlags(varflags, FScopeBarrier::Side_Virtual); @@ -2405,12 +2405,7 @@ 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. - if (varflags & VARF_UI) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_UI; - if (varflags & VARF_Play) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Play; - if (varflags & VARF_VirtualScope) - sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::Side_Virtual; + sym->Variants[0].Implementation->BarrierSide = FScopeBarrier::SideFromFlags(varflags); } PClass *clstype = static_cast(c->Type()); diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 148fd4bd5..56d9072fa 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -425,7 +425,7 @@ class Actor : Thinker native } } - virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + clearscope virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) { if (mod == 'Telefrag') { @@ -450,13 +450,13 @@ class Actor : Thinker native native static int FindUniqueTid(int start = 0, int limit = 0); native void SetShade(color col); - native string GetTag(string defstr = ""); + clearscope native string GetTag(string defstr = ""); native void SetTag(string defstr = ""); native double GetBobOffset(double frac = 0); native void ClearCounters(); native bool GiveBody (int num, int max=0); native bool HitFloor(); - native bool isTeammate(Actor other); + clearscope native bool isTeammate(Actor other); native int PlayerNumber(); native void SetFriendPlayer(PlayerInfo player); native void SoundAlert(Actor target, bool splash = false, double maxdist = 0); @@ -597,7 +597,7 @@ class Actor : Thinker native native void ClearInventory(); native bool GiveInventory(class type, int amount, bool givecheat = false); native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); - native Inventory FindInventory(class itemtype, bool subclass = false); + clearscope native Inventory FindInventory(class itemtype, bool subclass = false); native Inventory GiveInventoryType(class itemtype); native Inventory DropInventory (Inventory item, int amt = -1); native bool UseInventory(Inventory item); diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index b4e5c1ac8..81727ad2b 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -287,7 +287,7 @@ class Menu : Object native ui } -class MenuDescriptor : Object native +class MenuDescriptor : Object native ui { native Name mMenuName; native String mNetgameMessage; diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index a80210714..d6ea5b843 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -150,7 +150,7 @@ class PlayerPawn : Actor native } // This is for SBARINFO. - int, int GetEffectTicsForItem(class item) + clearscope int, int GetEffectTicsForItem(class item) { let pg = (class)(item); if (pg != null) @@ -167,10 +167,10 @@ class PlayerPawn : Actor native return -1, -1; } - native int GetMaxHealth(bool withupgrades = false); + clearscope native int GetMaxHealth(bool withupgrades = false); native bool ResetAirSupply (bool playgasp = false); native void CheckWeaponSwitch(class item); - native static String GetPrintableDisplayName(Class cls); + clearscope native static String GetPrintableDisplayName(Class cls); } @@ -192,7 +192,7 @@ class PlayerChunk : PlayerPawn } } -class PSprite : Object native +class PSprite : Object native play { enum PSPLayers { @@ -237,7 +237,7 @@ enum EPlayerState PST_GONE // Player has left the game } -struct PlayerInfo native // this is what internally is known as player_t +struct PlayerInfo native play // this is what internally is known as player_t { // technically engine constants but the only part of the playsim using them is the player. const NOFIXEDCOLORMAP = -1; @@ -338,22 +338,22 @@ usercmd_t original_cmd; native void PoisonDamage(Actor source, int damage, bool playPainSound); native void SetPsprite(int id, State stat, bool pending = false); native void SetSafeFlash(Weapon weap, State flashstate, int index); - native PSprite GetPSprite(int id); - native PSprite FindPSprite(int id); + native PSprite GetPSprite(int id) const; + native PSprite FindPSprite(int id) const; native void SetLogNumber (int text); native void SetLogText (String text); native void DropWeapon(); native void BringUpWeapon(); - native String GetUserName(); - native Color GetColor(); - native int GetColorSet(); - native int GetPlayerClassNum(); - native int GetSkin(); - native bool GetNeverSwitch(); - native int GetGender(); - native int GetTeam(); - native float GetAutoaim(); + native String GetUserName() const; + native Color GetColor() const; + native int GetColorSet() const; + native int GetPlayerClassNum() const; + native int GetSkin() const; + native bool GetNeverSwitch() const; + native int GetGender() const; + native int GetTeam() const; + native float GetAutoaim() const; native void SetFOV(float fov); } From 3338fb7f3321eff4e0180acca01c3072e79a4fba Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 00:57:41 +0200 Subject: [PATCH 39/73] Added SendNetworkEvent static method to EventHandler; Fixed qualified static method call from own class (previously was 'shadowed' by qualified virtual method call) --- src/events.cpp | 33 ++++++++++++++++++++++++++----- src/events.h | 3 +++ src/scripting/backend/codegen.cpp | 1 + wadsrc/static/zscript/events.txt | 2 ++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/events.cpp b/src/events.cpp index 75dd8cde8..9946a1059 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -117,6 +117,21 @@ bool E_UnregisterHandler(DStaticEventHandler* handler) return true; } +bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3) +{ + if (gamestate != GS_LEVEL) + return false; + + Net_WriteByte(DEM_NETEVENT); + Net_WriteString(name); + Net_WriteByte(3); + Net_WriteLong(arg1); + Net_WriteLong(arg2); + Net_WriteLong(arg3); + + return true; +} + bool E_CheckHandler(DStaticEventHandler* handler) { for (DStaticEventHandler* lhandler = E_FirstEventHandler; lhandler; lhandler = lhandler->next) @@ -521,6 +536,18 @@ DEFINE_ACTION_FUNCTION(DStaticEventHandler, SetOrder) return 0; } +DEFINE_ACTION_FUNCTION(DEventHandler, SendNetworkEvent) +{ + PARAM_PROLOGUE; + PARAM_STRING(name); + PARAM_INT(arg1); + PARAM_INT(arg2); + PARAM_INT(arg3); + // + + ACTION_RETURN_BOOL(E_SendNetworkEvent(name, arg1, arg2, arg3)); +} + DEFINE_ACTION_FUNCTION(DEventHandler, Create) { PARAM_PROLOGUE; @@ -1160,10 +1187,6 @@ CCMD(netevent) for (int i = 0; i < argn; i++) arg[i] = atoi(argv[2 + i]); // call networked - Net_WriteByte(DEM_NETEVENT); - Net_WriteString(argv[1]); - Net_WriteByte(argn); - for (int i = 0; i < 3; i++) - Net_WriteLong(arg[i]); + E_SendNetworkEvent(argv[1], arg[0], arg[1], arg[2]); } } diff --git a/src/events.h b/src/events.h index fa63e7325..b83890223 100755 --- a/src/events.h +++ b/src/events.h @@ -60,6 +60,9 @@ bool E_Responder(event_t* ev); // splits events into InputProcess and UiProcess // this executes on console/net events. void E_Console(int player, FString name, int arg1, int arg2, int arg3); +// send networked event. unified function. +bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3); + // check if there is anything that should receive GUI events bool E_CheckUiProcessors(); // check if we need native mouse due to UiProcessors diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index ad3ed7664..1a212f5af 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -7859,6 +7859,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) Self = new FxSelf(ScriptPosition); Self->ValueType = NewPointer(cls); } + else novirtual = false; } } } diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 5b5e067f4..6452d19e0 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -343,4 +343,6 @@ class EventHandler : StaticEventHandler native static native bool Register(StaticEventHandler handler); static native bool Unregister(StaticEventHandler handler); + + clearscope static native void SendNetworkEvent(String name, int arg1 = 0, int arg2 = 0, int arg3 = 0); } From fd4727e70121c3bacaa5c5538a4f46feb634e7d7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 10:28:51 +0100 Subject: [PATCH 40/73] - did a bit of cleanup. - disabled the runtime check in OP_CALL because as implemented it doesn't clean up properly and is not fully implemented. --- src/CMakeLists.txt | 1 + src/scripting/backend/codegen.cpp | 2 + src/scripting/backend/codegen.h | 176 +------------------------ src/scripting/backend/scopebarrier.cpp | 152 +++++++++++++++++++++ src/scripting/backend/scopebarrier.h | 52 ++++++++ src/scripting/vm/vmexec.h | 2 + 6 files changed, 210 insertions(+), 175 deletions(-) create mode 100644 src/scripting/backend/scopebarrier.cpp create mode 100644 src/scripting/backend/scopebarrier.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ff73e32c..8913ef6ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1168,6 +1168,7 @@ set (PCH_SOURCES scripting/thingdef_data.cpp scripting/thingdef_properties.cpp scripting/backend/codegen.cpp + scripting/backend/scopebarrier.cpp scripting/backend/dynarrays.cpp scripting/backend/vmbuilder.cpp scripting/backend/vmdisasm.cpp diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1a212f5af..eb92fe974 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -8725,6 +8725,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) { +#if 0 // [ZZ] if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual) { @@ -8734,6 +8735,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT)); count += 2; } +#endif assert(Self != nullptr); selfemit = Self->Emit(build); assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target)); diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 07425bd90..000688341 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -45,6 +45,7 @@ #include "s_sound.h" #include "actor.h" #include "vmbuilder.h" +#include "scopebarrier.h" #define CHECKRESOLVED() if (isresolved) return this; isresolved=true; @@ -70,181 +71,6 @@ class FxCompoundStatement; class FxLocalVariableDeclaration; typedef TDeletingArray FArgumentList; -// -// [ZZ] this really should be in codegen.h, but vmexec needs to access it -struct FScopeBarrier -{ - bool callable; - bool readable; - bool writable; - - // this is the error message - FString callerror; - FString readerror; - FString writeerror; - - // this is used to make the error message. - enum Side - { - Side_PlainData = 0, - Side_UI = 1, - Side_Play = 2, - Side_Virtual = 3, // do NOT change the value - Side_Clear = 4 - }; - int sidefrom; - int sidelast; - - // 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. - static int SideFromFlags(int flags) - { - if (flags & VARF_UI) - return Side_UI; - if (flags & VARF_Play) - return Side_Play; - if (flags & VARF_VirtualScope) - return Side_Virtual; - if (flags & VARF_ClearScope) - return Side_Clear; - return Side_PlainData; - } - - // same as above, but from object flags - static int SideFromObjectFlags(int flags) - { - if (flags & OF_UI) - return Side_UI; - if (flags & OF_Play) - return Side_Play; - return Side_PlainData; - } - - // - static int FlagsFromSide(int side) - { - switch (side) - { - case Side_Play: - return VARF_Play; - case Side_UI: - return VARF_UI; - case Side_Virtual: - return VARF_VirtualScope; - case Side_Clear: - return VARF_ClearScope; - default: - return 0; - } - } - - // used for errors - static const char* StringFromSide(int side) - { - switch (side) - { - case Side_PlainData: - return "data"; - case Side_UI: - return "ui"; - case Side_Play: - return "play"; - case Side_Virtual: - return "virtualscope"; // should not happen! - case Side_Clear: - return "clearscope"; // should not happen! - default: - return "unknown"; - } - } - - // this modifies VARF_ flags and sets the side properly. - static int ChangeSideInFlags(int flags, int side) - { - flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope | VARF_ClearScope); - flags |= FlagsFromSide(side); - return flags; - } - - FScopeBarrier() - { - sidefrom = -1; - sidelast = -1; - callable = true; - readable = true; - writable = true; - } - - FScopeBarrier(int flags1, int flags2, const char* name) - { - sidefrom = -1; - sidelast = -1; - callable = true; - readable = true; - writable = true; - - AddFlags(flags1, flags2, name); - } - - // AddFlags modifies ALLOWED actions by flags1->flags2. - // 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) - { - // note: if it's already non-readable, don't even try advancing - if (!readable) - return; - - // we aren't interested in any other flags - // - update: including VARF_VirtualScope. inside the function itself, we treat it as if it's PlainData. - flags1 &= VARF_UI | VARF_Play; - flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly; - - if (sidefrom < 0) sidefrom = SideFromFlags(flags1); - if (sidelast < 0) sidelast = sidefrom; - - // flags1 = what's trying to access - // flags2 = what's being accessed - - int sideto = SideFromFlags(flags2); - - // plain data inherits whatever scope modifiers that context or field container has. - // i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well. - if (sideto != Side_PlainData) - sidelast = sideto; - else sideto = sidelast; - - if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable - { - readable = false; - if (name) readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); - } - - if (!readable) - { - writable = false; - callable = false; - if (name) - { - writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); - callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); - } - return; - } - - if (writable && (sidefrom != sideto)) // only matching types are writable (plain data implicitly takes context type by default, unless overridden) - { - writable = false; - if (name) writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); - } - - if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context. - { - callable = false; - if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); - } - } -}; - struct FCompileContext { FxExpression *ControlStmt = nullptr; diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp new file mode 100644 index 000000000..4d9d6d2fc --- /dev/null +++ b/src/scripting/backend/scopebarrier.cpp @@ -0,0 +1,152 @@ +#include "scopebarrier.h" +#include "dobject.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. +int FScopeBarrier::SideFromFlags(int flags) +{ + if (flags & VARF_UI) + return Side_UI; + if (flags & VARF_Play) + return Side_Play; + if (flags & VARF_VirtualScope) + return Side_Virtual; + if (flags & VARF_ClearScope) + return Side_Clear; + return Side_PlainData; +} + +// same as above, but from object flags +int FScopeBarrier::SideFromObjectFlags(int flags) +{ + if (flags & OF_UI) + return Side_UI; + if (flags & OF_Play) + return Side_Play; + return Side_PlainData; +} + +// +int FScopeBarrier::FlagsFromSide(int side) +{ + switch (side) + { + case Side_Play: + return VARF_Play; + case Side_UI: + return VARF_UI; + case Side_Virtual: + return VARF_VirtualScope; + case Side_Clear: + return VARF_ClearScope; + default: + return 0; + } +} + +// used for errors +const char* FScopeBarrier::StringFromSide(int side) +{ + switch (side) + { + case Side_PlainData: + return "data"; + case Side_UI: + return "ui"; + case Side_Play: + return "play"; + case Side_Virtual: + return "virtualscope"; // should not happen! + case Side_Clear: + return "clearscope"; // should not happen! + default: + return "unknown"; + } +} + +// this modifies VARF_ flags and sets the side properly. +int FScopeBarrier::ChangeSideInFlags(int flags, int side) +{ + flags &= ~(VARF_UI | VARF_Play | VARF_VirtualScope | VARF_ClearScope); + flags |= FlagsFromSide(side); + return flags; +} + +FScopeBarrier::FScopeBarrier() +{ + sidefrom = -1; + sidelast = -1; + callable = true; + readable = true; + writable = true; +} + +FScopeBarrier::FScopeBarrier(int flags1, int flags2, const char* name) +{ + sidefrom = -1; + sidelast = -1; + callable = true; + readable = true; + writable = true; + + AddFlags(flags1, flags2, name); +} + +// AddFlags modifies ALLOWED actions by flags1->flags2. +// 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 FScopeBarrier::AddFlags(int flags1, int flags2, const char* name) +{ + // note: if it's already non-readable, don't even try advancing + if (!readable) + return; + + // we aren't interested in any other flags + // - update: including VARF_VirtualScope. inside the function itself, we treat it as if it's PlainData. + flags1 &= VARF_UI | VARF_Play; + flags2 &= VARF_UI | VARF_Play | VARF_ReadOnly; + + if (sidefrom < 0) sidefrom = SideFromFlags(flags1); + if (sidelast < 0) sidelast = sidefrom; + + // flags1 = what's trying to access + // flags2 = what's being accessed + + int sideto = SideFromFlags(flags2); + + // plain data inherits whatever scope modifiers that context or field container has. + // i.e. play String bla; is play, and all non-specified methods/fields inside it are play as well. + if (sideto != Side_PlainData) + sidelast = sideto; + else sideto = sidelast; + + if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable + { + readable = false; + if (name) readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } + + if (!readable) + { + writable = false; + callable = false; + if (name) + { + writeerror.Format("Can't write %s field %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + callerror.Format("Can't call %s function %s from %s context (not readable)", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } + return; + } + + if (writable && (sidefrom != sideto)) // only matching types are writable (plain data implicitly takes context type by default, unless overridden) + { + writable = false; + if (name) writeerror.Format("Can't write %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } + + if (callable && (sidefrom != sideto) && !(flags2 & VARF_ReadOnly)) // readonly on methods is used for plain data stuff that can be called from ui/play context. + { + callable = false; + if (name) callerror.Format("Can't call %s function %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); + } +} diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h new file mode 100644 index 000000000..ed4f25a10 --- /dev/null +++ b/src/scripting/backend/scopebarrier.h @@ -0,0 +1,52 @@ +#pragma once + +#include "zstring.h" + +// +// [ZZ] this really should be in codegen.h, but vmexec needs to access it +struct FScopeBarrier +{ + bool callable; + bool readable; + bool writable; + + // this is the error message + FString callerror; + FString readerror; + FString writeerror; + + // this is used to make the error message. + enum Side + { + Side_PlainData = 0, + Side_UI = 1, + Side_Play = 2, + Side_Virtual = 3, // do NOT change the value + Side_Clear = 4 + }; + int sidefrom; + int sidelast; + + // 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. + static int SideFromFlags(int flags); + + // same as above, but from object flags + static int SideFromObjectFlags(int flags); + + // + static int FlagsFromSide(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); + FScopeBarrier(); + FScopeBarrier(int flags1, int flags2, const char* name); + + // AddFlags modifies ALLOWED actions by flags1->flags2. + // 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); +}; + diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index b3d1b2426..c11b351c0 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -664,6 +664,7 @@ begin: VMReturn returns[MAX_RETURNS]; int numret; +#if 0 // [ZZ] hax! b = B; if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments. @@ -675,6 +676,7 @@ begin: FScopeBarrier_ValidateCall(calledfunc, callingfunc, selftype); b -= 2; } +#endif FillReturns(reg, f, returns, pc+1, C); if (call->Native) From a7fdf4b90d99677acbe294abd6796fbcb5a8d3c0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 10:58:34 +0100 Subject: [PATCH 41/73] - fixed: M_Init needs to catch VM exceptions from MENUDEF init to print out meaningful error messages in case something goes wrong. --- src/menu/menu.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 1c8e0b39b..dbe5eb9fb 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -796,7 +796,16 @@ void M_ClearMenus() void M_Init (void) { - M_ParseMenuDefs(); + try + { + M_ParseMenuDefs(); + } + catch (CVMAbortException &err) + { + err.MaybePrintMessage(); + Printf("%s", err.stacktrace); + I_FatalError("Failed to initialize menus"); + } M_CreateMenus(); } From 8a1e0ee4b52ccaacac8c2fdb80a6985d7e3f76c5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 4 Mar 2017 12:03:31 +0200 Subject: [PATCH 42/73] Fixed compilation with GCC/Clang src/menu/menu.cpp:806:20: error: cannot pass non-trivial object of type 'FString' to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs] --- src/menu/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index dbe5eb9fb..a481dd24b 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -803,7 +803,7 @@ void M_Init (void) catch (CVMAbortException &err) { err.MaybePrintMessage(); - Printf("%s", err.stacktrace); + Printf("%s", err.stacktrace.GetChars()); I_FatalError("Failed to initialize menus"); } M_CreateMenus(); From d8cee4d3a5867525f6661f2fac3ea20586b467cf Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 4 Mar 2017 12:17:53 +0200 Subject: [PATCH 43/73] Fixed most of warnings reported by Clang src/scripting/zscript/zcc_compile.cpp: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare] src/p_acs.cpp: warning: format specifies type 'char *' but the argument has type 'PClass *' [-Wformat] --- src/p_acs.cpp | 12 ++++++------ src/scripting/zscript/zcc_compile.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 027b614ea..8c2859479 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4765,24 +4765,24 @@ static int ScriptCall(unsigned argc, int32_t *args) auto cls = PClass::FindClass(clsname); if (!cls) { - I_Error("ACS call to unknown class in script function %s.%s", cls, funcname); + I_Error("ACS call to unknown class in script function %s.%s", clsname, funcname); } auto funcsym = dyn_cast(cls->Symbols.FindSymbol(funcname, true)); if (funcsym == nullptr) { - I_Error("ACS call to unknown script function %s.%s", cls, funcname); + I_Error("ACS call to unknown script function %s.%s", clsname, funcname); } auto func = funcsym->Variants[0].Implementation; if (func->ImplicitArgs > 0) { - I_Error("ACS call to non-static script function %s.%s", cls, funcname); + I_Error("ACS call to non-static script function %s.%s", clsname, funcname); } TArray params; for (unsigned i = 2; i < argc; i++) { if (func->Proto->ArgumentTypes.Size() < i - 1) { - I_Error("Too many parameters in call to %s.%s", cls, funcname); + I_Error("Too many parameters in call to %s.%s", clsname, funcname); } auto argtype = func->Proto->ArgumentTypes[i - 2]; // The only types allowed are int, bool, double, Name, Sound, Color and String @@ -4812,7 +4812,7 @@ static int ScriptCall(unsigned argc, int32_t *args) } else { - I_Error("Invalid type %s in call to %s.%s", argtype->DescriptiveName(), cls, funcname); + I_Error("Invalid type %s in call to %s.%s", argtype->DescriptiveName(), clsname, funcname); } } if (func->Proto->ArgumentTypes.Size() > params.Size()) @@ -4820,7 +4820,7 @@ static int ScriptCall(unsigned argc, int32_t *args) // Check if we got enough parameters. That means we either cover the full argument list of the function or the next argument is optional. if (!(funcsym->Variants[0].ArgFlags[params.Size()] & VARF_Optional)) { - I_Error("Insufficient parameters in call to %s.%s", cls, funcname); + I_Error("Insufficient parameters in call to %s.%s", clsname, funcname); } } // The return value can be the same types as the parameter types, plus void diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1f7e66442..d1cb78282 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -613,7 +613,7 @@ void ZCCCompiler::CreateClassTypes() // static int incompatible[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; int incompatiblecnt = 0; - for (int k = 0; k < countof(incompatible); k++) + for (size_t k = 0; k < countof(incompatible); k++) if (incompatible[k] & c->cls->Flags) incompatiblecnt++; if (incompatiblecnt > 1) @@ -1117,7 +1117,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope }; int excludeflags = 0; int fc = 0; - for (int i = 0; i < countof(excludescope); i++) + for (size_t i = 0; i < countof(excludescope); i++) { if (field->Flags & excludescope[i]) { @@ -2176,7 +2176,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool static int exclude[] = { ZCC_Virtual, ZCC_Override, ZCC_Action, ZCC_Static }; int excludeflags = 0; int fc = 0; - for (int i = 0; i < countof(exclude); i++) + for (size_t i = 0; i < countof(exclude); i++) { if (f->Flags & exclude[i]) { @@ -2213,7 +2213,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool static int excludescope[] = { ZCC_UIFlag, ZCC_Play, ZCC_ClearScope, ZCC_VirtualScope }; excludeflags = 0; fc = 0; - for (int i = 0; i < countof(excludescope); i++) + for (size_t i = 0; i < countof(excludescope); i++) { if (f->Flags & excludescope[i]) { From a5edd4899674ec34c836815b338a0e3eac72ccf5 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 4 Mar 2017 12:23:15 +0200 Subject: [PATCH 44/73] Fixed remaining warning reported by Clang src/scripting/zscript/zcc_compile.cpp:2456:54: warning: using the result of an assignment as a condition without parentheses [-Wparentheses] --- src/scripting/zscript/zcc_compile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index d1cb78282..2d5838b06 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2453,7 +2453,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool 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 ((sym->Variants[0].Implementation->FuncConst = oldfunc->FuncConst)) sym->Variants[0].Flags |= VARF_ReadOnly; clstype->Virtuals[vindex] = sym->Variants[0].Implementation; From 3879e67cee40493ce32be3912c95014d58f0adf9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 11:33:57 +0100 Subject: [PATCH 45/73] - the #if was in the wrong place. --- src/scripting/vm/vmexec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index c11b351c0..cd3925adc 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -664,9 +664,9 @@ begin: VMReturn returns[MAX_RETURNS]; int numret; + b = B; #if 0 // [ZZ] hax! - b = B; if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments. { PFunction* calledfunc = (PFunction*)(reg.param + f->NumParam - b)[0].a; From 5551f3a8c5423ac8a2ee87125f17747ba93d0a26 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 11:48:36 +0100 Subject: [PATCH 46/73] - declared the sectorplanes in Sector as read only and marked all relevant functions in the planes themselves const. This is to block modification of the planes directly. For future-proofness with renderer changes everything that alters these values should go through he function interface. --- src/scripting/thingdef_data.cpp | 4 ++-- wadsrc/static/zscript/base.txt | 1 - wadsrc/static/zscript/mapdata.txt | 16 ++++++++-------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index c50b7f65b..a09134d58 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -858,8 +858,8 @@ void InitThingdef() // As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up. sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native); - sectorstruct->AddNativeField("ceilingplane", secplanestruct, myoffsetof(sector_t, ceilingplane), VARF_Native); - sectorstruct->AddNativeField("floorplane", secplanestruct, myoffsetof(sector_t, floorplane), VARF_Native); + sectorstruct->AddNativeField("ceilingplane", secplanestruct, myoffsetof(sector_t, ceilingplane), VARF_Native | VARF_ReadOnly); + sectorstruct->AddNativeField("floorplane", secplanestruct, myoffsetof(sector_t, floorplane), VARF_Native | VARF_ReadOnly); diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 2c1fb6661..a15b78de8 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -386,7 +386,6 @@ class Thinker : Object native play class ThinkerIterator : Object native { - native static ThinkerIterator Create(class type = "Actor", int statnum=Thinker.MAX_STATNUM+1); native Thinker Next(bool exact = false); native void Reinit(); diff --git a/wadsrc/static/zscript/mapdata.txt b/wadsrc/static/zscript/mapdata.txt index 4bbfb7097..3d18f9f5e 100644 --- a/wadsrc/static/zscript/mapdata.txt +++ b/wadsrc/static/zscript/mapdata.txt @@ -143,7 +143,7 @@ struct Line native play native int special; native int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) native double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) - native Side sidedef[2]; + native readonly Side sidedef[2]; native readonly double bbox[4]; // bounding box, for the extent of the LineDef. native readonly Sector frontsector, backsector; native int validcount; // if == validcount, already checked @@ -177,15 +177,15 @@ struct SecPlane native play native double D; native double negiC; - native bool isSlope(); - native int PointOnSide(Vector3 pos); - native double ZatPoint (Vector2 v); - native double ZatPointDist(Vector2 v, double dist); - native bool isEqual(Secplane other); + native bool isSlope() const; + native int PointOnSide(Vector3 pos) const; + native double ZatPoint (Vector2 v) const; + native double ZatPointDist(Vector2 v, double dist) const; + native bool isEqual(Secplane other) const; native void ChangeHeight(double hdiff); - native double GetChangedHeight(double hdiff); + native double GetChangedHeight(double hdiff) const; native double HeightDiff(double oldd, double newd = 0.0); - native double PointToDist(Vector2 xy, double z); + native double PointToDist(Vector2 xy, double z) const; } // This encapsulates all info Doom's original 'special' field contained - for saving and transferring. From b3ba5bfe2c494f2737e9e8d5387173b1008e5bdd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 12:07:45 +0100 Subject: [PATCH 47/73] - allow 'const' on class functions. This is preferable to 'clearscope' so that the UI code can call getter functions without having to declare things as 'clearscope'. Clearscope is a dangerous context and should be limited to the minimum extent possible and preferably be blocked in user code. This may still need some work on const functions but better have it in now. --- src/scripting/zscript/zcc_compile.cpp | 4 ++++ wadsrc/static/zscript/actor.txt | 8 ++++---- wadsrc/static/zscript/inventory/powerups.txt | 2 +- wadsrc/static/zscript/inventory/weapons.txt | 2 +- wadsrc/static/zscript/shared/player.txt | 8 ++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 2d5838b06..2000075c9 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2141,10 +2141,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool // [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)))) { Error(f, "'Const' on a method can only be used in structs"); } + */ if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native)) { diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 56d9072fa..233f75d40 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -425,7 +425,7 @@ class Actor : Thinker native } } - clearscope virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const { if (mod == 'Telefrag') { @@ -450,13 +450,13 @@ class Actor : Thinker native native static int FindUniqueTid(int start = 0, int limit = 0); native void SetShade(color col); - clearscope native string GetTag(string defstr = ""); + native string GetTag(string defstr = "") const; native void SetTag(string defstr = ""); native double GetBobOffset(double frac = 0); native void ClearCounters(); native bool GiveBody (int num, int max=0); native bool HitFloor(); - clearscope native bool isTeammate(Actor other); + native bool isTeammate(Actor other) const; native int PlayerNumber(); native void SetFriendPlayer(PlayerInfo player); native void SoundAlert(Actor target, bool splash = false, double maxdist = 0); @@ -597,7 +597,7 @@ class Actor : Thinker native native void ClearInventory(); native bool GiveInventory(class type, int amount, bool givecheat = false); native bool TakeInventory(class itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); - clearscope native Inventory FindInventory(class itemtype, bool subclass = false); + native Inventory FindInventory(class itemtype, bool subclass = false) const; native Inventory GiveInventoryType(class itemtype); native Inventory DropInventory (Inventory item, int amt = -1); native bool UseInventory(Inventory item); diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index d4d78d119..f6b14a051 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -285,7 +285,7 @@ class Powerup : Inventory // //=========================================================================== - virtual clearscope bool isBlinking() + virtual bool isBlinking() const { return (EffectTics <= BLINKTHRESHOLD && (EffectTics & 8) && !bNoScreenBlink); } diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 860d387e2..0eba93bf8 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -89,7 +89,7 @@ class Weapon : StateProvider native return s; } - override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const { // Weapons may never return HitObituary by default. Override this if it is needed. return Obituary; diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index d6ea5b843..cbb361687 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -119,7 +119,7 @@ class PlayerPawn : Actor native } } - override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const { if (victim.player != player && victim.IsTeammate(self)) { @@ -150,7 +150,7 @@ class PlayerPawn : Actor native } // This is for SBARINFO. - clearscope int, int GetEffectTicsForItem(class item) + int, int GetEffectTicsForItem(class item) const { let pg = (class)(item); if (pg != null) @@ -167,10 +167,10 @@ class PlayerPawn : Actor native return -1, -1; } - clearscope native int GetMaxHealth(bool withupgrades = false); + native int GetMaxHealth(bool withupgrades = false) const; native bool ResetAirSupply (bool playgasp = false); native void CheckWeaponSwitch(class item); - clearscope native static String GetPrintableDisplayName(Class cls); + native clearscope static String GetPrintableDisplayName(Class cls); } From 4c5794b6d53269038732f5ac4cad472f6acef29f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 12:11:56 +0100 Subject: [PATCH 48/73] - made GetObituary non-constant. This gets only called from within the play code and making it const would block potential use cases that involve changing some internal variables like counters. --- wadsrc/static/zscript/actor.txt | 2 +- wadsrc/static/zscript/inventory/weapons.txt | 2 +- wadsrc/static/zscript/shared/player.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 233f75d40..029aebc0a 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -425,7 +425,7 @@ class Actor : Thinker native } } - virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const + virtual String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) { if (mod == 'Telefrag') { diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index 0eba93bf8..860d387e2 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -89,7 +89,7 @@ class Weapon : StateProvider native return s; } - override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) { // Weapons may never return HitObituary by default. Override this if it is needed. return Obituary; diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index cbb361687..b13b84015 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -119,7 +119,7 @@ class PlayerPawn : Actor native } } - override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) const + override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack) { if (victim.player != player && victim.IsTeammate(self)) { From 7dbc6939c4c7dddb74698f94b6a9f979befd45d0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 12:43:07 +0100 Subject: [PATCH 49/73] - declared most native getters in Actor as const. - made the self pointer of const functions readonly. This seems to work fine. Both calling a non-const function and trying to assign a value to a member fail with an error message. --- src/scripting/thingdef.cpp | 3 +- src/scripting/zscript/zcc_compile.cpp | 9 ++++++ wadsrc/static/zscript/actor.txt | 40 +++++++++++++-------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index d9eca5fca..c0e444893 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -110,12 +110,13 @@ void SetImplicitArgs(TArray *args, TArray *argflags, TArrayPush(NewPointer(cls)); + if (args != nullptr) args->Push(NewPointer(cls, !!(funcflags & VARF_ReadOnly))); if (argflags != nullptr) argflags->Push(VARF_Implicit | VARF_ReadOnly); if (argnames != nullptr) argnames->Push(NAME_self); } if (funcflags & VARF_Action) { + assert(!(funcflags & VARF_ReadOnly)); // implied caller and callingstate pointers if (args != nullptr) { diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 2000075c9..f8e9980a2 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2156,6 +2156,15 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool } if (f->Flags & ZCC_Action) { + if (varflags & VARF_ReadOnly) + { + Error(f, "Action functions cannot be declared const"); + varflags &= ~VARF_ReadOnly; + } + if (varflags & VARF_UI) + { + Error(f, "Action functions cannot be declared UI"); + } // Non-Actors cannot have action functions. if (!c->Type()->IsKindOf(RUNTIME_CLASS(PClassActor))) { diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 029aebc0a..ec5ce71d8 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -468,15 +468,15 @@ class Actor : Thinker native native bool UpdateWaterLevel (bool splash = true); native bool IsZeroDamage(); native void ClearInterpolation(); - native Vector3 PosRelative(sector sec); + native Vector3 PosRelative(sector sec) const; native void HandleSpawnFlags(); native void ExplodeMissile(line lin = null, Actor target = null, bool onsky = false); native void RestoreDamage(); - native int SpawnHealth(); + native int SpawnHealth() const; native void SetDamage(int dmg); - native double Distance2D(Actor other); - native double Distance3D(Actor other); + native double Distance2D(Actor other) const; + native double Distance3D(Actor other) const; native void SetOrigin(vector3 newpos, bool moving); native void SetXYZ(vector3 newpos); native Actor GetPointer(int aaptr); @@ -511,7 +511,7 @@ class Actor : Thinker native native void BloodSplatter (Vector3 pos, double hitangle, bool axe = false); native bool HitWater (sector sec, Vector3 pos, bool checkabove = false, bool alert = true, bool force = false); native void PlaySpawnSound(Actor missile); - native bool CountsAsKill(); + native bool CountsAsKill() const; native bool Teleport(Vector3 pos, double angle, int flags); native void TraceBleed(int damage, Actor missile); @@ -559,21 +559,21 @@ class Actor : Thinker native native void LinkToWorld(LinkContext ctx = null); native void UnlinkFromWorld(out LinkContext ctx = null); native bool CanSeek(Actor target); - native double AngleTo(Actor target, bool absolute = false); + native double AngleTo(Actor target, bool absolute = false) const; native void AddZ(double zadd, bool moving = true); native void SetZ(double z); - native vector2 Vec2To(Actor other); - native vector3 Vec3To(Actor other); - native vector3 Vec3Offset(double x, double y, double z, bool absolute = false); - native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false); - native vector2 Vec2Angle(double length, double angle, bool absolute = false); - native vector2 Vec2Offset(double x, double y, bool absolute = false); - native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false); - native void VelFromAngle(double speed = 0, double angle = 0); - native void Vel3DFromAngle(double speed, double angle, double pitch); + native vector2 Vec2To(Actor other) const; + native vector3 Vec3To(Actor other) const; + native vector3 Vec3Offset(double x, double y, double z, bool absolute = false) const; + native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false) const; + native vector2 Vec2Angle(double length, double angle, bool absolute = false) const; + native vector2 Vec2Offset(double x, double y, bool absolute = false) const; + native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false) const; + native void VelFromAngle(double speed = 0, double angle = 0) const; + native void Vel3DFromAngle(double speed, double angle, double pitch) const; native void Thrust(double speed = 0, double angle = 0); - native bool isFriend(Actor other); - native bool isHostile(Actor other); + native bool isFriend(Actor other) const; + native bool isHostile(Actor other) const; native void AdjustFloorClip(); native DropItem GetDropItems(); native void CopyFriendliness (Actor other, bool changeTarget, bool resetHealth = true); @@ -588,8 +588,8 @@ class Actor : Thinker native native void Howl(); native void DrawSplash (int count, double angle, int kind); native void GiveSecret(bool printmsg = true, bool playsound = true); - native double GetCameraHeight(); - native double GetGravity(); + native double GetCameraHeight() const; + native double GetGravity() const; native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); native void AddInventory(Inventory inv); @@ -613,7 +613,7 @@ class Actor : Thinker native native double GetDistance(bool checkz, int ptr = AAPTR_TARGET); native double GetAngle(int flags, int ptr = AAPTR_TARGET); native double GetZAt(double px = 0, double py = 0, double angle = 0, int flags = 0, int pick_pointer = AAPTR_DEFAULT); - native int GetSpawnHealth(); + native int GetSpawnHealth() const; native double GetCrouchFactor(int ptr = AAPTR_PLAYER1); native double GetCVar(string cvar); native int GetPlayerInput(int inputnum, int ptr = AAPTR_DEFAULT); From cfafbfe4f2284a047babd457ac914e79ff82ea14 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Mar 2017 12:55:20 +0100 Subject: [PATCH 50/73] - removed FStringNoInit. This was development garbage. --- src/zstring.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/zstring.h b/src/zstring.h index b925a39b1..e486a0e8b 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -450,13 +450,4 @@ template<> struct THashTraits int Compare(const FString &left, const FString &right) { return left.Compare(right); } }; -class FStringNoInit -{ - char mem[sizeof(FString)]; - operator FString&() - { - return *reinterpret_cast(&mem); - } -}; - #endif From 600f57405f36a49b1725be4c7429a42c3c0e4577 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 00:28:15 +0100 Subject: [PATCH 51/73] - fixed typo in TexMan.GetSize. --- src/textures/texturemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index bbbb016ea..39948f732 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1208,7 +1208,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetSize) } else x = y = -1; if (numret > 0) ret[0].SetInt(x); - if (numret > 1) ret[1].SetInt(x); + if (numret > 1) ret[1].SetInt(y); return MIN(numret, 2); } From f07bd5ec654d571845a53e7f78f4be50f962c5b0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 5 Mar 2017 00:36:32 +0100 Subject: [PATCH 52/73] - fixed some mess by two nearly similarly named flags. --- src/g_game.cpp | 2 +- src/g_level.h | 2 +- src/g_mapinfo.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 4391a0571..e0fbd9854 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1350,7 +1350,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) if (item->ItemFlags & IF_INVBAR && item->Amount > item->InterHubAmount) { item->Amount = item->InterHubAmount; - if ((level.flags3 & LEVEL3_RESETINVENTORY) && !(item->ItemFlags & IF_UNDROPPABLE)) + if ((level.flags3 & LEVEL3_REMOVEITEMS) && !(item->ItemFlags & IF_UNDROPPABLE)) { todelete.Push(item); } diff --git a/src/g_level.h b/src/g_level.h index 06a91259a..fd66527a7 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -223,7 +223,7 @@ enum ELevelFlags : unsigned int // More flags! LEVEL3_FORCEFAKECONTRAST = 0x00000001, // forces fake contrast even with fog enabled - LEVEL3_RESETINVENTORY = 0x00000002, // kills all INVBAR items on map change. + LEVEL3_REMOVEITEMS = 0x00000002, // kills all INVBAR items on map change. }; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 7a7960115..44b21bfd8 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1277,7 +1277,7 @@ MapFlagHandlers[] = { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 }, { "keepfullinventory", MITYPE_SETFLAG2, LEVEL2_KEEPFULLINVENTORY, 0 }, - { "resetitems", MITYPE_SETFLAG3, LEVEL2_RESETINVENTORY, 0 }, + { "resetitems", MITYPE_SETFLAG3, LEVEL3_REMOVEITEMS, 0 }, { "monsterfallingdamage", MITYPE_SETFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, { "nomonsterfallingdamage", MITYPE_CLRFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, { "clipmidtextures", MITYPE_SETFLAG2, LEVEL2_CLIPMIDTEX, 0 }, From 0a7aebae93589d7cdb2009724647b915e821c855 Mon Sep 17 00:00:00 2001 From: ZZYZX Date: Sat, 4 Mar 2017 20:55:59 +0200 Subject: [PATCH 53/73] 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 54/73] 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 55/73] 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 56/73] 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 57/73] 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 58/73] 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 59/73] 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 60/73] 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 61/73] 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 62/73] 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 63/73] 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 64/73] 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 65/73] 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 66/73] - 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 67/73] - 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 68/73] 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 69/73] 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 70/73] - 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 71/73] - 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 72/73] - 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 73/73] 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; }