diff --git a/src/C4Globals.cpp b/src/C4Globals.cpp index 9ea087f8d..cf744e8c8 100644 --- a/src/C4Globals.cpp +++ b/src/C4Globals.cpp @@ -33,6 +33,7 @@ C4Set C4PropList::PropLists; #endif C4Set C4PropListNumbered::PropLists; +C4Set C4PropListScript::PropLists; std::vector C4PropListNumbered::ShelvedPropLists; int32_t C4PropListNumbered::EnumerationIndex = 0; C4StringTable Strings; diff --git a/src/game/C4Game.cpp b/src/game/C4Game.cpp index ca7dc28d4..0ba73b559 100644 --- a/src/game/C4Game.cpp +++ b/src/game/C4Game.cpp @@ -603,6 +603,9 @@ void C4Game::Clear() C4PropListNumbered::ClearShelve(); // may be nonempty if there was a fatal error during section load ScriptEngine.Clear(); + // delete any remaining prop lists from circular chains + C4PropListNumbered::ClearNumberedPropLists(); + C4PropListScript::ClearScriptPropLists(); MainSysLangStringTable.Clear(); ScenarioLangStringTable.Clear(); CloseScenario(); diff --git a/src/mape/cpp-handles/stub-handle.cpp b/src/mape/cpp-handles/stub-handle.cpp index 33330e5aa..8ab569038 100644 --- a/src/mape/cpp-handles/stub-handle.cpp +++ b/src/mape/cpp-handles/stub-handle.cpp @@ -38,6 +38,7 @@ C4Set C4PropList::PropLists; #endif C4Set C4PropListNumbered::PropLists; +C4Set C4PropListScript::PropLists; std::vector C4PropListNumbered::ShelvedPropLists; int32_t C4PropListNumbered::EnumerationIndex = 0; C4StringTable Strings; diff --git a/src/script/C4PropList.cpp b/src/script/C4PropList.cpp index 751e5f051..4aa1ddbea 100644 --- a/src/script/C4PropList.cpp +++ b/src/script/C4PropList.cpp @@ -134,6 +134,20 @@ void C4PropListNumbered::ClearShelve() ShelvedPropLists.clear(); } +void C4PropListNumbered::ClearNumberedPropLists() +{ + // empty all proplists to ensure safe deletion of proplists with circular references + // note that this the call to Clear() might delete some prop lists. So it is assumed that + // PropLists does not shrink its table as the number of values goes down + C4PropListNumbered *const* p_next = PropLists.First(), *const* p; + while ((p = p_next)) + { + p_next = PropLists.Next(p); + // check *p since it might have been deleted by clearing the previous prop list + if (*p) (*p)->Clear(); + } +} + C4PropListNumbered::C4PropListNumbered(C4PropList * prototype): C4PropList(prototype), Number(-1) { } @@ -190,6 +204,28 @@ C4PropListNumbered::~C4PropListNumbered() Log("removing numbered proplist without number"); } +void C4PropListScript::ClearScriptPropLists() +{ + // empty all proplists to ensure safe deletion of proplists with circular references + // note that this the call to Clear() might delete some prop lists. So it is assumed that + // PropLists does not shrink its table as the number of values goes down + // However, some values may be skipped due to table consolidation. Just fix it by iterating over the table until it's empty. + C4PropListScript *const* p_next, *const* p; + while ((p_next = PropLists.First())) + { + while ((p = p_next)) + { + p_next = PropLists.Next(p); + // check *p since it might have been deleted by clearing the previous prop list + if (*p) + { + C4Value ref(C4VPropList(*p)); // keep a reference because prop list might delete itself within clearing method otherwise + (*p)->Clear(); + } + } + } +} + void C4PropListStatic::RefCompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers) const { assert(!pComp->isCompiler()); @@ -609,7 +645,7 @@ C4PropertyName C4PropList::GetPropertyP(C4PropertyName n) const return P_LAST; } -int32_t C4PropList::GetPropertyInt(C4PropertyName n) const +int32_t C4PropList::GetPropertyInt(C4PropertyName n, int32_t default_val) const { C4String * k = &Strings.P[n]; if (Properties.Has(k)) @@ -618,9 +654,9 @@ int32_t C4PropList::GetPropertyInt(C4PropertyName n) const } if (GetPrototype()) { - return GetPrototype()->GetPropertyInt(n); + return GetPrototype()->GetPropertyInt(n, default_val); } - return 0; + return default_val; } C4PropList *C4PropList::GetPropertyPropList(C4PropertyName n) const @@ -800,3 +836,11 @@ unsigned int C4Set::Hash(C4PropList * const & e) { return C4Set::Hash(static_cast(reinterpret_cast(e))); } + +template<> template<> +unsigned int C4Set::Hash(C4PropListScript * const & e) +{ + // since script prop lists are only put in the set for reference keeping, just hash by pointer + // but use only some of the more significant bits because + return reinterpret_cast(e) / 63; +} diff --git a/src/script/C4PropList.h b/src/script/C4PropList.h index 5bf519695..29a910ae6 100644 --- a/src/script/C4PropList.h +++ b/src/script/C4PropList.h @@ -113,7 +113,7 @@ public: C4Value Call(C4String * k, C4AulParSet *pPars=0, bool fPassErrors=false); C4Value Call(const char * k, C4AulParSet *pPars=0, bool fPassErrors=false); C4PropertyName GetPropertyP(C4PropertyName k) const; - int32_t GetPropertyInt(C4PropertyName k) const; + int32_t GetPropertyInt(C4PropertyName k, int32_t default_val = 0) const; C4PropList *GetPropertyPropList(C4PropertyName k) const; bool HasProperty(C4String * k) const { return Properties.Has(k); } // not allowed on frozen proplists @@ -216,6 +216,7 @@ public: static void ShelveNumberedPropLists(); // unnumber all proplists and put them on the shelve. To be used on remaining objects before a savegame load. static void UnshelveNumberedPropLists(); // re-insert shelved proplists into main list static void ClearShelve(); + static void ClearNumberedPropLists(); // empty all properties in numbered prop lists. Used on game clear to ensure prop lists with circular references get cleared. protected: C4PropListNumbered(C4PropList * prototype = 0); void AcquireNumber(); // acquire a number and add to internal list @@ -232,8 +233,14 @@ protected: class C4PropListScript: public C4PropList { public: - C4PropListScript(C4PropList * prototype = 0): C4PropList(prototype) { } + C4PropListScript(C4PropList * prototype = 0) : C4PropList(prototype) { PropLists.Add(this); } + virtual ~C4PropListScript() { PropLists.Remove(this); } bool Delete() { return true; } + + static void ClearScriptPropLists(); // empty all properties in script-created prop lists. Used on game clear to ensure prop lists with circular references get cleared. + +protected: + static C4Set PropLists; }; // PropLists declared in the game data diff --git a/src/script/C4ScriptStandalone.cpp b/src/script/C4ScriptStandalone.cpp index ed7b6ca9b..6567c333a 100644 --- a/src/script/C4ScriptStandalone.cpp +++ b/src/script/C4ScriptStandalone.cpp @@ -34,6 +34,7 @@ C4Set C4PropList::PropLists; #endif C4Set C4PropListNumbered::PropLists; +C4Set C4PropListScript::PropLists; std::vector C4PropListNumbered::ShelvedPropLists; int32_t C4PropListNumbered::EnumerationIndex = 0; C4StringTable Strings;