diff --git a/HISTORY.md b/HISTORY.md
index 50875bb3..29bc4876 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -67,6 +67,7 @@
* buttons for saving & loading projects to disk in the project dialog
* more language options for the Text2Speech library, thanks Joan!
* Notable Fixes:
+ * predicates inside generic WHEN hat blocks can now pass upvars
* eliminated "clicks" when playing music notes
* "relabel" blocks with translated drop-down choices
* transforming arrayed to linked lists without loosing the last element
@@ -85,6 +86,9 @@
* German
* French
+### 2019-06-03
+* Threads: fixed #2249, predicates inside generic WHEN hats should be able to pass upvars
+
### 2019-06-02
* Objects, Store: made "pen down?" and "shown?" attributes watchable onstage
* Objects, Blocks: made "shown?" attribute inheritable
diff --git a/snap.html b/snap.html
index ccd118e7..ceed48cf 100755
--- a/snap.html
+++ b/snap.html
@@ -7,7 +7,7 @@
-
+
diff --git a/src/threads.js b/src/threads.js
index c11fa407..bb077d49 100644
--- a/src/threads.js
+++ b/src/threads.js
@@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, Color,
TableFrameMorph, ColorSlotMorph, isSnapObject, Map, newCanvas, Symbol*/
-modules.threads = '2019-May-31';
+modules.threads = '2019-June-03';
var ThreadManager;
var Process;
@@ -115,7 +115,8 @@ function invoke(
timeout, // msecs
timeoutErrorMsg, // string
suppressErrors, // bool
- callerProcess // optional for JS-functions
+ callerProcess, // optional for JS-functions
+ returnContext // bool
) {
// execute the given block or context synchronously without yielding.
// Apply context (not a block) to a list of optional arguments.
@@ -176,7 +177,7 @@ function invoke(
}
proc.runStep(deadline);
}
- return proc.homeContext.inputs[0];
+ return returnContext ? proc.homeContext : proc.homeContext.inputs[0];
}
// ThreadManager ///////////////////////////////////////////////////////
@@ -205,7 +206,8 @@ ThreadManager.prototype.startProcess = function (
callback,
isClicked,
rightAway,
- atomic // special option used (only) for "onStop" scripts
+ atomic, // special option used (only) for "onStop" scripts
+ variables // optional variable frame, used for WHEN hats
) {
var top = block.topBlock(),
active = this.findProcess(top, receiver),
@@ -224,6 +226,18 @@ ThreadManager.prototype.startProcess = function (
newProc.isClicked = isClicked || false;
newProc.isAtomic = atomic || false;
+ // in case an optional variable frame has been passed,
+ // copy it into the new outer context.
+ // Relevance: When a predicate inside a generic WHEN hat block
+ // publishes an upvar, this code makes the upvar accessible
+ // to the script attached to the WHEN hat
+ if (variables instanceof VariableFrame) {
+ Object.keys(variables.vars).forEach(function (vName) {
+ newProc.context.outerContext.variables.vars[vName] =
+ variables.vars[vName];
+ });
+ }
+
// show a highlight around the running stack
// if there are more than one active processes
// for a block, display the thread count
@@ -416,7 +430,7 @@ ThreadManager.prototype.doWhen = function (block, receiver, stopIt) {
if ((!block) || this.findProcess(block, receiver)) {
return;
}
- var pred = block.inputs()[0], world;
+ var pred = block.inputs()[0], world, test;
if (block.removeHighlight()) {
world = block.world();
if (world) {
@@ -425,24 +439,16 @@ ThreadManager.prototype.doWhen = function (block, receiver, stopIt) {
}
if (stopIt) {return; }
try {
- if (invoke(
+ test = invoke(
pred,
null,
receiver,
- 50,
+ 50, // timeout in msecs
'the predicate takes\ntoo long for a\ncustom hat block',
- true // suppress errors => handle them right here instead
- ) === true) {
- this.startProcess(
- block,
- receiver,
- null,
- null,
- null,
- null,
- true // atomic
- );
- }
+ true, // suppress errors => handle them right here instead
+ null, // caller process for JS-functions
+ true // return the whole home context instead of just he result
+ );
} catch (error) {
block.addErrorHighlight();
block.showBubble(
@@ -451,6 +457,22 @@ ThreadManager.prototype.doWhen = function (block, receiver, stopIt) {
+ error.message
);
}
+ // since we're asking for the whole context instead of just the result
+ // of the computation, we need to look at the result-context's first
+ // input to find out whether the condition is met
+ if (test && test.inputs[0] === true) {
+ this.startProcess(
+ block,
+ receiver,
+ null, // isThreadSafe
+ null, // exportResult
+ null, // callback
+ null, // isClicked
+ true, // rightAway
+ null, // atomic
+ test.variables // make the test-context's variables available
+ );
+ }
};
ThreadManager.prototype.toggleSingleStepping = function () {
@@ -4811,7 +4833,7 @@ Process.prototype.reportNewCostume = function (pixels, width, height) {
ctx.putImageData(dta, 0, 0);
return new Costume(
canvas,
- this.blockReceiver().newCostumeName(localize('snap')) // +++
+ this.blockReceiver().newCostumeName(localize('snap'))
);
};