diff --git a/src/compiler/iroptimizer.js b/src/compiler/iroptimizer.js index 416f1c3e06..ae7d98e495 100644 --- a/src/compiler/iroptimizer.js +++ b/src/compiler/iroptimizer.js @@ -99,6 +99,14 @@ class TypeState { }); } + /** + * @param {TypeState} other + * @returns {boolean} + */ + overwrite (other) { + return this.mutate(other, varId => other.variables[varId] ?? InputType.ANY); + } + /** * @param {*} variable A variable codegen object. * @param {InputType} type The type to set this variable to @@ -490,6 +498,8 @@ class IROptimizer { if (!script || !script.cachedAnalysisEndState) { modified = state.clear() || modified; + } else if (script.yields) { + modified = state.overwrite(script.cachedAnalysisEndState) || modified; } else { modified = state.after(script.cachedAnalysisEndState) || modified; } @@ -578,6 +588,8 @@ class IROptimizer { if (!script || !script.cachedAnalysisEndState) { modified = state.clear() || modified; + } else if (script.yields) { + modified = state.overwrite(script.cachedAnalysisEndState) || modified; } else { modified = state.after(script.cachedAnalysisEndState) || modified; } diff --git a/test/fixtures/execute/tw-analyze-yields-due-to-direct-recursion.sb3 b/test/fixtures/execute/tw-analyze-yields-due-to-direct-recursion.sb3 new file mode 100644 index 0000000000..136864b417 Binary files /dev/null and b/test/fixtures/execute/tw-analyze-yields-due-to-direct-recursion.sb3 differ diff --git a/test/fixtures/execute/tw-exit-state-accounts-for-yields-in-procedures.sb3 b/test/fixtures/execute/tw-exit-state-accounts-for-yields-in-procedures.sb3 new file mode 100644 index 0000000000..6d19444963 Binary files /dev/null and b/test/fixtures/execute/tw-exit-state-accounts-for-yields-in-procedures.sb3 differ diff --git a/test/snapshot/__snapshots__/tw-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot new file mode 100644 index 0000000000..c19e45f704 --- /dev/null +++ b/test/snapshot/__snapshots__/tw-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot @@ -0,0 +1,39 @@ +// TW Snapshot +// Input SHA-256: 848a4efc16b174b53f0a4b581e6b6d1091ae9eaa916e040c963e360ce3883509 + +// 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"]; +const b2 = stage.variables["FpLI$ida6)qR,q~y`1|*"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "j", null); +b1.value = (1 + 2); +yield* thread.procedures["Znon-warp recursion %s"](2); +if ((("" + listGet(b2.value, b1.value)).toLowerCase() === "the only thing".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "t", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "s", null); +retire(); return; +}; }) + +// Sprite1 Znon-warp recursion %s +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +return function* genXYZ_non_warp_recursion_ (p0) { +if (compareGreaterThan(p0, 0)) { +yield; +yield* thread.procedures["Znon-warp recursion %s"](((+p0 || 0) - 1)); +} +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 () { +for (var a0 = 1; a0 >= 0.5; a0--) { +yield; +} +b0.value = "random"; +retire(); return; +}; }) diff --git a/test/snapshot/__snapshots__/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot b/test/snapshot/__snapshots__/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot new file mode 100644 index 0000000000..c8bf63116b --- /dev/null +++ b/test/snapshot/__snapshots__/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot @@ -0,0 +1,38 @@ +// TW Snapshot +// Input SHA-256: 4715c75fe8effcdcdea3ad810949e33a7b6beb3fbd253c1edc2ed4f2bd093df3 + +// 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"]; +const b2 = stage.variables["FpLI$ida6)qR,q~y`1|*"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "e", null); +b1.value = (1 + 2); +yield* thread.procedures["Zsomething that yields"](); +if ((("" + listGet(b2.value, b1.value)).toLowerCase() === "the only thing".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "o", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "n", null); +retire(); return; +}; }) + +// Sprite1 Zsomething that yields +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +return function* genXYZ_something_that_yield () { +for (var a0 = 2; a0 >= 0.5; a0--) { +yield; +} +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 () { +for (var a0 = 1; a0 >= 0.5; a0--) { +yield; +} +b0.value = "random"; +retire(); 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 index 6b2bc2a9f6..c242a148a3 100644 --- a/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot +++ b/test/snapshot/__snapshots__/tw-procedure-return-recursion.sb3.tw-snapshot @@ -10,7 +10,7 @@ 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 === 4)) { +if (((+b1.value || 0) === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; @@ -20,7 +20,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yiel } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if ((b1.value === 20)) { +if (((+b1.value || 0) === 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-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot new file mode 100644 index 0000000000..c19e45f704 --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-analyze-yields-due-to-direct-recursion.sb3.tw-snapshot @@ -0,0 +1,39 @@ +// TW Snapshot +// Input SHA-256: 848a4efc16b174b53f0a4b581e6b6d1091ae9eaa916e040c963e360ce3883509 + +// 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"]; +const b2 = stage.variables["FpLI$ida6)qR,q~y`1|*"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "j", null); +b1.value = (1 + 2); +yield* thread.procedures["Znon-warp recursion %s"](2); +if ((("" + listGet(b2.value, b1.value)).toLowerCase() === "the only thing".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "t", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "s", null); +retire(); return; +}; }) + +// Sprite1 Znon-warp recursion %s +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +return function* genXYZ_non_warp_recursion_ (p0) { +if (compareGreaterThan(p0, 0)) { +yield; +yield* thread.procedures["Znon-warp recursion %s"](((+p0 || 0) - 1)); +} +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 () { +for (var a0 = 1; a0 >= 0.5; a0--) { +yield; +} +b0.value = "random"; +retire(); return; +}; }) diff --git a/test/snapshot/__snapshots__/warp-timer/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot b/test/snapshot/__snapshots__/warp-timer/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot new file mode 100644 index 0000000000..c8bf63116b --- /dev/null +++ b/test/snapshot/__snapshots__/warp-timer/tw-exit-state-accounts-for-yields-in-procedures.sb3.tw-snapshot @@ -0,0 +1,38 @@ +// TW Snapshot +// Input SHA-256: 4715c75fe8effcdcdea3ad810949e33a7b6beb3fbd253c1edc2ed4f2bd093df3 + +// 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"]; +const b2 = stage.variables["FpLI$ida6)qR,q~y`1|*"]; +return function* genXYZ () { +yield* executeInCompatibilityLayer({"MESSAGE":"plan 1",}, b0, false, false, "e", null); +b1.value = (1 + 2); +yield* thread.procedures["Zsomething that yields"](); +if ((("" + listGet(b2.value, b1.value)).toLowerCase() === "the only thing".toLowerCase())) { +yield* executeInCompatibilityLayer({"MESSAGE":"pass",}, b0, false, false, "o", null); +} +yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "n", null); +retire(); return; +}; }) + +// Sprite1 Zsomething that yields +(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage(); +return function* genXYZ_something_that_yield () { +for (var a0 = 2; a0 >= 0.5; a0--) { +yield; +} +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 () { +for (var a0 = 1; a0 >= 0.5; a0--) { +yield; +} +b0.value = "random"; +retire(); 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 index 6b2bc2a9f6..c242a148a3 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,7 +10,7 @@ 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 === 4)) { +if (((+b1.value || 0) === 4)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp recursion yields",}, b0, false, false, "ao", null); } b1.value = 0; @@ -20,7 +20,7 @@ yield* executeInCompatibilityLayer({"MESSAGE":"pass warp recursion does not yiel } b1.value = 0; b2.value = (yield* thread.procedures["Zfib %s"](7)); -if ((b1.value === 20)) { +if (((+b1.value || 0) === 20)) { yield* executeInCompatibilityLayer({"MESSAGE":"pass non warp fib yielded",}, b0, false, false, "au", null); } yield* thread.procedures["Zrecursing yields between each %s"]("initial");