diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js
index 3eec20b64..56d3f3e7c 100644
--- a/src/blocks/scratch3_procedures.js
+++ b/src/blocks/scratch3_procedures.js
@@ -13,43 +13,46 @@ class Scratch3ProcedureBlocks {
      */
     getPrimitives () {
         return {
-            // procedures_definition is the top block of a procedure but has no
-            // effect of its own.
-            procedures_definition: null,
-
+            procedures_definition: this.definition,
             procedures_call: this.call,
             argument_reporter_string_number: this.argumentReporterStringNumber,
             argument_reporter_boolean: this.argumentReporterBoolean
         };
     }
 
+    definition () {
+        // No-op: execute the blocks.
+    }
+
     call (args, util) {
-        const procedureCode = args.mutation.proccode;
-        const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
+        if (!util.stackFrame.executed) {
+            const procedureCode = args.mutation.proccode;
+            const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
 
-        // If null, procedure could not be found, which can happen if custom
-        // block is dragged between sprites without the definition.
-        // Match Scratch 2.0 behavior and noop.
-        if (paramNamesIdsAndDefaults === null) {
-            return;
-        }
-
-        const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
-
-        util.startProcedure(procedureCode);
-
-        // Initialize params for the current stackFrame to {}, even if the procedure does
-        // not take any arguments. This is so that `getParam` down the line does not look
-        // at earlier stack frames for the values of a given parameter (#1729)
-        util.initParams();
-        for (let i = 0; i < paramIds.length; i++) {
-            if (args.hasOwnProperty(paramIds[i])) {
-                util.pushParam(paramNames[i], args[paramIds[i]]);
-            } else {
-                util.pushParam(paramNames[i], paramDefaults[i]);
+            // If null, procedure could not be found, which can happen if custom
+            // block is dragged between sprites without the definition.
+            // Match Scratch 2.0 behavior and noop.
+            if (paramNamesIdsAndDefaults === null) {
+                return;
             }
-        }
 
+            const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
+
+            // Initialize params for the current stackFrame to {}, even if the procedure does
+            // not take any arguments. This is so that `getParam` down the line does not look
+            // at earlier stack frames for the values of a given parameter (#1729)
+            util.initParams();
+            for (let i = 0; i < paramIds.length; i++) {
+                if (args.hasOwnProperty(paramIds[i])) {
+                    util.pushParam(paramNames[i], args[paramIds[i]]);
+                } else {
+                    util.pushParam(paramNames[i], paramDefaults[i]);
+                }
+            }
+
+            util.stackFrame.executed = true;
+            util.startProcedure(procedureCode);
+        }
     }
 
     argumentReporterStringNumber (args, util) {
diff --git a/src/engine/block-utility.js b/src/engine/block-utility.js
index d7f9ef17b..cfe18a227 100644
--- a/src/engine/block-utility.js
+++ b/src/engine/block-utility.js
@@ -60,7 +60,11 @@ class BlockUtility {
      * @type {object}
      */
     get stackFrame () {
-        return this.thread.getExecutionContext();
+        const frame = this.thread.peekStackFrame();
+        if (frame.executionContext === null) {
+            frame.executionContext = {};
+        }
+        return frame.executionContext;
     }
 
     /**
diff --git a/src/engine/execute.js b/src/engine/execute.js
index 4825ad6ab..c8638d01a 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -279,7 +279,7 @@ class BlockCached {
         // Assign opcode isHat and blockFunction data to avoid dynamic lookups.
         this._isHat = runtime.getIsHat(opcode);
         this._blockFunction = runtime.getOpcodeFunction(opcode);
-        this._definedBlockFunction = typeof this._blockFunction === 'function';
+        this._definedBlockFunction = typeof this._blockFunction !== 'undefined';
 
         // Store the current shadow value if there is a shadow value.
         const fieldKeys = Object.keys(fields);
@@ -385,6 +385,7 @@ const execute = function (sequencer, thread) {
 
     // Current block to execute is the one on the top of the stack.
     const currentBlockId = thread.peekStack();
+    const currentStackFrame = thread.peekStackFrame();
 
     let blockContainer = thread.blockContainer;
     let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);
@@ -403,8 +404,8 @@ const execute = function (sequencer, thread) {
     const length = ops.length;
     let i = 0;
 
-    if (thread.reported !== null) {
-        const reported = thread.reported;
+    if (currentStackFrame.reported !== null) {
+        const reported = currentStackFrame.reported;
         // Reinstate all the previous values.
         for (; i < reported.length; i++) {
             const {opCached: oldOpCached, inputValue} = reported[i];
@@ -440,7 +441,7 @@ const execute = function (sequencer, thread) {
         }
 
         // The reporting block must exist and must be the next one in the sequence of operations.
-        if (thread.justReported !== null && ops[i] && ops[i].id === thread.reportingBlockId) {
+        if (thread.justReported !== null && ops[i] && ops[i].id === currentStackFrame.reporting) {
             const opCached = ops[i];
             const inputValue = thread.justReported;
 
@@ -461,8 +462,8 @@ const execute = function (sequencer, thread) {
             i += 1;
         }
 
-        thread.reportingBlockId = null;
-        thread.reported = null;
+        currentStackFrame.reporting = null;
+        currentStackFrame.reported = null;
     }
 
     for (; i < length; i++) {
@@ -517,8 +518,8 @@ const execute = function (sequencer, thread) {
             // operation if it is promise waiting will set its parent value at
             // that time.
             thread.justReported = null;
-            thread.reportingBlockId = ops[i].id;
-            thread.reported = ops.slice(0, i).map(reportedCached => {
+            currentStackFrame.reporting = ops[i].id;
+            currentStackFrame.reported = ops.slice(0, i).map(reportedCached => {
                 const inputName = reportedCached._parentKey;
                 const reportedValues = reportedCached._parentValues;
 
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 57961940e..a3976e0e3 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -724,7 +724,7 @@ class Runtime extends EventEmitter {
                 if (packageObject.getPrimitives) {
                     const packagePrimitives = packageObject.getPrimitives();
                     for (const op in packagePrimitives) {
-                        if (typeof packagePrimitives[op] === 'function') {
+                        if (packagePrimitives.hasOwnProperty(op)) {
                             this._primitives[op] =
                                 packagePrimitives[op].bind(packageObject);
                         }
@@ -1567,7 +1567,7 @@ class Runtime extends EventEmitter {
     isActiveThread (thread) {
         return (
             (
-                thread.stackFrame !== null &&
+                thread.stack.length > 0 &&
                 thread.status !== Thread.STATUS_DONE) &&
             this.threads.indexOf(thread) > -1);
     }
@@ -1744,20 +1744,11 @@ class Runtime extends EventEmitter {
             // Start the thread with this top block.
             newThreads.push(this._pushThread(topBlockId, target));
         }, optTarget);
-        // For compatibility with Scratch 2, edge triggered hats need to be
-        // processed before threads are stepped. See ScratchRuntime.as for
-        // original implementation.
-        //
-        // TODO: Move the execute call to sequencer. Maybe in a method call
-        // stepHat or stepOne.
+        // For compatibility with Scratch 2, edge triggered hats need to be processed before
+        // threads are stepped. See ScratchRuntime.as for original implementation
         newThreads.forEach(thread => {
             execute(this.sequencer, thread);
-            if (thread.status !== Thread.STATUS_DONE) {
-                thread.goToNextBlock();
-                if (thread.stackFrame === null) {
-                    this.sequencer.retireThread(thread);
-                }
-            }
+            thread.goToNextBlock();
         });
         return newThreads;
     }
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index 00e67302f..54d4870a5 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -103,7 +103,7 @@ class Sequencer {
             for (let i = 0; i < threads.length; i++) {
                 const activeThread = this.activeThread = threads[i];
                 // Check if the thread is done so it is not executed.
-                if (activeThread.stackFrame === null ||
+                if (activeThread.stack.length === 0 ||
                     activeThread.status === Thread.STATUS_DONE) {
                     // Finished with this thread.
                     stoppedThread = true;
@@ -137,7 +137,7 @@ class Sequencer {
                 }
                 // Check if the thread completed while it just stepped to make
                 // sure we remove it before the next iteration of all threads.
-                if (activeThread.stackFrame === null ||
+                if (activeThread.stack.length === 0 ||
                     activeThread.status === Thread.STATUS_DONE) {
                     // Finished with this thread.
                     stoppedThread = true;
@@ -156,7 +156,7 @@ class Sequencer {
                 let nextActiveThread = 0;
                 for (let i = 0; i < this.runtime.threads.length; i++) {
                     const thread = this.runtime.threads[i];
-                    if (thread.stackFrame !== null &&
+                    if (thread.stack.length !== 0 &&
                         thread.status !== Thread.STATUS_DONE) {
                         this.runtime.threads[nextActiveThread] = thread;
                         nextActiveThread++;
@@ -184,7 +184,7 @@ class Sequencer {
             thread.popStack();
 
             // Did the null follow a hat block?
-            if (thread.peekStackFrame() === null) {
+            if (thread.stack.length === 0) {
                 thread.status = Thread.STATUS_DONE;
                 return;
             }
@@ -248,7 +248,7 @@ class Sequencer {
             while (!thread.peekStack()) {
                 thread.popStack();
 
-                if (thread.stackFrame === null) {
+                if (thread.stack.length === 0) {
                     // No more stack to run!
                     thread.status = Thread.STATUS_DONE;
                     return;
@@ -299,11 +299,7 @@ class Sequencer {
             currentBlockId,
             branchNum
         );
-        if (isLoop) {
-            const stackFrame = thread.peekStackFrame();
-            stackFrame.needsReset = true;
-            stackFrame.isLoop = true;
-        }
+        thread.peekStackFrame().isLoop = isLoop;
         if (branchId) {
             // Push branch ID to the thread's stack.
             thread.pushStack(branchId);
@@ -365,9 +361,7 @@ class Sequencer {
      */
     retireThread (thread) {
         thread.stack = [];
-        thread.pointer = null;
-        thread.stackFrames = [];
-        thread.stackFrame = null;
+        thread.stackFrame = [];
         thread.requestScriptGlowInFrame = false;
         thread.status = Thread.STATUS_DONE;
     }
diff --git a/src/engine/thread.js b/src/engine/thread.js
index 3c275d192..e381dd9bf 100644
--- a/src/engine/thread.js
+++ b/src/engine/thread.js
@@ -5,23 +5,14 @@
 const _stackFrameFreeList = [];
 
 /**
- * Default params object for stack frames outside of a procedure.
- *
- * StackFrame.params uses a null prototype object. It does not have Object
- * methods like hasOwnProperty. With a null prototype
- * `typeof params[key] !== 'undefined'` has similar behaviour to hasOwnProperty.
- * @type {object}
- */
-const defaultParams = Object.create(null);
-
-/**
- * A frame used for each level of the stack. A general purpose place to store a
- * bunch of execution contexts and parameters.
+ * A frame used for each level of the stack. A general purpose
+ * place to store a bunch of execution context and parameters
+ * @param {boolean} warpMode Whether this level of the stack is warping
  * @constructor
  * @private
  */
 class _StackFrame {
-    constructor () {
+    constructor (warpMode) {
         /**
          * Whether this level of the stack is a loop.
          * @type {boolean}
@@ -29,62 +20,90 @@ class _StackFrame {
         this.isLoop = false;
 
         /**
-         * Whether this level is in warp mode. Set to true by the sequencer for
-         * some procedures.
-         *
-         * After being set to true at the beginning of a procedure a thread
-         * will be in warpMode until it pops a stack frame to reveal one that
-         * is not in warpMode. Either this value is always false for a stack
-         * frame or always true after a procedure sets it.
+         * Whether this level is in warp mode.  Is set by some legacy blocks and
+         * "turbo mode"
          * @type {boolean}
          */
-        this.warpMode = false;
+        this.warpMode = warpMode;
+
+        /**
+         * Reported value from just executed block.
+         * @type {Any}
+         */
+        this.justReported = null;
+
+        /**
+         * The active block that is waiting on a promise.
+         * @type {string}
+         */
+        this.reporting = '';
+
+        /**
+         * Persists reported inputs during async block.
+         * @type {Object}
+         */
+        this.reported = null;
+
+        /**
+         * Name of waiting reporter.
+         * @type {string}
+         */
+        this.waitingReporter = null;
 
         /**
          * Procedure parameters.
-         *
-         * After being set by a procedure these values do not change and they
-         * will be copied to deeper stack frames.
          * @type {Object}
          */
-        this.params = defaultParams;
+        this.params = null;
 
         /**
          * A context passed to block implementations.
          * @type {Object}
          */
-        this.executionContext = {};
-
-        /**
-         * Has this frame changed and need a reset?
-         * @type {boolean}
-         */
-        this.needsReset = false;
+        this.executionContext = null;
     }
 
     /**
-     * Reset some properties of the frame to default values. Used to recycle.
+     * Reset all properties of the frame to pristine null and false states.
+     * Used to recycle.
      * @return {_StackFrame} this
      */
     reset () {
+
         this.isLoop = false;
-        this.executionContext = {};
-        this.needsReset = false;
+        this.warpMode = false;
+        this.justReported = null;
+        this.reported = null;
+        this.waitingReporter = null;
+        this.params = null;
+        this.executionContext = null;
 
         return this;
     }
 
     /**
-     * Create or recycle a stack frame object.
-     * @param {_StackFrame} parent Parent frame to copy "immutable" values.
-     * @returns {_StackFrame} The clean stack frame with correct warpMode
-     *   setting.
+     * Reuse an active stack frame in the stack.
+     * @param {?boolean} warpMode defaults to current warpMode
+     * @returns {_StackFrame} this
      */
-    static create (parent) {
-        const stackFrame = _stackFrameFreeList.pop() || new _StackFrame();
-        stackFrame.warpMode = parent.warpMode;
-        stackFrame.params = parent.params;
-        return stackFrame;
+    reuse (warpMode = this.warpMode) {
+        this.reset();
+        this.warpMode = Boolean(warpMode);
+        return this;
+    }
+
+    /**
+     * Create or recycle a stack frame object.
+     * @param {boolean} warpMode Enable warpMode on this frame.
+     * @returns {_StackFrame} The clean stack frame with correct warpMode setting.
+     */
+    static create (warpMode) {
+        const stackFrame = _stackFrameFreeList.pop();
+        if (typeof stackFrame !== 'undefined') {
+            stackFrame.warpMode = Boolean(warpMode);
+            return stackFrame;
+        }
+        return new _StackFrame(warpMode);
     }
 
     /**
@@ -92,22 +111,12 @@ class _StackFrame {
      * @param {_StackFrame} stackFrame The frame to reset and recycle.
      */
     static release (stackFrame) {
-        if (stackFrame !== null) {
-            _stackFrameFreeList.push(
-                stackFrame.needsReset ? stackFrame.reset() : stackFrame
-            );
+        if (typeof stackFrame !== 'undefined') {
+            _stackFrameFreeList.push(stackFrame.reset());
         }
     }
 }
 
-/**
- * The initial stack frame for all threads. A call to pushStack will create the
- * first to be used frame for a thread. That first frame will use the initial
- * values from initialStackFrame.
- * @type {_StackFrame}
- */
-const initialStackFrame = new _StackFrame();
-
 /**
  * A thread is a running stack context and all the metadata needed.
  * @param {?string} firstBlock First block to execute in the thread.
@@ -128,25 +137,12 @@ class Thread {
          */
         this.stack = [];
 
-        /**
-         * The "instruction" pointer the thread is currently at. This
-         * determines what block is executed.
-         * @type {string}
-         */
-        this.pointer = null;
-
         /**
          * Stack frames for the thread. Store metadata for the executing blocks.
          * @type {Array.<_StackFrame>}
          */
         this.stackFrames = [];
 
-        /**
-         * The current stack frame that goes along with the pointer.
-         * @type {_StackFrame}
-         */
-        this.stackFrame = null;
-
         /**
          * Status of the thread, one of three states (below)
          * @type {number}
@@ -190,25 +186,7 @@ class Thread {
          */
         this.warpTimer = null;
 
-        /**
-         * The value just reported by a promise.
-         * @type {*}
-         */
         this.justReported = null;
-
-        /**
-         * The id of the block that we will report the promise resolved value
-         * for.
-         * @type {string}
-         */
-        this.reportingBlockId = null;
-
-        /**
-         * The already reported values in a sequence of blocks to restore when
-         * the awaited promise resolves.
-         * @type {Array.<*>}
-         */
-        this.reported = null;
     }
 
     /**
@@ -261,16 +239,12 @@ class Thread {
      * @param {string} blockId Block ID to push to stack.
      */
     pushStack (blockId) {
-        if (this.stackFrame === null) {
-            this.pointer = blockId;
-            this.stackFrame = _StackFrame.create(initialStackFrame);
-        } else {
-            this.stack.push(this.pointer);
-            this.pointer = blockId;
-
-            const parent = this.stackFrame;
-            this.stackFrames.push(parent);
-            this.stackFrame = _StackFrame.create(parent);
+        this.stack.push(blockId);
+        // Push an empty stack frame, if we need one.
+        // Might not, if we just popped the stack.
+        if (this.stack.length > this.stackFrames.length) {
+            const parent = this.stackFrames[this.stackFrames.length - 1];
+            this.stackFrames.push(_StackFrame.create(typeof parent !== 'undefined' && parent.warpMode));
         }
     }
 
@@ -280,47 +254,34 @@ class Thread {
      * @param {string} blockId Block ID to push to stack.
      */
     reuseStackForNextBlock (blockId) {
-        this.pointer = blockId;
-        if (this.stackFrame.needsReset) this.stackFrame.reset();
+        this.stack[this.stack.length - 1] = blockId;
+        this.stackFrames[this.stackFrames.length - 1].reuse();
     }
 
     /**
-     * Move the instruction pointer to the last value before this stack of
-     * blocks was pushed and executed.
-     * @return {?string} Block ID popped from the stack.
+     * Pop last block on the stack and its stack frame.
+     * @return {string} Block ID popped from the stack.
      */
     popStack () {
-        const lastPointer = this.pointer;
-        this.pointer = this.stack.pop() || null;
-        _StackFrame.release(this.stackFrame);
-        this.stackFrame = this.stackFrames.pop() || null;
-        return lastPointer;
+        _StackFrame.release(this.stackFrames.pop());
+        return this.stack.pop();
     }
 
     /**
-     * Move the instruction pointer to the last procedure call block and resume
-     * execution there or to the end of this thread and stop executing this
-     * thread.
+     * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied
      */
     stopThisScript () {
         let blockID = this.peekStack();
         while (blockID !== null) {
             const block = this.target.blocks.getBlock(blockID);
             if (typeof block !== 'undefined' && block.opcode === 'procedures_call') {
-                // If we do not push this null, Sequencer will not step to the
-                // next block and will erroneously call the procedure a second
-                // time.
-                //
-                // By pushing null, Sequencer will treat it as the end of a
-                // substack, pop the stack and step to the next block.
-                this.pushStack(null);
                 break;
             }
             this.popStack();
             blockID = this.peekStack();
         }
 
-        if (this.stackFrame === null) {
+        if (this.stack.length === 0) {
             // Clean up!
             this.requestScriptGlowInFrame = false;
             this.status = Thread.STATUS_DONE;
@@ -332,7 +293,7 @@ class Thread {
      * @return {?string} Block ID on top of stack.
      */
     peekStack () {
-        return this.pointer;
+        return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;
     }
 
 
@@ -341,7 +302,7 @@ class Thread {
      * @return {?object} Last stack frame stored on this thread.
      */
     peekStackFrame () {
-        return this.stackFrame;
+        return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null;
     }
 
     /**
@@ -349,7 +310,7 @@ class Thread {
      * @return {?object} Second to last stack frame stored on this thread.
      */
     peekParentStackFrame () {
-        return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null;
+        return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null;
     }
 
     /**
@@ -360,22 +321,14 @@ class Thread {
         this.justReported = typeof value === 'undefined' ? null : value;
     }
 
-    /**
-     * Return an execution context for a block to use.
-     * @returns {object} the execution context
-     */
-    getExecutionContext () {
-        const frame = this.stackFrame;
-        frame.needsReset = true;
-        return frame.executionContext;
-    }
-
     /**
      * Initialize procedure parameters on this stack frame.
      */
     initParams () {
-        const stackFrame = this.stackFrame;
-        stackFrame.params = Object.create(null);
+        const stackFrame = this.peekStackFrame();
+        if (stackFrame.params === null) {
+            stackFrame.params = {};
+        }
     }
 
     /**
@@ -385,7 +338,7 @@ class Thread {
      * @param {*} value Value to set for parameter.
      */
     pushParam (paramName, value) {
-        const stackFrame = this.stackFrame;
+        const stackFrame = this.peekStackFrame();
         stackFrame.params[paramName] = value;
     }
 
@@ -395,9 +348,15 @@ class Thread {
      * @return {*} value Value for parameter.
      */
     getParam (paramName) {
-        const stackFrame = this.stackFrame;
-        if (typeof stackFrame.params[paramName] !== 'undefined') {
-            return stackFrame.params[paramName];
+        for (let i = this.stackFrames.length - 1; i >= 0; i--) {
+            const frame = this.stackFrames[i];
+            if (frame.params === null) {
+                continue;
+            }
+            if (frame.params.hasOwnProperty(paramName)) {
+                return frame.params[paramName];
+            }
+            return null;
         }
         return null;
     }
@@ -417,26 +376,26 @@ class Thread {
      * where execution proceeds from one block to the next.
      */
     goToNextBlock () {
-        const nextBlockId = this.target.blocks.getNextBlock(this.pointer);
+        const nextBlockId = this.target.blocks.getNextBlock(this.peekStack());
         this.reuseStackForNextBlock(nextBlockId);
     }
 
     /**
-     * Attempt to determine whether a procedure call is recursive, by examining
-     * the stack.
+     * Attempt to determine whether a procedure call is recursive,
+     * by examining the stack.
      * @param {!string} procedureCode Procedure code of procedure being called.
      * @return {boolean} True if the call appears recursive.
      */
     isRecursiveCall (procedureCode) {
-        const stackHeight = this.stack.length;
-        // Limit the number of stack levels that are examined for procedures.
-        const stackBottom = Math.max(stackHeight - 5, 0);
-        for (let i = stackHeight - 1; i >= stackBottom; i--) {
+        let callCount = 5; // Max number of enclosing procedure calls to examine.
+        const sp = this.stack.length - 1;
+        for (let i = sp - 1; i >= 0; i--) {
             const block = this.target.blocks.getBlock(this.stack[i]);
             if (block.opcode === 'procedures_call' &&
                 block.mutation.proccode === procedureCode) {
                 return true;
             }
+            if (--callCount < 0) return false;
         }
         return false;
     }
diff --git a/test/fixtures/execute/procedures-stop-this-script-ends.sb2 b/test/fixtures/execute/procedures-stop-this-script-ends.sb2
deleted file mode 100644
index 8bf7a0cde..000000000
Binary files a/test/fixtures/execute/procedures-stop-this-script-ends.sb2 and /dev/null differ
diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js
index b2cd26139..a383001fe 100644
--- a/test/unit/engine_sequencer.js
+++ b/test/unit/engine_sequencer.js
@@ -120,40 +120,16 @@ test('stepToBranch', t => {
     const r = new Runtime();
     const s = new Sequencer(r);
     const th = generateThread(r);
-
-    // Push substack 2 (null).
     s.stepToBranch(th, 2, false);
     t.strictEquals(th.peekStack(), null);
     th.popStack();
-    t.strictEquals(th.peekStackFrame().isLoop, false);
-    // Push substack 1 (null).
     s.stepToBranch(th, 1, false);
     t.strictEquals(th.peekStack(), null);
     th.popStack();
-    t.strictEquals(th.peekStackFrame().isLoop, false);
-    // Push loop substack (null).
-    s.stepToBranch(th, 1, true);
-    t.strictEquals(th.peekStack(), null);
     th.popStack();
-    t.strictEquals(th.peekStackFrame().isLoop, true);
-    // isLoop resets when thread goes to next block.
-    th.goToNextBlock();
-    t.strictEquals(th.peekStackFrame().isLoop, false);
-    th.popStack();
-    // Push substack 1 (not null).
     s.stepToBranch(th, 1, false);
     t.notEquals(th.peekStack(), null);
-    th.popStack();
-    t.strictEquals(th.peekStackFrame().isLoop, false);
-    // Push loop substack (not null).
-    s.stepToBranch(th, 1, true);
-    t.notEquals(th.peekStack(), null);
-    th.popStack();
-    t.strictEquals(th.peekStackFrame().isLoop, true);
-    // isLoop resets when thread goes to next block.
-    th.goToNextBlock();
-    t.strictEquals(th.peekStackFrame().isLoop, false);
-
+    
     t.end();
 });
 
@@ -161,7 +137,7 @@ test('retireThread', t => {
     const r = new Runtime();
     const s = new Sequencer(r);
     const th = generateThread(r);
-    t.strictEquals(th.stack.length, 11);
+    t.strictEquals(th.stack.length, 12);
     s.retireThread(th);
     t.strictEquals(th.stack.length, 0);
     t.strictEquals(th.status, Thread.STATUS_DONE);
diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js
index 047ac60f1..36206eb99 100644
--- a/test/unit/engine_thread.js
+++ b/test/unit/engine_thread.js
@@ -40,7 +40,7 @@ test('popStack', t => {
     const th = new Thread('arbitraryString');
     th.pushStack('arbitraryString');
     t.strictEquals(th.popStack(), 'arbitraryString');
-    t.strictEquals(th.popStack(), null);
+    t.strictEquals(th.popStack(), undefined);
 
     t.end();
 });
@@ -209,54 +209,21 @@ test('stopThisScript', t => {
         x: 0,
         y: 0
     };
-    const block3 = {fields: Object,
-        id: 'thirdString',
-        inputs: Object,
-        STEPS: Object,
-        block: 'fakeBlock',
-        name: 'STEPS',
-        next: null,
-        opcode: 'procedures_definition',
-        mutation: {proccode: 'fakeCode'},
-        parent: null,
-        shadow: false,
-        topLevel: true,
-        x: 0,
-        y: 0
-    };
 
     rt.blocks.createBlock(block1);
     rt.blocks.createBlock(block2);
-    rt.blocks.createBlock(block3);
     th.target = rt;
 
     th.stopThisScript();
     t.strictEquals(th.peekStack(), null);
-    t.strictEquals(th.peekStackFrame(), null);
-
     th.pushStack('arbitraryString');
     t.strictEquals(th.peekStack(), 'arbitraryString');
-    t.notEqual(th.peekStackFrame(), null);
     th.stopThisScript();
     t.strictEquals(th.peekStack(), null);
-    t.strictEquals(th.peekStackFrame(), null);
-
     th.pushStack('arbitraryString');
     th.pushStack('secondString');
     th.stopThisScript();
-    t.strictEquals(th.peekStack(), null);
-    t.same(th.stack, ['arbitraryString', 'secondString']);
-    t.notEqual(th.peekStackFrame(), null);
-
-    while (th.peekStackFrame()) th.popStack();
-
-    th.pushStack('arbitraryString');
-    th.pushStack('secondString');
-    th.pushStack('thirdString');
-    th.stopThisScript();
-    t.strictEquals(th.peekStack(), null);
-    t.same(th.stack, ['arbitraryString', 'secondString']);
-    t.notEqual(th.peekStackFrame(), null);
+    t.strictEquals(th.peekStack(), 'secondString');
 
     t.end();
 });