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: module.metadata = { michael@0: "stability": "experimental" michael@0: }; michael@0: michael@0: "use strict"; michael@0: michael@0: const { Cu } = require("chrome"); michael@0: michael@0: function makeGetterFor(Type) { michael@0: let cache = new WeakMap(); michael@0: michael@0: return function getFor(target) { michael@0: if (!cache.has(target)) michael@0: cache.set(target, new Type()); michael@0: michael@0: return cache.get(target); michael@0: } michael@0: } michael@0: michael@0: let getLookupFor = makeGetterFor(WeakMap); michael@0: let getRefsFor = makeGetterFor(Set); michael@0: michael@0: function add(target, value) { michael@0: if (has(target, value)) michael@0: return; michael@0: michael@0: getLookupFor(target).set(value, true); michael@0: getRefsFor(target).add(Cu.getWeakReference(value)); michael@0: } michael@0: exports.add = add; michael@0: michael@0: function remove(target, value) { michael@0: getLookupFor(target).delete(value); michael@0: } michael@0: exports.remove = remove; michael@0: michael@0: function has(target, value) { michael@0: return getLookupFor(target).has(value); michael@0: } michael@0: exports.has = has; michael@0: michael@0: function clear(target) { michael@0: getLookupFor(target).clear(); michael@0: getRefsFor(target).clear(); michael@0: } michael@0: exports.clear = clear; michael@0: michael@0: function iterator(target) { michael@0: let refs = getRefsFor(target); michael@0: michael@0: for (let ref of refs) { michael@0: let value = ref.get(); michael@0: michael@0: // If `value` is already gc'ed, it would be `null`. michael@0: // The `has` function is using a WeakMap as lookup table, so passing `null` michael@0: // would raise an exception because WeakMap accepts as value only non-null michael@0: // object. michael@0: // Plus, if `value` is already gc'ed, we do not have to take it in account michael@0: // during the iteration, and remove it from the references. michael@0: if (value !== null && has(target, value)) michael@0: yield value; michael@0: else michael@0: refs.delete(ref); michael@0: } michael@0: } michael@0: exports.iterator = iterator;