ir: Fix variable cache collision when variable or list field has null ID

Fixes https://github.com/TurboWarp/scratch-vm/issues/239
This commit is contained in:
Thomas Weber committed 2025-01-18 00:09:09 -06:00
1 parent 36c02f3fec
commit 2c9d301da2
4 files changed
+83 -3

No files matched your search

+5 -3
View File
@@ -1229,17 +1229,19 @@ class ScriptTreeGenerator {
const variable = block.fields[fieldName];
const id = variable.id;
if (Object.prototype.hasOwnProperty.call(this.variableCache, id)) {
if (id && Object.prototype.hasOwnProperty.call(this.variableCache, id)) {
return this.variableCache[id];
}
const data = this._descendVariable(id, variable.value, type);
this.variableCache[id] = data;
// If variable ID was null, this might do some unnecessary updates, but that is a rare
// edge case and it won't have any adverse effects anyways.
this.variableCache[data.id] = data;
return data;
}
/**
* @param {string} id The ID of the variable.
* @param {string|null} id The ID of the variable.
* @param {string} name The name of the variable.
* @param {''|'list'} type The variable type.
* @private
Binary file not shown.
@@ -0,0 +1,39 @@
// TW Snapshot
// Input SHA-256: 99bb22ac4fb2542ce3c21fa7d2025f03d15392a33850585a4d12f4ec558663ac
// 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[")|GMR5fz;%F_H,c0wGVM"];
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "c", null);
b1.value = 1;
b2.value = 2;
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 1" }));
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 2" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", 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"];
const b1 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
if (((+b0.value || 0) === 1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 1",}, b1, false, false, "m", null);
}
retire(); return;
}; })
// Sprite1 script
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
const b0 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
const b1 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
if (((+b0.value || 0) === 2)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 2",}, b1, false, false, "q", null);
}
retire(); return;
}; })
@@ -0,0 +1,39 @@
// TW Snapshot
// Input SHA-256: 99bb22ac4fb2542ce3c21fa7d2025f03d15392a33850585a4d12f4ec558663ac
// 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[")|GMR5fz;%F_H,c0wGVM"];
return function* genXYZ () {
yield* executeInCompatibilityLayer({"MESSAGE":"plan 2",}, b0, false, false, "c", null);
b1.value = 1;
b2.value = 2;
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 1" }));
yield* waitThreads(startHats("event_whenbroadcastreceived", { BROADCAST_OPTION: "check 2" }));
yield* executeInCompatibilityLayer({"MESSAGE":"end",}, b0, false, false, "k", 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"];
const b1 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
if (((+b0.value || 0) === 1)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 1",}, b1, false, false, "m", null);
}
retire(); return;
}; })
// Sprite1 script
(function factoryXYZ(thread) { const target = thread.target; const runtime = target.runtime; const stage = runtime.getTargetForStage();
const b0 = stage.variables[")|GMR5fz;%F_H,c0wGVM"];
const b1 = runtime.getOpcodeFunction("looks_say");
return function* genXYZ () {
if (((+b0.value || 0) === 2)) {
yield* executeInCompatibilityLayer({"MESSAGE":"pass variable 2",}, b1, false, false, "q", null);
}
retire(); return;
}; })