tc39-weakrefs-shim
Version:
Shim for TC39 Weakrefs proposal
121 lines • 4.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const WeakRefJobs_js_1 = require("../internal/WeakRefJobs.js");
const FinalizationGroupJobs_js_1 = require("../internal/FinalizationGroupJobs.js");
const WeakRef_js_1 = require("../internal/WeakRef.js");
const FinalizationGroup_js_1 = require("../internal/FinalizationGroup.js");
const setImmediate_js_1 = require("../utils/tasks/setImmediate.js");
const gc_js_1 = require("../global/gc.js");
const targetToInfoMap = new WeakMap();
exports.observedAliveInfos = new Set();
let knownFinalizeCount = 0;
let knownAliveObjectsCount = 0;
const gcCheckIntervalMin = 100;
const gcCheckIntervalMax = 5000;
let gcCheckInterval = gcCheckIntervalMin;
let gcCheckTaskId = 0;
function stopCollectMonitor() {
if (gcCheckTaskId === 0)
return;
console.log("Warn: Force stopping collection");
clearTimeout(gcCheckTaskId);
gcCheckTaskId = 0;
}
exports.stopCollectMonitor = stopCollectMonitor;
class ObjectInfo {
constructor(target) {
this.weakMap = new WeakMap();
this.isAlive = true;
this.weakMap.set(target, undefined);
}
getTarget() {
if (!this.isAlive)
return undefined;
const keys = nondeterministicGetWeakMapKeys(this.weakMap);
if (keys.length === 0) {
this.isAlive = false;
return undefined;
}
return keys[0];
}
}
function getInfo(target) {
let info = targetToInfoMap.get(target);
if (!info) {
info = new ObjectInfo(target);
targetToInfoMap.set(target, info);
knownAliveObjectsCount++;
}
return info;
}
function checkOnKnownObjects() {
let previousKnownAliveObjectCount = knownAliveObjectsCount;
let aliveObjects;
const previousFinalizeCount = knownFinalizeCount;
knownFinalizeCount = finalizeCount();
let start = gcCheckTaskId === 0;
// If the marker is still there, assume no one else we track got collected
if (!start && previousFinalizeCount != knownFinalizeCount) {
if (knownFinalizeCount < previousFinalizeCount) {
console.log("Warning: finalizeCount got reset");
}
// Record the fact our marker got collected
knownAliveObjectsCount--;
previousKnownAliveObjectCount = knownAliveObjectsCount;
// let's see which of our objects are still around
aliveObjects = nondeterministicGetWeakMapKeys(targetToInfoMap);
knownAliveObjectsCount = aliveObjects.length;
if (previousKnownAliveObjectCount != knownAliveObjectsCount) {
if (previousKnownAliveObjectCount < knownAliveObjectsCount) {
console.log("Warning: found more objects alive than expected");
}
const aliveInfos = new Set(aliveObjects.map(object => targetToInfoMap.get(object)));
for (const info of exports.observedAliveInfos) {
if (aliveInfos.has(info))
continue;
exports.observedAliveInfos.delete(info);
finalizationGroupJobs.setFinalized(info);
}
}
}
if (!start && previousKnownAliveObjectCount == knownAliveObjectsCount) {
// Only our marker got collected, back off
gcCheckInterval = Math.min(gcCheckInterval * 2, gcCheckIntervalMax);
}
else {
gcCheckInterval = gcCheckIntervalMin;
}
if (exports.observedAliveInfos.size > 0) {
if (start || previousFinalizeCount != knownFinalizeCount) {
getInfo(makeFinalizeObserver());
}
gcCheckTaskId = setTimeout(checkOnKnownObjects, gcCheckInterval);
}
else {
clearTimeout(gcCheckTaskId);
gcCheckTaskId = 0;
}
}
const finalizationGroupJobs = FinalizationGroupJobs_js_1.createFinalizationGroupJobsForTaskQueue(setImmediate_js_1.setImmediate, {
registerObjectInfo: info => {
exports.observedAliveInfos.add(info);
if (exports.observedAliveInfos.size == 1)
checkOnKnownObjects();
},
unregisterObjectInfo: info => {
const wasObserved = exports.observedAliveInfos.delete(info);
if (wasObserved && exports.observedAliveInfos.size == 0)
checkOnKnownObjects();
},
});
exports.WeakRef = WeakRef_js_1.createWeakRefClassShim(WeakRefJobs_js_1.createWeakRefJobsForTaskQueue(setImmediate_js_1.setImmediate), getInfo, info => info.getTarget())[0];
exports.FinalizationGroup = FinalizationGroup_js_1.createFinalizationGroupClassShim(finalizationGroupJobs, getInfo, info => exports.observedAliveInfos.has(info) && info.getTarget !== undefined);
function spidermonkeyGc() {
gc_js_1.gc();
if (gcCheckTaskId !== 0) {
clearTimeout(gcCheckTaskId);
checkOnKnownObjects();
}
}
exports.gc = gc_js_1.gc && spidermonkeyGc;
//# sourceMappingURL=internal.js.map