function rtrace(onerror) { var blocks = new Map(); var rtrace = { allocCount: 0, freeCount: 0, incrementCount: 0, decrementCount: 0, onalloc: function(block) { ++rtrace.allocCount; if (blocks.has(block)) { onerror(Error("duplicate alloc: " + block)); } else { blocks.set(block, 0); } }, onfree: function(block) { ++rtrace.freeCount; if (!blocks.has(block)) { onerror(Error("invalid free: " + block)); } else { blocks.delete(block); } }, onincrement: function(block) { ++rtrace.incrementCount; if (!blocks.has(block)) { onerror(Error("invalid increment: " + block)); } else { let rc = blocks.get(block) + 1; blocks.set(block, rc); } }, ondecrement: function(block) { ++rtrace.decrementCount; if (!blocks.has(block)) { onerror(Error("invalid decrement: " + block)); } else { let rc = blocks.get(block) - 1; if (rc < 0) { onerror(Error("invalid decrement: " + block)); } else { blocks.set(block, rc); } } }, get active() { return Boolean(rtrace.allocCount + rtrace.freeCount + rtrace.incrementCount + rtrace.decrementCount); }, get leakCount() { return blocks.size; } }; return rtrace; } module.exports = rtrace;