@@ -12,6 +12,7 @@ import 'package:analyzer/dart/element/element.dart';
1212import 'package:analyzer/src/generated/engine.dart' ;
1313import 'package:analyzer/src/generated/sdk.dart' ;
1414import 'package:analyzer/src/generated/source_io.dart' ;
15+ import 'package:collection/collection.dart' ;
1516import 'package:dartdoc/src/model.dart' ;
1617import 'package:quiver_hashcode/hashcode.dart' ;
1718
@@ -191,8 +192,40 @@ class _HashableList extends UnmodifiableListView<dynamic> {
191192 get hashCode => hashObjects (this );
192193}
193194
194- /// Extend or use as a mixin to track object-specific cached values, or
195- /// instantiate directly to track other values.
195+ /// Like [Memoizer] , except in checked mode will validate that the value of the
196+ /// memoized function is unchanging using [DeepCollectionEquality] . Still
197+ /// returns the cached value assuming the assertion passes.
198+ class ValidatingMemoizer extends Memoizer {
199+ bool _assert_on_difference = false ;
200+
201+ ValidatingMemoizer () : super () {
202+ // Assignment within assert to take advantage of the expression only
203+ // being executed in checked mode.
204+ assert (_assert_on_difference = true );
205+ invalidateMemos ();
206+ }
207+
208+ /// In checked mode and when constructed with assert_on_difference == true,
209+ /// validate that the return value from f() equals the memoized value.
210+ /// Otherwise, a wrapper around putIfAbsent.
211+ @override
212+ R _cacheIfAbsent <R >(_HashableList key, R Function () f) {
213+ if (_assert_on_difference) {
214+ if (_memoizationTable.containsKey (key)) {
215+ R value = f ();
216+ if (! new DeepCollectionEquality ()
217+ .equals (value, _memoizationTable[key])) {
218+ throw new AssertionError ('${value } != $_memoizationTable [key]' );
219+ }
220+ }
221+ }
222+ return super ._cacheIfAbsent (key, f);
223+ }
224+ }
225+
226+ /// A basic Memoizer class. Instantiate as a member variable, extend, or use
227+ /// as a mixin to track object-specific cached values, or instantiate directly
228+ /// to track other values.
196229///
197230/// For all methods in this class, the parameter [f] must be a tear-off method
198231/// or top level function (not an inline closure) for memoization to work.
@@ -219,44 +252,45 @@ class _HashableList extends UnmodifiableListView<dynamic> {
219252/// ```
220253class Memoizer {
221254 /// Map of a function and its positional parameters (if any), to a value.
222- Map <_HashableList , dynamic > _memoizationTable;
223-
224- Memoizer () {
225- invalidateMemos ();
226- }
255+ Map <_HashableList , dynamic > _memoizationTable = new Map ();
227256
228257 /// Reset the memoization table, forcing calls of the underlying functions.
229258 void invalidateMemos () {
230259 _memoizationTable = new Map ();
231260 }
232261
262+ /// A wrapper around putIfAbsent, exposed to allow overrides.
263+ R _cacheIfAbsent <R >(_HashableList key, R Function () f) {
264+ return _memoizationTable.putIfAbsent (key, f);
265+ }
266+
233267 /// Calls and caches the return value of [f] () if not in the cache, then
234268 /// returns the cached value of [f] ().
235269 R memoized <R >(Function f) {
236270 _HashableList key = new _HashableList ([f]);
237- return _memoizationTable. putIfAbsent (key, f);
271+ return _cacheIfAbsent (key, f);
238272 }
239273
240274 /// Calls and caches the return value of [f] ([param1] ) if not in the cache, then
241275 /// returns the cached value of [f] ([param1] ).
242276 R memoized1 <R , A >(R Function (A ) f, A param1) {
243277 _HashableList key = new _HashableList ([f, param1]);
244- return _memoizationTable. putIfAbsent (key, () => f (param1));
278+ return _cacheIfAbsent (key, () => f (param1));
245279 }
246280
247281 /// Calls and caches the return value of [f] ([param1] , [param2] ) if not in the
248282 /// cache, then returns the cached value of [f] ([param1] , [param2] ).
249283 R memoized2 <R , A , B >(R Function (A , B ) f, A param1, B param2) {
250284 _HashableList key = new _HashableList ([f, param1, param2]);
251- return _memoizationTable. putIfAbsent (key, () => f (param1, param2));
285+ return _cacheIfAbsent (key, () => f (param1, param2));
252286 }
253287
254288 /// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ) if
255289 /// not in the cache, then returns the cached value of [f] ([param1] ,
256290 /// [param2] , [param3] ).
257291 R memoized3 <R , A , B , C >(R Function (A , B , C ) f, A param1, B param2, C param3) {
258292 _HashableList key = new _HashableList ([f, param1, param2, param3]);
259- return _memoizationTable. putIfAbsent (key, () => f (param1, param2, param3));
293+ return _cacheIfAbsent (key, () => f (param1, param2, param3));
260294 }
261295
262296 /// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -265,8 +299,7 @@ class Memoizer {
265299 R memoized4 <R , A , B , C , D >(
266300 R Function (A , B , C , D ) f, A param1, B param2, C param3, D param4) {
267301 _HashableList key = new _HashableList ([f, param1, param2, param3, param4]);
268- return _memoizationTable.putIfAbsent (
269- key, () => f (param1, param2, param3, param4));
302+ return _cacheIfAbsent (key, () => f (param1, param2, param3, param4));
270303 }
271304
272305 /// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -276,8 +309,7 @@ class Memoizer {
276309 C param3, D param4, E param5) {
277310 _HashableList key =
278311 new _HashableList ([f, param1, param2, param3, param4, param5]);
279- return _memoizationTable.putIfAbsent (
280- key, () => f (param1, param2, param3, param4, param5));
312+ return _cacheIfAbsent (key, () => f (param1, param2, param3, param4, param5));
281313 }
282314
283315 /// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -287,7 +319,7 @@ class Memoizer {
287319 B param2, C param3, D param4, E param5, F param6) {
288320 _HashableList key =
289321 new _HashableList ([f, param1, param2, param3, param4, param5, param6]);
290- return _memoizationTable. putIfAbsent (
322+ return _cacheIfAbsent (
291323 key, () => f (param1, param2, param3, param4, param5, param6));
292324 }
293325
@@ -299,7 +331,7 @@ class Memoizer {
299331 A param1, B param2, C param3, D param4, E param5, F param6, G param7) {
300332 _HashableList key = new _HashableList (
301333 [f, param1, param2, param3, param4, param5, param6, param7]);
302- return _memoizationTable. putIfAbsent (
334+ return _cacheIfAbsent (
303335 key, () => f (param1, param2, param3, param4, param5, param6, param7));
304336 }
305337
@@ -319,7 +351,7 @@ class Memoizer {
319351 H param8) {
320352 _HashableList key = new _HashableList (
321353 [f, param1, param2, param3, param4, param5, param6, param7, param8]);
322- return _memoizationTable. putIfAbsent (
354+ return _cacheIfAbsent (
323355 key,
324356 () =>
325357 f (param1, param2, param3, param4, param5, param6, param7, param8));
0 commit comments