From c12dfd7e4dc533dd0d13167452dfa6185c276c56 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 24 Jan 2017 10:04:46 +0100 Subject: [PATCH 1/3] - fixed: only explicit class type casts must obey strict namespace rules, i.e. only '(class)(variable_to_cast)' The general rule is as follows: A class name as a string will always be looked up fully, even if the class name gets shadows by another variable because strings are not identifiers. It is only class names as identifiers that must obey the rule that if it is not known yet or hidden by something else that it may not be found to ensure that the older variable does not take over the name if it gets reused. --- src/scripting/codegeneration/codegen.cpp | 15 +++++++++------ src/scripting/codegeneration/codegen.h | 3 ++- src/scripting/decorate/thingdef_parse.cpp | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 1d16b182f..d090fa6e4 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -1613,7 +1613,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) } else if (ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { - FxExpression *x = new FxClassTypeCast(static_cast(ValueType), basex); + FxExpression *x = new FxClassTypeCast(static_cast(ValueType), basex, Explicit); x = x->Resolve(ctx); basex = nullptr; delete this; @@ -4412,7 +4412,7 @@ FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx) if (left->ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer))) { - left = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), left); + left = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), left, false); ClassCheck = true; } else @@ -4420,7 +4420,7 @@ FxExpression *FxTypeCheck::Resolve(FCompileContext& ctx) left = new FxTypeCast(left, NewPointer(RUNTIME_CLASS(DObject)), false); ClassCheck = false; } - right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), right); + right = new FxClassTypeCast(NewClassPointer(RUNTIME_CLASS(DObject)), right, false); RESOLVE(left, ctx); RESOLVE(right, ctx); @@ -9922,12 +9922,13 @@ VMFunction *FxReturnStatement::GetDirectFunction() // //========================================================================== -FxClassTypeCast::FxClassTypeCast(PClassPointer *dtype, FxExpression *x) +FxClassTypeCast::FxClassTypeCast(PClassPointer *dtype, FxExpression *x, bool explicitily) : FxExpression(EFX_ClassTypeCast, x->ScriptPosition) { ValueType = dtype; desttype = dtype->ClassRestriction; basex=x; + Explicit = explicitily; } //========================================================================== @@ -9991,7 +9992,9 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) if (clsname != NAME_None) { - cls = FindClassType(clsname, ctx); + if (Explicit) cls = FindClassType(clsname, ctx); + else cls = PClass::FindClass(clsname); + if (cls == nullptr) { /* lax */ @@ -10141,7 +10144,7 @@ FxExpression *FxClassPtrCast::Resolve(FCompileContext &ctx) } else if (basex->ValueType == TypeString || basex->ValueType == TypeName) { - FxExpression *x = new FxClassTypeCast(to, basex); + FxExpression *x = new FxClassTypeCast(to, basex, true); basex = nullptr; delete this; return x->Resolve(ctx); diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 7c18589c5..42d5b0ccf 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -1913,10 +1913,11 @@ class FxClassTypeCast : public FxExpression { PClass *desttype; FxExpression *basex; + bool Explicit; public: - FxClassTypeCast(PClassPointer *dtype, FxExpression *x); + FxClassTypeCast(PClassPointer *dtype, FxExpression *x, bool explicitly); ~FxClassTypeCast(); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 81ab765f5..9b78ef3d5 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -197,7 +197,7 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type) sc.SetEscape(true); sc.MustGetString(); sc.SetEscape(false); - x = new FxClassTypeCast(static_cast(type), new FxConstant(FName(sc.String), sc)); + x = new FxClassTypeCast(static_cast(type), new FxConstant(FName(sc.String), sc), false); } else { From e3c36998b6ce182f9dfe513c19891e3d54db81e4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 24 Jan 2017 11:57:42 +0100 Subject: [PATCH 2/3] - delete all compile-time symbols for scripting after finishing compiling data. Even the bare-bones gzdoom.pk3 gets rid of over 2000 symbols this way that otherwise would need to be tracked by the garbage collector. --- src/d_main.cpp | 3 +++ src/dobjtype.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ src/dobjtype.h | 6 ++++++ 3 files changed, 51 insertions(+) diff --git a/src/d_main.cpp b/src/d_main.cpp index 971e0ece0..6041af9b4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2517,6 +2517,9 @@ void D_DoomMain (void) // Create replacements for dehacked pickups FinishDehPatch(); + + // clean up the compiler symbols which are not needed any longer. + RemoveUnusedSymbols(); InitActorNumsFromMapinfo(); InitSpawnablesFromMapinfo(); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index dbd3e6d6f..76330867e 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -3898,3 +3898,45 @@ void FNamespaceManager::ReleaseSymbols() GlobalNamespace = nullptr; AllNamespaces.Clear(); } + +// removes all symbols from the symbol tables. +// After running the compiler these are not needed anymore. +// Only the namespaces themselves are kept because the type table references them. +int FNamespaceManager::RemoveSymbols() +{ + int count = 0; + for (auto ns : AllNamespaces) + { + count += ns->Symbols.Symbols.CountUsed(); + ns->Symbols.ReleaseSymbols(); + } + return count; +} + +void RemoveUnusedSymbols() +{ + // Global symbols are not needed anymore after running the compiler. + int count = Namespaces.RemoveSymbols(); + + // We do not need any non-field and non-function symbols in structs and classes anymore. + for (size_t i = 0; i < countof(TypeTable.TypeHash); ++i) + { + for (PType *ty = TypeTable.TypeHash[i]; ty != NULL; ty = ty->HashNext) + { + if (ty->IsKindOf(RUNTIME_CLASS(PStruct))) + { + auto it = ty->Symbols.GetIterator(); + PSymbolTable::MapType::Pair *pair; + while (it.NextPair(pair)) + { + if (!pair->Value->IsKindOf(RUNTIME_CLASS(PField)) && !pair->Value->IsKindOf(RUNTIME_CLASS(PFunction))) + { + ty->Symbols.RemoveSymbol(pair->Value); + count++; + } + } + } + } + } + DPrintf(DMSG_SPAMMY, "%d symbols removed after compilation\n", count); +} diff --git a/src/dobjtype.h b/src/dobjtype.h index 61747134c..632ef96bb 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -76,6 +76,7 @@ class VMFrameStack; struct VMValue; struct VMReturn; class VMFunction; +struct FNamespaceManager; // A VM function ------------------------------------------------------------ @@ -157,6 +158,7 @@ private: MapType Symbols; friend class DObject; + friend struct FNamespaceManager; }; // A symbol for a compiler tree node ---------------------------------------- @@ -1010,6 +1012,7 @@ struct FNamespaceManager PNamespace *NewNamespace(int filenum); size_t MarkSymbols(); void ReleaseSymbols(); + int RemoveSymbols(); }; extern FNamespaceManager Namespaces; @@ -1047,4 +1050,7 @@ inline T *&DObject::PointerVar(FName field) { return *(T**)ScriptVar(field, nullptr); // pointer check is more tricky and for the handful of uses in the DECORATE parser not worth the hassle. } + +void RemoveUnusedSymbols(); + #endif From 02f678dccc8b6a635b07f9a96b20aed7b3b5b9ce Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 24 Jan 2017 11:59:59 +0100 Subject: [PATCH 3/3] - there seem to be ACS compilers which let 'delay' pass inside a function. Since this is an unsupported feature which brings the ACS VM into an unstable state it has to be handled with a hard abort to avoid crashes. --- src/p_acs.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 50f50ff31..24142e0ec 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -9716,7 +9716,16 @@ scriptwait: if (runaway != 0 && InModuleScriptNumber >= 0) { - activeBehavior->GetScriptPtr(InModuleScriptNumber)->ProfileData.AddRun(runaway); + auto scriptptr = activeBehavior->GetScriptPtr(InModuleScriptNumber); + if (scriptptr != nullptr) + { + scriptptr->ProfileData.AddRun(runaway); + } + else + { + // It is pointless to continue execution. The script is broken and needs to be aborted. + I_Error("Bad script definition encountered. Script %d is reported running but not present.\nThe most likely cause for this message is using 'delay' inside a function which is not supported.\nPlease check the ACS compiler used for compiling the script!", InModuleScriptNumber); + } } if (state == SCRIPT_DivideBy0)