From 6e5ebcf6dfb8f690fdc666d80cc8b9be1052f56e Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 9 Jun 2016 13:27:30 -0400
Subject: [PATCH] Split execution into separate function and file

---
 src/engine/execute.js   | 154 ++++++++++++++++++++++++++++++++++++++
 src/engine/sequencer.js | 160 +---------------------------------------
 2 files changed, 156 insertions(+), 158 deletions(-)
 create mode 100644 src/engine/execute.js

diff --git a/src/engine/execute.js b/src/engine/execute.js
new file mode 100644
index 000000000..fcb2db8e3
--- /dev/null
+++ b/src/engine/execute.js
@@ -0,0 +1,154 @@
+var Thread = require('./thread');
+var YieldTimers = require('../util/yieldtimers.js');
+
+var execute = function (sequencer, thread, blockId) {
+    var runtime = sequencer.runtime;
+
+    // Save the yield timer ID, in case a primitive makes a new one
+    // @todo hack - perhaps patch this to allow more than one timer per
+    // primitive, for example...
+    var oldYieldTimerId = YieldTimers.timerId;
+
+    var opcode = runtime.blocks.getOpcode(blockId);
+
+    // Push the current block to the stack
+    thread.stack.push(blockId);
+    // Push an empty stack frame, if we need one.
+    // Might not, if we just popped the stack.
+    if (thread.stack.length > thread.stackFrames.length) {
+        thread.stackFrames.push({});
+    }
+    var currentStackFrame = thread.stackFrames[thread.stackFrames.length - 1];
+
+    /**
+     * A callback for the primitive to indicate its thread should yield.
+     * @type {Function}
+     */
+    var threadYieldCallback = function () {
+        thread.status = Thread.STATUS_YIELD;
+    };
+
+    /**
+     * A callback for the primitive to indicate its thread is finished
+     * @type {Function}
+     */
+    var threadDoneCallback = function () {
+        thread.status = Thread.STATUS_DONE;
+        // Refresh nextBlock in case it has changed during a yield.
+        thread.nextBlock = runtime.blocks.getNextBlock(blockId);
+        // Pop the stack and stack frame
+        thread.stack.pop();
+        thread.stackFrames.pop();
+        // Stop showing run feedback in the editor.
+        runtime.glowBlock(blockId, false);
+    };
+
+    /**
+     * A callback for the primitive to start hats.
+     * @todo very hacked...
+     * Provide a callback that is passed in a block and returns true
+     * if it is a hat that should be triggered.
+     * @param {Function} callback Provided callback.
+     */
+    var startHats = function(callback) {
+        var stacks = runtime.blocks.getStacks();
+        for (var i = 0; i < stacks.length; i++) {
+            var stack = stacks[i];
+            var stackBlock = runtime.blocks.getBlock(stack);
+            var result = callback(stackBlock);
+            if (result) {
+                // Check if the stack is already running
+                var stackRunning = false;
+
+                for (var j = 0; j < runtime.threads.length; j++) {
+                    if (runtime.threads[j].topBlock == stack) {
+                        stackRunning = true;
+                        break;
+                    }
+                }
+                if (!stackRunning) {
+                    runtime._pushThread(stack);
+                }
+            }
+        }
+    };
+
+    /**
+     * Record whether we have switched stack,
+     * to avoid proceeding the thread automatically.
+     * @type {boolean}
+     */
+    var switchedStack = false;
+    /**
+     * A callback for a primitive to start a substack.
+     * @type {Function}
+     */
+    var threadStartSubstack = function () {
+        // Set nextBlock to the start of the substack
+        var substack = runtime.blocks.getSubstack(blockId);
+        if (substack && substack.value) {
+            thread.nextBlock = substack.value;
+        } else {
+            thread.nextBlock = null;
+        }
+        switchedStack = true;
+    };
+
+    var argValues = {};
+
+    // Start showing run feedback in the editor.
+    runtime.glowBlock(blockId, true);
+
+    if (!opcode) {
+        console.warn('Could not get opcode for block: ' + blockId);
+        console.groupEnd();
+        return;
+    }
+
+    var blockFunction = runtime.getOpcodeFunction(opcode);
+    if (!blockFunction) {
+        console.warn('Could not get implementation for opcode: ' + opcode);
+        console.groupEnd();
+        return;
+    }
+
+    if (sequencer.DEBUG_BLOCK_CALLS) {
+        console.groupCollapsed('Executing: ' + opcode);
+        console.log('with arguments: ', argValues);
+        console.log('and stack frame: ', currentStackFrame);
+    }
+    var blockFunctionReturnValue = null;
+    try {
+        // @todo deal with the return value
+        blockFunctionReturnValue = blockFunction(argValues, {
+            yield: threadYieldCallback,
+            done: threadDoneCallback,
+            timeout: YieldTimers.timeout,
+            stackFrame: currentStackFrame,
+            startSubstack: threadStartSubstack,
+            startHats: startHats
+        });
+    }
+    catch(e) {
+        console.error(
+            'Exception calling block function for opcode: ' +
+            opcode + '\n' + e);
+    } finally {
+        // Update if the thread has set a yield timer ID
+        // @todo hack
+        if (YieldTimers.timerId > oldYieldTimerId) {
+            thread.yieldTimerId = YieldTimers.timerId;
+        }
+        if (thread.status === Thread.STATUS_RUNNING && !switchedStack) {
+            // Thread executed without yielding - move to done
+            threadDoneCallback();
+        }
+        if (sequencer.DEBUG_BLOCK_CALLS) {
+            console.log('ending stack frame: ', currentStackFrame);
+            console.log('returned: ', blockFunctionReturnValue);
+            console.groupEnd();
+        }
+    }
+};
+
+module.exports = execute;
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index c31081798..785bcbb0c 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -1,6 +1,7 @@
 var Timer = require('../util/timer');
 var Thread = require('./thread');
 var YieldTimers = require('../util/yieldtimers.js');
+var execute = require('./execute.js');
 
 function Sequencer (runtime) {
     /**
@@ -95,11 +96,6 @@ Sequencer.prototype.stepThreads = function (threads) {
  * @param {!Thread} thread Thread object to step
  */
 Sequencer.prototype.stepThread = function (thread) {
-    // Save the yield timer ID, in case a primitive makes a new one
-    // @todo hack - perhaps patch this to allow more than one timer per
-    // primitive, for example...
-    var oldYieldTimerId = YieldTimers.timerId;
-
     // Save the current block and set the nextBlock.
     // If the primitive would like to do control flow,
     // it can overwrite nextBlock.
@@ -110,159 +106,7 @@ Sequencer.prototype.stepThread = function (thread) {
     }
     thread.nextBlock = this.runtime.blocks.getNextBlock(currentBlock);
 
-    var opcode = this.runtime.blocks.getOpcode(currentBlock);
-
-    // Push the current block to the stack
-    thread.stack.push(currentBlock);
-    // Push an empty stack frame, if we need one.
-    // Might not, if we just popped the stack.
-    if (thread.stack.length > thread.stackFrames.length) {
-        thread.stackFrames.push({});
-    }
-    var currentStackFrame = thread.stackFrames[thread.stackFrames.length - 1];
-
-    /**
-     * A callback for the primitive to indicate its thread should yield.
-     * @type {Function}
-     */
-    var threadYieldCallback = function () {
-        thread.status = Thread.STATUS_YIELD;
-    };
-
-    /**
-     * A callback for the primitive to indicate its thread is finished
-     * @type {Function}
-     */
-    var instance = this;
-    var threadDoneCallback = function () {
-        thread.status = Thread.STATUS_DONE;
-        // Refresh nextBlock in case it has changed during a yield.
-        thread.nextBlock = instance.runtime.blocks.getNextBlock(currentBlock);
-        // Pop the stack and stack frame
-        thread.stack.pop();
-        thread.stackFrames.pop();
-        // Stop showing run feedback in the editor.
-        instance.runtime.glowBlock(currentBlock, false);
-    };
-
-    /**
-     * A callback for the primitive to start hats.
-     * @todo very hacked...
-     * Provide a callback that is passed in a block and returns true
-     * if it is a hat that should be triggered.
-     * @param {Function} callback Provided callback.
-     */
-    var startHats = function(callback) {
-        var stacks = instance.runtime.blocks.getStacks();
-        for (var i = 0; i < stacks.length; i++) {
-            var stack = stacks[i];
-            var stackBlock = instance.runtime.blocks.getBlock(stack);
-            var result = callback(stackBlock);
-            if (result) {
-                // Check if the stack is already running
-                var stackRunning = false;
-
-                for (var j = 0; j < instance.runtime.threads.length; j++) {
-                    if (instance.runtime.threads[j].topBlock == stack) {
-                        stackRunning = true;
-                        break;
-                    }
-                }
-                if (!stackRunning) {
-                    instance.runtime._pushThread(stack);
-                }
-            }
-        }
-    };
-
-    /**
-     * Record whether we have switched stack,
-     * to avoid proceeding the thread automatically.
-     * @type {boolean}
-     */
-    var switchedStack = false;
-    /**
-     * A callback for a primitive to start a substack.
-     * @type {Function}
-     */
-    var threadStartSubstack = function () {
-        // Set nextBlock to the start of the substack
-        var substack = instance.runtime.blocks.getSubstack(currentBlock);
-        if (substack && substack.value) {
-            thread.nextBlock = substack.value;
-        } else {
-            thread.nextBlock = null;
-        }
-        switchedStack = true;
-    };
-
-    // @todo extreme hack to get the single argument value for prototype
-    var argValues = [];
-    var blockInputs = this.runtime.blocks.getBlock(currentBlock).fields;
-    for (var bi in blockInputs) {
-        var outer = blockInputs[bi];
-        for (var b in outer.blocks) {
-            var block = outer.blocks[b];
-            var fields = block.fields;
-            for (var f in fields) {
-                var field = fields[f];
-                argValues.push(field.value);
-            }
-        }
-    }
-
-    // Start showing run feedback in the editor.
-    this.runtime.glowBlock(currentBlock, true);
-
-    if (!opcode) {
-        console.warn('Could not get opcode for block: ' + currentBlock);
-    }
-    else {
-        var blockFunction = this.runtime.getOpcodeFunction(opcode);
-        if (!blockFunction) {
-            console.warn('Could not get implementation for opcode: ' + opcode);
-        }
-        else {
-            if (Sequencer.DEBUG_BLOCK_CALLS) {
-                console.groupCollapsed('Executing: ' + opcode);
-                console.log('with arguments: ', argValues);
-                console.log('and stack frame: ', currentStackFrame);
-            }
-            var blockFunctionReturnValue = null;
-            try {
-                // @todo deal with the return value
-                blockFunctionReturnValue = blockFunction(argValues, {
-                    yield: threadYieldCallback,
-                    done: threadDoneCallback,
-                    timeout: YieldTimers.timeout,
-                    stackFrame: currentStackFrame,
-                    startSubstack: threadStartSubstack,
-                    startHats: startHats
-                });
-            }
-            catch(e) {
-                console.error(
-                    'Exception calling block function for opcode: ' +
-                    opcode + '\n' + e);
-            } finally {
-                // Update if the thread has set a yield timer ID
-                // @todo hack
-                if (YieldTimers.timerId > oldYieldTimerId) {
-                    thread.yieldTimerId = YieldTimers.timerId;
-                }
-                if (thread.status === Thread.STATUS_RUNNING && !switchedStack) {
-                    // Thread executed without yielding - move to done
-                    threadDoneCallback();
-                }
-                if (Sequencer.DEBUG_BLOCK_CALLS) {
-                    console.log('ending stack frame: ', currentStackFrame);
-                    console.log('returned: ', blockFunctionReturnValue);
-                    console.groupEnd();
-                }
-            }
-        }
-    }
-
+    execute(this, thread, currentBlock, false);
 };
 
 module.exports = Sequencer;