Skip to content

Commit 2c9d301

Browse files
committed
ir: Fix variable cache collision when variable or list field has null ID
Fixes TurboWarp#239
1 parent 36c02f3 commit 2c9d301

File tree

4 files changed

+83
-3
lines changed

4 files changed

+83
-3
lines changed

src/compiler/irgen.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,17 +1229,19 @@ class ScriptTreeGenerator {
12291229
const variable = block.fields[fieldName];
12301230
const id = variable.id;
12311231

1232-
if (Object.prototype.hasOwnProperty.call(this.variableCache, id)) {
1232+
if (id && Object.prototype.hasOwnProperty.call(this.variableCache, id)) {
12331233
return this.variableCache[id];
12341234
}
12351235

12361236
const data = this._descendVariable(id, variable.value, type);
1237-
this.variableCache[id] = data;
1237+
// If variable ID was null, this might do some unnecessary updates, but that is a rare
1238+
// edge case and it won't have any adverse effects anyways.
1239+
this.variableCache[data.id] = data;
12381240
return data;
12391241
}
12401242

12411243
/**
1242-
* @param {string} id The ID of the variable.
1244+
* @param {string|null} id The ID of the variable.
12431245
* @param {string} name The name of the variable.
12441246
* @param {''|'list'} type The variable type.
12451247
* @private
3.53 KB
Binary file not shown.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// TW Snapshot
2+
// Input SHA-256: 99bb22ac4fb2542ce3c21fa7d2025f03d15392a33850585a4d12f4ec558663ac
3+
4+
// Sprite1 script
5+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
6+
const b0 = runtime.getOpcodeFunction("looks_say");
7+
const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
8+
const b2 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
9+
return function* genXYZ () {
10+
yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "c", null);
11+
b1.value = 1;
12+
b2.value = 2;
13+
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 1" }));
14+
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 2" }));
15+
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", null);
16+
retire(); return;
17+
}; })
18+
19+
// Sprite1 script
20+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
21+
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
22+
const b1 = runtime.getOpcodeFunction("looks_say");
23+
return function* genXYZ () {
24+
if (((+b0.value || 0) === 1)) {
25+
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 1",}, b1, false, false, "m", null);
26+
}
27+
retire(); return;
28+
}; })
29+
30+
// Sprite1 script
31+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
32+
const b0 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
33+
const b1 = runtime.getOpcodeFunction("looks_say");
34+
return function* genXYZ () {
35+
if (((+b0.value || 0) === 2)) {
36+
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 2",}, b1, false, false, "q", null);
37+
}
38+
retire(); return;
39+
}; })
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// TW Snapshot
2+
// Input SHA-256: 99bb22ac4fb2542ce3c21fa7d2025f03d15392a33850585a4d12f4ec558663ac
3+
4+
// Sprite1 script
5+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
6+
const b0 = runtime.getOpcodeFunction("looks_say");
7+
const b1 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
8+
const b2 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
9+
return function* genXYZ () {
10+
yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "c", null);
11+
b1.value = 1;
12+
b2.value = 2;
13+
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 1" }));
14+
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 2" }));
15+
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", null);
16+
retire(); return;
17+
}; })
18+
19+
// Sprite1 script
20+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
21+
const b0 = stage.variables["`jEk@4|i[#Fk?(8x)AV.-my variable"];
22+
const b1 = runtime.getOpcodeFunction("looks_say");
23+
return function* genXYZ () {
24+
if (((+b0.value || 0) === 1)) {
25+
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 1",}, b1, false, false, "m", null);
26+
}
27+
retire(); return;
28+
}; })
29+
30+
// Sprite1 script
31+
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
32+
const b0 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
33+
const b1 = runtime.getOpcodeFunction("looks_say");
34+
return function* genXYZ () {
35+
if (((+b0.value || 0) === 2)) {
36+
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 2",}, b1, false, false, "q", null);
37+
}
38+
retire(); return;
39+
}; })

0 commit comments

Comments
 (0)