tc39-weakrefs-shim
Version:
Shim for TC39 Weakrefs proposal
151 lines • 4.93 kB
JavaScript
// @ts-ignore
import WeakTag from "@mhofman/weak-napi-native/weak-tag.js";
// @ts-ignore
import ObjectInfo from "@mhofman/weak-napi-native/object-info.js";
import { gc } from "./gc.js";
import isObject from "../utils/lodash/isObject.js";
import { setImmediate } from "../utils/tasks/setImmediate.js";
const map = new WeakMap();
const registrations = new WeakMap();
function hasEmptyCell(context) {
for (const info of context.cells.keys()) {
if (!info.target)
return true;
}
return false;
}
function* getEmptyCellEntries(context) {
for (const [info, cell] of context.cells.entries()) {
if (!context.cells)
throw new TypeError();
if (info.target)
continue;
context.cells.delete(info);
yield [info, cell];
}
}
function* getCellEntry(context) {
const cell = context.cells.get(context.info);
context.cells.delete(context.info);
yield [context.info, cell];
}
const CleanupIterator = function* CleanupIterator(entries) {
for (const [info, cell] of entries) {
if (cell.registrationSet)
cell.registrationSet.delete(info);
yield cell.holdings;
}
};
Object.defineProperty(Object.getPrototypeOf(CleanupIterator.prototype), Symbol.toStringTag, {
value: "FinalizationGroup Cleanup Iterator",
configurable: true,
});
class FinalizationGroupNodeStub {
constructor(cleanupCallback) {
this.cleanupCallback = cleanupCallback;
this.isCleanupJobActive = false;
if (typeof cleanupCallback != "function")
throw new TypeError();
const cells = (this.cells = new Map());
this.finalizedCallback = function () {
if (!cells.get(this))
return;
const context = { info: this, cells };
try {
cleanupCallback(CleanupIterator(getCellEntry(context)));
}
catch (error) {
setImmediate(() => {
throw error;
});
}
context.info = context.cells = undefined;
};
}
register(target, holdings, unregisterToken = undefined) {
if (!this.cells)
throw new TypeError();
let tagSet = map.get(target);
if (!tagSet) {
tagSet = new Set();
map.set(target, tagSet);
}
if (target === holdings)
throw new TypeError();
let registrationSet = unregisterToken
? registrations.get(unregisterToken)
: undefined;
if (!registrationSet && unregisterToken !== undefined) {
if (!isObject(unregisterToken))
throw new TypeError();
registrationSet = new Set();
registrations.set(unregisterToken, registrationSet);
}
const info = new ObjectInfo(target, this.finalizedCallback);
this.cells.set(info, {
holdings,
registrationSet,
});
tagSet.add(new WeakTag(info));
if (registrationSet)
registrationSet.add(info);
}
unregister(unregisterToken) {
if (!this.cells)
throw new TypeError();
const registrationSet = registrations.get(unregisterToken);
if (!registrationSet) {
if (!isObject(unregisterToken))
throw new TypeError();
return false;
}
let removed = false;
for (const info of registrationSet) {
const cell = this.cells.get(info);
if (!cell)
continue;
this.cells.delete(info);
registrationSet.delete(info);
removed = true;
}
return removed;
}
cleanupSome(cleanupCallback = undefined) {
if (!this.cells)
throw new TypeError();
const context = { cells: this.cells };
const emptyObjectInfos = getEmptyCellEntries(context);
if (this.isCleanupJobActive)
throw new TypeError();
if (cleanupCallback === undefined) {
cleanupCallback = this
.cleanupCallback;
}
if (!hasEmptyCell(context)) {
if (typeof cleanupCallback != "function") {
throw new TypeError();
}
return;
}
try {
this.isCleanupJobActive = true;
cleanupCallback(CleanupIterator(emptyObjectInfos));
}
finally {
context.cells = undefined;
this.isCleanupJobActive = false;
}
}
}
class WeakRefNodeStub {
constructor(target) {
if (!isObject(target))
throw new TypeError();
this.info = new ObjectInfo(target, () => { });
}
deref() {
return this.info.target;
}
}
export { FinalizationGroupNodeStub as FinalizationGroup, WeakRefNodeStub as WeakRef, gc, };
//# sourceMappingURL=stub.js.map