tc39-weakrefs-shim
Version:
Shim for TC39 Weakrefs proposal
140 lines • 5.48 kB
JavaScript
import makePrivates from "../utils/privates.js";
import isObject from "../utils/lodash/isObject.js";
// Note: agent will hold FinalizationGroup instance until all registered targets have been collected
class FinalizationGroupCell {
constructor(info, holdings, unregisterTokenSiblings) {
this.info = info;
this.holdings = holdings;
this.unregisterTokenSiblings = unregisterTokenSiblings;
}
}
class FinalizationGroupSlots {
constructor(cleanupCallback) {
this.cleanupCallback = cleanupCallback;
this.cellsForTarget = new Map();
this.cellsForToken = new WeakMap();
this.isCleanupJobActive = false;
}
}
export function createFinalizationGroupClassShim(jobs, getInfo, isAlive) {
const privates = makePrivates();
function* getEmptyCells(context) {
for (const info of context.finalized) {
const cells = context.cellsForInfo.get(info);
if (!cells)
continue;
for (const cell of cells) {
if (!context.finalized)
throw new TypeError();
yield cell;
}
}
}
const CleanupIterator = function* CleanupIterator(context) {
for (const cell of context.cells) {
if (isAlive(cell.info))
continue;
const holding = cell.holdings;
unregisterCell(context.group, cell);
yield holding;
}
};
Object.defineProperty(Object.getPrototypeOf(CleanupIterator.prototype), Symbol.toStringTag, {
value: "FinalizationGroup Cleanup Iterator",
configurable: true,
});
function unregisterCell(group, cell) {
const slots = privates(group);
if (cell.unregisterTokenSiblings) {
cell.unregisterTokenSiblings.delete(cell);
}
const cellsForTarget = slots.cellsForTarget.get(cell.info);
cellsForTarget.delete(cell);
if (cellsForTarget.size == 0) {
slots.cellsForTarget.delete(cell.info);
jobs.unregisterFinalizationGroup(group, cell.info);
}
}
class FinalizationGroup {
constructor(cleanupCallback) {
if (typeof cleanupCallback != "function")
throw new TypeError();
privates.init(this, new FinalizationGroupSlots(cleanupCallback));
}
register(target, holdings, unregisterToken = undefined) {
if (!isObject(target) || target === holdings)
throw new TypeError();
if (unregisterToken !== undefined && !isObject(unregisterToken))
throw TypeError();
const slots = privates(this);
const objectInfo = getInfo(target);
let cellsForToken;
if (unregisterToken) {
cellsForToken = slots.cellsForToken.get(unregisterToken);
if (!cellsForToken) {
cellsForToken = new Set();
slots.cellsForToken.set(unregisterToken, cellsForToken);
}
}
const cell = new FinalizationGroupCell(objectInfo, holdings, cellsForToken);
let cellsForTarget = slots.cellsForTarget.get(objectInfo);
if (!cellsForTarget) {
jobs.registerFinalizationGroup(this, objectInfo);
cellsForTarget = new Set();
slots.cellsForTarget.set(objectInfo, cellsForTarget);
}
cellsForTarget.add(cell);
if (cellsForToken)
cellsForToken.add(cell);
}
unregister(unregisterToken) {
if (!isObject(unregisterToken))
throw new TypeError();
const slots = privates(this);
let removed = false;
const cellsForToken = slots.cellsForToken.get(unregisterToken);
if (!cellsForToken)
return removed;
for (const cell of cellsForToken) {
unregisterCell(this, cell);
removed = true;
}
return removed;
}
cleanupSome(cleanupCallback = undefined) {
let slots = privates(this);
if (slots.isCleanupJobActive) {
throw new TypeError();
}
if (cleanupCallback === undefined) {
cleanupCallback = slots.cleanupCallback;
}
else if (typeof cleanupCallback != "function") {
throw new TypeError();
}
const getEmptyCellsContext = {
finalized: jobs.getFinalizedInFinalizationGroup(this),
cellsForInfo: slots.cellsForTarget,
};
if (getEmptyCellsContext.finalized.size == 0)
return;
const cleanupIteratorContext = {
group: this,
cells: getEmptyCells(getEmptyCellsContext),
};
const iterator = CleanupIterator(cleanupIteratorContext);
try {
slots.isCleanupJobActive = true;
cleanupCallback(iterator);
}
finally {
slots.isCleanupJobActive = false;
cleanupIteratorContext.group = undefined;
getEmptyCellsContext.finalized = undefined;
getEmptyCellsContext.cellsForInfo = undefined;
}
}
}
return FinalizationGroup;
}
//# sourceMappingURL=FinalizationGroup.js.map