Skip to content

Commit

Permalink
Add Thread.StackFrame class
Browse files Browse the repository at this point in the history
Use a private StackFrame class to help internally manage use of Stack
Frame values and memory. Create params, reported, and executionContext
on demand.
  • Loading branch information
mzgoddard committed May 4, 2018
1 parent 4cee734 commit 0fe87f6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 21 deletions.
6 changes: 5 additions & 1 deletion src/engine/block-utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions src/engine/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
}
Expand Down
77 changes: 61 additions & 16 deletions src/engine/thread.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,54 @@
const _stackFrameFreeList = [];

class StackFrame {
constructor (warpMode) {
// Whether this level of the stack is a loop.
this.isLoop = false;
// Whether this level is in warp mode.
this.warpMode = warpMode;
// Reported value from just executed block.
this.justReported = null;
// Persists reported inputs during async block.
this.reported = null;
// Name of waiting reporter.
this.waitingReporter = null;
// Procedure parameters.
this.params = null;
// A context passed to block implementations.
this.executionContext = null;
}

reset () {
this.isLoop = false;
this.warpMode = false;
this.justReported = null;
this.reported = null;
this.waitingReporter = null;
this.params = null;
this.executionContext = null;
return this;
}

use (warpMode) {
this.reset();
this.warpMode = warpMode;
return this;
}

static create (warpMode) {
const stackFrame = _stackFrameFreeList.pop();
if (typeof stackFrame !== 'undefined') {
stackFrame.warpMode = warpMode;
return stackFrame;
}
return new StackFrame(warpMode);
}

static release (stackFrame) {
_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.
Expand Down Expand Up @@ -127,15 +178,7 @@ class Thread {
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.
});
this.stackFrames.push(StackFrame.create(warpMode));
}
}

Expand All @@ -147,20 +190,16 @@ 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 = {};
// warp mode stays the same when reusing the stack frame.
frame.use(frame.warpMode);
}

/**
* Pop last block on the stack and its stack frame.
* @return {string} Block ID popped from the stack.
*/
popStack () {
this.stackFrames.pop();
StackFrame.release(this.stackFrames.pop());
return this.stack.pop();
}

Expand Down Expand Up @@ -229,6 +268,9 @@ class Thread {
*/
pushParam (paramName, value) {
const stackFrame = this.peekStackFrame();
if (stackFrame.params === null) {
stackFrame.params = {};
}
stackFrame.params[paramName] = value;
}

Expand All @@ -240,6 +282,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];
}
Expand Down

0 comments on commit 0fe87f6

Please sign in to comment.