mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-07-26 22:20:41 -04:00
Interpreter fixes, enhancements, features (#280)
* Thread stepping rework; interp.redraw equivalent * Add turbo mode and pause mode * Yielding behavior to match Scratch 2.0 * Implement warp-mode procedure threads * Add check for recursive call * Inline wait block timer * Revert to setInterval and always drawing * Restore yielding in glide * 30TPS compatibility mode * 5-call count recursion limit * Removing dead primitive code * To simplify, access runtime.threads inline in `stepThreads`. * Warp mode/timer fixes; recursive check fixes; clean-up * Add basic single-stepping * Add single-stepping speed slider * Allow yielding threads to run in single-stepping * Restore inactive threads tracking for block glows * Add clock pausing during pause mode * Documentation and clean-up throughout * Don't look for block glows in `thread.topBlock`. * Add null check for block glows; rename `_updateScriptGlows` to reflect block glowing * Use the current executed block for glow, instead of stack * Add more comments to `stepToProcedure`, and re-arrange to match 2.0 * Tweak to Blocks.prototype.getTopLevelScript * Revert previous * Fix threads array to be resilient to changes during `stepThreads` * Restore inactive threads filtering * Fix typo in "procedure" * !! instead of == true
This commit is contained in:
parent
060d1ab2a5
commit
e49f076fa1
14 changed files with 705 additions and 281 deletions
src/engine
|
@ -90,7 +90,7 @@ var execute = function (sequencer, thread) {
|
|||
runtime.visualReport(currentBlockId, resolvedValue);
|
||||
}
|
||||
// Finished any yields.
|
||||
thread.setStatus(Thread.STATUS_RUNNING);
|
||||
thread.status = Thread.STATUS_RUNNING;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -133,15 +133,22 @@ var execute = function (sequencer, thread) {
|
|||
var input = inputs[inputName];
|
||||
var inputBlockId = input.block;
|
||||
// Is there no value for this input waiting in the stack frame?
|
||||
if (typeof currentStackFrame.reported[inputName] === 'undefined') {
|
||||
if (typeof currentStackFrame.reported[inputName] === 'undefined'
|
||||
&& inputBlockId) {
|
||||
// If there's not, we need to evaluate the block.
|
||||
var reporterYielded = (
|
||||
sequencer.stepToReporter(thread, inputBlockId, inputName)
|
||||
);
|
||||
// If the reporter yielded, return immediately;
|
||||
// it needs time to finish and report its value.
|
||||
if (reporterYielded) {
|
||||
// Push to the stack to evaluate the reporter block.
|
||||
thread.pushStack(inputBlockId);
|
||||
// Save name of input for `Thread.pushReportedValue`.
|
||||
currentStackFrame.waitingReporter = inputName;
|
||||
// Actually execute the block.
|
||||
execute(sequencer, thread);
|
||||
if (thread.status === Thread.STATUS_PROMISE_WAIT) {
|
||||
return;
|
||||
} else {
|
||||
// Execution returned immediately,
|
||||
// and presumably a value was reported, so pop the stack.
|
||||
currentStackFrame.waitingReporter = null;
|
||||
thread.popStack();
|
||||
}
|
||||
}
|
||||
argValues[inputName] = currentStackFrame.reported[inputName];
|
||||
|
@ -164,17 +171,10 @@ var execute = function (sequencer, thread) {
|
|||
stackFrame: currentStackFrame.executionContext,
|
||||
target: target,
|
||||
yield: function() {
|
||||
thread.setStatus(Thread.STATUS_YIELD);
|
||||
thread.status = Thread.STATUS_YIELD;
|
||||
},
|
||||
yieldFrame: function() {
|
||||
thread.setStatus(Thread.STATUS_YIELD_FRAME);
|
||||
},
|
||||
done: function() {
|
||||
thread.setStatus(Thread.STATUS_RUNNING);
|
||||
sequencer.proceedThread(thread);
|
||||
},
|
||||
startBranch: function (branchNum) {
|
||||
sequencer.stepToBranch(thread, branchNum);
|
||||
startBranch: function (branchNum, isLoop) {
|
||||
sequencer.stepToBranch(thread, branchNum, isLoop);
|
||||
},
|
||||
stopAll: function () {
|
||||
runtime.stopAll();
|
||||
|
@ -185,11 +185,11 @@ var execute = function (sequencer, thread) {
|
|||
stopThread: function() {
|
||||
sequencer.retireThread(thread);
|
||||
},
|
||||
startProcedure: function (procedureName) {
|
||||
sequencer.stepToProcedure(thread, procedureName);
|
||||
startProcedure: function (procedureCode) {
|
||||
sequencer.stepToProcedure(thread, procedureCode);
|
||||
},
|
||||
getProcedureParamNames: function (procedureName) {
|
||||
return blockContainer.getProcedureParamNames(procedureName);
|
||||
getProcedureParamNames: function (procedureCode) {
|
||||
return blockContainer.getProcedureParamNames(procedureCode);
|
||||
},
|
||||
pushParam: function (paramName, paramValue) {
|
||||
thread.pushParam(paramName, paramValue);
|
||||
|
@ -221,18 +221,24 @@ var execute = function (sequencer, thread) {
|
|||
if (isPromise(primitiveReportedValue)) {
|
||||
if (thread.status === Thread.STATUS_RUNNING) {
|
||||
// Primitive returned a promise; automatically yield thread.
|
||||
thread.setStatus(Thread.STATUS_YIELD);
|
||||
thread.status = Thread.STATUS_PROMISE_WAIT;
|
||||
}
|
||||
// Promise handlers
|
||||
primitiveReportedValue.then(function(resolvedValue) {
|
||||
handleReport(resolvedValue);
|
||||
sequencer.proceedThread(thread);
|
||||
if (typeof resolvedValue !== 'undefined') {
|
||||
thread.popStack();
|
||||
} else {
|
||||
var popped = thread.popStack();
|
||||
var nextBlockId = thread.target.blocks.getNextBlock(popped);
|
||||
thread.pushStack(nextBlockId);
|
||||
}
|
||||
}, function(rejectionReason) {
|
||||
// Promise rejected: the primitive had some error.
|
||||
// Log it and proceed.
|
||||
console.warn('Primitive rejected promise: ', rejectionReason);
|
||||
thread.setStatus(Thread.STATUS_RUNNING);
|
||||
sequencer.proceedThread(thread);
|
||||
thread.status = Thread.STATUS_RUNNING;
|
||||
thread.popStack();
|
||||
});
|
||||
} else if (thread.status === Thread.STATUS_RUNNING) {
|
||||
handleReport(primitiveReportedValue);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue