@@ -12,6 +12,7 @@ import 'package:analyzer/dart/element/element.dart';
12
12
import 'package:analyzer/src/generated/engine.dart' ;
13
13
import 'package:analyzer/src/generated/sdk.dart' ;
14
14
import 'package:analyzer/src/generated/source_io.dart' ;
15
+ import 'package:collection/collection.dart' ;
15
16
import 'package:dartdoc/src/model.dart' ;
16
17
import 'package:quiver_hashcode/hashcode.dart' ;
17
18
@@ -191,8 +192,40 @@ class _HashableList extends UnmodifiableListView<dynamic> {
191
192
get hashCode => hashObjects (this );
192
193
}
193
194
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.
196
229
///
197
230
/// For all methods in this class, the parameter [f] must be a tear-off method
198
231
/// or top level function (not an inline closure) for memoization to work.
@@ -219,44 +252,45 @@ class _HashableList extends UnmodifiableListView<dynamic> {
219
252
/// ```
220
253
class Memoizer {
221
254
/// 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 ();
227
256
228
257
/// Reset the memoization table, forcing calls of the underlying functions.
229
258
void invalidateMemos () {
230
259
_memoizationTable = new Map ();
231
260
}
232
261
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
+
233
267
/// Calls and caches the return value of [f] () if not in the cache, then
234
268
/// returns the cached value of [f] ().
235
269
R memoized <R >(Function f) {
236
270
_HashableList key = new _HashableList ([f]);
237
- return _memoizationTable. putIfAbsent (key, f);
271
+ return _cacheIfAbsent (key, f);
238
272
}
239
273
240
274
/// Calls and caches the return value of [f] ([param1] ) if not in the cache, then
241
275
/// returns the cached value of [f] ([param1] ).
242
276
R memoized1 <R , A >(R Function (A ) f, A param1) {
243
277
_HashableList key = new _HashableList ([f, param1]);
244
- return _memoizationTable. putIfAbsent (key, () => f (param1));
278
+ return _cacheIfAbsent (key, () => f (param1));
245
279
}
246
280
247
281
/// Calls and caches the return value of [f] ([param1] , [param2] ) if not in the
248
282
/// cache, then returns the cached value of [f] ([param1] , [param2] ).
249
283
R memoized2 <R , A , B >(R Function (A , B ) f, A param1, B param2) {
250
284
_HashableList key = new _HashableList ([f, param1, param2]);
251
- return _memoizationTable. putIfAbsent (key, () => f (param1, param2));
285
+ return _cacheIfAbsent (key, () => f (param1, param2));
252
286
}
253
287
254
288
/// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ) if
255
289
/// not in the cache, then returns the cached value of [f] ([param1] ,
256
290
/// [param2] , [param3] ).
257
291
R memoized3 <R , A , B , C >(R Function (A , B , C ) f, A param1, B param2, C param3) {
258
292
_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));
260
294
}
261
295
262
296
/// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -265,8 +299,7 @@ class Memoizer {
265
299
R memoized4 <R , A , B , C , D >(
266
300
R Function (A , B , C , D ) f, A param1, B param2, C param3, D param4) {
267
301
_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));
270
303
}
271
304
272
305
/// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -276,8 +309,7 @@ class Memoizer {
276
309
C param3, D param4, E param5) {
277
310
_HashableList key =
278
311
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));
281
313
}
282
314
283
315
/// Calls and caches the return value of [f] ([param1] , [param2] , [param3] ,
@@ -287,7 +319,7 @@ class Memoizer {
287
319
B param2, C param3, D param4, E param5, F param6) {
288
320
_HashableList key =
289
321
new _HashableList ([f, param1, param2, param3, param4, param5, param6]);
290
- return _memoizationTable. putIfAbsent (
322
+ return _cacheIfAbsent (
291
323
key, () => f (param1, param2, param3, param4, param5, param6));
292
324
}
293
325
@@ -299,7 +331,7 @@ class Memoizer {
299
331
A param1, B param2, C param3, D param4, E param5, F param6, G param7) {
300
332
_HashableList key = new _HashableList (
301
333
[f, param1, param2, param3, param4, param5, param6, param7]);
302
- return _memoizationTable. putIfAbsent (
334
+ return _cacheIfAbsent (
303
335
key, () => f (param1, param2, param3, param4, param5, param6, param7));
304
336
}
305
337
@@ -319,7 +351,7 @@ class Memoizer {
319
351
H param8) {
320
352
_HashableList key = new _HashableList (
321
353
[f, param1, param2, param3, param4, param5, param6, param7, param8]);
322
- return _memoizationTable. putIfAbsent (
354
+ return _cacheIfAbsent (
323
355
key,
324
356
() =>
325
357
f (param1, param2, param3, param4, param5, param6, param7, param8));
0 commit comments