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: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "deprecated" michael@0: }; michael@0: michael@0: const { Cc, Ci, Cu, components } = require("chrome"); michael@0: const { when: unload } = require("../system/unload") michael@0: michael@0: var trackedObjects = {}; michael@0: const Compacter = { michael@0: notify: function() { michael@0: var newTrackedObjects = {}; michael@0: michael@0: for (let name in trackedObjects) { michael@0: let oldBin = trackedObjects[name]; michael@0: let newBin = []; michael@0: let strongRefs = []; michael@0: michael@0: for (let i = 0, l = oldBin.length; i < l; i++) { michael@0: let strongRef = oldBin[i].weakref.get(); michael@0: michael@0: if (strongRef && strongRefs.indexOf(strongRef) == -1) { michael@0: strongRefs.push(strongRef); michael@0: newBin.push(oldBin[i]); michael@0: } michael@0: } michael@0: michael@0: if (newBin.length) michael@0: newTrackedObjects[name] = newBin; michael@0: } michael@0: michael@0: trackedObjects = newTrackedObjects; michael@0: } michael@0: }; michael@0: michael@0: var timer = Cc["@mozilla.org/timer;1"] michael@0: .createInstance(Ci.nsITimer); michael@0: timer.initWithCallback(Compacter, michael@0: 5000, michael@0: Ci.nsITimer.TYPE_REPEATING_SLACK); michael@0: michael@0: function track(object, bin, stackFrameNumber) { michael@0: var frame = components.stack.caller; michael@0: var weakref = Cu.getWeakReference(object); michael@0: michael@0: if (!bin && 'constructor' in object) michael@0: bin = object.constructor.name; michael@0: if (bin == "Object") michael@0: bin = frame.name; michael@0: if (!bin) michael@0: bin = "generic"; michael@0: if (!(bin in trackedObjects)) michael@0: trackedObjects[bin] = []; michael@0: michael@0: if (stackFrameNumber > 0) michael@0: for (var i = 0; i < stackFrameNumber; i++) michael@0: frame = frame.caller; michael@0: michael@0: trackedObjects[bin].push({weakref: weakref, michael@0: created: new Date(), michael@0: filename: frame.filename, michael@0: lineNo: frame.lineNumber, michael@0: bin: bin}); michael@0: } michael@0: exports.track = track; michael@0: michael@0: var getBins = exports.getBins = function getBins() { michael@0: var names = []; michael@0: for (let name in trackedObjects) michael@0: names.push(name); michael@0: return names; michael@0: }; michael@0: michael@0: function getObjects(bin) { michael@0: var results = []; michael@0: michael@0: function getLiveObjectsInBin(bin) { michael@0: for (let i = 0, l = bin.length; i < l; i++) { michael@0: let object = bin[i].weakref.get(); michael@0: michael@0: if (object) { michael@0: results.push(bin[i]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (bin) { michael@0: if (bin in trackedObjects) michael@0: getLiveObjectsInBin(trackedObjects[bin]); michael@0: } michael@0: else { michael@0: for (let name in trackedObjects) michael@0: getLiveObjectsInBin(trackedObjects[name]); michael@0: } michael@0: michael@0: return results; michael@0: } michael@0: exports.getObjects = getObjects; michael@0: michael@0: function gc() { michael@0: // Components.utils.forceGC() doesn't currently perform michael@0: // cycle collection, which means that e.g. DOM elements michael@0: // won't be collected by it. Fortunately, there are michael@0: // other ways... michael@0: var test_utils = Cc["@mozilla.org/appshell/appShellService;1"] michael@0: .getService(Ci.nsIAppShellService) michael@0: .hiddenDOMWindow michael@0: .QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: test_utils.garbageCollect(); michael@0: // Clean metadata for dead objects michael@0: Compacter.notify(); michael@0: // Not sure why, but sometimes it appears that we don't get michael@0: // them all with just one CC, so let's do it again. michael@0: test_utils.garbageCollect(); michael@0: }; michael@0: exports.gc = gc; michael@0: michael@0: unload(_ => { michael@0: trackedObjects = {}; michael@0: if (timer) { michael@0: timer.cancel(); michael@0: timer = null; michael@0: } michael@0: });