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')) ); };