From 8db8e465d02e974a7f926769f45394ec799b6c23 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Thu, 25 Jul 2019 15:44:21 -0700 Subject: [PATCH 1/2] move extensionManager from vm to runtime --- src/engine/runtime.js | 6 ++++++ src/virtual-machine.js | 17 +++++++---------- test/integration/internal-extension.js | 6 +++--- test/integration/load-extensions.js | 4 ++-- test/integration/monitors_sb3.js | 4 ++-- .../sb2-import-extension-monitors.js | 8 ++++---- test/unit/virtual-machine.js | 2 +- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 441bd50bae8..995a84d2b90 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -5,6 +5,7 @@ const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); const BlocksRuntimeCache = require('./blocks-runtime-cache'); const BlockType = require('../extension-support/block-type'); +const ExtensionManager = require('../extension-support/extension-manager'); const Profiler = require('./profiler'); const Sequencer = require('./sequencer'); const execute = require('./execute.js'); @@ -367,6 +368,11 @@ class Runtime extends EventEmitter { * @type {function} */ this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); + + /** + * The Extension Manager handles loading and registering extensions, their services, and their blocks. + */ + this.extensionManager = new ExtensionManager(this); } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 47ed7aef9d6..c6dfaca0e5a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -10,7 +10,6 @@ const JSZip = require('jszip'); const Buffer = require('buffer').Buffer; const centralDispatch = require('./dispatch/central-dispatch'); -const ExtensionManager = require('./extension-support/extension-manager'); const log = require('./util/log'); const MathUtil = require('./util/math-util'); const Runtime = require('./engine/runtime'); @@ -129,7 +128,7 @@ class VirtualMachine extends EventEmitter { this.emit(Runtime.BLOCK_UPDATE, blockId, blockInfo); }); this.runtime.on(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE, () => { - this.extensionManager.refreshBlocks(); + this.runtime.extensionManager.refreshBlocks(); }); this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => { this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info); @@ -159,11 +158,9 @@ class VirtualMachine extends EventEmitter { this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData); }); - this.extensionManager = new ExtensionManager(this.runtime); - // Load core extensions for (const id of CORE_EXTENSIONS) { - this.extensionManager.loadExtensionIdSync(id); + this.runtime.extensionManager.loadExtensionIdSync(id); } this.blockListener = this.blockListener.bind(this); @@ -509,9 +506,9 @@ class VirtualMachine extends EventEmitter { const extensionPromises = []; extensions.extensionIDs.forEach(extensionID => { - if (!this.extensionManager.isExtensionLoaded(extensionID)) { + if (!this.runtime.extensionManager.isExtensionLoaded(extensionID)) { const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID; - extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL)); + extensionPromises.push(this.runtime.extensionManager.loadExtensionURL(extensionURL)); } }); @@ -1128,7 +1125,7 @@ class VirtualMachine extends EventEmitter { if (locale !== formatMessage.setup().locale) { formatMessage.setup({locale: locale, translations: {[locale]: messages}}); } - return this.extensionManager.refreshBlocks(); + return this.runtime.extensionManager.refreshBlocks(); } /** @@ -1231,12 +1228,12 @@ class VirtualMachine extends EventEmitter { const extensionIDs = new Set(copiedBlocks .map(b => sb3.getExtensionIdForOpcode(b.opcode)) .filter(id => !!id) // Remove ids that do not exist - .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions + .filter(id => !this.runtime.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions ); // Create an array promises for extensions to load const extensionPromises = Array.from(extensionIDs, - id => this.extensionManager.loadExtensionURL(id) + id => this.runtime.extensionManager.loadExtensionURL(id) ); return Promise.all(extensionPromises).then(() => { diff --git a/test/integration/internal-extension.js b/test/integration/internal-extension.js index bd52c9c2c31..4555b602784 100644 --- a/test/integration/internal-extension.js +++ b/test/integration/internal-extension.js @@ -59,7 +59,7 @@ test('internal extension', t => { t.ok(extension.status.constructorCalled); t.notOk(extension.status.getInfoCalled); - vm.extensionManager._registerInternalExtension(extension); + vm.runtime.extensionManager._registerInternalExtension(extension); t.ok(extension.status.getInfoCalled); const func = vm.runtime.getOpcodeFunction('testInternalExtension_go'); @@ -102,8 +102,8 @@ test('internal extension', t => { test('load sync', t => { const vm = new VirtualMachine(); - vm.extensionManager.loadExtensionIdSync('coreExample'); - t.ok(vm.extensionManager.isExtensionLoaded('coreExample')); + vm.runtime.extensionManager.loadExtensionIdSync('coreExample'); + t.ok(vm.runtime.extensionManager.isExtensionLoaded('coreExample')); t.equal(vm.runtime._blockInfo.length, 1); diff --git a/test/integration/load-extensions.js b/test/integration/load-extensions.js index bdeafb001fa..b67c5379a43 100644 --- a/test/integration/load-extensions.js +++ b/test/integration/load-extensions.js @@ -20,7 +20,7 @@ test('Load external extensions', async t => { await t.test('Confirm expected extension is installed in example sb2 and sb3 projects', extTest => { vm.loadProject(project) .then(() => { - extTest.ok(vm.extensionManager.isExtensionLoaded(ext)); + extTest.ok(vm.runtime.extensionManager.isExtensionLoaded(ext)); extTest.end(); }); }); @@ -53,7 +53,7 @@ test('Load video sensing extension and video properties', async t => { const stage = vm.runtime.getTargetForStage(); - t.ok(vm.extensionManager.isExtensionLoaded('videoSensing')); + t.ok(vm.runtime.extensionManager.isExtensionLoaded('videoSensing')); // Check that the stage target has the video state values we expect // based on the test project files, then check that the video io device diff --git a/test/integration/monitors_sb3.js b/test/integration/monitors_sb3.js index f55edb86434..a46ee4ddbd9 100644 --- a/test/integration/monitors_sb3.js +++ b/test/integration/monitors_sb3.js @@ -235,7 +235,7 @@ test('importing sb3 project with monitors', t => { t.equal(monitorRecord.visible, true); t.equal(monitorRecord.spriteName, null); t.equal(monitorRecord.targetId, null); - t.equal(vm.extensionManager.isExtensionLoaded('music'), true); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('music'), true); monitorId = 'ev3_getDistance'; monitorRecord = vm.runtime._monitorState.get(monitorId); @@ -245,7 +245,7 @@ test('importing sb3 project with monitors', t => { t.equal(monitorRecord.visible, true); t.equal(monitorRecord.spriteName, null); t.equal(monitorRecord.targetId, null); - t.equal(vm.extensionManager.isExtensionLoaded('ev3'), true); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('ev3'), true); t.end(); process.nextTick(process.exit); diff --git a/test/integration/sb2-import-extension-monitors.js b/test/integration/sb2-import-extension-monitors.js index 5420429bf75..22de9398663 100644 --- a/test/integration/sb2-import-extension-monitors.js +++ b/test/integration/sb2-import-extension-monitors.js @@ -37,7 +37,7 @@ test('loading sb2 project with invisible video monitor should not load monitor o vm.setCompatibilityMode(false); vm.setTurboMode(false); vm.loadProject(invisibleVideoMonitorProject).then(() => { - t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('videoSensing'), false); t.equal(vm.runtime._monitorState.size, 0); t.end(); }); @@ -53,7 +53,7 @@ test('loading sb2 project with visible video monitor should not load extension', vm.setCompatibilityMode(false); vm.setTurboMode(false); vm.loadProject(visibleVideoMonitorProject).then(() => { - t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('videoSensing'), false); t.equal(vm.runtime._monitorState.size, 0); t.end(); }); @@ -84,7 +84,7 @@ test('sb2 project with invisible music monitor should not load monitor or extens vm.setCompatibilityMode(false); vm.setTurboMode(false); vm.loadProject(invisibleTempoMonitorProject).then(() => { - t.equal(vm.extensionManager.isExtensionLoaded('music'), false); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('music'), false); t.equal(vm.runtime._monitorState.size, 0); t.end(); }); @@ -100,7 +100,7 @@ test('sb2 project with visible music monitor should load monitor and extension', vm.setCompatibilityMode(false); vm.setTurboMode(false); vm.loadProject(visibleTempoMonitorProject).then(() => { - t.equal(vm.extensionManager.isExtensionLoaded('music'), true); + t.equal(vm.runtime.extensionManager.isExtensionLoaded('music'), true); t.equal(vm.runtime._monitorState.size, 1); t.equal(vm.runtime._monitorState.has('music_getTempo'), true); t.equal(vm.runtime._monitorState.get('music_getTempo').visible, true); diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index f280169368f..e186b688387 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -964,7 +964,7 @@ test('shareBlocksToTarget loads extensions that have not yet been loaded', t => // Stub the extension manager const loadedIds = []; - vm.extensionManager = { + vm.runtime.extensionManager = { isExtensionLoaded: id => id === 'loaded', loadExtensionURL: id => new Promise(resolve => { loadedIds.push(id); From ebe71862f3026923a5d7199f8ee7f3f5c41a37b9 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford Date: Tue, 30 Jul 2019 12:44:18 -0700 Subject: [PATCH 2/2] add compatibility accessor for the Extension Manager --- src/virtual-machine.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index c6dfaca0e5a..ce4b11ec368 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -169,6 +169,14 @@ class VirtualMachine extends EventEmitter { this.variableListener = this.variableListener.bind(this); } + /** + * @returns {ExtensionManager} the extension manager, now owned by the runtime. + * @deprecated Please access the extension manager through the runtime instead. + */ + get extensionManager () { + return this.runtime.extensionManager; + } + /** * Start running the VM - do this before anything else. */