From 41af457060a0814dfae161c61c6fa1507dfe6c27 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 17:05:30 -0500 Subject: [PATCH 01/11] Move analyzeStackBlock analyzeInputs call into the switch statement Makes later commits easier. --- src/compiler/iroptimizer.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index f20a47ea4f..d3bbbeaf00 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -541,18 +541,19 @@ class IROptimizer { state = state.clone(); } - modified = this.analyzeInputs(inputs, state) || modified; - switch (stackBlock.opcode) { case StackOpcode.VAR_SET: + modified = this.analyzeInputs(inputs, state) || modified; modified = state.setVariableType(inputs.variable, inputs.value.type) || modified; break; case StackOpcode.CONTROL_WHILE: case StackOpcode.CONTROL_FOR: case StackOpcode.CONTROL_REPEAT: + modified = this.analyzeInputs(inputs, state) || modified; modified = this.analyzeLoopedStack(inputs.do, state, stackBlock) || modified; break; case StackOpcode.CONTROL_IF_ELSE: { + modified = this.analyzeInputs(inputs, state) || modified; const trueState = state.clone(); modified = this.analyzeStack(inputs.whenTrue, trueState) || modified; modified = this.analyzeStack(inputs.whenFalse, state) || modified; @@ -560,10 +561,12 @@ class IROptimizer { break; } case StackOpcode.CONTROL_STOP_SCRIPT: { + modified = this.analyzeInputs(inputs, state) || modified; this.addPossibleExitState(state); break; } case StackOpcode.PROCEDURE_CALL: { + modified = this.analyzeInputs(inputs, state) || modified; modified = this.analyzeInputs(inputs.inputs, state) || modified; const script = this.ir.procedures[inputs.variant]; @@ -575,6 +578,7 @@ class IROptimizer { break; } case StackOpcode.COMPATIBILITY_LAYER: { + modified = this.analyzeInputs(inputs, state) || modified; this.analyzeInputs(inputs.inputs, state); for (const substackName in inputs.substacks) { const newState = state.clone(); @@ -583,6 +587,9 @@ class IROptimizer { } break; } + default: + modified = this.analyzeInputs(inputs, state) || modified; + break; } return modified; From 44e2589e65fe2c4ed97bc6aa2f4ead75b8a72c50 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 17:06:21 -0500 Subject: [PATCH 02/11] Fix https://github.com/TurboWarp/scratch-vm/issues/277 Need to clear the state before analyzing the condition as the condition may be re-evaluated after yielding. --- src/compiler/iroptimizer.js | 5 +++ .../tw-wait-until-condition-gh-277.sb3 | Bin 0 -> 2928 bytes ...ait-until-condition-gh-277.sb3.tw-snapshot | 34 ++++++++++++++++++ ...ait-until-condition-gh-277.sb3.tw-snapshot | 34 ++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 test/fixtures/execute/tw-wait-until-condition-gh-277.sb3 create mode 100644 test/snapshot/__snapshots__/tw-wait-until-condition-gh-277.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-wait-until-condition-gh-277.sb3.tw-snapshot diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index d3bbbeaf00..91ebd5cf46 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -565,6 +565,11 @@ class IROptimizer { this.addPossibleExitState(state); break; } + case StackOpcode.CONTROL_WAIT_UNTIL: { + modified = state.clear() || modified; + modified = this.analyzeInputs(inputs, state) || modified; + break; + } case StackOpcode.PROCEDURE_CALL: { modified = this.analyzeInputs(inputs, state) || modified; modified = this.analyzeInputs(inputs.inputs, state) || modified; diff --git a/test/fixtures/execute/tw-wait-until-condition-gh-277.sb3 b/test/fixtures/execute/tw-wait-until-condition-gh-277.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..a170d42f7370e5d81c336d3298b08a1c52dee520 GIT binary patch literal 2928 zcma);c{CL28^>qtJDIU>W8bokWlX}@%?y&zD50^HEEBTVFpOkNwu&T-U6w{9!jK}_ zuB=0mtdlKUR9yG`{SU$*MUW4ndaqMfP3$CoJ%}~`tkvx zXycZG%8%j4yw-ICVx9&;YT5^HivdXrgL3FuCtm!F1@&4zT zuyr3kMaFqTbmb&f{kErj?vE@ToC%-K-(A1;N^DQK4;W=wU`I#CzRa0?d&?)NJ0q@y zR@C9lQft#>-xTk+5{`v^qQWal|iwc)MGTQx&;wxSS=Nfl?>x$gLdXQQnK z>ZEwhV*IEjXlvn!%x(lMN zJiy3ASZxJE=&XH`G5aJkWKt|eKDHNC?w6#w3!|J@4(e=I6ACNwLhP!KKlxP1MQbAI zHBHg2x;ne0RML@?8;4r;mMN4z$%?sGRH`U~2v@2h?Bhdk?;oy3vxb*zGw6w`R<~X# z9;}Y3`b>!J{rKW5cLF+8U88?7P--))-+x4G z`n-)|iY@sI)_sALt>N^qn2-^O&^}{0%gEyTdGinP6*1f~J7R2l;SK}-dH-i@pI0}_|?R+~e zNIgZl`A39j)z|8p%h;oLUkf^KeWrgXE*|jW#;i>qBa;Q}kih`{xdhMZW+kw?hsFc` zZE95}KTVqv07zv2pD8#L=Ai?F!lBxpFf1JI1=H2gglTHKYw2pa>%gJVzf3)Kr213M zd4BN`7!&y+V|sdIa~ zT6$)zuXHnPQhc{ETl9I~+^RqR!gJVgaA^4T&5ey2@o}-#F#$>>9(dF>|LuC?H2$Z& zg;KHehf=?-+G#6TrDG4W2T5JOY+G30SGxLrV{7*M$U%Ro_rTi3?5}2; z*Qx5g(#p=wkde{e=?G!`QmmS)X-xr5@kvB#48H0xcc*@BeKd5guCdqQ$KoD-N}?pc zq4d)z^;iA0>(R5r)8gK9^T90=M?SfgtNvpW>!EWM(-OO`Z{`a8Y=$>CPQgbYE{nlG4Fqs_?K@)5LTPD}m{ zXR}>Gne=^i<*Rghs;|qFDEsLTn57o|RxT;n@SZj4lHFkw2thfyo2+2B6L`C>4yMP8 z3&2NTOe`vJ&fqu#03ZUKIJk|5t{#;7>SO1PS#PC_Dat=G+gFMq9Tbb|YX3?H-C88M zPU4xv+k_+puI%kKPt&O&rZS-m8(K7CUhAtcxTX0mPe#U|kd>kV6IW5BW%(9bQEF21j_Tcl|xFDP0Ha0?a0nC}EtVp38#@ zfaDPKc9wXcIBCa<$FV_3Q%eUkcaJf{LE!XlkK4A#rkbo$zR77M)c@BYLFu{~Q zuJ(^Y83^pP(wfk z_Q3{eBDt=L0c^K^b9XXr1jM_27u4BwKS=pXzFCqarp4z<|IH_yw$v>udXLAR#7gHpXS&CYxev^#rene}5M>`R+`uxi zrkMiLLzrrC2sVC}YBIlq@S*rU&|~+zbk4pfxbip+GPoKX>dFxg$R)uqX}-<*$>rmW zkTN$eK83-arJ5VwMT=$T&KI|nv-k_x%`Nmj?cbyiWK$A)4Sc2}>Yx@+#lFf11R@(8 zqAm%^O7-QoyVBx=4tG)gJtoGPgBZ4k^d5eWik%X?&ahm-v kRQkQ+?-2OABjYg|{tFUp(Tq%gspyaI$KyZhFQb4z0Uli<>i_@% literal 0 HcmV?d00001 diff --git a/test/snapshot/__snapshots__/tw-wait-until-condition-gh-277.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-wait-until-condition-gh-277.sb3.tw-snapshot new file mode 100644 index 0000000000..4312367087 --- /dev/null +++ b/test/snapshot/__snapshots__/tw-wait-until-condition-gh-277.sb3.tw-snapshot @@ -0,0 +1,34 @@ +// TW Snapshot +// Input SHA-256: 2422e407892596fbf410e032196131f9bae718563fd80b1ff32cf972859c034b + +// 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 0",}, b0, false, false, "e", null); +startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" }); +b1.value = "bwah"; +while (!(("" + b1.value).toLowerCase() === "bleh".toLowerCase())) { +yield; +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "h", null); +retire(); return; +}; }) + +// Sprite1 script +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +b0.value = (1 + 2); +thread.timer = timer(); +var a0 = Math.max(0, 1000 * 0); +runtime.requestRedraw(); +yield; +while (thread.timer.timeElapsed() < a0) { +yield; +} +thread.timer = null; +b0.value = "bleh"; +retire(); return; +}; }) diff --git a/test/snapshot/__snapshots__/warp-timer/tw-wait-until-condition-gh-277.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-wait-until-condition-gh-277.sb3.tw-snapshot new file mode 100644 index 0000000000..4312367087 --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-wait-until-condition-gh-277.sb3.tw-snapshot @@ -0,0 +1,34 @@ +// TW Snapshot +// Input SHA-256: 2422e407892596fbf410e032196131f9bae718563fd80b1ff32cf972859c034b + +// 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 0",}, b0, false, false, "e", null); +startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "message1" }); +b1.value = "bwah"; +while (!(("" + b1.value).toLowerCase() === "bleh".toLowerCase())) { +yield; +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "h", null); +retire(); return; +}; }) + +// Sprite1 script +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +b0.value = (1 + 2); +thread.timer = timer(); +var a0 = Math.max(0, 1000 * 0); +runtime.requestRedraw(); +yield; +while (thread.timer.timeElapsed() < a0) { +yield; +} +thread.timer = null; +b0.value = "bleh"; +retire(); return; +}; }) From 5d917cca0580b9e5dcff5fdeeb95d8994859421f Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 17:36:38 -0500 Subject: [PATCH 03/11] Use log.error instead of console.error --- src/compiler/iroptimizer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index 91ebd5cf46..73e348a672 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -1,6 +1,7 @@ // @ts-check const {StackOpcode, InputOpcode, InputType} = require('./enums.js'); +const log = require('../util/log'); // These imports are used by jsdoc comments but eslint doesn't know that /* eslint-disable no-unused-vars */ @@ -647,7 +648,7 @@ class IROptimizer { do { // If we are stuck in an apparent infinite loop, give up and assume the worst. if (iterations > 10000) { - console.error('analyzeLoopedStack stuck in likely infinite loop; quitting', block, state); + log.error('analyzeLoopedStack stuck in likely infinite loop; quitting', block, state); modified = state.clear(); block.entryState = state.clone(); block.exitState = state.clone(); From 2639653647652199f46995af0a7e1ad1d13dc1ee Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 18:32:13 -0500 Subject: [PATCH 04/11] Revert "Fix #273" This reverts commit c16be2517896a0f825f494fd3b171b6de33eb7e8. Caused https://github.com/TurboWarp/scratch-vm/issues/276 --- src/compiler/iroptimizer.js | 2 +- .../__snapshots__/tw-gh-249-quicksort.sb3.tw-snapshot | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index 73e348a672..9713578727 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -659,8 +659,8 @@ class IROptimizer { const newState = state.clone(); modified = this.analyzeStack(stack, newState) || modified; - modified = this.analyzeInputs(block.inputs, newState) || modified; modified = (keepLooping = state.or(newState)) || modified; + modified = this.analyzeInputs(block.inputs, state) || modified; } while (keepLooping); block.entryState = state.clone(); return modified; diff --git a/test/snapshot/__snapshots__/tw-gh-249-quicksort.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-gh-249-quicksort.sb3.tw-snapshot index d2efe972b7..224f800f45 100644 --- a/test/snapshot/__snapshots__/tw-gh-249-quicksort.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-gh-249-quicksort.sb3.tw-snapshot @@ -67,10 +67,10 @@ b0.value = (b1.value[(((((+p1 || 0) + (+p0 || 0)) || 0) / 2) | 0) - 1] ?? ""); b2.value = p0; b3.value = p1; while (true) { -while (compareLessThan((b1.value[(b2.value | 0) - 1] ?? ""), b0.value)) { +while (compareLessThan(listGet(b1.value, b2.value), b0.value)) { b2.value = ((+b2.value || 0) + 1); } -while (compareGreaterThan((b1.value[(b3.value | 0) - 1] ?? ""), b0.value)) { +while (compareGreaterThan(listGet(b1.value, b3.value), b0.value)) { b3.value = ((+b3.value || 0) + -1); } if (compareGreaterThan(b2.value, b3.value)) { From 40715dc259e24d811514b6ca49597c03685d1c53 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 18:32:21 -0500 Subject: [PATCH 05/11] Add test case for https://github.com/TurboWarp/scratch-vm/issues/276 --- .../tw-loop-condition-optimization-gh-276.sb3 | Bin 0 -> 3277 bytes ...dition-optimization-gh-276.sb3.tw-snapshot | 28 +++++++++++++++++ ...dition-optimization-gh-276.sb3.tw-snapshot | 29 ++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 test/fixtures/execute/tw-loop-condition-optimization-gh-276.sb3 create mode 100644 test/snapshot/__snapshots__/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot diff --git a/test/fixtures/execute/tw-loop-condition-optimization-gh-276.sb3 b/test/fixtures/execute/tw-loop-condition-optimization-gh-276.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..48099303ce15a5d8950c76836b2560cd7b0e4bf3 GIT binary patch literal 3277 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 literal 0 HcmV?d00001 diff --git a/test/snapshot/__snapshots__/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot new file mode 100644 index 0000000000..20e23cc605 --- /dev/null +++ b/test/snapshot/__snapshots__/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot @@ -0,0 +1,28 @@ +// TW Snapshot +// Input SHA-256: 3c81a01417b9a927457132a5f89b63e54b2499714376246739535b51dbce2d45 + +// 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 1",}, b0, false, false, "g", null); +thread.procedures["Wtest %s"]("random"); +if ((("" + b1.value).toLowerCase() === "random".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "p", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "n", null); +retire(); return; +}; }) + +// Sprite1 Wtest %s +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +const b1 = stage.variables["t)]?yi[*8XU73qhMqOa8"]; +return function funXYZ_test_ (p0) { +b0.value = p0; +while (!(("" + listGet(b1.value, b0.value)).toLowerCase() === "something".toLowerCase())) { +b0.value = ((+b0.value || 0) + 1); +} +return ""; +}; }) diff --git a/test/snapshot/__snapshots__/warp-timer/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot new file mode 100644 index 0000000000..5cef84247b --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-loop-condition-optimization-gh-276.sb3.tw-snapshot @@ -0,0 +1,29 @@ +// TW Snapshot +// Input SHA-256: 3c81a01417b9a927457132a5f89b63e54b2499714376246739535b51dbce2d45 + +// 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 1",}, b0, false, false, "g", null); +yield* thread.procedures["Wtest %s"]("random"); +if ((("" + b1.value).toLowerCase() === "random".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "p", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "n", null); +retire(); return; +}; }) + +// Sprite1 Wtest %s +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +const b1 = stage.variables["t)]?yi[*8XU73qhMqOa8"]; +return function* genXYZ_test_ (p0) { +b0.value = p0; +while (!(("" + listGet(b1.value, b0.value)).toLowerCase() === "something".toLowerCase())) { +b0.value = ((+b0.value || 0) + 1); +if (isStuck()) yield; +} +return ""; +}; }) From 5a90e13981cb62201724fd16497bb64728405055 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 18:37:42 -0500 Subject: [PATCH 06/11] Fix TypeState#after setting variable types to undefined If a variable existed in `this` but not in `other`, its resulting type would be set to undefined. Changing it to use the type from `this` seems to fix https://github.com/TurboWarp/scratch-vm/issues/273. --- src/compiler/iroptimizer.js | 2 +- .../tw-procedure-return-recursion.sb3.tw-snapshot | 6 +++--- .../tw-procedure-return-recursion.sb3.tw-snapshot | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index 9713578727..6cadd77168 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -94,7 +94,7 @@ class TypeState { after (other) { return this.mutate(other, varId => { const otherType = other.variables[varId]; - if (otherType !== 0) return otherType; + if (otherType) return otherType; return this.variables[varId] ?? InputType.ANY; }); } diff --git a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot index b7824ee767..6b2bc2a9f6 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot @@ -10,17 +10,17 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, "G", null); b1.value = 0; b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8)); -if (((+b1.value || 0) === 4)) { +if ((b1.value === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; b2.value = thread.procedures["Wwarp recursion should not yield %s"](8); -if (compareEqual(b1.value, 0)) { +if ((b1.value === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, "ar", null); } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if (((+b1.value || 0) === 20)) { +if ((b1.value === 20)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, "au", null); } yield* thread.procedures["Zrecursing yields between each %s"]("initial"); 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 index b7824ee767..6b2bc2a9f6 100644 --- 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 @@ -10,17 +10,17 @@ return function* genXYZ () { yield* executeInCompatibilityLayer({"MESSAGE":"plan 18",}, b0, false, false, "G", null); b1.value = 0; b2.value = (yield* thread.procedures["Znon warp recursion should yield %s"](8)); -if (((+b1.value || 0) === 4)) { +if ((b1.value === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; b2.value = thread.procedures["Wwarp recursion should not yield %s"](8); -if (compareEqual(b1.value, 0)) { +if ((b1.value === 0)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yield",}, b0, false, false, "ar", null); } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if (((+b1.value || 0) === 20)) { +if ((b1.value === 20)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, "au", null); } yield* thread.procedures["Zrecursing yields between each %s"]("initial"); From c372e925a52c20cc5410e7128d0c88e9ca8b6c60 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 19:27:08 -0500 Subject: [PATCH 07/11] Add test case for 6a37fee3866cb96fbc054009b6b1f31bb9244cf9 --- .../tw-warp-loop-condition-analysis.sb3 | Bin 0 -> 3363 bytes ...rp-loop-condition-analysis.sb3.tw-snapshot | 39 +++++++++++++++++ ...rp-loop-condition-analysis.sb3.tw-snapshot | 40 ++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test/fixtures/execute/tw-warp-loop-condition-analysis.sb3 create mode 100644 test/snapshot/__snapshots__/tw-warp-loop-condition-analysis.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-warp-loop-condition-analysis.sb3.tw-snapshot diff --git a/test/fixtures/execute/tw-warp-loop-condition-analysis.sb3 b/test/fixtures/execute/tw-warp-loop-condition-analysis.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..126857522d82ea44de2d6b67d6097a33716bdeb4 GIT binary patch literal 3363 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 Date: Mon, 22 Sep 2025 19:28:17 -0500 Subject: [PATCH 08/11] Add test case for 7cc8f580b3e15b805ef5b1b441b190392cfcd49b --- .../tw-non-warp-loop-condition-analysis.sb3 | Bin 0 -> 3185 bytes ...rp-loop-condition-analysis.sb3.tw-snapshot | 29 ++++++++++++++++++ ...rp-loop-condition-analysis.sb3.tw-snapshot | 29 ++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 test/fixtures/execute/tw-non-warp-loop-condition-analysis.sb3 create mode 100644 test/snapshot/__snapshots__/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot diff --git a/test/fixtures/execute/tw-non-warp-loop-condition-analysis.sb3 b/test/fixtures/execute/tw-non-warp-loop-condition-analysis.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..59c1cfcf66cee6cdfcb5254567d83f32845187be GIT binary patch literal 3185 zcma);c{CJ!7stmklWpv4vS#063}ftBVh}^oC|iS(eJvD?y{yT;reS1Fqz0+UBqW7s zY-2=Y%M!9h;q{*PkLP(h=RN27-E+_HoO^!vp7Z_V_x)U?B?BW1003YD__n-5UM(l~ zo&W*>iCh2x+i^80B+$pxJxs+XG%(=#CI7&$TAYW9&Slmwa_x!|0Ce8Zdk8Bc6L}9< zr~ z1ap1jDUDjy5{OZx&9pd+TIS3L+lr@7NkNvT0FKNF^=!QAGzd@aGQWZk&2P5#T) zXH<5|OWcg;Bp{kn*{LhI2wd|146ZX-JJp&*A<+sz4R_2kdPKUml;St4Q09P++Fl_S2<**^J{BwTijFcj z*q7Ll0)~Z0>RHJ739I5_a?k^`1`Dc&hb%|oLp{ekDio?qgWHqSxl6*_ z{8czmhVp8JjLRfSI4sE3({vpOtcp&7`V+2NQ|yheb-;96#8XNkl(tt8y8vad5Q3kS z)pi-;dSAc7pSx7e$uO+&Z0BB4->qjY`m+!@hTUQA#lx|*FXExkcvMXc49)Nd1Fh#>u<`pyXd`5WJ)YWuU*iOu1xE#xu-nU&gUH8x? z8C^AGNgrSUJ?$^@>zeBKdBk>suhNr%iY1p@j7xn;E^jQW%W=eA^g&EKvF4^-jJ~ka z1(l}V=Ym`R7QUc)vd@Dl;1YL>qHvnj`3G;>)e9XWn6C$~_#5{-Tluyw_bDPi8^>%f ztQRRzVQ9g~!LAfVe!Q)fS43W7mZ^YJ2xG1#M=TGgJXn&+RYtJvJ>EB^+Q44>>|B=1 zki8D;8EJg}2M_x6z^qSlZM=BaFh?<5P)I(K6T@B-b!Jr`!h$_@m)ze+W(?bh- zRVDO>m;ZT7_IPpL$y+I-EhpYKK;M)jYzBFrStMNS>Zlu}j9$(^k5MZO6dt8>;3?m1 zHtLl&3ej^+F24~rug_Ml&}Zk@X@>8u zC@wpSd)naJ)LzXRv-4KaY4a;Xp{QtJ>-8DyQXt3-cF3p?|N02a>}Dmfx@(CA{8QAb zR9U9RF$Gds|0fC#fq7`cAaIC=CkzdTV_;fp>M(T;cc>QBT@wz0{8!Xt2TB0t9OnUF z9597nd{jrLxw$Ku9zy$H%YX42)0)jQMPN@V%7k5#Kb*EeT^$@~4`UIX99XAatzG1?8g zRdt;kH^?J>)SD-cMzBUq0YijAU|Fy7-ofLaiMyUES zLOFOwbv=4|s4MD4n+tm>cH~oBw;C`iwiZsSrHbvicGGbF*25d?TuEzD2RgzxpTXFd z+wFhN+%9r)#2Wai^-)%#qGc=F0WDn|+F16dj%5KOJ+%GJ+GtN5C+E_0tqubN4g;$@ zO-GHwyTyk)jY4ON_0Ba-;mGaVVZ(k7t50I%vIQ-^`|)zCy*}$?UIKYUgUTj@+wJaF zpUziR^rqKQd0Uf8+RG|9A-Ukc zz90vl4q^)g@WjCr2e&LCtNT@cdgwVL=G$2!^0FnSdkV4m{mN5X8V6ZCw-@lP6W9}x zuZ6?}uI=u&Q|XlrCvzb4>(J%IQsS!!xcRx;oWUr= zAoKJLnNwQU7^WCWBW{X17Bh``vXgi%jHl)VQ}rD|Z_Q;?IAlylBu5gw`YUZ0NX$72 za&WJ+5z4%l0-RDcC@9^F%F`Fiv|cv~q7m5-mse6Ex~;N!B!R;D$uA3r#!*UC&&B~UMYuJuPh1S2V%6!BLRIlq<7g*L(R z;(WyzG(En9Rc@@j4<2s8^SO8M^b{Uz%JJ2f?i|sO6b!?&q4P&Q2BI44fLQO#zg57#_MPhbEXL( z>jvmakRb~{Q$sVq+{r`H`#cZa@6kK?aj}`ZlF$*ue#HHG6eeE!@TT^gh|-|$P2K?yW zIMK*RTtkUy7n)mL*&maAC&MtE^Ud!_2~@ErU;Ty92jKYovOUPN&0t>AbS*~|ssJ{n z0P+2hzobn6B77kg6M4}!4(KXBlh_qT;C)#{ca6lS6;CsT~{nzoF8f i6aR$3UlTzf0N~#s0ci<5ehC0D9LLo09es=w0N_8jvX`I$ literal 0 HcmV?d00001 diff --git a/test/snapshot/__snapshots__/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot new file mode 100644 index 0000000000..018d12fbdc --- /dev/null +++ b/test/snapshot/__snapshots__/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot @@ -0,0 +1,29 @@ +// TW Snapshot +// Input SHA-256: be149d21cc69628647000cc698ebd60949252730a0ea7b19f71cc85fe927b294 + +// 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["%ehs:~!Y0l-5=!mnd-B?"]; +const b2 = stage.variables["=3aHfv[mKa)v,Wfpy:y?"]; +const b3 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); +b1.value = []; +b1.value.push((1 + 2)); +b1._monitorUpToDate = false; +b1.value.push("eof"); +b1._monitorUpToDate = false; +b2.value = 0; +b3.value = "bwah"; +while (!(("" + b3.value).toLowerCase() === "eof".toLowerCase())) { +b2.value = ((+b2.value || 0) + 1); +b3.value = (b1.value[(b2.value | 0) - 1] ?? ""); +yield; +} +if (((+b2.value || 0) === 2)) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "q", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "o", null); +retire(); return; +}; }) diff --git a/test/snapshot/__snapshots__/warp-timer/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot new file mode 100644 index 0000000000..018d12fbdc --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-non-warp-loop-condition-analysis.sb3.tw-snapshot @@ -0,0 +1,29 @@ +// TW Snapshot +// Input SHA-256: be149d21cc69628647000cc698ebd60949252730a0ea7b19f71cc85fe927b294 + +// 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["%ehs:~!Y0l-5=!mnd-B?"]; +const b2 = stage.variables["=3aHfv[mKa)v,Wfpy:y?"]; +const b3 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "d", null); +b1.value = []; +b1.value.push((1 + 2)); +b1._monitorUpToDate = false; +b1.value.push("eof"); +b1._monitorUpToDate = false; +b2.value = 0; +b3.value = "bwah"; +while (!(("" + b3.value).toLowerCase() === "eof".toLowerCase())) { +b2.value = ((+b2.value || 0) + 1); +b3.value = (b1.value[(b2.value | 0) - 1] ?? ""); +yield; +} +if (((+b2.value || 0) === 2)) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "q", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "o", null); +retire(); return; +}; }) From 214b86a063f72c4a126bb47d77a56049cb398e48 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Mon, 22 Sep 2025 19:29:06 -0500 Subject: [PATCH 09/11] Add test case for d8c73c71d255d1a9a6526b1cd7e3da8c077fac89 --- ...-analysis-understands-stop-this-script.sb3 | Bin 0 -> 3262 bytes ...derstands-stop-this-script.sb3.tw-snapshot | 30 ++++++++++++++++++ ...derstands-stop-this-script.sb3.tw-snapshot | 30 ++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 test/fixtures/execute/tw-state-analysis-understands-stop-this-script.sb3 create mode 100644 test/snapshot/__snapshots__/tw-state-analysis-understands-stop-this-script.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-state-analysis-understands-stop-this-script.sb3.tw-snapshot diff --git a/test/fixtures/execute/tw-state-analysis-understands-stop-this-script.sb3 b/test/fixtures/execute/tw-state-analysis-understands-stop-this-script.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..735f45d77caa48a86c503a91a37b36a1dee968fb GIT binary patch literal 3262 zcma);c{CJ!7srQ;eT%_J_K1`;&0w;ReT%GPXBcE(Bij@a%D%-|B0FI$BTFJXg_5xh zN|Pm;5JSoGde8gE^E{pNp7Z?fx#ym9&+p!IzW;nb*I54y4I=;mpa*!i4jJoyu}7Ez z0RYrl0D$?l>lfhbfp!j(_6YR#d1+}A@>!PsSgPvay;86h@yUih)^MgUKh=eA$UaZK zs=en3dx zbbS2y4Z`$|sA8gIZ6pGNAsa;P94CFpM5z3tRIHG&lPOsa>9fPAv-uB4Oqu$TI?#rR zZ^TYY8ULM*y`rKUD1ldJRZfa6NI$8@`x4sES!}%OIgi&wtn*#1mliD8k`;_y@FlP< z^f(-aD76?}Q6(s)lO$N)sgim9;}TNdYQa)+RU`+L^*QpSyMDq~WfYtdD|%KoS0
ROiMLqst~xX&NKW&c~nw* z`9mjvgvahJfd)K^b=3gHi`!-PGdbpMu^V5^j8gNixINy}9=$AiYsN_tjk6fRd83bc zm*w(}>sm6Q!(fkTC;23LbY!w=nv+bNiaL1?!Q7QI0=R6hOf?@#7?q$5+U3W!t?1&0KPh^;P20u0C0*IN9 z)AV?bbDf9=3%3hqYK%CIKX7!*9}JMhbSs)xRIS44Iz!(wD(j26ZObrP*bBSotgn4r zIXEoA_y@AM+c8C&FBOOE*G;P^bwEYJUSu>o*4>m8Me!~pAHKQxDxM2_0i>2@RcRWz>0adBw)Xlw`)~Pk#U4y9m!Z#aEfx zR~fWtv2Bu1U!={uh2V$GFs->CC*dTJPxn0sQejzUP=3U&Ee+i;Yv8-V$?taBzv=pi zp3E#ZT&-<;DIYmaRvgz1Z<>y>siemj+9-h?b&&d$uVf${4m0TEi~UWtn)-Eq6HwKUr1YR(`Sd)=m&;iCyW0z3(g z_g@=KQz@?LE*B&wqc(mvR-;F%Jk^ZQ6YFMv#8*C2TVy*VACdgBgtgJmo{K0vImaQRbbEC0LP-6E_n!s2pNhm`M?St)-U<(5AT=NHnx(Mei z!MUEJ8eFze%MhDkq6o!;tb+K^@6(qSL4$A@J~Tf5yeDM<$~b7krnZqsXvaITFVsrD zuo;~Zn=ko+iF*OsuynUv?zXNvyJi$a}D&irczc1at(G3 z>LLTXe$Caz)fEMSKxCk38MHGD?II6#k`BD%=546Y8Y2Xn4^NzKrR%GMzAn)1S+diF7#2eWC|SjFMZ>TXYiLSm%lBsARgg*9W%U`Ftio{lE^+7>lV4mppd)%+gn?o z1ZMb?r+Iq9F;pl0%Rl^E7chqxwJ(*~eyH%?X;?6jtFai?9o8kTxX#;uG#JtxE;BA^ zep^BQxwW(8KXot`;x_($cIl{N!}Uhpenn01_MIuh=t9^z%v#Jp{hNIKZ5jzlZi+7)QMuoxe=7$@)HP8#_3vB!H2yjQVGTDZmHsSZleq?ZM`{(f`@ zSns1I6L z_?8$tV35u!0_4j{=zQ{ic2}IfK8_9gLr#cBx3sj+Zi`_OMK#qODKm7STS8NK-!HmQ z!T(KcTe>XDo+`Vvv?r8>fc`om0eE~ztD8QKN}!Ztz;1!(y$+Q{lG1<~<~)iIT^<_W zGXgNu2>C{^CaZ?OuF8w1nTXdo^Pu5<$ps-06w7EZo&Vyjs46vrxiV8E`GPsUz~i9= z#mujaS+&GTVhf2*ECQnFMB4mW|!Zc$lo3#Qys5t}qfS z%T;R{SjZBSc5%>Ht%(KH^QoFG0{KL4@e;)IP0soi>h_S;N3M6x9HM~^l1s7uK_t$% zIx1XWgiRrjxE%Q)pyfjsF!yv~iz?C!$@yzHNads}Y5s)aQ$OWLO3L~^=8UmE6*UO( z|8MzIp#1Iq{j&da@=p%^H5PSh Date: Mon, 22 Sep 2025 19:30:23 -0500 Subject: [PATCH 10/11] Add test case for 7ed29f5801c4ad4399d852bf5b4f19244b13cc5b --- ...avoids-number-literal-if-name-conflict.sb3 | Bin 0 -> 2035 bytes ...r-literal-if-name-conflict.sb3.tw-snapshot | 21 ++++++++++++++++++ ...r-literal-if-name-conflict.sb3.tw-snapshot | 21 ++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/fixtures/execute/tw-sound-menu-avoids-number-literal-if-name-conflict.sb3 create mode 100644 test/snapshot/__snapshots__/tw-sound-menu-avoids-number-literal-if-name-conflict.sb3.tw-snapshot create mode 100644 test/snapshot/__snapshots__/warp-timer/tw-sound-menu-avoids-number-literal-if-name-conflict.sb3.tw-snapshot diff --git a/test/fixtures/execute/tw-sound-menu-avoids-number-literal-if-name-conflict.sb3 b/test/fixtures/execute/tw-sound-menu-avoids-number-literal-if-name-conflict.sb3 new file mode 100644 index 0000000000000000000000000000000000000000..ce978f1954e0192d3a7d2bc9941df72e9e62949e GIT binary patch literal 2035 zcma)-cTm&W7RP_|P#4zhQk0@}1YSrXVNpPE3DUcZfFy)OAQTq}eU~B$y^KLXq%1{N zjDi%Y@}w70L}Hevp+tBrgoh-oNHZ_9vor28^Jctv&L8*AJ>NT@bIzROY$qfv0RR99 zAhwS>+fDe6;DiByB?kaf{M+jh;lX%ZlwojWco?I^H*(n+OyNn?Fk4}U+t(~Rf-dz( zLme>Mz5k7FR@C!u-ag}b{wtcJys*jTq~Z|)T1^U-p-m(O3-5`OI%rw1d~(gfl<7Y#4? zvR`B_W(su6>2He}p3$#T_Z6N@qZdAl6eQ)BP;fIBWAq2K|GiaOS3mFmYlYRNkzokx zj7(2)LQbfBwxnT`R9vD&nS0^|U;1yAZJ$!QtkHhVK%EsYd$4JteA$Y%;&=9#k11JI zYVPkmZIv`nk-nWlftZ|h~y|^mmufm>rk{5o`S=Aa% zwY9>Q1<<}fDuB8c(G|50!*ccFA_Ryn91=|=f?9!u zL4~?y>3 zQ)#Pn$&#SAz|~p&s+e|o+b=!3IG?Q!RJM;EXZ$_|agVfwiHMteS}GGyKAC;D;-LB4 zY`BTE0z>(Qtxn%c|DEmvWInOk$)}j8aFC8B*e3@A+i6Q<^yy{dG`zLg-AxPjkUbE5xlJtDYh{gcs?5l=_-0Ry&6}BqB1?Qsc?Q zJP(gWHJwj%oyNgiuIhWgT#+L@Ad|n=)6PI8@;Y=Dc9Wf0VC!Pis66*j_wFl8>}lG= zcG4dlO1{d}JmG2zGTvuhXhEWW$CYX;p95Rsy!=69vRu55Bcw}hO}MV+@ltuGwG$*W z8PRzt=KPOllrbSIK;<`~XMEG(8!iB9gfM~!KoBq^LI8o_4~0S@a6AN$L*N5YaI9fu z^tDh2JDD45;5P|b)9qq|4e@; zA7l4wP*5xZPC&vDus@;lUP#=4$fo&evO7x$E5NKi zq+Ds`P)T73q|Ir;{R#M)N;G9pfl&Ll*;l(?0X*s{Xlkr#lQO<%a{a1p(0EVo7{?LR zcQ)ztz>?7ep2h6$d2=_6i0Z7aY;T$M21hsMH9il`Xy;;-3}9*)_`~1enKD*`e$KRJ!+Hu9>iT$kT nF9HBxlIG)z$3yv9(NYuuz9g{Db|U Date: Mon, 22 Sep 2025 19:35:06 -0500 Subject: [PATCH 11/11] Use Object.create(null) instead of {} Avoids any weird issues if variables have strange IDs like "toString" --- src/compiler/iroptimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index 6cadd77168..416f1c3e06 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -17,7 +17,7 @@ const { class TypeState { constructor () { /** @type {Object.}*/ - this.variables = {}; + this.variables = Object.create(null); } /** @@ -31,7 +31,7 @@ class TypeState { break; } } - this.variables = {}; + this.variables = Object.create(null); return modified; }