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;