From d7af9698f9322c29063ed70f8723531955910dc6 Mon Sep 17 00:00:00 2001 From: Joseph Humfrey Date: Wed, 21 Nov 2018 12:14:38 +0000 Subject: [PATCH] Fix for ResetCallstack() not resetting base callstack element. --- ink-engine-runtime/CallStack.cs | 23 +++++++++++++++++------ ink-engine-runtime/StoryState.cs | 8 ++------ tests/Tests.cs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/ink-engine-runtime/CallStack.cs b/ink-engine-runtime/CallStack.cs index 15d44300..4e396e07 100644 --- a/ink-engine-runtime/CallStack.cs +++ b/ink-engine-runtime/CallStack.cs @@ -176,22 +176,31 @@ public bool canPop { } } - public CallStack (Container rootContentContainer) + public CallStack (Story storyContext) { - _threads = new List (); - _threads.Add (new Thread ()); - - _threads [0].callstack.Add (new Element (PushPopType.Tunnel, Pointer.StartOf(rootContentContainer))); + _startOfRoot = Pointer.StartOf(storyContext.rootContentContainer); + Reset(); } + public CallStack(CallStack toCopy) { _threads = new List (); foreach (var otherThread in toCopy._threads) { _threads.Add (otherThread.Copy ()); } + _startOfRoot = toCopy._startOfRoot; } - + + public void Reset() + { + _threads = new List(); + _threads.Add(new Thread()); + + _threads[0].callstack.Add(new Element(PushPopType.Tunnel, _startOfRoot)); + } + + // Unfortunately it's not possible to implement jsonToken since // the setter needs to take a Story as a context in order to // look up objects from paths for currentContainer within elements. @@ -208,6 +217,7 @@ public void SetJsonToken(Dictionary jObject, Story storyContext) } _threadCounter = (int)jObject ["threadCounter"]; + _startOfRoot = Pointer.StartOf(storyContext.rootContentContainer); } // See above for why we can't implement jsonToken @@ -399,6 +409,7 @@ internal string callStackTrace { List _threads; int _threadCounter; + Pointer _startOfRoot; } } diff --git a/ink-engine-runtime/StoryState.cs b/ink-engine-runtime/StoryState.cs index a3e8ea72..1ed41925 100755 --- a/ink-engine-runtime/StoryState.cs +++ b/ink-engine-runtime/StoryState.cs @@ -247,7 +247,7 @@ internal StoryState (Story story) evaluationStack = new List (); - callStack = new CallStack (story.rootContentContainer); + callStack = new CallStack (story); variablesState = new VariablesState (callStack, story.listDefinitions); visitCounts = new Dictionary (); @@ -809,11 +809,7 @@ internal Runtime.Object PeekEvaluationStack() /// public void ForceEnd() { - while (callStack.canPopThread) - callStack.PopThread (); - - while (callStack.canPop) - PopCallstack (); + callStack.Reset(); _currentChoices.Clear(); diff --git a/tests/Tests.cs b/tests/Tests.cs index 7b5fd80f..d4a1dd62 100644 --- a/tests/Tests.cs +++ b/tests/Tests.cs @@ -3658,6 +3658,37 @@ public void TestFallbackChoiceOnThread() Assert.AreEqual("Should be 1 not 0: 1.\n", story.Continue()); } + // Test for bug where after a call to ChoosePathString, + // the callstack is not fully/cleanly reset, e.g. leaving + // "inExpressionEvaluation" variable left to true, as set during + // the call to {RunAThing()}. + // This was when we unwound the callstack, but we didn't reset + // the base element. + [Test()] + public void TestCleanCallstackResetOnPathChoice() + { + var storyStr = + @" +{RunAThing()} + +== function RunAThing == +The first line. +The second line. + +== SomewhereElse == +{""somewhere else""} +->END +"; + + var story = CompileString(storyStr); + + Assert.AreEqual("The first line.\n", story.Continue()); + + story.ChoosePathString("SomewhereElse"); + + Assert.AreEqual("somewhere else\n", story.ContinueMaximally()); + } + // Helper compile function protected Story CompileString(string str, bool countAllVisits = false, bool testingErrors = false) {