diff --git a/src/engine/execute.js b/src/engine/execute.js index a4bf76b2a..e0da40cc2 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -11,6 +11,62 @@ const isPromise = function (value) { return value && value.then && typeof value.then === 'function'; }; +/** + * Handle any reported value from the primitive, either directly returned + * or after a promise resolves. + * @param {*} resolvedValue Value eventually returned from the primitive. + * @param {!Sequencer} sequencer Sequencer stepping the thread for the ran + * primitive. + * @param {!Thread} thread Thread containing the primitive. + * @param {!string} currentBlockId Id of the block in its thread for value from + * the primitive. + * @param {!boolean} isHat Is the current block a hat? + */ +// @todo move this to callback attached to the thread when we have performance +// metrics (dd) +const handleReport = function ( + resolvedValue, sequencer, thread, currentBlockId, isHat) { + thread.pushReportedValue(resolvedValue); + if (isHat) { + // Hat predicate was evaluated. + if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) { + // If this is an edge-activated hat, only proceed if the value is + // true and used to be false, or the stack was activated explicitly + // via stack click + if (!thread.stackClick) { + const oldEdgeValue = sequencer.runtime.updateEdgeActivatedValue( + currentBlockId, + resolvedValue + ); + const edgeWasActivated = !oldEdgeValue && resolvedValue; + if (!edgeWasActivated) { + sequencer.retireThread(thread); + } + } + } else if (!resolvedValue) { + // Not an edge-activated hat: retire the thread + // if predicate was false. + sequencer.retireThread(thread); + } + } else { + // In a non-hat, report the value visually if necessary if + // at the top of the thread stack. + if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { + if (thread.stackClick) { + sequencer.runtime.visualReport(currentBlockId, resolvedValue); + } + if (thread.updateMonitor) { + sequencer.runtime.requestUpdateMonitor(Map({ + id: currentBlockId, + value: String(resolvedValue) + })); + } + } + // Finished any yields. + thread.status = Thread.STATUS_RUNNING; + } +}; + /** * Execute a block. * @param {!Sequencer} sequencer Which sequencer is executing. @@ -61,55 +117,6 @@ const execute = function (sequencer, thread) { return; } - /** - * Handle any reported value from the primitive, either directly returned - * or after a promise resolves. - * @param {*} resolvedValue Value eventually returned from the primitive. - */ - // @todo move this to callback attached to the thread when we have performance - // metrics (dd) - const handleReport = function (resolvedValue) { - thread.pushReportedValue(resolvedValue); - if (isHat) { - // Hat predicate was evaluated. - if (runtime.getIsEdgeActivatedHat(opcode)) { - // If this is an edge-activated hat, only proceed if - // the value is true and used to be false, or the stack was activated - // explicitly via stack click - if (!thread.stackClick) { - const oldEdgeValue = runtime.updateEdgeActivatedValue( - currentBlockId, - resolvedValue - ); - const edgeWasActivated = !oldEdgeValue && resolvedValue; - if (!edgeWasActivated) { - sequencer.retireThread(thread); - } - } - } else if (!resolvedValue) { - // Not an edge-activated hat: retire the thread - // if predicate was false. - sequencer.retireThread(thread); - } - } else { - // In a non-hat, report the value visually if necessary if - // at the top of the thread stack. - if (typeof resolvedValue !== 'undefined' && thread.atStackTop()) { - if (thread.stackClick) { - runtime.visualReport(currentBlockId, resolvedValue); - } - if (thread.updateMonitor) { - runtime.requestUpdateMonitor(Map({ - id: currentBlockId, - value: String(resolvedValue) - })); - } - } - // Finished any yields. - thread.status = Thread.STATUS_RUNNING; - } - }; - // Hats and single-field shadows are implemented slightly differently // from regular blocks. // For hats: if they have an associated block function, @@ -124,7 +131,7 @@ const execute = function (sequencer, thread) { const keys = Object.keys(fields); if (keys.length === 1 && Object.keys(inputs).length === 0) { // One field and no inputs - treat as arg. - handleReport(fields[keys[0]].value); + handleReport(fields[keys[0]].value, sequencer, thread, currentBlockId, isHat); } else { log.warn(`Could not get implementation for opcode: ${opcode}`); } @@ -244,7 +251,7 @@ const execute = function (sequencer, thread) { } // Promise handlers primitiveReportedValue.then(resolvedValue => { - handleReport(resolvedValue); + handleReport(resolvedValue, sequencer, thread, currentBlockId, isHat); if (typeof resolvedValue === 'undefined') { let stackFrame; let nextBlockId; @@ -277,7 +284,7 @@ const execute = function (sequencer, thread) { thread.popStack(); }); } else if (thread.status === Thread.STATUS_RUNNING) { - handleReport(primitiveReportedValue); + handleReport(primitiveReportedValue, sequencer, thread, currentBlockId, isHat); } };