function writeGraph(root, headerful) { if (headerful) { writeMagic(); } var refInfo = findBackRefsAndHoles(root, holeFilter); backRefSet = refInfo.backRefs; holeMap = refInfo.holes; if (backRefSet.size() !== 0) { backRefMap = oid.createMap(); } iterate.iterate(root, visitor); if (headerful) { writeFooter(); } }
/** * Determine the sets of all back-referenced objects and all non-data * "holes" under the given root. The holes are filtered using the * given filter, resulting in a map with bindings from the original * to the filtered objects. */ function findBackRefsAndHoles(root, holeFilter) { var allowHoles = typ.isDefined(holeFilter); var allRefs = oid.createMap(); var holes = allowHoles ? oid.createMap() : undefined; function nop() { // This space intentionally left blank. } function keepGoing(obj, name, value, innerVisitor) { if (innerVisitor) { innerVisitor(); } } function addRef(obj, innerVisitor, mustBeHole) { if (mustBeHole && !allowHoles) { throw new Error("Hole-ful graph, but no hole filter."); } var count = allRefs.get(obj, 0) + 1; allRefs.set(obj, count); if (count === 1) { if (mustBeHole) { var already = holes.get(obj); if (!already) { var replacement = fixReplacement(obj, holeFilter(obj)); holes.set(obj, replacement); iterate.iterate(replacement.obj, visitor); } } else if (innerVisitor) { innerVisitor(); } } } function visitFunction(func, innerVisitor) { addRef(func, innerVisitor, true); } function visitObject(obj, innerVisitor) { if (Buffer.isBuffer(obj)) { // Suppress the inner visit for buffers, because it is too // odious to imagine having to cons up all the keys for // the buffer indices. (Too bad there's no // `Object.getOwnNamedProperties()` or somesuch.) addRef(obj, undefined); } else { addRef(obj, innerVisitor, !typ.isMap(obj)); } } var visitor = { visitObject: visitObject, visitArray: addRef, visitFunction: visitFunction, visitString: addRef, visitObjectPrototype: keepGoing, visitObjectBinding: keepGoing, visitObjectGetter: keepGoing, visitObjectSetter: keepGoing, visitArrayElement: keepGoing, visitNumber: nop, visitBoolean: nop, visitUndefined: nop, visitNull: nop }; iterate.iterate(root, visitor); var backRefs = oid.createSet(); allRefs.forEach(function (ref, count) { if (count > 1) { backRefs.add(ref); } }); return { backRefs: backRefs, holes: holes }; }