@@ -75,7 +75,54 @@ async function runAndBreathe(fn, repeat, waitTime = 20) {
75
75
}
76
76
}
77
77
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
+
78
124
module . exports = {
79
125
checkIfCollectable,
80
126
runAndBreathe,
127
+ checkIfCollectableByCounting,
81
128
} ;
0 commit comments