Skip to content

Commit

Permalink
elevate stack block id and frame info into thread
Browse files Browse the repository at this point in the history
- Add pointer member to thread. This is the current executing block.
- Add stackFrame member to thread. This is the current frame
  information like procedure parameters.

This is a step potentially towards stack-less threads. With further
modifications we could have stack and stackFrames be null if a script
does not call a procedure.
  • Loading branch information
mzgoddard committed May 6, 2019
1 parent eb5a727 commit b422339
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 150 deletions.
44 changes: 21 additions & 23 deletions src/blocks/scratch3_procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,32 @@ class Scratch3ProcedureBlocks {
}

call (args, util) {
if (!util.stackFrame.executed) {
const procedureCode = args.mutation.proccode;
const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
const procedureCode = args.mutation.proccode;
const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);

// If null, procedure could not be found, which can happen if custom
// block is dragged between sprites without the definition.
// Match Scratch 2.0 behavior and noop.
if (paramNamesIdsAndDefaults === null) {
return;
}
// If null, procedure could not be found, which can happen if custom
// block is dragged between sprites without the definition.
// Match Scratch 2.0 behavior and noop.
if (paramNamesIdsAndDefaults === null) {
return;
}

const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;

// Initialize params for the current stackFrame to {}, even if the procedure does
// not take any arguments. This is so that `getParam` down the line does not look
// at earlier stack frames for the values of a given parameter (#1729)
util.initParams();
for (let i = 0; i < paramIds.length; i++) {
if (args.hasOwnProperty(paramIds[i])) {
util.pushParam(paramNames[i], args[paramIds[i]]);
} else {
util.pushParam(paramNames[i], paramDefaults[i]);
}
}
util.startProcedure(procedureCode);

util.stackFrame.executed = true;
util.startProcedure(procedureCode);
// Initialize params for the current stackFrame to {}, even if the procedure does
// not take any arguments. This is so that `getParam` down the line does not look
// at earlier stack frames for the values of a given parameter (#1729)
util.initParams();
for (let i = 0; i < paramIds.length; i++) {
if (args.hasOwnProperty(paramIds[i])) {
util.pushParam(paramNames[i], args[paramIds[i]]);
} else {
util.pushParam(paramNames[i], paramDefaults[i]);
}
}

}

argumentReporterStringNumber (args, util) {
Expand Down
6 changes: 1 addition & 5 deletions src/engine/block-utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ class BlockUtility {
* @type {object}
*/
get stackFrame () {
const frame = this.thread.peekStackFrame();
if (frame.executionContext === null) {
frame.executionContext = {};
}
return frame.executionContext;
return this.thread.getExecutionContext();
}

/**
Expand Down
15 changes: 7 additions & 8 deletions src/engine/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,6 @@ const execute = function (sequencer, thread) {

// Current block to execute is the one on the top of the stack.
const currentBlockId = thread.peekStack();
const currentStackFrame = thread.peekStackFrame();

let blockContainer = thread.blockContainer;
let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);
Expand All @@ -404,8 +403,8 @@ const execute = function (sequencer, thread) {
const length = ops.length;
let i = 0;

if (currentStackFrame.reported !== null) {
const reported = currentStackFrame.reported;
if (thread.reported !== null) {
const reported = thread.reported;
// Reinstate all the previous values.
for (; i < reported.length; i++) {
const {opCached: oldOpCached, inputValue} = reported[i];
Expand Down Expand Up @@ -441,7 +440,7 @@ const execute = function (sequencer, thread) {
}

// 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) {
if (thread.justReported !== null && ops[i] && ops[i].id === thread.reportingBlockId) {
const opCached = ops[i];
const inputValue = thread.justReported;

Expand All @@ -462,8 +461,8 @@ const execute = function (sequencer, thread) {
i += 1;
}

currentStackFrame.reporting = null;
currentStackFrame.reported = null;
thread.reportingBlockId = null;
thread.reported = null;
}

for (; i < length; i++) {
Expand Down Expand Up @@ -518,8 +517,8 @@ const execute = function (sequencer, thread) {
// 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 => {
thread.reportingBlockId = ops[i].id;
thread.reported = ops.slice(0, i).map(reportedCached => {
const inputName = reportedCached._parentKey;
const reportedValues = reportedCached._parentValues;

Expand Down
17 changes: 13 additions & 4 deletions src/engine/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,7 @@ class Runtime extends EventEmitter {
isActiveThread (thread) {
return (
(
thread.stack.length > 0 &&
thread.stackFrame !== null &&
thread.status !== Thread.STATUS_DONE) &&
this.threads.indexOf(thread) > -1);
}
Expand Down Expand Up @@ -1676,11 +1676,20 @@ class Runtime extends EventEmitter {
// Start the thread with this top block.
newThreads.push(instance._pushThread(topBlockId, target));
}, optTarget);
// For compatibility with Scratch 2, edge triggered hats need to be processed before
// threads are stepped. See ScratchRuntime.as for original implementation
// For compatibility with Scratch 2, edge triggered hats need to be
// processed before threads are stepped. See ScratchRuntime.as for
// original implementation.
//
// TODO: Move the execute call to sequencer. Maybe in a method call
// stepHat or stepOne.
newThreads.forEach(thread => {
execute(this.sequencer, thread);
thread.goToNextBlock();
if (thread.status !== Thread.STATUS_DONE) {
thread.goToNextBlock();
if (thread.stackFrame === null) {
this.sequencer.retireThread(thread);
}
}
});
return newThreads;
}
Expand Down
12 changes: 7 additions & 5 deletions src/engine/sequencer.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class Sequencer {
for (let i = 0; i < this.runtime.threads.length; i++) {
const activeThread = this.runtime.threads[i];
// Check if the thread is done so it is not executed.
if (activeThread.stack.length === 0 ||
if (activeThread.stackFrame === null ||
activeThread.status === Thread.STATUS_DONE) {
// Finished with this thread.
stoppedThread = true;
Expand Down Expand Up @@ -134,7 +134,7 @@ class Sequencer {
}
// Check if the thread completed while it just stepped to make
// sure we remove it before the next iteration of all threads.
if (activeThread.stack.length === 0 ||
if (activeThread.stackFrame === null ||
activeThread.status === Thread.STATUS_DONE) {
// Finished with this thread.
stoppedThread = true;
Expand All @@ -153,7 +153,7 @@ class Sequencer {
let nextActiveThread = 0;
for (let i = 0; i < this.runtime.threads.length; i++) {
const thread = this.runtime.threads[i];
if (thread.stack.length !== 0 &&
if (thread.stackFrame !== null &&
thread.status !== Thread.STATUS_DONE) {
this.runtime.threads[nextActiveThread] = thread;
nextActiveThread++;
Expand Down Expand Up @@ -237,7 +237,7 @@ class Sequencer {
while (!thread.peekStack()) {
thread.popStack();

if (thread.stack.length === 0) {
if (thread.stackFrame === null) {
// No more stack to run!
thread.status = Thread.STATUS_DONE;
return;
Expand Down Expand Up @@ -350,7 +350,9 @@ class Sequencer {
*/
retireThread (thread) {
thread.stack = [];
thread.stackFrame = [];
thread.pointer = null;
thread.stackFrames = [];
thread.stackFrame = null;
thread.requestScriptGlowInFrame = false;
thread.status = Thread.STATUS_DONE;
}
Expand Down
Loading

0 comments on commit b422339

Please sign in to comment.