mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Add Thread.StackFrame class
Use a private StackFrame class to help internally manage use of Stack Frame values and memory. Create params, reported, and executionContext on demand.
This commit is contained in:
parent
16c6868779
commit
0634e962a8
3 changed files with 136 additions and 28 deletions
|
@ -43,7 +43,11 @@ class BlockUtility {
|
|||
* @type {object}
|
||||
*/
|
||||
get stackFrame () {
|
||||
return this.thread.peekStackFrame().executionContext;
|
||||
const frame = this.thread.peekStackFrame();
|
||||
if (frame.executionContext === null) {
|
||||
frame.executionContext = {};
|
||||
}
|
||||
return frame.executionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -277,6 +277,9 @@ const execute = function (sequencer, thread, recursiveCall) {
|
|||
// Actually execute the block.
|
||||
execute(sequencer, thread, RECURSIVE);
|
||||
if (thread.status === Thread.STATUS_PROMISE_WAIT) {
|
||||
// Create a reported value on the stack frame to store the
|
||||
// already built values.
|
||||
currentStackFrame.reported = {};
|
||||
// Waiting for the block to resolve, store the current argValues
|
||||
// onto a member of the currentStackFrame that can be used once
|
||||
// the nested block resolves to rebuild argValues up to this
|
||||
|
@ -310,10 +313,10 @@ const execute = function (sequencer, thread, recursiveCall) {
|
|||
currentStackFrame.justReported = null;
|
||||
// We have rebuilt argValues with all the stored values in the
|
||||
// currentStackFrame from the nested block's promise resolving.
|
||||
// Using the reported value from the block we waited on, reset the
|
||||
// storage member of currentStackFrame so the next execute call at
|
||||
// this level can use it in a clean state.
|
||||
currentStackFrame.reported = {};
|
||||
// Using the reported value from the block we waited on, unset the
|
||||
// value. The next execute needing to store reported values will
|
||||
// creates its own temporary storage.
|
||||
currentStackFrame.reported = null;
|
||||
} else if (typeof currentStackFrame.reported[inputName] !== 'undefined') {
|
||||
inputValue = currentStackFrame.reported[inputName];
|
||||
}
|
||||
|
|
|
@ -1,3 +1,116 @@
|
|||
/**
|
||||
* Recycle bin for empty stackFrame objects
|
||||
* @type Array<_StackFrame>
|
||||
*/
|
||||
const _stackFrameFreeList = [];
|
||||
|
||||
/**
|
||||
* A frame used for each level of the stack. A general purpose
|
||||
* place to store a bunch of execution context and parameters
|
||||
* @param {boolean} warpMode Whether this level of the stack is warping
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
class _StackFrame {
|
||||
constructor (warpMode) {
|
||||
/**
|
||||
* Whether this level of the stack is a loop.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isLoop = false;
|
||||
|
||||
/**
|
||||
* Whether this level is in warp mode. Is set by some legacy blocks and
|
||||
* "turbo mode"
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.warpMode = warpMode;
|
||||
|
||||
/**
|
||||
* Reported value from just executed block.
|
||||
* @type {Any}
|
||||
*/
|
||||
this.justReported = null;
|
||||
|
||||
/**
|
||||
* Persists reported inputs during async block.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.reported = null;
|
||||
|
||||
/**
|
||||
* Name of waiting reporter.
|
||||
* @type {string}
|
||||
*/
|
||||
this.waitingReporter = null;
|
||||
|
||||
/**
|
||||
* Procedure parameters.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.params = null;
|
||||
|
||||
/**
|
||||
* A context passed to block implementations.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.executionContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all properties of the frame to pristine null and false states.
|
||||
* Used to recycle.
|
||||
* @return {_StackFrame} this
|
||||
*/
|
||||
reset () {
|
||||
|
||||
this.isLoop = false;
|
||||
this.warpMode = false;
|
||||
this.justReported = null;
|
||||
this.reported = null;
|
||||
this.waitingReporter = null;
|
||||
this.params = null;
|
||||
this.executionContext = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reuse an active stack frame in the stack.
|
||||
* @param {?boolean} warpMode defaults to current warpMode
|
||||
* @returns {_StackFrame} this
|
||||
*/
|
||||
reuse (warpMode = this.warpMode) {
|
||||
this.reset();
|
||||
this.warpMode = Boolean(warpMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or recycle a stack frame object.
|
||||
* @param {boolean} warpMode Enable warpMode on this frame.
|
||||
* @returns {_StackFrame} The clean stack frame with correct warpMode setting.
|
||||
*/
|
||||
static create (warpMode) {
|
||||
const stackFrame = _stackFrameFreeList.pop();
|
||||
if (typeof stackFrame !== 'undefined') {
|
||||
stackFrame.warpMode = Boolean(warpMode);
|
||||
return stackFrame;
|
||||
}
|
||||
return new _StackFrame(warpMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a stack frame object into the recycle bin for reuse.
|
||||
* @param {_StackFrame} stackFrame The frame to reset and recycle.
|
||||
*/
|
||||
static release (stackFrame) {
|
||||
if (typeof stackFrame !== 'undefined') {
|
||||
_stackFrameFreeList.push(stackFrame.reset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread is a running stack context and all the metadata needed.
|
||||
* @param {?string} firstBlock First block to execute in the thread.
|
||||
|
@ -20,7 +133,7 @@ class Thread {
|
|||
|
||||
/**
|
||||
* Stack frames for the thread. Store metadata for the executing blocks.
|
||||
* @type {Array.<Object>}
|
||||
* @type {Array.<_StackFrame>}
|
||||
*/
|
||||
this.stackFrames = [];
|
||||
|
||||
|
@ -122,20 +235,8 @@ class Thread {
|
|||
// Push an empty stack frame, if we need one.
|
||||
// Might not, if we just popped the stack.
|
||||
if (this.stack.length > this.stackFrames.length) {
|
||||
// Copy warp mode from any higher level.
|
||||
let warpMode = false;
|
||||
if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) {
|
||||
warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode;
|
||||
}
|
||||
this.stackFrames.push({
|
||||
isLoop: false, // Whether this level of the stack is a loop.
|
||||
warpMode: warpMode, // Whether this level is in warp mode.
|
||||
justReported: null, // Reported value from just executed block.
|
||||
reported: {}, // Persists reported inputs during async block.
|
||||
waitingReporter: null, // Name of waiting reporter.
|
||||
params: {}, // Procedure parameters.
|
||||
executionContext: {} // A context passed to block implementations.
|
||||
});
|
||||
const parent = this.stackFrames[this.stackFrames.length - 1];
|
||||
this.stackFrames.push(_StackFrame.create(typeof parent !== 'undefined' && parent.warpMode));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,13 +247,7 @@ class Thread {
|
|||
*/
|
||||
reuseStackForNextBlock (blockId) {
|
||||
this.stack[this.stack.length - 1] = blockId;
|
||||
const frame = this.stackFrames[this.stackFrames.length - 1];
|
||||
frame.isLoop = false;
|
||||
// frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame.
|
||||
frame.reported = {};
|
||||
frame.waitingReporter = null;
|
||||
frame.params = {};
|
||||
frame.executionContext = {};
|
||||
this.stackFrames[this.stackFrames.length - 1].reuse();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +255,7 @@ class Thread {
|
|||
* @return {string} Block ID popped from the stack.
|
||||
*/
|
||||
popStack () {
|
||||
this.stackFrames.pop();
|
||||
_StackFrame.release(this.stackFrames.pop());
|
||||
return this.stack.pop();
|
||||
}
|
||||
|
||||
|
@ -229,6 +324,9 @@ class Thread {
|
|||
*/
|
||||
pushParam (paramName, value) {
|
||||
const stackFrame = this.peekStackFrame();
|
||||
if (stackFrame.params === null) {
|
||||
stackFrame.params = {};
|
||||
}
|
||||
stackFrame.params[paramName] = value;
|
||||
}
|
||||
|
||||
|
@ -240,6 +338,9 @@ class Thread {
|
|||
getParam (paramName) {
|
||||
for (let i = this.stackFrames.length - 1; i >= 0; i--) {
|
||||
const frame = this.stackFrames[i];
|
||||
if (frame.params === null) {
|
||||
continue;
|
||||
}
|
||||
if (frame.params.hasOwnProperty(paramName)) {
|
||||
return frame.params[paramName];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue