diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 412a758eb..d49365671 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -1098,6 +1098,20 @@ const deserializeMonitor = function (monitorData, runtime, targets, extensions) // This will be undefined for extension blocks const monitorBlockInfo = runtime.monitorBlockInfo[monitorData.opcode]; + // Due to a bug (see https://github.com/LLK/scratch-vm/pull/2322), renamed list monitors may have been serialized + // with an outdated/incorrect LIST parameter. Fix it up to use the current name of the actual corresponding list. + if (monitorData.opcode === 'data_listcontents') { + const listTarget = monitorData.targetId ? + targets.find(t => t.id === monitorData.targetId) : + targets.find(t => t.isStage); + if ( + listTarget && + Object.prototype.hasOwnProperty.call(listTarget.variables, monitorData.id) + ) { + monitorData.params.LIST = listTarget.variables[monitorData.id].name; + } + } + // Convert the serialized monitorData params into the block fields structure const fields = {}; for (const paramKey in monitorData.params) { diff --git a/test/fixtures/list-monitor-rename.sb3 b/test/fixtures/list-monitor-rename.sb3 new file mode 100644 index 000000000..e3d20ea79 Binary files /dev/null and b/test/fixtures/list-monitor-rename.sb3 differ diff --git a/test/integration/list-monitor-rename.js b/test/integration/list-monitor-rename.js new file mode 100644 index 000000000..bb57daf16 --- /dev/null +++ b/test/integration/list-monitor-rename.js @@ -0,0 +1,52 @@ +const path = require('path'); +const test = require('tap').test; +const makeTestStorage = require('../fixtures/make-test-storage'); +const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer; +const VirtualMachine = require('../../src/index'); + +const projectUri = path.resolve(__dirname, '../fixtures/list-monitor-rename.sb3'); +const project = readFileToBuffer(projectUri); + +test('importing sb3 project with incorrect list monitor name', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Evaluate playground data and exit + vm.on('playgroundData', () => { + const stage = vm.runtime.targets[0]; + const cat = vm.runtime.targets[1]; + + for (const {target, renamedListName} of [ + {target: stage, renamedListName: 'renamed global'}, + {target: cat, renamedListName: 'renamed local'} + ]) { + const listId = Object.keys(target.variables).find(k => target.variables[k].name === renamedListName); + + const monitorRecord = vm.runtime._monitorState.get(listId); + const monitorBlock = vm.runtime.monitorBlocks.getBlock(listId); + t.equal(monitorRecord.opcode, 'data_listcontents'); + + // The list name should be properly renamed + t.equal(monitorRecord.params.LIST, renamedListName); + t.equal(monitorBlock.fields.LIST.value, renamedListName); + } + + t.end(); + process.nextTick(process.exit); + }); + + // Start VM, load project, and run + t.doesNotThrow(() => { + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(project).then(() => { + vm.greenFlag(); + setTimeout(() => { + vm.getPlaygroundData(); + vm.stopAll(); + }, 100); + }); + }); +});