diff --git a/src/blocks/scratch3_procedures.js b/src/blocks/scratch3_procedures.js
index 498116de65..b981a0389e 100644
--- a/src/blocks/scratch3_procedures.js
+++ b/src/blocks/scratch3_procedures.js
@@ -15,6 +15,7 @@ class Scratch3ProcedureBlocks {
return {
procedures_definition: this.definition,
procedures_call: this.call,
+ procedures_return: this.return,
argument_reporter_string_number: this.argumentReporterStringNumber,
argument_reporter_boolean: this.argumentReporterBoolean
};
@@ -25,45 +26,76 @@ class Scratch3ProcedureBlocks {
}
call (args, util) {
- if (!util.stackFrame.executed) {
- const procedureCode = args.mutation.proccode;
- const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
-
- // If null, procedure could not be found, which can happen if custom
- // block is dragged between sprites without the definition.
- // Match Scratch 2.0 behavior and noop.
- if (paramNamesIdsAndDefaults === null) {
- return;
+ const stackFrame = util.stackFrame;
+ const isReporter = !!args.mutation.return;
+
+ if (stackFrame.executed) {
+ if (isReporter) {
+ const returnValue = stackFrame.returnValue;
+ // This stackframe will be reused for other reporters in this block, so clean it up for them.
+ // Can't use reset() because that will reset too much.
+ const threadStackFrame = util.thread.peekStackFrame();
+ threadStackFrame.params = null;
+ threadStackFrame.executionContext = null;
+ return returnValue;
}
+ return;
+ }
- const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
-
- // Initialize params for the current stackFrame to {}, even if the procedure does
- // not take any arguments. This is so that `getParam` down the line does not look
- // at earlier stack frames for the values of a given parameter (#1729)
- util.initParams();
- for (let i = 0; i < paramIds.length; i++) {
- if (args.hasOwnProperty(paramIds[i])) {
- util.pushParam(paramNames[i], args[paramIds[i]]);
- } else {
- util.pushParam(paramNames[i], paramDefaults[i]);
- }
+ const procedureCode = args.mutation.proccode;
+ const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
+
+ // If null, procedure could not be found, which can happen if custom
+ // block is dragged between sprites without the definition.
+ // Match Scratch 2.0 behavior and noop.
+ if (paramNamesIdsAndDefaults === null) {
+ if (isReporter) {
+ return '';
}
+ return;
+ }
- const addonBlock = util.runtime.getAddonBlock(procedureCode);
- if (addonBlock) {
- const result = addonBlock.callback(util.thread.getAllparams(), util);
- if (util.thread.status === 1 /* STATUS_PROMISE_WAIT */) {
- // If the addon block is using STATUS_PROMISE_WAIT to force us to sleep,
- // make sure to not re-run this block when we resume.
- util.stackFrame.executed = true;
- }
- return result;
+ const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
+
+ // Initialize params for the current stackFrame to {}, even if the procedure does
+ // not take any arguments. This is so that `getParam` down the line does not look
+ // at earlier stack frames for the values of a given parameter (#1729)
+ util.initParams();
+ for (let i = 0; i < paramIds.length; i++) {
+ if (args.hasOwnProperty(paramIds[i])) {
+ util.pushParam(paramNames[i], args[paramIds[i]]);
+ } else {
+ util.pushParam(paramNames[i], paramDefaults[i]);
}
+ }
- util.stackFrame.executed = true;
+ const addonBlock = util.runtime.getAddonBlock(procedureCode);
+ if (addonBlock) {
+ const result = addonBlock.callback(util.thread.getAllparams(), util);
+ if (util.thread.status === 1 /* STATUS_PROMISE_WAIT */) {
+ // If the addon block is using STATUS_PROMISE_WAIT to force us to sleep,
+ // make sure to not re-run this block when we resume.
+ stackFrame.executed = true;
+ }
+ return result;
+ }
+
+ stackFrame.executed = true;
+
+ if (isReporter) {
+ util.thread.peekStackFrame().waitingReporter = true;
+ // Default return value
+ stackFrame.returnValue = '';
+ }
+
+ util.startProcedure(procedureCode);
+ }
- util.startProcedure(procedureCode);
+ return (args, util) {
+ util.stopThisScript();
+ // If used outside of a custom block, there may be no stackframe.
+ if (util.thread.peekStackFrame()) {
+ util.stackFrame.returnValue = args.VALUE;
}
}
diff --git a/src/compiler/irgen.js b/src/compiler/irgen.js
index 4a00a626f2..45f3735111 100644
--- a/src/compiler/irgen.js
+++ b/src/compiler/irgen.js
@@ -535,6 +535,9 @@ class ScriptTreeGenerator {
right: this.descendInputOfBlock(block, 'NUM2')
};
+ case 'procedures_call':
+ return this.descendProcedure(block);
+
case 'sensing_answer':
return {
kind: 'sensing.answer'
@@ -1106,102 +1109,25 @@ class ScriptTreeGenerator {
};
case 'procedures_call': {
- // setting of yields will be handled later in the analysis phase
-
const procedureCode = block.mutation.proccode;
- if (procedureCode === 'tw:debugger;') {
- return {
- kind: 'tw.debugger'
- };
- }
- const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
- if (paramNamesIdsAndDefaults === null) {
- return {
- kind: 'noop'
- };
- }
-
- const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
-
- const addonBlock = this.runtime.getAddonBlock(procedureCode);
- if (addonBlock) {
- this.script.yields = true;
- const args = {};
- for (let i = 0; i < paramIds.length; i++) {
- let value;
- if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
- value = this.descendInputOfBlock(block, paramIds[i]);
- } else {
- value = {
- kind: 'constant',
- value: paramDefaults[i]
- };
- }
- args[paramNames[i]] = value;
+ if (block.mutation.return) {
+ const visualReport = this.descendVisualReport(block);
+ if (visualReport) {
+ return visualReport;
}
- return {
- kind: 'addons.call',
- code: procedureCode,
- arguments: args,
- blockId: block.id
- };
}
-
- const definitionId = this.blocks.getProcedureDefinition(procedureCode);
- const definitionBlock = this.blocks.getBlock(definitionId);
- if (!definitionBlock) {
+ if (procedureCode === 'tw:debugger;') {
return {
- kind: 'noop'
+ kind: 'tw.debugger'
};
}
- const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
-
- let isWarp = this.script.isWarp;
- if (!isWarp) {
- if (innerDefinition && innerDefinition.mutation) {
- const warp = innerDefinition.mutation.warp;
- if (typeof warp === 'boolean') {
- isWarp = warp;
- } else if (typeof warp === 'string') {
- isWarp = JSON.parse(warp);
- }
- }
- }
-
- const variant = generateProcedureVariant(procedureCode, isWarp);
-
- if (!this.script.dependedProcedures.includes(variant)) {
- this.script.dependedProcedures.push(variant);
- }
-
- // Non-warp direct recursion yields.
- if (!this.script.isWarp) {
- if (procedureCode === this.script.procedureCode) {
- this.script.yields = true;
- }
- }
-
- const args = [];
- for (let i = 0; i < paramIds.length; i++) {
- let value;
- if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
- value = this.descendInputOfBlock(block, paramIds[i]);
- } else {
- value = {
- kind: 'constant',
- value: paramDefaults[i]
- };
- }
- args.push(value);
- }
-
+ return this.descendProcedure(block);
+ }
+ case 'procedures_return':
return {
- kind: 'procedures.call',
- code: procedureCode,
- variant,
- arguments: args
+ kind: 'procedures.return',
+ value: this.descendInputOfBlock(block, 'VALUE')
};
- }
case 'sensing_resettimer':
return {
@@ -1225,18 +1151,9 @@ class ScriptTreeGenerator {
}
}
- // When this thread was triggered by a stack click, attempt to compile as an input.
- // TODO: perhaps this should be moved to generate()?
- if (this.thread.stackClick) {
- try {
- const inputNode = this.descendInput(block);
- return {
- kind: 'visualReport',
- input: inputNode
- };
- } catch (e) {
- // Ignore
- }
+ const asVisualReport = this.descendVisualReport(block);
+ if (asVisualReport) {
+ return asVisualReport;
}
log.warn(`IR: Unknown stacked block: ${block.opcode}`, block);
@@ -1370,6 +1287,97 @@ class ScriptTreeGenerator {
return createVariableData('target', newVariable);
}
+ descendProcedure (block) {
+ const procedureCode = block.mutation.proccode;
+ const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
+ if (paramNamesIdsAndDefaults === null) {
+ return {
+ kind: 'noop'
+ };
+ }
+
+ const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
+
+ const addonBlock = this.runtime.getAddonBlock(procedureCode);
+ if (addonBlock) {
+ this.script.yields = true;
+ const args = {};
+ for (let i = 0; i < paramIds.length; i++) {
+ let value;
+ if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
+ value = this.descendInputOfBlock(block, paramIds[i]);
+ } else {
+ value = {
+ kind: 'constant',
+ value: paramDefaults[i]
+ };
+ }
+ args[paramNames[i]] = value;
+ }
+ return {
+ kind: 'addons.call',
+ code: procedureCode,
+ arguments: args,
+ blockId: block.id
+ };
+ }
+
+ const definitionId = this.blocks.getProcedureDefinition(procedureCode);
+ const definitionBlock = this.blocks.getBlock(definitionId);
+ if (!definitionBlock) {
+ return {
+ kind: 'noop'
+ };
+ }
+ const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
+
+ let isWarp = this.script.isWarp;
+ if (!isWarp) {
+ if (innerDefinition && innerDefinition.mutation) {
+ const warp = innerDefinition.mutation.warp;
+ if (typeof warp === 'boolean') {
+ isWarp = warp;
+ } else if (typeof warp === 'string') {
+ isWarp = JSON.parse(warp);
+ }
+ }
+ }
+
+ const variant = generateProcedureVariant(procedureCode, isWarp);
+
+ if (!this.script.dependedProcedures.includes(variant)) {
+ this.script.dependedProcedures.push(variant);
+ }
+
+ // Non-warp direct recursion yields.
+ if (!this.script.isWarp) {
+ if (procedureCode === this.script.procedureCode) {
+ this.script.yields = true;
+ }
+ }
+
+ const args = [];
+ for (let i = 0; i < paramIds.length; i++) {
+ let value;
+ if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
+ value = this.descendInputOfBlock(block, paramIds[i]);
+ } else {
+ value = {
+ kind: 'constant',
+ value: paramDefaults[i]
+ };
+ }
+ args.push(value);
+ }
+
+ return {
+ kind: 'procedures.call',
+ code: procedureCode,
+ variant,
+ arguments: args
+ };
+ }
+
/**
* Descend into a block that uses the compatibility layer.
* @param {*} block The block to use the compatibility layer for.
@@ -1431,6 +1439,20 @@ class ScriptTreeGenerator {
}
}
+ descendVisualReport (block) {
+ if (!this.thread.stackClick || block.next) {
+ return null;
+ }
+ try {
+ return {
+ kind: 'visualReport',
+ input: this.descendInput(block)
+ };
+ } catch (e) {
+ return null;
+ }
+ }
+
/**
* @param {string} topBlockId The ID of the top block of the script.
* @returns {IntermediateScript}
diff --git a/src/compiler/jsexecute.js b/src/compiler/jsexecute.js
index e9e4a3ac68..40109b339f 100644
--- a/src/compiler/jsexecute.js
+++ b/src/compiler/jsexecute.js
@@ -549,6 +549,26 @@ runtimeFunctions.tan = `const tan = (angle) => {
return Math.round(Math.tan((Math.PI * angle) / 180) * 1e10) / 1e10;
}`;
+/**
+ * @param {function} callback The function to run
+ * @param {...unknown} args The arguments to pass to the function
+ * @returns {unknown} A generator that will yield once then call the function and return its value.
+ */
+runtimeFunctions.yieldThenCall = `const yieldThenCall = function* (callback, ...args) {
+ yield;
+ return callback(...args);
+}`;
+
+/**
+ * @param {function} callback The generator function to run
+ * @param {...unknown} args The arguments to pass to the generator function
+ * @returns {unknown} A generator that will yield once then delegate to the generator function and return its value.
+ */
+runtimeFunctions.yieldThenCallGenerator = `const yieldThenCallGenerator = function* (callback, ...args) {
+ yield;
+ return yield* callback(...args);
+}`;
+
/**
* Step a compiled thread.
* @param {Thread} thread The thread to step.
diff --git a/src/compiler/jsgen.js b/src/compiler/jsgen.js
index 6dea28b15f..ac3d3e2f20 100644
--- a/src/compiler/jsgen.js
+++ b/src/compiler/jsgen.js
@@ -428,6 +428,9 @@ class JSGenerator {
*/
descendInput (node) {
switch (node.kind) {
+ case 'addons.call':
+ return new TypedInput(`(${this.descendAddonCall(node)})`, TYPE_UNKNOWN);
+
case 'args.boolean':
return new TypedInput(`toBoolean(p${node.index})`, TYPE_BOOLEAN);
case 'args.stringNumber':
@@ -623,6 +626,37 @@ class JSGenerator {
case 'op.10^':
return new TypedInput(`(10 ** ${this.descendInput(node.value).asNumber()})`, TYPE_NUMBER);
+ case 'procedures.call': {
+ const procedureCode = node.code;
+ const procedureVariant = node.variant;
+ const procedureData = this.ir.procedures[procedureVariant];
+ if (procedureData.stack === null) {
+ // TODO still need to evaluate arguments for side effects
+ return new TypedInput('""', TYPE_STRING);
+ }
+
+ // Recursion makes this complicated because:
+ // - We need to yield *between* each call in the same command block
+ // - We need to evaluate arguments *before* that yield happens
+
+ const procedureReference = `thread.procedures["${sanitize(procedureVariant)}"]`;
+ const args = [];
+ for (const input of node.arguments) {
+ args.push(this.descendInput(input).asSafe());
+ }
+ const joinedArgs = args.join(',');
+
+ const yieldForRecursion = !this.isWarp && procedureCode === this.script.procedureCode;
+ if (yieldForRecursion) {
+ const runtimeFunction = procedureData.yields ? 'yieldThenCallGenerator' : 'yieldThenCall';
+ return new TypedInput(`(yield* ${runtimeFunction}(${procedureReference}, ${joinedArgs}))`, TYPE_UNKNOWN);
+ }
+ if (procedureData.yields) {
+ return new TypedInput(`(yield* ${procedureReference}(${joinedArgs}))`, TYPE_UNKNOWN);
+ }
+ return new TypedInput(`${procedureReference}(${joinedArgs})`, TYPE_UNKNOWN);
+ }
+
case 'sensing.answer':
return new TypedInput(`runtime.ext_scratch3_sensing._answer`, TYPE_STRING);
case 'sensing.colorTouchingColor':
@@ -713,13 +747,9 @@ class JSGenerator {
*/
descendStackedBlock (node) {
switch (node.kind) {
- case 'addons.call': {
- const inputs = this.descendInputRecord(node.arguments);
- const blockFunction = `runtime.getAddonBlock("${sanitize(node.code)}").callback`;
- const blockId = `"${sanitize(node.blockId)}"`;
- this.source += `yield* executeInCompatibilityLayer(${inputs}, ${blockFunction}, ${this.isWarp}, false, ${blockId});\n`;
+ case 'addons.call':
+ this.source += `${this.descendAddonCall(node)};\n`;
break;
- }
case 'compat': {
// If the last command in a loop returns a promise, immediately continue to the next iteration.
@@ -781,11 +811,7 @@ class JSGenerator {
this.source += 'runtime.stopForTarget(target, thread);\n';
break;
case 'control.stopScript':
- if (this.isProcedure) {
- this.source += 'return;\n';
- } else {
- this.retire();
- }
+ this.stopScript();
break;
case 'control.wait': {
const duration = this.localVariables.next();
@@ -1016,35 +1042,34 @@ class JSGenerator {
case 'procedures.call': {
const procedureCode = node.code;
const procedureVariant = node.variant;
- // Do not generate any code for empty procedures.
const procedureData = this.ir.procedures[procedureVariant];
if (procedureData.stack === null) {
+ // TODO still need to evaluate arguments
break;
}
- if (!this.isWarp && procedureCode === this.script.procedureCode) {
- // Direct recursion yields.
+
+ const yieldForRecursion = !this.isWarp && procedureCode === this.script.procedureCode;
+ if (yieldForRecursion) {
this.yieldNotWarp();
}
+
if (procedureData.yields) {
this.source += 'yield* ';
- if (!this.script.yields) {
- throw new Error('Script uses yielding procedure but is not marked as yielding.');
- }
}
this.source += `thread.procedures["${sanitize(procedureVariant)}"](`;
- // Only include arguments if the procedure accepts any.
- if (procedureData.arguments.length) {
- const args = [];
- for (const input of node.arguments) {
- args.push(this.descendInput(input).asSafe());
- }
- this.source += args.join(',');
+ const args = [];
+ for (const input of node.arguments) {
+ args.push(this.descendInput(input).asSafe());
}
- this.source += `);\n`;
- // Variable input types may have changes after a procedure call.
+ this.source += args.join(',');
+ this.source += ');\n';
+
this.resetVariableInputs();
break;
}
+ case 'procedures.return':
+ this.stopScriptAndReturn(this.descendInput(node.value).asSafe());
+ break;
case 'timer.reset':
this.source += 'runtime.ioDevices.clock.resetProjectTimer();\n';
@@ -1137,6 +1162,13 @@ class JSGenerator {
return this.evaluateOnce(`stage.variables["${sanitize(variable.id)}"]`);
}
+ descendAddonCall (node) {
+ const inputs = this.descendInputRecord(node.arguments);
+ const blockFunction = `runtime.getAddonBlock("${sanitize(node.code)}").callback`;
+ const blockId = `"${sanitize(node.blockId)}"`;
+ return `yield* executeInCompatibilityLayer(${inputs}, ${blockFunction}, ${this.isWarp}, false, ${blockId})`;
+ }
+
evaluateOnce (source) {
if (this._setupVariables.hasOwnProperty(source)) {
return this._setupVariables[source];
@@ -1157,6 +1189,25 @@ class JSGenerator {
}
}
+ stopScript () {
+ if (this.isProcedure) {
+ this.source += 'return "";\n';
+ } else {
+ this.retire();
+ }
+ }
+
+ /**
+ * @param {string} valueJS JS code of value to return.
+ */
+ stopScriptAndReturn (valueJS) {
+ if (this.isProcedure) {
+ this.source += `return ${valueJS};\n`;
+ } else {
+ this.retire();
+ }
+ }
+
yieldLoop () {
if (this.warpTimer) {
this.yieldStuckOrNotWarp();
@@ -1286,10 +1337,6 @@ class JSGenerator {
script += this.source;
- if (!this.isProcedure) {
- script += 'retire();\n';
- }
-
script += '}; })';
return script;
@@ -1303,6 +1350,7 @@ class JSGenerator {
if (this.script.stack) {
this.descendStack(this.script.stack, new Frame(false));
}
+ this.stopScript();
const factory = this.createScriptFactory();
const fn = jsexecute.scopedEval(factory);
diff --git a/src/engine/execute.js b/src/engine/execute.js
index 1dfe7fa19b..6324798b86 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -490,6 +490,7 @@ const execute = function (sequencer, thread) {
currentStackFrame.reporting = null;
currentStackFrame.reported = null;
+ currentStackFrame.waitingReporter = false;
}
const start = i;
@@ -497,6 +498,7 @@ const execute = function (sequencer, thread) {
for (; i < length; i++) {
const lastOperation = i === length - 1;
const opCached = ops[i];
+ currentStackFrame.op = opCached;
const blockFunction = opCached._blockFunction;
@@ -517,9 +519,11 @@ const execute = function (sequencer, thread) {
const primitiveReportedValue = blockFunction(argValues, blockUtility);
- // If it's a promise, wait until promise resolves.
- if (isPromise(primitiveReportedValue)) {
- handlePromise(primitiveReportedValue, sequencer, thread, opCached, lastOperation);
+ const primitiveIsPromise = isPromise(primitiveReportedValue);
+ if (primitiveIsPromise || currentStackFrame.waitingReporter) {
+ if (primitiveIsPromise) {
+ handlePromise(primitiveReportedValue, sequencer, thread, opCached, lastOperation);
+ }
// Store the already reported values. They will be thawed into the
// future versions of the same operations by block id. The reporting
@@ -543,7 +547,7 @@ const execute = function (sequencer, thread) {
};
});
- // We are waiting for a promise. Stop running this set of operations
+ // We are waiting to be resumed later. Stop running this set of operations
// and continue them later after thawing the reported values.
break;
} else if (thread.status === Thread.STATUS_RUNNING) {
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index eae8b773fd..0fddccbb12 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -2587,14 +2587,17 @@ class Runtime extends EventEmitter {
* @param {object} options Options object
* @param {string} options.procedureCode The ID of the block
* @param {function} options.callback The callback, called with (args, BlockUtility). May return a promise.
- * @param {string[]} options.arguments Names of the arguments accepted
- * @param {boolean} [hidden] True to not include this block in the block palette
+ * @param {string[]} [options.arguments] Names of the arguments accepted. Optional if no arguments.
+ * @param {boolean} [options.hidden] True to not include this block in the block palette
+ * @param {1|2} [options.return] 1 for round reporter, 2 for boolean reported, leave empty for statement.
*/
addAddonBlock (options) {
const procedureCode = options.procedureCode;
- const names = options.arguments;
- const ids = options.arguments.map((_, i) => `arg${i}`);
- const defaults = options.arguments.map(() => '');
+
+ const argumentNames = options.arguments || [];
+ const names = argumentNames;
+ const ids = argumentNames.map((_, i) => `arg${i}`);
+ const defaults = argumentNames.map(() => '');
this.addonBlocks[procedureCode] = {
namesIdsDefaults: [names, ids, defaults],
...options
@@ -2631,6 +2634,7 @@ class Runtime extends EventEmitter {
` argumentnames="${xmlEscape(JSON.stringify(names))}"` +
` argumentids="${xmlEscape(JSON.stringify(ids))}"` +
` argumentdefaults="${xmlEscape(JSON.stringify(defaults))}"` +
+ `${options.return ? ` return="${xmlEscape(options.return.toString())}"` : ''}` +
'>'
});
}
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index 74f6474052..bcc4a3c281 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -239,7 +239,7 @@ class Sequencer {
return;
}
// If no control flow has happened, switch to next block.
- if (thread.peekStack() === currentBlockId) {
+ if (thread.peekStack() === currentBlockId && !thread.peekStackFrame().waitingReporter) {
thread.goToNextBlock();
}
// If no next block has been found at this point, look on the stack.
@@ -274,7 +274,7 @@ class Sequencer {
// This level of the stack was waiting for a value.
// This means a reporter has just returned - so don't go
// to the next block for this level of the stack.
- return;
+ continue;
}
// Get next block of existing block on the stack.
thread.goToNextBlock();
diff --git a/src/engine/thread.js b/src/engine/thread.js
index 018a16e2f6..a48b356dcb 100644
--- a/src/engine/thread.js
+++ b/src/engine/thread.js
@@ -63,6 +63,13 @@ class _StackFrame {
* @type {Object}
*/
this.executionContext = null;
+
+ /**
+ * Internal block object being executed. This is *not* the same as the object found
+ * in target.blocks.
+ * @type {object}
+ */
+ this.op = null;
}
/**
@@ -79,6 +86,7 @@ class _StackFrame {
this.waitingReporter = null;
this.params = null;
this.executionContext = null;
+ this.op = null;
return this;
}
@@ -307,7 +315,10 @@ class Thread {
let blockID = this.peekStack();
while (blockID !== null) {
const block = this.target.blocks.getBlock(blockID);
- if (typeof block !== 'undefined' && block.opcode === 'procedures_call') {
+ if (
+ (typeof block !== 'undefined' && block.opcode === 'procedures_call') ||
+ this.peekStackFrame().waitingReporter
+ ) {
break;
}
this.popStack();
@@ -426,9 +437,9 @@ class Thread {
*/
isRecursiveCall (procedureCode) {
let callCount = 5; // Max number of enclosing procedure calls to examine.
- const sp = this.stack.length - 1;
+ const sp = this.stackFrames.length - 1;
for (let i = sp - 1; i >= 0; i--) {
- const block = this.target.blocks.getBlock(this.stack[i]);
+ const block = this.target.blocks.getBlock(this.stackFrames[i].op.id);
if (block.opcode === 'procedures_call' &&
block.mutation.proccode === procedureCode) {
return true;
diff --git a/test/fixtures/execute/tw-procedure-return-non-existant.sb3 b/test/fixtures/execute/tw-procedure-return-non-existant.sb3
new file mode 100644
index 0000000000..0804d7a10c
Binary files /dev/null and b/test/fixtures/execute/tw-procedure-return-non-existant.sb3 differ
diff --git a/test/fixtures/execute/tw-procedure-return-recursion.sb3 b/test/fixtures/execute/tw-procedure-return-recursion.sb3
new file mode 100644
index 0000000000..9f46c91b64
Binary files /dev/null and b/test/fixtures/execute/tw-procedure-return-recursion.sb3 differ
diff --git a/test/fixtures/execute/tw-procedure-return-simple.sb3 b/test/fixtures/execute/tw-procedure-return-simple.sb3
new file mode 100644
index 0000000000..1714cc0e42
Binary files /dev/null and b/test/fixtures/execute/tw-procedure-return-simple.sb3 differ
diff --git a/test/fixtures/execute/tw-procedure-return-stops-scripts.sb3 b/test/fixtures/execute/tw-procedure-return-stops-scripts.sb3
new file mode 100644
index 0000000000..0acdda88f1
Binary files /dev/null and b/test/fixtures/execute/tw-procedure-return-stops-scripts.sb3 differ
diff --git a/test/fixtures/execute/tw-procedure-return-warp.sb3 b/test/fixtures/execute/tw-procedure-return-warp.sb3
new file mode 100644
index 0000000000..9043730428
Binary files /dev/null and b/test/fixtures/execute/tw-procedure-return-warp.sb3 differ
diff --git a/test/fixtures/tw-addon-blocks.sb3 b/test/fixtures/tw-addon-blocks.sb3
index 245a7a30ef..dd2b66b995 100644
Binary files a/test/fixtures/tw-addon-blocks.sb3 and b/test/fixtures/tw-addon-blocks.sb3 differ
diff --git a/test/fixtures/tw-stackframe-op.sb3 b/test/fixtures/tw-stackframe-op.sb3
new file mode 100644
index 0000000000..238216c945
Binary files /dev/null and b/test/fixtures/tw-stackframe-op.sb3 differ
diff --git a/test/integration/tw_addon_blocks.js b/test/integration/tw_addon_blocks.js
index a8a1dc240a..e48ddfe816 100644
--- a/test/integration/tw_addon_blocks.js
+++ b/test/integration/tw_addon_blocks.js
@@ -2,145 +2,269 @@ const tap = require('tap');
const fs = require('fs');
const path = require('path');
const VirtualMachine = require('../../src/virtual-machine');
+const Thread = require('../../src/engine/thread');
const fixtureData = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'tw-addon-blocks.sb3'));
-const runExecutionTests = compilerEnabled => async test => {
+const runExecutionTests = compilerEnabled => test => {
const load = async () => {
const vm = new VirtualMachine();
vm.setCompilerOptions({
enabled: compilerEnabled
});
+ vm.on('COMPILE_ERROR', (target, error) => {
+ test.fail(`Compile error in ${target.getName()}: ${error}`);
+ });
await vm.loadProject(fixtureData);
- vm.on('COMPILE_ERROR', (target, error) => test.fail(`Compile error ${target.getName()} ${error}`));
return vm;
};
- const getOutput = vm => vm.runtime.getTargetForStage().lookupVariableByNameAndType('output').value;
-
- await test.test('simple use', async t => {
- t.plan(7);
-
- const vm = await load();
-
- let calledBlock1 = false;
- let calledBlock2 = false;
-
- vm.addAddonBlock({
- procedureCode: 'block 2 %s',
- callback: (args, util) => {
- calledBlock1 = true;
- t.type(util.thread, 'object');
- // may have to update this ID when the project changes to match whatever the ID is for the
- // procedures_call block to block 2 %s
- t.equal(util.thread.peekStack(), 'c');
- t.same(args, {
- 'number or text': 'banana'
- });
- },
- arguments: ['number or text']
- });
-
- vm.addAddonBlock({
- procedureCode: 'block 3',
- // eslint-disable-next-line no-unused-vars
- callback: (args, util) => {
- calledBlock2 = true;
- t.same(args, {});
- },
- arguments: []
+ const getVar = (vm, variableName) => {
+ const variable = vm.runtime.getTargetForStage().lookupVariableByNameAndType(variableName, '');
+ return variable.value;
+ };
+
+ test.test('baseline - no addon blocks', t => {
+ load().then(vm => {
+ t.equal(getVar(vm, 'block 1'), 'initial');
+ t.equal(getVar(vm, 'block 2'), 'initial');
+ t.equal(getVar(vm, 'block 3'), 'initial');
+ t.equal(getVar(vm, 'block 4'), 'initial');
+ t.equal(getVar(vm, 'block 4 output'), 'initial');
+
+ vm.greenFlag();
+ vm.runtime._step();
+
+ t.equal(getVar(vm, 'block 1'), 'block 1 ran');
+ t.equal(getVar(vm, 'block 2'), 'block 2: banana');
+ t.equal(getVar(vm, 'block 3'), 'block 3 ran');
+ t.equal(getVar(vm, 'block 4'), 'block 4 ran');
+ t.equal(getVar(vm, 'block 4 output'), 'block 4: apple');
+
+ t.end();
});
-
- vm.greenFlag();
- vm.runtime._step();
-
- t.equal(getOutput(vm), 'block 1 value');
- t.ok(calledBlock1);
- t.ok(calledBlock2);
- t.end();
});
- await test.test('yield by thread.status = STATUS_PROMISE_WAIT', async t => {
- const vm = await load();
+ test.test('simple statement blocks', t => {
+ load().then(vm => {
+ t.plan(9);
+
+ let calledBlock1 = false;
+ let calledBlock2 = false;
+
+ vm.addAddonBlock({
+ procedureCode: 'block 1',
+ callback: (args, util) => {
+ calledBlock1 = true;
+ t.same(args, {});
+ t.ok(util.thread instanceof Thread);
+ // may have to update when project changes
+ t.equal(util.thread.peekStack(), 'd');
+ }
+ });
+
+ vm.addAddonBlock({
+ procedureCode: 'block 2 %s',
+ arguments: ['number or text'],
+ callback: (args, util) => {
+ calledBlock2 = true;
+ t.same(args, {
+ 'number or text': 'banana'
+ });
+ // may have to update when project changes
+ t.equal(util.thread.peekStack(), 'c');
+ }
+ });
+
+ vm.greenFlag();
+ vm.runtime._step();
+
+ t.ok(calledBlock1);
+ t.ok(calledBlock2);
- let threadToResume;
+ // Overridden blocks should not run
+ t.equal(getVar(vm, 'block 1'), 'false');
+ t.equal(getVar(vm, 'block 2'), 'false');
- vm.addAddonBlock({
- procedureCode: 'block 1',
- callback: (args, util) => {
- util.thread.status = 1; // STATUS_PROMISE_WAIT
- threadToResume = util.thread;
- },
- arguments: []
+ t.end();
});
+ });
- vm.greenFlag();
- vm.runtime._step();
- if (!threadToResume) {
- t.fail('did not run addon block');
- }
+ test.test('yield with thread.status = STATUS_PROMISE_WAIT', t => {
+ load().then(vm => {
+ t.plan(7);
+
+ let threadToResume;
+ let ranBlock3 = false;
+ vm.addAddonBlock({
+ procedureCode: 'block 3',
+ callback: (args, util) => {
+ ranBlock3 = true;
+ util.thread.status = Thread.STATUS_PROMISE_WAIT;
+ threadToResume = util.thread;
+ },
+ arguments: []
+ });
+
+ vm.greenFlag();
+ vm.runtime._step();
- t.equal(getOutput(vm), 'initial value');
- threadToResume.status = 0; // STATUS_RUNNING
- vm.runtime._step();
- t.equal(getOutput(vm), 'block 3 value');
+ // Make sure we paused it
+ t.ok(ranBlock3);
+ t.equal(threadToResume.status, Thread.STATUS_PROMISE_WAIT);
- t.end();
- });
+ // Should've stopped after block 2
+ t.equal(getVar(vm, 'block 2'), 'block 2: banana');
+ t.equal(getVar(vm, 'block 3'), 'false');
+ t.equal(getVar(vm, 'block 4'), 'false');
- await test.test('yield by block utility methods', async t => {
- const vm = await load();
+ threadToResume.status = Thread.STATUS_RUNNING;
+ vm.runtime._step();
- let shouldYield = true;
+ // Should've finished running
+ t.equal(getVar(vm, 'block 3'), 'false'); // overridden, should not run
+ t.equal(getVar(vm, 'block 4'), 'block 4 ran');
- vm.addAddonBlock({
- procedureCode: 'block 1',
- callback: (args, util) => {
- if (shouldYield) {
- util.runtime.requestRedraw();
- util.yield();
- }
- },
- arguments: []
+ t.end();
});
+ });
- vm.greenFlag();
- for (let i = 0; i < 10; i++) {
+ test.test('yield with util.yield()', t => {
+ load().then(vm => {
+ t.plan(10);
+
+ let shouldYield = true;
+ let calledBlock1 = 0;
+
+ vm.addAddonBlock({
+ procedureCode: 'block 1',
+ callback: (args, util) => {
+ calledBlock1++;
+
+ if (shouldYield) {
+ util.runtime.requestRedraw();
+ util.yield();
+ }
+ },
+ arguments: []
+ });
+
+ vm.greenFlag();
+ for (let i = 0; i < 10; i++) {
+ vm.runtime._step();
+ }
+
+ t.equal(calledBlock1, 10);
+ t.equal(getVar(vm, 'block 1'), 'false');
+ t.equal(getVar(vm, 'block 2'), 'false');
+ t.equal(getVar(vm, 'block 3'), 'false');
+ t.equal(getVar(vm, 'block 4'), 'false');
+
+ shouldYield = false;
vm.runtime._step();
- }
- t.equal(getOutput(vm), 'initial value');
- shouldYield = false;
- vm.runtime._step();
- t.equal(getOutput(vm), 'block 3 value');
+ t.equal(calledBlock1, 11);
+ t.equal(getVar(vm, 'block 1'), 'false'); // overrridden, should not run
+ t.equal(getVar(vm, 'block 2'), 'block 2: banana');
+ t.equal(getVar(vm, 'block 3'), 'block 3 ran');
+ t.equal(getVar(vm, 'block 4'), 'block 4 ran');
- t.end();
+ t.end();
+ });
});
- await test.test('yield by returning Promise', async t => {
- const vm = await load();
-
- let resolveCallback;
- vm.addAddonBlock({
- procedureCode: 'block 1',
- callback: () => new Promise(resolve => {
- resolveCallback = resolve;
- }),
- arguments: []
+ test.test('yield with resolved Promise', t => {
+ load().then(vm => {
+ let resolveCallback;
+ vm.addAddonBlock({
+ procedureCode: 'block 2 %s',
+ arguments: ['number or text'],
+ callback: () => new Promise(resolve => {
+ resolveCallback = resolve;
+ })
+ });
+
+ vm.greenFlag();
+ for (let i = 0; i < 5; i++) {
+ vm.runtime._step();
+ }
+
+ t.type(resolveCallback, 'function');
+ t.equal(getVar(vm, 'block 1'), 'block 1 ran');
+ t.equal(getVar(vm, 'block 2'), 'false');
+ t.equal(getVar(vm, 'block 3'), 'false');
+ t.equal(getVar(vm, 'block 4'), 'false');
+
+ resolveCallback();
+ Promise.resolve().then(() => {
+ vm.runtime._step();
+ t.equal(getVar(vm, 'block 2'), 'false'); // overridden, should not run
+ t.equal(getVar(vm, 'block 3'), 'block 3 ran');
+ t.equal(getVar(vm, 'block 4'), 'block 4 ran');
+
+ t.end();
+ });
});
+ });
- vm.greenFlag();
- vm.runtime._step();
- t.equal(getOutput(vm), 'initial value');
+ test.skip('yield with resolved Promise', t => {
+ load().then(vm => {
+ let rejectCallback;
+ vm.addAddonBlock({
+ procedureCode: 'block 2 %s',
+ arguments: ['number or text'],
+ callback: () => new Promise((resolve, reject) => {
+ rejectCallback = reject;
+ })
+ });
+
+ vm.greenFlag();
+ for (let i = 0; i < 5; i++) {
+ vm.runtime._step();
+ }
+
+ t.type(rejectCallback, 'function');
+ t.equal(getVar(vm, 'block 1'), 'block 1 ran');
+ t.equal(getVar(vm, 'block 2'), 'false');
+ t.equal(getVar(vm, 'block 3'), 'false');
+ t.equal(getVar(vm, 'block 4'), 'false');
+
+ rejectCallback(new Error('Intentional error for testing'));
+ Promise.resolve().then(() => {
+ vm.runtime._step();
+ t.equal(getVar(vm, 'block 2'), 'false'); // overridden, should not run
+ t.equal(getVar(vm, 'block 3'), 'block 3 ran');
+ t.equal(getVar(vm, 'block 4'), 'block 4 ran');
+
+ t.end();
+ });
+ });
+ });
- resolveCallback();
- // Allow the promise callback to run
- await Promise.resolve();
+ test.test('returning values', t => {
+ load().then(vm => {
+ vm.addAddonBlock({
+ procedureCode: 'block 4 %s',
+ callback: args => {
+ t.same(args, {
+ 'number or text': 'apple'
+ });
+ return `value from addon block: ${args['number or text']}`;
+ },
+ arguments: ['number or text']
+ });
+
+ vm.greenFlag();
+ vm.runtime._step();
- vm.runtime._step();
- t.equal(getOutput(vm), 'block 3 value');
+ t.equal(getVar(vm, 'block 1'), 'block 1 ran');
+ t.equal(getVar(vm, 'block 2'), 'block 2: banana');
+ t.equal(getVar(vm, 'block 3'), 'block 3 ran');
+ t.equal(getVar(vm, 'block 4'), 'false'); // block 4 itself should not have run, we overrode it
+ t.equal(getVar(vm, 'block 4 output'), 'value from addon block: apple');
- t.end();
+ t.end();
+ });
});
test.end();
@@ -165,10 +289,16 @@ tap.test('block info', t => {
t.equal(blockInfo, undefined);
vm.addAddonBlock({
- procedureCode: 'something %s',
+ procedureCode: 'statement %s',
arguments: ['number or text'],
callback: () => {}
});
+ vm.addAddonBlock({
+ procedureCode: 'input %s',
+ arguments: ['an argument'],
+ callback: () => {},
+ return: 1
+ });
blockInfo = vm.runtime._blockInfo.find(i => i.id === BLOCK_INFO_ID);
t.type(blockInfo.id, 'string');
@@ -180,7 +310,12 @@ tap.test('block info', t => {
{
info: {},
// eslint-disable-next-line max-len
- xml: ''
+ xml: ''
+ },
+ {
+ info: {},
+ // eslint-disable-next-line max-len
+ xml: ''
}
]);
diff --git a/test/integration/tw_stackframe_op.js b/test/integration/tw_stackframe_op.js
new file mode 100644
index 0000000000..2d632588ca
--- /dev/null
+++ b/test/integration/tw_stackframe_op.js
@@ -0,0 +1,29 @@
+const fs = require('fs');
+const path = require('path');
+const {test} = require('tap');
+const VirtualMachine = require('../../src/virtual-machine');
+
+const projectPath = path.join(__dirname, '..', 'fixtures', 'tw-stackframe-op.sb3');
+
+test('util.thread.peekStackFrame().op', t => {
+ const vm = new VirtualMachine();
+
+ vm.runtime.setCompilerOptions({
+ enabled: false
+ });
+
+ // Easiest way to test this is to overwrite an existing block
+ vm.runtime._primitives.operator_add = function (args, util) {
+ const op = util.thread.peekStackFrame().op;
+ t.equal(op.id, 'c');
+ t.end();
+
+ // return value not used
+ return 4;
+ };
+
+ vm.loadProject(fs.readFileSync(projectPath)).then(() => {
+ vm.greenFlag();
+ vm.runtime._step();
+ });
+});
diff --git a/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot b/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot
index 83a524246e..9fb6e5dcc3 100644
--- a/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/order-library-reverse.sb3.tw-snapshot
@@ -22,7 +22,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
// Sprite3 script
@@ -56,7 +56,7 @@ yield;
}
thread.timer = null;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -80,5 +80,5 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot b/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot
index 8d5b86d442..b3d290734d 100644
--- a/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/order-library.sb3.tw-snapshot
@@ -32,7 +32,7 @@ yield;
}
thread.timer = null;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -56,7 +56,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -80,5 +80,5 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot
index 3da0d357d4..9ef871b3d1 100644
--- a/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-NaN.sb3.tw-snapshot
@@ -67,5 +67,5 @@ if (compareEqual(((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) ||
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-add-can-return-nan.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-add-can-return-nan.sb3.tw-snapshot
index c0559ddbba..8d5ef26746 100644
--- a/test/snapshot/__snapshots__/tw-add-can-return-nan.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-add-can-return-nan.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (!((Infinity + -Infinity) <= 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-all-at-once.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-all-at-once.sb3.tw-snapshot
index 6654884071..a411f91426 100644
--- a/test/snapshot/__snapshots__/tw-all-at-once.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-all-at-once.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (true) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-broadcast-id-and-name-desync.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
index 9fcae5532e..0001992170 100644
--- a/test/snapshot/__snapshots__/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -16,5 +16,5 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
index d44534337e..47a89a5940 100644
--- a/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
@@ -18,5 +18,5 @@ if (((+b1.value || 0) === 20)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot
index f5cbbe5960..d0d058ca2f 100644
--- a/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-color-input-returns-hex.sb3.tw-snapshot
@@ -12,5 +12,5 @@ if ((("" + b1.value).toLowerCase() === "#22388a".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot
index 5c76fc1cad..6983ba45b8 100644
--- a/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-comparison-matrix-inline.sb3.tw-snapshot
@@ -1771,5 +1771,5 @@ if (!(("" + compareGreaterThan("", "")).toLowerCase() === "false".toLowerCase())
yield* executeInCompatibilityLayer({"MESSAGE":"fail 588: should be > ",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot
index 9981e8e130..e0b33e4cd6 100644
--- a/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-comparison-matrix-runtime.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
yield* thread.procedures["Wrun test"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 run test
@@ -43,6 +43,7 @@ if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;}
}
}
}
+return "";
}; })
// Sprite1 setup values
@@ -126,4 +127,5 @@ b0.value.push(" ");
b0._monitorUpToDate = false;
b0.value.push("🎉");
b0._monitorUpToDate = false;
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-coordinate-precision.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-coordinate-precision.sb3.tw-snapshot
index 06c67b745a..8b09c54033 100644
--- a/test/snapshot/__snapshots__/tw-coordinate-precision.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-coordinate-precision.sb3.tw-snapshot
@@ -32,5 +32,5 @@ if ((limitPrecision(target.x) === 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass x slightly below 0 rounds",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
index c984a1e75d..f1c7c9c441 100644
--- a/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
@@ -19,5 +19,5 @@ if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), (1 + 0
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
index 13f910ed0e..19981ca8de 100644
--- a/test/snapshot/__snapshots__/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
@@ -24,5 +24,5 @@ if ((target.direction === 90)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass 4",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
index c3c297df67..44ea509f38 100644
--- a/test/snapshot/__snapshots__/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
@@ -28,5 +28,5 @@ if ((("" + b1.value).toLowerCase().indexOf(".".toLowerCase()) !== -1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
index 9b47965efd..d394fffa07 100644
--- a/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
@@ -19,5 +19,5 @@ if (((+(b2.value[(1 | 0) - 1] ?? "") || 0) === 3)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass list",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
index 901446f175..c474c52c88 100644
--- a/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
@@ -18,7 +18,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b2, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b2, false, false, null);
runtime.stopAll();
retire(); return;
-retire();
+retire(); return;
}; })
// Sprite1 no refresh
@@ -38,12 +38,14 @@ yield* executeInCompatibilityLayer({"BEATS":0,}, b0, true, false, null);
yield* executeInCompatibilityLayer({"DRUM":1,"BEATS":0,}, b1, true, false, null);
runtime.ext_scratch3_motion._moveSteps(0, target);
}
+return "";
}; })
// Sprite1 runs below with no refresh
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function* genXYZ_runs_below_with_no_r () {
yield* thread.procedures["Whas refresh"]();
+return "";
}; })
// Sprite1 has refresh
@@ -63,6 +65,7 @@ yield* executeInCompatibilityLayer({"BEATS":0,}, b0, true, false, null);
yield* executeInCompatibilityLayer({"DRUM":1,"BEATS":0,}, b1, true, false, null);
runtime.ext_scratch3_motion._moveSteps(0, target);
}
+return "";
}; })
// Sprite1 script
@@ -73,5 +76,5 @@ while (true) {
b0.value = ((+b0.value || 0) + 1);
yield;
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot
index 9c1558e0ec..47fb9c0042 100644
--- a/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-list-any.sb3.tw-snapshot
@@ -34,5 +34,5 @@ if (compareEqual(listGet(b1.value, "*"), "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-obsolete-blocks.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-obsolete-blocks.sb3.tw-snapshot
index 9abad94588..4a2f8c0f8b 100644
--- a/test/snapshot/__snapshots__/tw-obsolete-blocks.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-obsolete-blocks.sb3.tw-snapshot
@@ -29,5 +29,5 @@ yield* executeInCompatibilityLayer({"CHANGE":10,}, b9, false, false, null);
yield* executeInCompatibilityLayer({}, b10, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot
index 5b219446e5..ef51b2fa84 100644
--- a/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-one-divide-negative-zero.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if ((((1 / -0) || 0) === -Infinity)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
index 026192b774..0624028f3a 100644
--- a/test/snapshot/__snapshots__/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
@@ -23,5 +23,5 @@ yield;
}
b0.value = 1;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
index 44587a8de6..b7521033ca 100644
--- a/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* thread.procedures["ZSet Costume"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Player Set Costume
@@ -16,4 +16,5 @@ retire();
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ_Set_Costume () {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
index bcf0381a94..81002ba0a4 100644
--- a/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
@@ -9,7 +9,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null
yield* thread.procedures["Znumber or text %s %s"]("bad","ok");
yield* thread.procedures["Zboolean %b %b"]("false",!false);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 number or text %s %s
@@ -19,6 +19,7 @@ return function* genXYZ_number_or_text__ (p0,p1) {
if ((("" + p1).toLowerCase() === "ok".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
+return "";
}; })
// Sprite1 boolean %b %b
@@ -28,4 +29,5 @@ return function* genXYZ_boolean__ (p0,p1) {
if (toBoolean(p1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
index 3c7f4981bb..e01bbe8d5b 100644
--- a/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
@@ -13,7 +13,7 @@ if (!compareEqual(b1.value, "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 do something
@@ -21,4 +21,5 @@ retire();
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function funXYZ_do_something () {
b0.value = "help";
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
index d45d0aee37..7f69ac3fad 100644
--- a/test/snapshot/__snapshots__/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
@@ -7,5 +7,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot
new file mode 100644
index 0000000000..87f5825985
--- /dev/null
+++ b/test/snapshot/__snapshots__/tw-procedure-return-non-existant.sb3.tw-snapshot
@@ -0,0 +1,54 @@
+// TW Snapshot
+// Input SHA-256: b9110339d39947f68168cbb129a20b23b7d4e607ffd5624bd67e096451f6a329
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null);
+b1.value = 0;
+if (compareEqual(thread.procedures["Zinvalid params - reporter"](), 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass invalid params reporter",}, b0, false, false, null);
+}
+if (compareEqual(thread.procedures["Zinvalid params - boolean"](), 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass invalid params boolean",}, b0, false, false, null);
+}
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 invalid params - reporter
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_invalid_params___rep () {
+return 0;
+return "";
+}; })
+
+// Sprite1 invalid params - boolean
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_invalid_params___boo () {
+return 0;
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot
new file mode 100644
index 0000000000..7afc9c764a
--- /dev/null
+++ b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot
@@ -0,0 +1,146 @@
+// TW Snapshot
+// Input SHA-256: b1802636bbbd479dcf8634a525798c5b4dd9b0201cf6af6f5b4879455368c328
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b2 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, null);
+b1.value = 0;
+b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8));
+if (((+b1.value || 0) === 4)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, null);
+}
+b1.value = 0;
+b2.value = thread.procedures["Wwarp recursion should not yield %s"](8);
+if (((+b1.value || 0) === 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, null);
+}
+b1.value = 0;
+b2.value = (yield* thread.procedures["Zfib %s"](7));
+if (((+b1.value || 0) === 20)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, null);
+}
+yield* thread.procedures["Zrecursing yields between each %s"]("initial");
+yield* thread.procedures["Zrecursing arguments eval order %s %s %s %s"]("initial","","","");
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 non warp recursion should yield %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function* genXYZ_non_warp_recursion_s (p0) {
+if (compareGreaterThan(p0, 0)) {
+return (yield* yieldThenCallGenerator(thread.procedures["Znon warp recursion should yield %s"], ((+p0 || 0) - 1)));
+}
+return "";
+}; })
+
+// Sprite1 warp recursion should not yield %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_warp_recursion_shoul (p0) {
+if (compareGreaterThan(p0, 0)) {
+return thread.procedures["Wwarp recursion should not yield %s"](((+p0 || 0) - 1));
+}
+return "";
+}; })
+
+// Sprite1 fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function* genXYZ_fib_ (p0) {
+if (compareLessThan(p0, 2)) {
+return p0;
+} else {
+return ((+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || 0) - 1))) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || 0) - 2))) || 0));
+}
+return "";
+}; })
+
+// Sprite1 recursing yields between each %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+const b2 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_recursing_yields_bet (p0) {
+if ((("" + p0).toLowerCase() === "initial".toLowerCase())) {
+b0.value = 0;
+b1.value = ((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0)) || 0)) || 0)) || 0)) || 0));
+if (((+b0.value || 0) === 3)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recursing between calls yields final",}, b2, false, false, null);
+} else {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail recursing between calls yields final",}, b2, false, false, null);
+}
+} else {
+if (compareEqual(b0.value, p0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":("pass recursing between calls yields " + ("" + p0)),}, b2, false, false, null);
+} else {
+yield* executeInCompatibilityLayer({"MESSAGE":("fail recursing between calls yields " + ("" + p0)),}, b2, false, false, null);
+}
+}
+return "";
+}; })
+
+// Sprite1 recursing arguments eval order %s %s %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["4HH82mPlVMOONdl(Ot*7"];
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b2 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+const b3 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_recursing_arguments_ (p0,p1,p2,p3) {
+if ((("" + p0).toLowerCase() === "initial".toLowerCase())) {
+b0.value = [];
+b1.value = 0;
+b2.value = (yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 1",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 2",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 3",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 4","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 5","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 6","","","")))),"","")),"",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 7","","",""))));
+if ((("" + (b0.value[(1 | 0) - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 1",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(2 | 0) - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 2",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(3 | 0) - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 3",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(4 | 0) - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 4",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(5 | 0) - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 5",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(6 | 0) - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 6",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(7 | 0) - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 7",}, b3, false, false, null);
+}
+if ((b0.value.length === 7)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - length is correct",}, b3, false, false, null);
+}
+} else {
+b0.value.push((("" + b1.value) + ("/" + ("" + p0))));
+b0._monitorUpToDate = false;
+}
+return "";
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot
new file mode 100644
index 0000000000..0c8bd71d47
--- /dev/null
+++ b/test/snapshot/__snapshots__/tw-procedure-return-simple.sb3.tw-snapshot
@@ -0,0 +1,111 @@
+// TW Snapshot
+// Input SHA-256: cd516553f3cbf4a41ae9a8bc1f5d06a3392d365226bccdfa294578314f18facb
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 8",}, b0, false, false, null);
+if ((("" + thread.procedures["Zsimplest"]()).toLowerCase() === "It works!".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass simplest",}, b0, false, false, null);
+}
+if ((("" + thread.procedures["Znesting 1"]()).toLowerCase() === "42-54".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass nesting1",}, b0, false, false, null);
+}
+if (((+thread.procedures["Wwarp fib %s"](12) || 0) === 144)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass fib 12",}, b0, false, false, null);
+}
+if (((+thread.procedures["Wfactorial %s"](12) || 0) === 479001600)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass factorial 12",}, b0, false, false, null);
+}
+b1.value = (yield* thread.procedures["Zno shadowing 1 %s %s"]("f","g"));
+if (compareEqual(b1.value, "")) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass default return value",}, b0, false, false, null);
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 simplest
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_simplest () {
+return "It works!";
+return "";
+}; })
+
+// Sprite1 nesting 1
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_1 () {
+thread.procedures["Znesting 2"]();
+return (("" + thread.procedures["Znesting 3 %s %s"](6,7)) + ("" + thread.procedures["Znesting 3 %s %s"](-1,54)));
+return "";
+}; })
+
+// Sprite1 warp fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_warp_fib_ (p0) {
+return thread.procedures["Wfib %s"](p0);
+return "";
+}; })
+
+// Sprite1 factorial %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_factorial_ (p0) {
+if (compareGreaterThan(p0, 1)) {
+return ((+p0 || 0) * (+thread.procedures["Wfactorial %s"](((+p0 || 0) - 1)) || 0));
+}
+return 1;
+return "";
+}; })
+
+// Sprite1 no shadowing 1 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ_no_shadowing_1__ (p0,p1) {
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 1",}, b0, false, false, null);
+}
+b1.value = thread.procedures["Zno shadowing 2 %s %s"](1,2);
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 2",}, b0, false, false, null);
+}
+b1.value = thread.procedures["Zno shadowing 2 %s %s"](3,4);
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 3",}, b0, false, false, null);
+}
+return "";
+}; })
+
+// Sprite1 nesting 2
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_2 () {
+return "discard nesting 2";
+return "";
+}; })
+
+// Sprite1 nesting 3 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_3__ (p0,p1) {
+return ((+p0 || 0) * (+p1 || 0));
+return "";
+}; })
+
+// Sprite1 fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_fib_ (p0) {
+if (compareLessThan(p0, 2)) {
+return p0;
+} else {
+return ((+thread.procedures["Wfib %s"](((+p0 || 0) - 1)) || 0) + (+thread.procedures["Wfib %s"](((+p0 || 0) - 2)) || 0));
+}
+return "";
+}; })
+
+// Sprite1 no shadowing 2 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_no_shadowing_2__ (p0,p1) {
+return "discard shadow 2";
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot
new file mode 100644
index 0000000000..0a31092a84
--- /dev/null
+++ b/test/snapshot/__snapshots__/tw-procedure-return-stops-scripts.sb3.tw-snapshot
@@ -0,0 +1,51 @@
+// TW Snapshot
+// Input SHA-256: 27368530b97e0ab5053bf4faf8fd116aa30cea34adcbd95fc6ddcf0a9d6d4976
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null);
+yield* thread.procedures["Wreturn stops the script immediately"]();
+if (((+b1.value || 0) === 25)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass return stopped the script immediately",}, b0, false, false, null);
+}
+yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "Test return outside of custom block" }));
+if (((+b1.value || 0) === 18)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass return worked to stop outside of custom block",}, b0, false, false, null);
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 return stops the script immediately
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_return_stops_the_scr () {
+b0.value = 0;
+for (var a0 = 100; a0 >= 0.5; a0--) {
+b0.value = ((+b0.value || 0) + 1);
+if (((b0.value || 0) === 25)) {
+return "stopped!";
+}
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"fail return did not stop the script immediately",}, b1, true, false, null);
+return "";
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+if (((b0.value || 0) === 18)) {
+retire(); return;
+}
+yield;
+}
+retire(); return;
+}; })
diff --git a/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot
new file mode 100644
index 0000000000..7ea1f2b53c
--- /dev/null
+++ b/test/snapshot/__snapshots__/tw-procedure-return-warp.sb3.tw-snapshot
@@ -0,0 +1,120 @@
+// TW Snapshot
+// Input SHA-256: e652f448fbf09a37afacb03afa9f3448cce43aef06439b58b44aeec73edb0438
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 3",}, b0, false, false, null);
+yield* thread.procedures["Znon warp"]();
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 non warp
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_non_warp () {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a1) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (((+b0.value || 0) === 5)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 1",}, b1, false, false, null);
+}
+if ((("" + (yield* thread.procedures["Wverify runs warp %s"]((yield* thread.procedures["Zverify runs non warp %s"]((yield* thread.procedures["Wverify runs warp %s"]("abc"))))))).toLowerCase() === "warp: non warp: warp: abc".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp and warp mix",}, b1, false, false, null);
+}
+b0.value = 0;
+for (var a2 = 5; a2 >= 0.5; a2--) {
+thread.timer = timer();
+var a3 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a3) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (((+b0.value || 0) === 5)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 2",}, b1, false, false, null);
+}
+return "";
+}; })
+
+// Sprite1 verify runs warp %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_verify_runs_warp_ (p0) {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+while (thread.timer.timeElapsed() < a1) {
+if (isStuck()) yield;
+}
+thread.timer = null;
+}
+if (!compareEqual(b0.value, 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail did not run warp",}, b1, true, false, null);
+}
+return ("warp: " + ("" + p0));
+return "";
+}; })
+
+// Sprite1 verify runs non warp %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_verify_runs_non_warp (p0) {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a1) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (compareEqual(b0.value, 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail ran warp",}, b1, false, false, null);
+}
+return ("non warp: " + ("" + p0));
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
index e519a948af..25f742e3c5 100644
--- a/test/snapshot/__snapshots__/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
@@ -18,7 +18,7 @@ if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;}
}
yield;
}
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -48,5 +48,5 @@ yield;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
runtime.stopAll();
retire(); return;
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-restart-broadcast-threads.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-restart-broadcast-threads.sb3.tw-snapshot
index 4459d5baf0..ccec1e74d7 100644
--- a/test/snapshot/__snapshots__/tw-restart-broadcast-threads.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-restart-broadcast-threads.sb3.tw-snapshot
@@ -22,7 +22,7 @@ if (((+b1.value || 0) === 1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -30,5 +30,5 @@ retire();
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function* genXYZ () {
b0.value = ((+b0.value || 0) + 1);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot
index 4802e14767..c7dbdb0cde 100644
--- a/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-safe-procedure-argument-casting.sb3.tw-snapshot
@@ -15,11 +15,12 @@ if ((((target.currentCostume + 1) === 1) && ((+target.getCostumes()[target.curre
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 switch %s
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function funXYZ_switch_ (p0) {
runtime.ext_scratch3_looks._setCostume(target, p0);
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot
index 239d1f55f9..96b07d55d8 100644
--- a/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-sensing-of.sb3.tw-snapshot
@@ -110,5 +110,5 @@ if (((b6 ? b6.volume : 0) === 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass NE direction",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-stage-cannot-move-layers.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-stage-cannot-move-layers.sb3.tw-snapshot
index 974d8cbe06..923e8f1ece 100644
--- a/test/snapshot/__snapshots__/tw-stage-cannot-move-layers.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-stage-cannot-move-layers.sb3.tw-snapshot
@@ -18,7 +18,7 @@ yield;
thread.timer = null;
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "Test 1" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -29,13 +29,13 @@ return function* genXYZ () {
if ((("" + b0.value).toLowerCase() === "Initial".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b1, false, false, null);
}
-retire();
+retire(); return;
}; })
// Stage script
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function* genXYZ () {
-retire();
+retire(); return;
}; })
// Stage script
@@ -43,5 +43,5 @@ retire();
const b0 = target.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function* genXYZ () {
b0.value = "Stage callback";
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-subtract-can-return-nan.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-subtract-can-return-nan.sb3.tw-snapshot
index 7ce71511ea..e9573d3a27 100644
--- a/test/snapshot/__snapshots__/tw-subtract-can-return-nan.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-subtract-can-return-nan.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (!((Infinity - Infinity) <= 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot
index 4b5c21bd64..7ba4410d7f 100644
--- a/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-tab-equals-zero.sb3.tw-snapshot
@@ -9,7 +9,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 6",}, b0, false, false, null);
b1.value = "\t";
startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" });
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -36,5 +36,5 @@ if (compareEqual((" " + ("" + b0.value)), (0 + 0))) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t and other spaces = number 0",}, b1, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot
index a1349a1f5f..ea9202ade3 100644
--- a/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-tangent.sb3.tw-snapshot
@@ -52,5 +52,5 @@ if (((tan(-450) || 0) === -Infinity)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass -450",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot
index 0d508339fc..34ae2a649e 100644
--- a/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-unsafe-equals.sb3.tw-snapshot
@@ -82,5 +82,5 @@ if (compareEqual(0, "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
index 10d08edb04..7037c0256f 100644
--- a/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* thread.procedures["Wrun without screen refresh"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 run without screen refresh
@@ -24,4 +24,5 @@ if (isStuck()) yield;
if (compareLessThan((daysSince2000() * 86400), b0.value)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b1, true, false, null);
}
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
index 58f2fcf03f..646f9d73a8 100644
--- a/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -15,5 +15,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
runtime.ext_scratch3_looks._setBackdrop(stage, stage.currentCostume + 1, true);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
index c1528683ad..30302e62b2 100644
--- a/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -15,5 +15,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
runtime.ext_scratch3_looks._setBackdrop(stage, "backdrop2");
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
index d6e4f2e995..fc4a516916 100644
--- a/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
@@ -16,7 +16,7 @@ yield;
}
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -28,5 +28,5 @@ b0.value = ((+b0.value || 0) + 1);
if (((b0.value || 0) === 5)) {
b1.value = "end";
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot
index 83a524246e..9fb6e5dcc3 100644
--- a/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/order-library-reverse.sb3.tw-snapshot
@@ -22,7 +22,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
// Sprite3 script
@@ -56,7 +56,7 @@ yield;
}
thread.timer = null;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -80,5 +80,5 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot
index 8d5b86d442..b3d290734d 100644
--- a/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/order-library.sb3.tw-snapshot
@@ -32,7 +32,7 @@ yield;
}
thread.timer = null;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -56,7 +56,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (1)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 1 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -80,5 +80,5 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass order is correct (2)",}, b2,
} else {
yield* executeInCompatibilityLayer({"MESSAGE":("fail order is incorrect 2 != " + ("" + b0.value)),}, b2, false, false, null);
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot
index 3da0d357d4..9ef871b3d1 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-NaN.sb3.tw-snapshot
@@ -67,5 +67,5 @@ if (compareEqual(((runtime.ext_scratch3_operators._random((-1 / 0), (1 / 0)) ||
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-add-can-return-nan.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-add-can-return-nan.sb3.tw-snapshot
index c0559ddbba..8d5ef26746 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-add-can-return-nan.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-add-can-return-nan.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (!((Infinity + -Infinity) <= 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-all-at-once.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-all-at-once.sb3.tw-snapshot
index 6654884071..a411f91426 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-all-at-once.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-all-at-once.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (true) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-broadcast-id-and-name-desync.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
index 9fcae5532e..0001992170 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-broadcast-id-and-name-desync.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -16,5 +16,5 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
index d44534337e..47a89a5940 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-change-size-does-not-use-rounded-size.sb3.tw-snapshot
@@ -18,5 +18,5 @@ if (((+b1.value || 0) === 20)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot
index f5cbbe5960..d0d058ca2f 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-color-input-returns-hex.sb3.tw-snapshot
@@ -12,5 +12,5 @@ if ((("" + b1.value).toLowerCase() === "#22388a".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot
index 5c76fc1cad..6983ba45b8 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-inline.sb3.tw-snapshot
@@ -1771,5 +1771,5 @@ if (!(("" + compareGreaterThan("", "")).toLowerCase() === "false".toLowerCase())
yield* executeInCompatibilityLayer({"MESSAGE":"fail 588: should be > ",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot
index e796d21e79..8e1981ba0b 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-comparison-matrix-runtime.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
yield* thread.procedures["Wrun test"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 run test
@@ -45,6 +45,7 @@ if (isStuck()) yield;
}
if (isStuck()) yield;
}
+return "";
}; })
// Sprite1 setup values
@@ -128,4 +129,5 @@ b0.value.push(" ");
b0._monitorUpToDate = false;
b0.value.push("🎉");
b0._monitorUpToDate = false;
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-coordinate-precision.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-coordinate-precision.sb3.tw-snapshot
index 06c67b745a..8b09c54033 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-coordinate-precision.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-coordinate-precision.sb3.tw-snapshot
@@ -32,5 +32,5 @@ if ((limitPrecision(target.x) === 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass x slightly below 0 rounds",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
index c984a1e75d..f1c7c9c441 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-boolean-number-comparison.sb3.tw-snapshot
@@ -19,5 +19,5 @@ if (compareLessThan(("something".toLowerCase() === "else".toLowerCase()), (1 + 0
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
index 13f910ed0e..19981ca8de 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-non-finite-direction.sb3.tw-snapshot
@@ -24,5 +24,5 @@ if ((target.direction === 90)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass 4",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
index c3c297df67..44ea509f38 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-random-with-invalid-number-with-period.sb3.tw-snapshot
@@ -28,5 +28,5 @@ if ((("" + b1.value).toLowerCase().indexOf(".".toLowerCase()) !== -1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
index 9b47965efd..d394fffa07 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-variable-id-name-desync-name-fallback.sb3.tw-snapshot
@@ -19,5 +19,5 @@ if (((+(b2.value[(1 | 0) - 1] ?? "") || 0) === 3)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass list",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
index 592940dd7e..e8b77424eb 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-forkphorus-515-wait-zero-seconds-in-warp-mode.sb3.tw-snapshot
@@ -18,7 +18,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b2, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b2, false, false, null);
runtime.stopAll();
retire(); return;
-retire();
+retire(); return;
}; })
// Sprite1 no refresh
@@ -39,12 +39,14 @@ yield* executeInCompatibilityLayer({"DRUM":1,"BEATS":0,}, b1, true, false, null)
runtime.ext_scratch3_motion._moveSteps(0, target);
if (isStuck()) yield;
}
+return "";
}; })
// Sprite1 runs below with no refresh
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function* genXYZ_runs_below_with_no_r () {
yield* thread.procedures["Whas refresh"]();
+return "";
}; })
// Sprite1 has refresh
@@ -65,6 +67,7 @@ yield* executeInCompatibilityLayer({"DRUM":1,"BEATS":0,}, b1, true, false, null)
runtime.ext_scratch3_motion._moveSteps(0, target);
if (isStuck()) yield;
}
+return "";
}; })
// Sprite1 script
@@ -75,5 +78,5 @@ while (true) {
b0.value = ((+b0.value || 0) + 1);
yield;
}
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot
index 9c1558e0ec..47fb9c0042 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-list-any.sb3.tw-snapshot
@@ -34,5 +34,5 @@ if (compareEqual(listGet(b1.value, "*"), "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-obsolete-blocks.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-obsolete-blocks.sb3.tw-snapshot
index 9abad94588..4a2f8c0f8b 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-obsolete-blocks.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-obsolete-blocks.sb3.tw-snapshot
@@ -29,5 +29,5 @@ yield* executeInCompatibilityLayer({"CHANGE":10,}, b9, false, false, null);
yield* executeInCompatibilityLayer({}, b10, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot
index 5b219446e5..ef51b2fa84 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-one-divide-negative-zero.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if ((((1 / -0) || 0) === -Infinity)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
index 026192b774..0624028f3a 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-preciseProjectTimer-drift-453118719.sb3.tw-snapshot
@@ -23,5 +23,5 @@ yield;
}
b0.value = 1;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
index 44587a8de6..b7521033ca 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-prefers-first-occurence-of-procedure-387608267.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* thread.procedures["ZSet Costume"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Player Set Costume
@@ -16,4 +16,5 @@ retire();
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ_Set_Costume () {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
index bcf0381a94..81002ba0a4 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-arguments-with-same-name.sb3.tw-snapshot
@@ -9,7 +9,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null
yield* thread.procedures["Znumber or text %s %s"]("bad","ok");
yield* thread.procedures["Zboolean %b %b"]("false",!false);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 number or text %s %s
@@ -19,6 +19,7 @@ return function* genXYZ_number_or_text__ (p0,p1) {
if ((("" + p1).toLowerCase() === "ok".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
+return "";
}; })
// Sprite1 boolean %b %b
@@ -28,4 +29,5 @@ return function* genXYZ_boolean__ (p0,p1) {
if (toBoolean(p1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
index 3c7f4981bb..e01bbe8d5b 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-call-resets-variable-input-types-430811055.sb3.tw-snapshot
@@ -13,7 +13,7 @@ if (!compareEqual(b1.value, "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 do something
@@ -21,4 +21,5 @@ retire();
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function funXYZ_do_something () {
b0.value = "help";
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
index d45d0aee37..7f69ac3fad 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-prototype-exists-but-not-definition-549160843.sb3.tw-snapshot
@@ -7,5 +7,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot
new file mode 100644
index 0000000000..87f5825985
--- /dev/null
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-non-existant.sb3.tw-snapshot
@@ -0,0 +1,54 @@
+// TW Snapshot
+// Input SHA-256: b9110339d39947f68168cbb129a20b23b7d4e607ffd5624bd67e096451f6a329
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null);
+b1.value = 0;
+if (compareEqual(thread.procedures["Zinvalid params - reporter"](), 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass invalid params reporter",}, b0, false, false, null);
+}
+if (compareEqual(thread.procedures["Zinvalid params - boolean"](), 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass invalid params boolean",}, b0, false, false, null);
+}
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 invalid params - reporter
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_invalid_params___rep () {
+return 0;
+return "";
+}; })
+
+// Sprite1 invalid params - boolean
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_invalid_params___boo () {
+return 0;
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot
new file mode 100644
index 0000000000..7afc9c764a
--- /dev/null
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-recursion.sb3.tw-snapshot
@@ -0,0 +1,146 @@
+// TW Snapshot
+// Input SHA-256: b1802636bbbd479dcf8634a525798c5b4dd9b0201cf6af6f5b4879455368c328
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b2 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, null);
+b1.value = 0;
+b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8));
+if (((+b1.value || 0) === 4)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, null);
+}
+b1.value = 0;
+b2.value = thread.procedures["Wwarp recursion should not yield %s"](8);
+if (((+b1.value || 0) === 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, null);
+}
+b1.value = 0;
+b2.value = (yield* thread.procedures["Zfib %s"](7));
+if (((+b1.value || 0) === 20)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, null);
+}
+yield* thread.procedures["Zrecursing yields between each %s"]("initial");
+yield* thread.procedures["Zrecursing arguments eval order %s %s %s %s"]("initial","","","");
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 non warp recursion should yield %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function* genXYZ_non_warp_recursion_s (p0) {
+if (compareGreaterThan(p0, 0)) {
+return (yield* yieldThenCallGenerator(thread.procedures["Znon warp recursion should yield %s"], ((+p0 || 0) - 1)));
+}
+return "";
+}; })
+
+// Sprite1 warp recursion should not yield %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_warp_recursion_shoul (p0) {
+if (compareGreaterThan(p0, 0)) {
+return thread.procedures["Wwarp recursion should not yield %s"](((+p0 || 0) - 1));
+}
+return "";
+}; })
+
+// Sprite1 fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function* genXYZ_fib_ (p0) {
+if (compareLessThan(p0, 2)) {
+return p0;
+} else {
+return ((+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || 0) - 1))) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zfib %s"], ((+p0 || 0) - 2))) || 0));
+}
+return "";
+}; })
+
+// Sprite1 recursing yields between each %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+const b2 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_recursing_yields_bet (p0) {
+if ((("" + p0).toLowerCase() === "initial".toLowerCase())) {
+b0.value = 0;
+b1.value = ((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 1)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 2)) || 0) + (((+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0) + (+(yield* yieldThenCallGenerator(thread.procedures["Zrecursing yields between each %s"], 3)) || 0)) || 0)) || 0)) || 0)) || 0));
+if (((+b0.value || 0) === 3)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recursing between calls yields final",}, b2, false, false, null);
+} else {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail recursing between calls yields final",}, b2, false, false, null);
+}
+} else {
+if (compareEqual(b0.value, p0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":("pass recursing between calls yields " + ("" + p0)),}, b2, false, false, null);
+} else {
+yield* executeInCompatibilityLayer({"MESSAGE":("fail recursing between calls yields " + ("" + p0)),}, b2, false, false, null);
+}
+}
+return "";
+}; })
+
+// Sprite1 recursing arguments eval order %s %s %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["4HH82mPlVMOONdl(Ot*7"];
+const b1 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b2 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+const b3 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_recursing_arguments_ (p0,p1,p2,p3) {
+if ((("" + p0).toLowerCase() === "initial".toLowerCase())) {
+b0.value = [];
+b1.value = 0;
+b2.value = (yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 1",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 2",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 3",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 4","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 5","","","")),(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 6","","","")))),"","")),"",(yield* yieldThenCallGenerator(thread.procedures["Zrecursing arguments eval order %s %s %s %s"], "child 7","","",""))));
+if ((("" + (b0.value[(1 | 0) - 1] ?? "")).toLowerCase() === "1/child 4".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 1",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(2 | 0) - 1] ?? "")).toLowerCase() === "1/child 5".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 2",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(3 | 0) - 1] ?? "")).toLowerCase() === "2/child 6".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 3",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(4 | 0) - 1] ?? "")).toLowerCase() === "2/child 3".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 4",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(5 | 0) - 1] ?? "")).toLowerCase() === "3/child 2".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 5",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(6 | 0) - 1] ?? "")).toLowerCase() === "3/child 7".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 6",}, b3, false, false, null);
+}
+if ((("" + (b0.value[(7 | 0) - 1] ?? "")).toLowerCase() === "4/child 1".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - 7",}, b3, false, false, null);
+}
+if ((b0.value.length === 7)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass recurse arg order - length is correct",}, b3, false, false, null);
+}
+} else {
+b0.value.push((("" + b1.value) + ("/" + ("" + p0))));
+b0._monitorUpToDate = false;
+}
+return "";
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot
new file mode 100644
index 0000000000..0c8bd71d47
--- /dev/null
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-simple.sb3.tw-snapshot
@@ -0,0 +1,111 @@
+// TW Snapshot
+// Input SHA-256: cd516553f3cbf4a41ae9a8bc1f5d06a3392d365226bccdfa294578314f18facb
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 8",}, b0, false, false, null);
+if ((("" + thread.procedures["Zsimplest"]()).toLowerCase() === "It works!".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass simplest",}, b0, false, false, null);
+}
+if ((("" + thread.procedures["Znesting 1"]()).toLowerCase() === "42-54".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass nesting1",}, b0, false, false, null);
+}
+if (((+thread.procedures["Wwarp fib %s"](12) || 0) === 144)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass fib 12",}, b0, false, false, null);
+}
+if (((+thread.procedures["Wfactorial %s"](12) || 0) === 479001600)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass factorial 12",}, b0, false, false, null);
+}
+b1.value = (yield* thread.procedures["Zno shadowing 1 %s %s"]("f","g"));
+if (compareEqual(b1.value, "")) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass default return value",}, b0, false, false, null);
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 simplest
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_simplest () {
+return "It works!";
+return "";
+}; })
+
+// Sprite1 nesting 1
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_1 () {
+thread.procedures["Znesting 2"]();
+return (("" + thread.procedures["Znesting 3 %s %s"](6,7)) + ("" + thread.procedures["Znesting 3 %s %s"](-1,54)));
+return "";
+}; })
+
+// Sprite1 warp fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_warp_fib_ (p0) {
+return thread.procedures["Wfib %s"](p0);
+return "";
+}; })
+
+// Sprite1 factorial %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_factorial_ (p0) {
+if (compareGreaterThan(p0, 1)) {
+return ((+p0 || 0) * (+thread.procedures["Wfactorial %s"](((+p0 || 0) - 1)) || 0));
+}
+return 1;
+return "";
+}; })
+
+// Sprite1 no shadowing 1 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
+return function* genXYZ_no_shadowing_1__ (p0,p1) {
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 1",}, b0, false, false, null);
+}
+b1.value = thread.procedures["Zno shadowing 2 %s %s"](1,2);
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 2",}, b0, false, false, null);
+}
+b1.value = thread.procedures["Zno shadowing 2 %s %s"](3,4);
+if (((("" + p0).toLowerCase() === "f".toLowerCase()) && (("" + p1).toLowerCase() === "g".toLowerCase()))) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass shadow check 3",}, b0, false, false, null);
+}
+return "";
+}; })
+
+// Sprite1 nesting 2
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_2 () {
+return "discard nesting 2";
+return "";
+}; })
+
+// Sprite1 nesting 3 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_nesting_3__ (p0,p1) {
+return ((+p0 || 0) * (+p1 || 0));
+return "";
+}; })
+
+// Sprite1 fib %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_fib_ (p0) {
+if (compareLessThan(p0, 2)) {
+return p0;
+} else {
+return ((+thread.procedures["Wfib %s"](((+p0 || 0) - 1)) || 0) + (+thread.procedures["Wfib %s"](((+p0 || 0) - 2)) || 0));
+}
+return "";
+}; })
+
+// Sprite1 no shadowing 2 %s %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+return function funXYZ_no_shadowing_2__ (p0,p1) {
+return "discard shadow 2";
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot
new file mode 100644
index 0000000000..fa4325d2f3
--- /dev/null
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-stops-scripts.sb3.tw-snapshot
@@ -0,0 +1,52 @@
+// TW Snapshot
+// Input SHA-256: 27368530b97e0ab5053bf4faf8fd116aa30cea34adcbd95fc6ddcf0a9d6d4976
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+const b1 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, null);
+yield* thread.procedures["Wreturn stops the script immediately"]();
+if (((+b1.value || 0) === 25)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass return stopped the script immediately",}, b0, false, false, null);
+}
+yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "Test return outside of custom block" }));
+if (((+b1.value || 0) === 18)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass return worked to stop outside of custom block",}, b0, false, false, null);
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 return stops the script immediately
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_return_stops_the_scr () {
+b0.value = 0;
+for (var a0 = 100; a0 >= 0.5; a0--) {
+b0.value = ((+b0.value || 0) + 1);
+if (((b0.value || 0) === 25)) {
+return "stopped!";
+}
+if (isStuck()) yield;
+}
+yield* executeInCompatibilityLayer({"MESSAGE":"fail return did not stop the script immediately",}, b1, true, false, null);
+return "";
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["PsAI*C{QHI3*4?O8p#TM"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+if (((b0.value || 0) === 18)) {
+retire(); return;
+}
+yield;
+}
+retire(); return;
+}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot
new file mode 100644
index 0000000000..6b2ec81d26
--- /dev/null
+++ b/test/snapshot/__snapshots__/warp-timer/tw-procedure-return-warp.sb3.tw-snapshot
@@ -0,0 +1,121 @@
+// TW Snapshot
+// Input SHA-256: e652f448fbf09a37afacb03afa9f3448cce43aef06439b58b44aeec73edb0438
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+return function* genXYZ () {
+b0.value = 0;
+while (true) {
+b0.value = ((+b0.value || 0) + 1);
+thread.timer = timer();
+var a0 = Math.max(0, 1000 * -1);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a0) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+retire(); return;
+}; })
+
+// Sprite1 script
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ () {
+yield* executeInCompatibilityLayer({"MESSAGE":"plan 3",}, b0, false, false, null);
+yield* thread.procedures["Znon warp"]();
+runtime.stopForTarget(target, thread);
+yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
+retire(); return;
+}; })
+
+// Sprite1 non warp
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_non_warp () {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a1) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (((+b0.value || 0) === 5)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 1",}, b1, false, false, null);
+}
+if ((("" + (yield* thread.procedures["Wverify runs warp %s"]((yield* thread.procedures["Zverify runs non warp %s"]((yield* thread.procedures["Wverify runs warp %s"]("abc"))))))).toLowerCase() === "warp: non warp: warp: abc".toLowerCase())) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp and warp mix",}, b1, false, false, null);
+}
+b0.value = 0;
+for (var a2 = 5; a2 >= 0.5; a2--) {
+thread.timer = timer();
+var a3 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a3) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (((+b0.value || 0) === 5)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp 2",}, b1, false, false, null);
+}
+return "";
+}; })
+
+// Sprite1 verify runs warp %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_verify_runs_warp_ (p0) {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+while (thread.timer.timeElapsed() < a1) {
+if (isStuck()) yield;
+}
+thread.timer = null;
+if (isStuck()) yield;
+}
+if (!compareEqual(b0.value, 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail did not run warp",}, b1, true, false, null);
+}
+return ("warp: " + ("" + p0));
+return "";
+}; })
+
+// Sprite1 verify runs non warp %s
+(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
+const b0 = stage.variables["Go=PJS7BFXYo_qi2S:kQ"];
+const b1 = runtime.getOpcodeFunction("looks_say");
+return function* genXYZ_verify_runs_non_warp (p0) {
+b0.value = 0;
+for (var a0 = 5; a0 >= 0.5; a0--) {
+thread.timer = timer();
+var a1 = Math.max(0, 1000 * 0);
+runtime.requestRedraw();
+yield;
+while (thread.timer.timeElapsed() < a1) {
+yield;
+}
+thread.timer = null;
+yield;
+}
+if (compareEqual(b0.value, 0)) {
+yield* executeInCompatibilityLayer({"MESSAGE":"fail ran warp",}, b1, false, false, null);
+}
+return ("non warp: " + ("" + p0));
+return "";
+}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
index e519a948af..25f742e3c5 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-promise-loop-double-yield-kouzeru.sb3.tw-snapshot
@@ -18,7 +18,7 @@ if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;}
}
yield;
}
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -48,5 +48,5 @@ yield;
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
runtime.stopAll();
retire(); return;
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-restart-broadcast-threads.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-restart-broadcast-threads.sb3.tw-snapshot
index 4459d5baf0..ccec1e74d7 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-restart-broadcast-threads.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-restart-broadcast-threads.sb3.tw-snapshot
@@ -22,7 +22,7 @@ if (((+b1.value || 0) === 1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -30,5 +30,5 @@ retire();
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function* genXYZ () {
b0.value = ((+b0.value || 0) + 1);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot
index 4802e14767..c7dbdb0cde 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-safe-procedure-argument-casting.sb3.tw-snapshot
@@ -15,11 +15,12 @@ if ((((target.currentCostume + 1) === 1) && ((+target.getCostumes()[target.curre
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 switch %s
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function funXYZ_switch_ (p0) {
runtime.ext_scratch3_looks._setCostume(target, p0);
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot
index 239d1f55f9..96b07d55d8 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-sensing-of.sb3.tw-snapshot
@@ -110,5 +110,5 @@ if (((b6 ? b6.volume : 0) === 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass NE direction",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-stage-cannot-move-layers.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-stage-cannot-move-layers.sb3.tw-snapshot
index 974d8cbe06..923e8f1ece 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-stage-cannot-move-layers.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-stage-cannot-move-layers.sb3.tw-snapshot
@@ -18,7 +18,7 @@ yield;
thread.timer = null;
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "Test 1" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -29,13 +29,13 @@ return function* genXYZ () {
if ((("" + b0.value).toLowerCase() === "Initial".toLowerCase())) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b1, false, false, null);
}
-retire();
+retire(); return;
}; })
// Stage script
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
return function* genXYZ () {
-retire();
+retire(); return;
}; })
// Stage script
@@ -43,5 +43,5 @@ retire();
const b0 = target.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
return function* genXYZ () {
b0.value = "Stage callback";
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-subtract-can-return-nan.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-subtract-can-return-nan.sb3.tw-snapshot
index 7ce71511ea..e9573d3a27 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-subtract-can-return-nan.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-subtract-can-return-nan.sb3.tw-snapshot
@@ -10,5 +10,5 @@ if (!((Infinity - Infinity) <= 0)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot
index 4b5c21bd64..7ba4410d7f 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-tab-equals-zero.sb3.tw-snapshot
@@ -9,7 +9,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 6",}, b0, false, false, null);
b1.value = "\t";
startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" });
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -36,5 +36,5 @@ if (compareEqual((" " + ("" + b0.value)), (0 + 0))) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass \\t and other spaces = number 0",}, b1, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b1, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot
index a1349a1f5f..ea9202ade3 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-tangent.sb3.tw-snapshot
@@ -52,5 +52,5 @@ if (((tan(-450) || 0) === -Infinity)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass -450",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot
index 0d508339fc..34ae2a649e 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-unsafe-equals.sb3.tw-snapshot
@@ -82,5 +82,5 @@ if (compareEqual(0, "")) {
yield* executeInCompatibilityLayer({"MESSAGE":"fail",}, b0, false, false, null);
}
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
index 10d08edb04..7037c0256f 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-warp-repeat-until-timer-greater-than.sb3.tw-snapshot
@@ -8,7 +8,7 @@ return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, null);
yield* thread.procedures["Wrun without screen refresh"]();
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 run without screen refresh
@@ -24,4 +24,5 @@ if (isStuck()) yield;
if (compareLessThan((daysSince2000() * 86400), b0.value)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b1, true, false, null);
}
+return "";
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
index 58f2fcf03f..646f9d73a8 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-next-backdrop.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -15,5 +15,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
runtime.ext_scratch3_looks._setBackdrop(stage, stage.currentCostume + 1, true);
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
index c1528683ad..30302e62b2 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-when-backdrop-switches-to-switch-backdrop-to.sb3.tw-snapshot
@@ -6,7 +6,7 @@
const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite1 script
@@ -15,5 +15,5 @@ const b0 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 0",}, b0, false, false, null);
runtime.ext_scratch3_looks._setBackdrop(stage, "backdrop2");
-retire();
+retire(); return;
}; })
diff --git a/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
index d6e4f2e995..fc4a516916 100644
--- a/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
+++ b/test/snapshot/__snapshots__/warp-timer/tw-zombie-cube-escape-284516654.sb3.tw-snapshot
@@ -16,7 +16,7 @@ yield;
}
yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, null);
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, null);
-retire();
+retire(); return;
}; })
// Sprite2 script
@@ -28,5 +28,5 @@ b0.value = ((+b0.value || 0) + 1);
if (((b0.value || 0) === 5)) {
b1.value = "end";
}
-retire();
+retire(); return;
}; })
diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js
index a383001fee..284c61e0d4 100644
--- a/test/unit/engine_sequencer.js
+++ b/test/unit/engine_sequencer.js
@@ -74,9 +74,14 @@ const generateThread = function (runtime) {
let next = randomString();
let inp = randomString();
let name = th.topBlock;
+
+ const pushStack = id => {
+ th.pushStack(id);
+ th.peekStackFrame().op = {id};
+ };
rt.blocks.createBlock(generateBlockInput(name, next, inp));
- th.pushStack(name);
+ pushStack(name);
rt.blocks.createBlock(generateBlock(inp));
for (let i = 0; i < 10; i++) {
@@ -85,11 +90,11 @@ const generateThread = function (runtime) {
inp = randomString();
rt.blocks.createBlock(generateBlockInput(name, next, inp));
- th.pushStack(name);
+ pushStack(name);
rt.blocks.createBlock(generateBlock(inp));
}
rt.blocks.createBlock(generateBlock(next));
- th.pushStack(next);
+ pushStack(next);
th.target = rt;
th.blockContainer = rt.blocks;
diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js
index 36206eb99b..0a7c3f3e42 100644
--- a/test/unit/engine_thread.js
+++ b/test/unit/engine_thread.js
@@ -267,12 +267,17 @@ test('isRecursiveCall', t => {
rt.blocks.createBlock(block2);
th.target = rt;
+ const pushStack = id => {
+ th.pushStack(id);
+ th.peekStackFrame().op = {id};
+ };
+
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
- th.pushStack('secondString');
+ pushStack('secondString');
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
- th.pushStack('arbitraryString');
+ pushStack('arbitraryString');
t.strictEquals(th.isRecursiveCall('fakeCode'), true);
- th.pushStack('arbitraryString');
+ pushStack('arbitraryString');
t.strictEquals(th.isRecursiveCall('fakeCode'), true);
th.popStack();
t.strictEquals(th.isRecursiveCall('fakeCode'), true);