Merge pull request #1335 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) {
// Primitive returned a promise; automatically yield thread.
thread.status = Thread.STATUS_PROMISE_WAIT;
@ -110,7 +110,8 @@ const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached) =
// Promise handlers
primitiveReportedValue.then(resolvedValue => {
handleReport(resolvedValue, sequencer, thread, blockCached);
if (typeof resolvedValue === 'undefined') {
// If its a command block.
if (lastOperation && typeof resolvedValue === 'undefined') {
let stackFrame;
let nextBlockId;
do {
@ -131,8 +132,6 @@ const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached) =
} while (stackFrame !== null && !stackFrame.isLoop);
thread.pushStack(nextBlockId);
} else {
thread.popStack();
}
}, rejectionReason => {
// Promise rejected: the primitive had some error.
@ -407,8 +406,11 @@ const execute = function (sequencer, thread) {
const reported = currentStackFrame.reported;
// Reinstate all the previous values.
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;
@ -421,16 +423,44 @@ const execute = function (sequencer, thread) {
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[0].id));
i = ops.findIndex(opCached => opCached.id === lastExisting.id) + 1;
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 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;
}
i += 1;
}
currentStackFrame.reporting = null;
currentStackFrame.reported = null;
}
@ -471,23 +501,33 @@ const execute = function (sequencer, thread) {
// If it's a promise, wait until promise resolves.
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 => {
const inputName = reportedCached._parentKey;
const reportedValues = reportedCached._parentValues;
if (inputName === 'BROADCAST_INPUT') {
return {
opCached: reportedCached,
opCached: reportedCached.id,
inputValue: reportedValues[inputName].BROADCAST_OPTION.name
};
}
return {
opCached: reportedCached,
opCached: reportedCached.id,
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) {
if (last) {
if (typeof primitiveReportedValue === 'undefined') {

View file

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