michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Parts of this module were taken from narwhal: michael@0: // michael@0: // http://narwhaljs.org michael@0: michael@0: module.metadata = { michael@0: "stability": "experimental" michael@0: }; michael@0: michael@0: const { on, off } = require('./events'); michael@0: const unloadSubject = require('@loader/unload'); michael@0: michael@0: const observers = []; michael@0: const unloaders = []; michael@0: michael@0: var when = exports.when = function when(observer) { michael@0: if (observers.indexOf(observer) != -1) michael@0: return; michael@0: observers.unshift(observer); michael@0: }; michael@0: michael@0: var ensure = exports.ensure = function ensure(obj, destructorName) { michael@0: if (!destructorName) michael@0: destructorName = "unload"; michael@0: if (!(destructorName in obj)) michael@0: throw new Error("object has no '" + destructorName + "' property"); michael@0: michael@0: let called = false; michael@0: let originalDestructor = obj[destructorName]; michael@0: michael@0: function unloadWrapper(reason) { michael@0: if (!called) { michael@0: called = true; michael@0: let index = unloaders.indexOf(unloadWrapper); michael@0: if (index == -1) michael@0: throw new Error("internal error: unloader not found"); michael@0: unloaders.splice(index, 1); michael@0: originalDestructor.call(obj, reason); michael@0: originalDestructor = null; michael@0: destructorName = null; michael@0: obj = null; michael@0: } michael@0: }; michael@0: michael@0: // TODO: Find out why the order is inverted here. It seems that michael@0: // it may be causing issues! michael@0: unloaders.push(unloadWrapper); michael@0: michael@0: obj[destructorName] = unloadWrapper; michael@0: }; michael@0: michael@0: function unload(reason) { michael@0: observers.forEach(function(observer) { michael@0: try { michael@0: observer(reason); michael@0: } michael@0: catch (error) { michael@0: console.exception(error); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: when(function(reason) { michael@0: unloaders.slice().forEach(function(unloadWrapper) { michael@0: unloadWrapper(reason); michael@0: }); michael@0: }); michael@0: michael@0: on('sdk:loader:destroy', function onunload({ subject, data: reason }) { michael@0: // If this loader is unload then `subject.wrappedJSObject` will be michael@0: // `destructor`. michael@0: if (subject.wrappedJSObject === unloadSubject) { michael@0: off('sdk:loader:destroy', onunload); michael@0: unload(reason); michael@0: } michael@0: // Note that we use strong reference to listener here to make sure it's not michael@0: // GC-ed, which may happen otherwise since nothing keeps reference to `onunolad` michael@0: // function. michael@0: }, true);