Evaluator optimizations

reducing the stack size for reporters
pull/3/merge
jmoenig 2014-11-25 12:24:20 +01:00
rodzic 4be96bb240
commit 75849a59a2
2 zmienionych plików z 67 dodań i 66 usunięć

Wyświetl plik

@ -2342,7 +2342,7 @@ ______
-------
* Threads: Fix “stop this block” primitive for tail-call-elimination
1411214
1411224
-------
* Threads: Fixed #318
* Objects: Fixed #416
@ -2353,3 +2353,7 @@ ______
* snap.html, favicon.ico: new Favicon, thanks, Michael!
* Threads: improved whitespace detection for “split” primitive, thanks, Michael!
* Threads: tail-call-elimination for reporters experiment (commented out, under construction)
1411225
-------
* Threads: Evaluator optimizations (reducing the stack size for reporters)

Wyświetl plik

@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff ////////////////////////////////////////////////////////
modules.threads = '2014-November-24';
modules.threads = '2014-November-25';
var ThreadManager;
var Process;
@ -218,11 +218,10 @@ ThreadManager.prototype.resumeAll = function (stage) {
};
ThreadManager.prototype.step = function () {
/*
run each process until it gives up control, skipping processes
for sprites that are currently picked up, then filter out any
processes that have been terminated
*/
// run each process until it gives up control, skipping processes
// for sprites that are currently picked up, then filter out any
// processes that have been terminated
this.processes.forEach(function (proc) {
if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) {
proc.runStep();
@ -384,13 +383,13 @@ Process.prototype.isRunning = function () {
// Process entry points
Process.prototype.runStep = function () {
/*
a step is an an uninterruptable 'atom', it can consist
of several contexts, even of several blocks
*/
// a step is an an uninterruptable 'atom', it can consist
// of several contexts, even of several blocks
if (this.isPaused) { // allow pausing in between atomic steps:
return this.pauseStep();
}
this.readyToYield = false;
while (!this.readyToYield
&& this.context
@ -459,6 +458,9 @@ Process.prototype.pauseStep = function () {
Process.prototype.evaluateContext = function () {
var exp = this.context.expression;
this.frameCount += 1;
if (this.context.tag === 'exit') {
this.expectReport();
}
if (exp instanceof Array) {
return this.evaluateSequence(exp);
}
@ -482,7 +484,7 @@ Process.prototype.evaluateContext = function () {
Process.prototype.evaluateBlock = function (block, argCount) {
// check for special forms
if (contains(['reportOr', 'reportAnd'], block.selector)) {
if (contains(['reportOr', 'reportAnd', 'doReport'], block.selector)) {
return this[block.selector](block);
}
@ -548,33 +550,29 @@ Process.prototype.reportAnd = function (block) {
}
};
/*
tail-call-elimination for reporters
-----------------------------------
currently under construction, commented out for now
to activate add 'doReport' to the list of special forms
selectors in evaluateBlock() and comment out / remove
the current 'doReport' primitive in the "return"
section of the code
Process.prototype.doReport = function (block) {
// if (this.context.expression.partOfCustomCommand) {
// return this.doStopCustomBlock();
// }
if (this.context.expression.partOfCustomCommand) {
this.doStopCustomBlock();
this.popContext();
return;
}
var outer = this.context.outerContext;
while (this.context && this.context.expression !== 'exitReporter') {
while (this.context && this.context.tag !== 'exit') {
if (this.context.expression === 'doStopWarping') {
this.doStopWarping();
} else {
this.popContext();
}
}
this.popContext();
if (this.context.expression === 'expectReport') {
// pop off inserted top-level exit context
this.popContext();
} else {
// un-tag and preserve original caller
this.context.tag = null;
}
this.pushContext(block.inputs()[0], outer);
};
*/
// Process: Non-Block evaluation
@ -724,9 +722,8 @@ Process.prototype.doYield = function () {
}
};
Process.prototype.exitReporter = function () {
// catch-tag for REPORT and STOP BLOCK primitives
this.handleError(new Error("missing 'report' statement in reporter"));
Process.prototype.expectReport = function () {
this.handleError(new Error("reporter didn't report"));
};
// Process Exception Handling
@ -831,9 +828,10 @@ Process.prototype.evaluate = function (
}
var outer = new Context(null, null, context.outerContext),
caller = this.context.parentContext,
exit,
runnable,
extra,
exit,
parms = args.asArray(),
i,
value;
@ -904,17 +902,22 @@ Process.prototype.evaluate = function (
if (runnable.expression instanceof CommandBlockMorph) {
runnable.expression = runnable.expression.blockSequence();
// insert a reporter exit tag for the
// CALL SCRIPT primitive variant
if (!isCommand) {
exit = new Context(
runnable.parentContext,
'exitReporter',
outer,
outer.receiver
);
runnable.parentContext = exit;
if (caller) {
// tag caller, so "report" can catch it later
caller.tag = 'exit';
} else {
// top-level context, insert a tagged exit context
// which "report" can catch later
exit = new Context(
runnable.parentContext,
'expectReport',
outer,
outer.receiver
);
exit.tag = 'exit';
runnable.parentContext = exit;
}
}
}
};
@ -993,21 +996,7 @@ Process.prototype.fork = function (context, args) {
stage.threads.processes.push(proc);
};
// Process "return" primitives
Process.prototype.doReport = function (value) {
if (this.context.expression.partOfCustomCommand) {
return this.doStopCustomBlock();
}
while (this.context && this.context.expression !== 'exitReporter') {
if (this.context.expression === 'doStopWarping') {
this.doStopWarping();
} else {
this.popContext();
}
}
return value;
};
// Process stopping blocks primitives
Process.prototype.doStopBlock = function () {
var target = this.context.expression.exitTag;
@ -1119,13 +1108,21 @@ Process.prototype.evaluateCustomBlock = function () {
// insert a reporter exit tag for the
// CALL SCRIPT primitive variant
if (this.context.expression.definition.type !== 'command') {
exit = new Context(
runnable.parentContext,
'exitReporter',
outer,
outer.receiver
);
runnable.parentContext = exit;
if (caller) {
// tag caller, so "report" can catch it later
caller.tag = 'exit';
} else {
// top-level context, insert a tagged exit context
// which "report" can catch later
exit = new Context(
runnable.parentContext,
'expectReport',
outer,
outer.receiver
);
exit.tag = 'exit';
runnable.parentContext = exit;
}
} else {
// tag all "stop this block" blocks with the current
// procedureCount as exitTag, and mark all "report" blocks
@ -2925,7 +2922,7 @@ Context.prototype.continuation = function () {
} else {
return new Context(null, 'doStop');
}
if (cont.expression === 'exitReporter') {
if (cont.expression === 'expectReport') {
return cont.continuation();
}
cont = cont.copyForContinuation();