Merge pull request from mzgoddard/fix-execute

fix asynchronous block execution
This commit is contained in:
Paul Kaplan 2018-07-13 08:32:20 -04:00 committed by GitHub
commit b33d9ba4e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 17 deletions

View file

@ -102,7 +102,7 @@ const handleReport = function (resolvedValue, sequencer, thread, blockCached) {
} }
}; };
const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached) => { const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached, lastOperation) => {
if (thread.status === Thread.STATUS_RUNNING) { if (thread.status === Thread.STATUS_RUNNING) {
// Primitive returned a promise; automatically yield thread. // Primitive returned a promise; automatically yield thread.
thread.status = Thread.STATUS_PROMISE_WAIT; thread.status = Thread.STATUS_PROMISE_WAIT;
@ -110,7 +110,8 @@ const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached) =
// Promise handlers // Promise handlers
primitiveReportedValue.then(resolvedValue => { primitiveReportedValue.then(resolvedValue => {
handleReport(resolvedValue, sequencer, thread, blockCached); handleReport(resolvedValue, sequencer, thread, blockCached);
if (typeof resolvedValue === 'undefined') { // If its a command block.
if (lastOperation && typeof resolvedValue === 'undefined') {
let stackFrame; let stackFrame;
let nextBlockId; let nextBlockId;
do { do {
@ -131,8 +132,6 @@ const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached) =
} while (stackFrame !== null && !stackFrame.isLoop); } while (stackFrame !== null && !stackFrame.isLoop);
thread.pushStack(nextBlockId); thread.pushStack(nextBlockId);
} else {
thread.popStack();
} }
}, rejectionReason => { }, rejectionReason => {
// Promise rejected: the primitive had some error. // Promise rejected: the primitive had some error.
@ -407,7 +406,44 @@ const execute = function (sequencer, thread) {
const reported = currentStackFrame.reported; const reported = currentStackFrame.reported;
// Reinstate all the previous values. // Reinstate all the previous values.
for (; i < reported.length; i++) { for (; i < reported.length; i++) {
const {opCached, inputValue} = reported[i]; const {opCached: oldOpCached, inputValue} = reported[i];
const opCached = ops.find(op => op.id === oldOpCached);
if (opCached) {
const inputName = opCached._parentKey;
const argValues = opCached._parentValues;
if (inputName === 'BROADCAST_INPUT') {
// Something is plugged into the broadcast input.
// Cast it to a string. We don't need an id here.
argValues.BROADCAST_OPTION.id = null;
argValues.BROADCAST_OPTION.name = cast.toString(inputValue);
} else {
argValues[inputName] = inputValue;
}
}
}
// Find the last reported block that is still in the set of operations.
// This way if the last operation was removed, we'll find the next
// candidate. If an earlier block that was performed was removed then
// we'll find the index where the last operation is now.
if (reported.length > 0) {
const lastExisting = reported.reverse().find(report => ops.find(op => op.id === report.opCached));
if (lastExisting) {
i = ops.findIndex(opCached => opCached.id === lastExisting.opCached) + 1;
} else {
i = 0;
}
}
// 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 === currentStackFrame.reporting) {
const opCached = ops[i];
const inputValue = thread.justReported;
thread.justReported = null;
const inputName = opCached._parentKey; const inputName = opCached._parentKey;
const argValues = opCached._parentValues; const argValues = opCached._parentValues;
@ -420,17 +456,11 @@ const execute = function (sequencer, thread) {
} else { } else {
argValues[inputName] = inputValue; argValues[inputName] = inputValue;
} }
i += 1;
} }
// Find the last reported block that is still in the set of operations. currentStackFrame.reporting = null;
// This way if the last operation was removed, we'll find the next
// candidate. If an earlier block that was performed was removed then
// we'll find the index where the last operation is now.
if (reported.length > 0) {
const lastExisting = reported.reverse().find(report => ops.find(op => op.id === report[0].id));
i = ops.findIndex(opCached => opCached.id === lastExisting.id) + 1;
}
currentStackFrame.reported = null; currentStackFrame.reported = null;
} }
@ -471,23 +501,33 @@ const execute = function (sequencer, thread) {
// If it's a promise, wait until promise resolves. // If it's a promise, wait until promise resolves.
if (isPromise(primitiveReportedValue)) { if (isPromise(primitiveReportedValue)) {
handlePromise(primitiveReportedValue, sequencer, thread, opCached); handlePromise(primitiveReportedValue, sequencer, thread, opCached, last);
// Store the already reported values. They will be thawed into the
// future versions of the same operations by block id. The reporting
// operation if it is promise waiting will set its parent value at
// that time.
thread.justReported = null;
currentStackFrame.reporting = ops[i].id;
currentStackFrame.reported = ops.slice(0, i).map(reportedCached => { currentStackFrame.reported = ops.slice(0, i).map(reportedCached => {
const inputName = reportedCached._parentKey; const inputName = reportedCached._parentKey;
const reportedValues = reportedCached._parentValues; const reportedValues = reportedCached._parentValues;
if (inputName === 'BROADCAST_INPUT') { if (inputName === 'BROADCAST_INPUT') {
return { return {
opCached: reportedCached, opCached: reportedCached.id,
inputValue: reportedValues[inputName].BROADCAST_OPTION.name inputValue: reportedValues[inputName].BROADCAST_OPTION.name
}; };
} }
return { return {
opCached: reportedCached, opCached: reportedCached.id,
inputValue: reportedValues[inputName] inputValue: reportedValues[inputName]
}; };
}); });
// We are waiting for a promise. Stop running this set of operations
// and continue them later after thawing the reported values.
break;
} else if (thread.status === Thread.STATUS_RUNNING) { } else if (thread.status === Thread.STATUS_RUNNING) {
if (last) { if (last) {
if (typeof primitiveReportedValue === 'undefined') { if (typeof primitiveReportedValue === 'undefined') {

View file

@ -32,6 +32,12 @@ class _StackFrame {
*/ */
this.justReported = null; this.justReported = null;
/**
* The active block that is waiting on a promise.
* @type {string}
*/
this.reporting = '';
/** /**
* Persists reported inputs during async block. * Persists reported inputs during async block.
* @type {Object} * @type {Object}