mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-08 22:11:59 -05:00
Merge pull request #19 from tmickel/feature/repeats
Stacks management, stack frames, repeat, forever
This commit is contained in:
commit
5b834667eb
5 changed files with 155 additions and 21 deletions
|
@ -22,12 +22,23 @@ Scratch3Blocks.prototype.getPrimitives = function() {
|
|||
};
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.repeat = function() {
|
||||
Scratch3Blocks.prototype.repeat = function(argValues, util) {
|
||||
console.log('Running: control_repeat');
|
||||
// Initialize loop
|
||||
if (util.stackFrame.loopCounter === undefined) {
|
||||
util.stackFrame.loopCounter = 4; // @todo arg
|
||||
}
|
||||
// Decrease counter
|
||||
util.stackFrame.loopCounter--;
|
||||
// If we still have some left, start the substack
|
||||
if (util.stackFrame.loopCounter >= 0) {
|
||||
util.startSubstack();
|
||||
}
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.forever = function() {
|
||||
Scratch3Blocks.prototype.forever = function(argValues, util) {
|
||||
console.log('Running: control_forever');
|
||||
util.startSubstack();
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.wait = function(argValues, util) {
|
||||
|
|
|
@ -57,9 +57,20 @@ Sequencer.prototype.stepThreads = function (threads) {
|
|||
activeThread.status = Thread.STATUS_RUNNING;
|
||||
// @todo Deal with the return value
|
||||
}
|
||||
// First attempt to pop from the stack
|
||||
if (activeThread.stack.length > 0 &&
|
||||
activeThread.nextBlock === null &&
|
||||
activeThread.status === Thread.STATUS_DONE) {
|
||||
activeThread.nextBlock = activeThread.stack.pop();
|
||||
// Don't pop stack frame - we need the data.
|
||||
// A new one won't be created when we execute.
|
||||
if (activeThread.nextBlock !== null) {
|
||||
activeThread.status === Thread.STATUS_RUNNING;
|
||||
}
|
||||
}
|
||||
if (activeThread.nextBlock === null &&
|
||||
activeThread.status === Thread.STATUS_DONE) {
|
||||
// Finished with this thread - tell the runtime to clean it up.
|
||||
// Finished with this thread - tell runtime to clean it up.
|
||||
inactiveThreads.push(activeThread);
|
||||
} else {
|
||||
// Keep this thead in the loop.
|
||||
|
@ -86,10 +97,23 @@ Sequencer.prototype.stepThread = function (thread) {
|
|||
// If the primitive would like to do control flow,
|
||||
// it can overwrite nextBlock.
|
||||
var currentBlock = thread.nextBlock;
|
||||
if (!currentBlock) {
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
return;
|
||||
}
|
||||
thread.nextBlock = this.runtime._getNextBlock(currentBlock);
|
||||
|
||||
var opcode = this.runtime._getOpcode(currentBlock);
|
||||
|
||||
// Push the current block to the stack
|
||||
thread.stack.push(currentBlock);
|
||||
// Push an empty stack frame, if we need one.
|
||||
// Might not, if we just popped the stack.
|
||||
if (thread.stack.length > thread.stackFrames.length) {
|
||||
thread.stackFrames.push({});
|
||||
}
|
||||
var currentStackFrame = thread.stackFrames[thread.stackFrames.length - 1];
|
||||
|
||||
/**
|
||||
* A callback for the primitive to indicate its thread should yield.
|
||||
* @type {Function}
|
||||
|
@ -105,9 +129,34 @@ Sequencer.prototype.stepThread = function (thread) {
|
|||
var instance = this;
|
||||
var threadDoneCallback = function () {
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
// Refresh nextBlock in case it has changed during the yield.
|
||||
// Refresh nextBlock in case it has changed during a yield.
|
||||
thread.nextBlock = instance.runtime._getNextBlock(currentBlock);
|
||||
instance.runtime.glowBlock(currentBlock, false);
|
||||
// Pop the stack and stack frame
|
||||
thread.stack.pop();
|
||||
thread.stackFrames.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* Record whether we have switched stack,
|
||||
* to avoid proceeding the thread automatically.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var switchedStack = false;
|
||||
/**
|
||||
* A callback for a primitive to start a substack.
|
||||
* @type {Function}
|
||||
*/
|
||||
var threadStartSubstack = function () {
|
||||
// Set nextBlock to the start of the substack
|
||||
var substack = instance.runtime._getSubstack(currentBlock);
|
||||
if (substack && substack.value) {
|
||||
thread.nextBlock = substack.value;
|
||||
} else {
|
||||
thread.nextBlock = null;
|
||||
}
|
||||
instance.runtime.glowBlock(currentBlock, false);
|
||||
switchedStack = true;
|
||||
};
|
||||
|
||||
// @todo
|
||||
|
@ -128,7 +177,9 @@ Sequencer.prototype.stepThread = function (thread) {
|
|||
blockFunction(argValues, {
|
||||
yield: threadYieldCallback,
|
||||
done: threadDoneCallback,
|
||||
timeout: YieldTimers.timeout
|
||||
timeout: YieldTimers.timeout,
|
||||
stackFrame: currentStackFrame,
|
||||
startSubstack: threadStartSubstack
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
|
@ -141,10 +192,9 @@ Sequencer.prototype.stepThread = function (thread) {
|
|||
if (YieldTimers.timerId > oldYieldTimerId) {
|
||||
thread.yieldTimerId = YieldTimers.timerId;
|
||||
}
|
||||
if (thread.status === Thread.STATUS_RUNNING) {
|
||||
if (thread.status === Thread.STATUS_RUNNING && !switchedStack) {
|
||||
// Thread executed without yielding - move to done
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
this.runtime.glowBlock(currentBlock, false);
|
||||
threadDoneCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,12 @@ function Thread (firstBlock) {
|
|||
*/
|
||||
this.stack = [];
|
||||
|
||||
/**
|
||||
* Stack frames for the thread. Store metadata for the executing blocks.
|
||||
* @type {Array.<Object>}
|
||||
*/
|
||||
this.stackFrames = [];
|
||||
|
||||
/**
|
||||
* Status of the thread, one of three states (below)
|
||||
* @type {number}
|
||||
|
|
83
vm.js
83
vm.js
|
@ -1621,9 +1621,20 @@
|
|||
activeThread.status = Thread.STATUS_RUNNING;
|
||||
// @todo Deal with the return value
|
||||
}
|
||||
// First attempt to pop from the stack
|
||||
if (activeThread.stack.length > 0 &&
|
||||
activeThread.nextBlock === null &&
|
||||
activeThread.status === Thread.STATUS_DONE) {
|
||||
activeThread.nextBlock = activeThread.stack.pop();
|
||||
// Don't pop stack frame - we need the data.
|
||||
// A new one won't be created when we execute.
|
||||
if (activeThread.nextBlock !== null) {
|
||||
activeThread.status === Thread.STATUS_RUNNING;
|
||||
}
|
||||
}
|
||||
if (activeThread.nextBlock === null &&
|
||||
activeThread.status === Thread.STATUS_DONE) {
|
||||
// Finished with this thread - tell the runtime to clean it up.
|
||||
// Finished with this thread - tell runtime to clean it up.
|
||||
inactiveThreads.push(activeThread);
|
||||
} else {
|
||||
// Keep this thead in the loop.
|
||||
|
@ -1650,10 +1661,23 @@
|
|||
// If the primitive would like to do control flow,
|
||||
// it can overwrite nextBlock.
|
||||
var currentBlock = thread.nextBlock;
|
||||
if (!currentBlock) {
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
return;
|
||||
}
|
||||
thread.nextBlock = this.runtime._getNextBlock(currentBlock);
|
||||
|
||||
var opcode = this.runtime._getOpcode(currentBlock);
|
||||
|
||||
// Push the current block to the stack
|
||||
thread.stack.push(currentBlock);
|
||||
// Push an empty stack frame, if we need one.
|
||||
// Might not, if we just popped the stack.
|
||||
if (thread.stack.length > thread.stackFrames.length) {
|
||||
thread.stackFrames.push({});
|
||||
}
|
||||
var currentStackFrame = thread.stackFrames[thread.stackFrames.length - 1];
|
||||
|
||||
/**
|
||||
* A callback for the primitive to indicate its thread should yield.
|
||||
* @type {Function}
|
||||
|
@ -1669,9 +1693,34 @@
|
|||
var instance = this;
|
||||
var threadDoneCallback = function () {
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
// Refresh nextBlock in case it has changed during the yield.
|
||||
// Refresh nextBlock in case it has changed during a yield.
|
||||
thread.nextBlock = instance.runtime._getNextBlock(currentBlock);
|
||||
instance.runtime.glowBlock(currentBlock, false);
|
||||
// Pop the stack and stack frame
|
||||
thread.stack.pop();
|
||||
thread.stackFrames.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* Record whether we have switched stack,
|
||||
* to avoid proceeding the thread automatically.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var switchedStack = false;
|
||||
/**
|
||||
* A callback for a primitive to start a substack.
|
||||
* @type {Function}
|
||||
*/
|
||||
var threadStartSubstack = function () {
|
||||
// Set nextBlock to the start of the substack
|
||||
var substack = instance.runtime._getSubstack(currentBlock);
|
||||
if (substack && substack.value) {
|
||||
thread.nextBlock = substack.value;
|
||||
} else {
|
||||
thread.nextBlock = null;
|
||||
}
|
||||
instance.runtime.glowBlock(currentBlock, false);
|
||||
switchedStack = true;
|
||||
};
|
||||
|
||||
// @todo
|
||||
|
@ -1692,7 +1741,9 @@
|
|||
blockFunction(argValues, {
|
||||
yield: threadYieldCallback,
|
||||
done: threadDoneCallback,
|
||||
timeout: YieldTimers.timeout
|
||||
timeout: YieldTimers.timeout,
|
||||
stackFrame: currentStackFrame,
|
||||
startSubstack: threadStartSubstack
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
|
@ -1704,10 +1755,9 @@
|
|||
if (YieldTimers.timerId > oldYieldTimerId) {
|
||||
thread.yieldTimerId = YieldTimers.timerId;
|
||||
}
|
||||
if (thread.status === Thread.STATUS_RUNNING) {
|
||||
if (thread.status === Thread.STATUS_RUNNING && !switchedStack) {
|
||||
// Thread executed without yielding - move to done
|
||||
thread.status = Thread.STATUS_DONE;
|
||||
this.runtime.glowBlock(currentBlock, false);
|
||||
threadDoneCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1771,6 +1821,12 @@
|
|||
*/
|
||||
this.stack = [];
|
||||
|
||||
/**
|
||||
* Stack frames for the thread. Store metadata for the executing blocks.
|
||||
* @type {Array.<Object>}
|
||||
*/
|
||||
this.stackFrames = [];
|
||||
|
||||
/**
|
||||
* Status of the thread, one of three states (below)
|
||||
* @type {number}
|
||||
|
@ -1933,12 +1989,23 @@
|
|||
};
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.repeat = function() {
|
||||
Scratch3Blocks.prototype.repeat = function(argValues, util) {
|
||||
console.log('Running: control_repeat');
|
||||
// Initialize loop
|
||||
if (util.stackFrame.loopCounter === undefined) {
|
||||
util.stackFrame.loopCounter = 4; // @todo arg
|
||||
}
|
||||
// Decrease counter
|
||||
util.stackFrame.loopCounter--;
|
||||
// If we still have some left, start the substack
|
||||
if (util.stackFrame.loopCounter >= 0) {
|
||||
util.startSubstack();
|
||||
}
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.forever = function() {
|
||||
Scratch3Blocks.prototype.forever = function(argValues, util) {
|
||||
console.log('Running: control_forever');
|
||||
util.startSubstack();
|
||||
};
|
||||
|
||||
Scratch3Blocks.prototype.wait = function(argValues, util) {
|
||||
|
|
10
vm.min.js
vendored
10
vm.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue