@@ -75,7 +75,54 @@ async function runAndBreathe(fn, repeat, waitTime = 20) {
7575 }
7676}
7777
78+ /**
79+ * This requires --expose-internals.
80+ * This function can be used to check if an object factory leaks or not by
81+ * iterating over the heap and count objects with the specified class
82+ * (which is checked by looking up the prototype chain).
83+ * @param {(i: number) => number } fn The factory receiving iteration count
84+ * and returning number of objects created. The return value should be
85+ * precise otherwise false negatives can be produced.
86+ * @param {Function } klass The class whose object is used to count the objects
87+ * @param {number } count Number of iterations that this check should be done
88+ * @param {number } waitTime Optional breathing time for GC.
89+ */
90+ async function checkIfCollectableByCounting ( fn , klass , count , waitTime = 20 ) {
91+ const { internalBinding } = require ( 'internal/test/binding' ) ;
92+ const { countObjectsWithPrototype } = internalBinding ( 'heap_utils' ) ;
93+ const { prototype, name } = klass ;
94+ let initialCount = countObjectsWithPrototype ( prototype ) ;
95+ console . log ( `Initial count of ${ name } : ${ initialCount } ` ) ;
96+ let totalCreated = 0 ;
97+ for ( let i = 0 ; i < count ; ++ i ) {
98+ const created = await fn ( i ) ;
99+ totalCreated += created ;
100+ console . log ( `#${ i } : created ${ created } ${ name } , total ${ totalCreated } ` ) ;
101+ await wait ( waitTime ) ; // give GC some breathing room.
102+ const currentCount = countObjectsWithPrototype ( prototype ) ;
103+ const collected = totalCreated - ( currentCount - initialCount ) ;
104+ console . log ( `#${ i } : counted ${ currentCount } ${ name } , collected ${ collected } ` ) ;
105+ if ( collected > 0 ) {
106+ console . log ( `Detected ${ collected } collected ${ name } , finish early` ) ;
107+ return ;
108+ }
109+ }
110+
111+ await wait ( waitTime ) ; // give GC some breathing room.
112+ const currentCount = countObjectsWithPrototype ( prototype ) ;
113+ const collected = totalCreated - ( currentCount - initialCount ) ;
114+ console . log ( `Last count: counted ${ currentCount } ${ name } , collected ${ collected } ` ) ;
115+ // Some objects with the prototype can be collected.
116+ if ( collected > 0 ) {
117+ console . log ( `Detected ${ collected } collected ${ name } ` ) ;
118+ return ;
119+ }
120+
121+ throw new Error ( `${ name } cannot be collected` ) ;
122+ }
123+
78124module . exports = {
79125 checkIfCollectable,
80126 runAndBreathe,
127+ checkIfCollectableByCounting,
81128} ;
0 commit comments