Make Cache.get() just return the value.

Change-Id: I816a1c28569011215ad6cba9a7559513a7900386
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246880
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/byte_store.dart b/pkg/analyzer/lib/src/dart/analysis/byte_store.dart
index 7fa38f7..0c096b3 100644
--- a/pkg/analyzer/lib/src/dart/analysis/byte_store.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/byte_store.dart
@@ -50,7 +50,18 @@
 
   @override
   Uint8List? get(String key) {
-    return _cache.get(key, () => _store.get(key));
+    final cached = _cache.get(key);
+    if (cached != null) {
+      return cached;
+    }
+
+    final fromStore = _store.get(key);
+    if (fromStore != null) {
+      _cache.put(key, fromStore);
+      return fromStore;
+    }
+
+    return null;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/analysis/cache.dart b/pkg/analyzer/lib/src/dart/analysis/cache.dart
index ca97db6..bddb6fa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/cache.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/cache.dart
@@ -2,37 +2,33 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:meta/meta.dart';
+
 /// LRU cache of objects.
 class Cache<K, V> {
   final int _maxSizeBytes;
   final int Function(V) _meter;
 
-  final _map = <K, V>{};
+  @visibleForTesting
+  final map = <K, V>{};
   int _currentSizeBytes = 0;
 
   Cache(this._maxSizeBytes, this._meter);
 
-  V? get(K key, V? Function() getNotCached) {
-    V? value = _map.remove(key);
-    if (value == null) {
-      value = getNotCached();
-      if (value != null) {
-        _map[key] = value;
-        _currentSizeBytes += _meter(value);
-        _evict();
-      }
-    } else {
-      _map[key] = value;
+  V? get(K key) {
+    final value = map.remove(key);
+    if (value != null) {
+      map[key] = value;
     }
     return value;
   }
 
   void put(K key, V value) {
-    V? oldValue = _map[key];
+    V? oldValue = map[key];
     if (oldValue != null) {
       _currentSizeBytes -= _meter(oldValue);
     }
-    _map[key] = value;
+    map[key] = value;
     _currentSizeBytes += _meter(value);
     _evict();
   }
@@ -40,7 +36,7 @@
   void _evict() {
     if (_currentSizeBytes > _maxSizeBytes) {
       var keysToRemove = <K>[];
-      for (var entry in _map.entries) {
+      for (var entry in map.entries) {
         keysToRemove.add(entry.key);
         _currentSizeBytes -= _meter(entry.value);
         if (_currentSizeBytes <= _maxSizeBytes) {
@@ -48,7 +44,7 @@
         }
       }
       for (var key in keysToRemove) {
-        _map.remove(key);
+        map.remove(key);
       }
     }
   }
diff --git a/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart b/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
index dc78189..0ffa205 100644
--- a/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
+++ b/pkg/analyzer/lib/src/dart/micro/cider_byte_store.dart
@@ -54,7 +54,7 @@
 
   @override
   CacheData? get(String key, Uint8List signature) {
-    var entry = _cache.get(key, () => null);
+    final entry = _cache.get(key);
 
     if (entry != null &&
         const ListEquality<int>().equals(entry.signature, signature)) {
diff --git a/pkg/analyzer/test/src/dart/analysis/cache_test.dart b/pkg/analyzer/test/src/dart/analysis/cache_test.dart
index 9a6cac3..0531aa6 100644
--- a/pkg/analyzer/test/src/dart/analysis/cache_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/cache_test.dart
@@ -2,7 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:typed_data';
+
 import 'package:analyzer/src/dart/analysis/cache.dart';
+import 'package:analyzer_utilities/testing/map_entry_matcher.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -12,52 +15,47 @@
   });
 }
 
-List<int> _b(int length) {
-  return List<int>.filled(length, 0);
+Uint8List _b(int length) {
+  return Uint8List(length);
 }
 
 @reflectiveTest
 class CacheTest {
-  test_get_notFound_evict() {
-    var cache = _newBytesCache(100);
+  test_get() {
+    final cache = _newBytesCache(100);
 
-    // Request '1'.  Nothing found.
-    expect(cache.get('1', _noBytes), isNull);
+    expect(cache.get('1'), isNull);
+    expect(cache.map.entries, isEmpty);
 
-    // Add enough data to the store to force an eviction.
-    cache.put('2', _b(40));
-    cache.put('3', _b(40));
-    cache.put('4', _b(40));
+    cache.put('1', _b(10));
+    expect(cache.map.entries, [
+      isMapEntry('1', hasLength(10)),
+    ]);
+
+    expect(cache.get('1'), hasLength(10));
   }
 
-  test_get_notFound_retry() {
-    var cache = _newBytesCache(100);
+  test_get_reorders() {
+    final cache = _newBytesCache(100);
 
-    // Request '1'.  Nothing found.
-    expect(cache.get('1', _noBytes), isNull);
+    cache.put('1', _b(1));
+    cache.put('2', _b(2));
+    cache.put('3', _b(3));
+    cache.put('4', _b(4));
+    expect(cache.map.entries, [
+      isMapEntry('1', hasLength(1)),
+      isMapEntry('2', hasLength(2)),
+      isMapEntry('3', hasLength(3)),
+      isMapEntry('4', hasLength(4)),
+    ]);
 
-    // Request '1' again.
-    // The previous `null` result should not have been cached.
-    expect(cache.get('1', () => _b(40)), isNotNull);
-  }
-
-  test_get_put_evict() {
-    var cache = _newBytesCache(100);
-
-    // Keys: [1, 2].
-    cache.put('1', _b(40));
-    cache.put('2', _b(50));
-
-    // Request '1', so now it is the most recently used.
-    // Keys: [2, 1].
-    cache.get('1', _noBytes);
-
-    // 40 + 50 + 30 > 100
-    // So, '2' is evicted.
-    cache.put('3', _b(30));
-    expect(cache.get('1', _noBytes), hasLength(40));
-    expect(cache.get('2', _noBytes), isNull);
-    expect(cache.get('3', _noBytes), hasLength(30));
+    expect(cache.get('2'), hasLength(2));
+    expect(cache.map.entries, [
+      isMapEntry('1', hasLength(1)),
+      isMapEntry('3', hasLength(3)),
+      isMapEntry('4', hasLength(4)),
+      isMapEntry('2', hasLength(2)),
+    ]);
   }
 
   test_put_evict_first() {
@@ -66,36 +64,39 @@
     // 40 + 50 < 100
     cache.put('1', _b(40));
     cache.put('2', _b(50));
-    expect(cache.get('1', _noBytes), hasLength(40));
-    expect(cache.get('2', _noBytes), hasLength(50));
+    expect(cache.map.entries, [
+      isMapEntry('1', hasLength(40)),
+      isMapEntry('2', hasLength(50)),
+    ]);
 
     // 40 + 50 + 30 > 100
     // So, '1' is evicted.
     cache.put('3', _b(30));
-    expect(cache.get('1', _noBytes), isNull);
-    expect(cache.get('2', _noBytes), hasLength(50));
-    expect(cache.get('3', _noBytes), hasLength(30));
+    expect(cache.map.entries, [
+      isMapEntry('2', hasLength(50)),
+      isMapEntry('3', hasLength(30)),
+    ]);
   }
 
-  test_put_evict_firstAndSecond() {
+  test_put_evict_firstSecond() {
     var cache = _newBytesCache(100);
 
     // 10 + 80 < 100
     cache.put('1', _b(10));
     cache.put('2', _b(80));
-    expect(cache.get('1', _noBytes), hasLength(10));
-    expect(cache.get('2', _noBytes), hasLength(80));
+    expect(cache.map.entries, [
+      isMapEntry('1', hasLength(10)),
+      isMapEntry('2', hasLength(80)),
+    ]);
 
     // 10 + 80 + 30 > 100
     // So, '1' and '2' are evicted.
     cache.put('3', _b(30));
-    expect(cache.get('1', _noBytes), isNull);
-    expect(cache.get('2', _noBytes), isNull);
-    expect(cache.get('3', _noBytes), hasLength(30));
+    expect(cache.map.entries, [
+      isMapEntry('3', hasLength(30)),
+    ]);
   }
 
-  Cache<String, List<int>> _newBytesCache(int maxSizeBytes) =>
-      Cache<String, List<int>>(maxSizeBytes, (bytes) => bytes.length);
-
-  static List<int>? _noBytes() => null;
+  Cache<String, Uint8List> _newBytesCache(int maxSizeBytes) =>
+      Cache<String, Uint8List>(maxSizeBytes, (bytes) => bytes.length);
 }
diff --git a/pkg/analyzer_utilities/lib/testing/map_entry_matcher.dart b/pkg/analyzer_utilities/lib/testing/map_entry_matcher.dart
new file mode 100644
index 0000000..9556ee1
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/testing/map_entry_matcher.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+/// Matches a [MapEntry] with matching [MapEntry.key] and [MapEntry.value].
+MapEntryMatcher isMapEntry(key, value) => MapEntryMatcher(key, value);
+
+class MapEntryMatcher extends Matcher {
+  final Matcher keyMatcher;
+  final Matcher valueMatcher;
+
+  MapEntryMatcher(key, value)
+      : keyMatcher = wrapMatcher(key),
+        valueMatcher = wrapMatcher(value);
+
+  @override
+  Description describe(Description description) => description
+      .add('MapEntry(key: ')
+      .addDescriptionOf(keyMatcher)
+      .add(', value: ')
+      .addDescriptionOf(valueMatcher)
+      .add(')');
+
+  @override
+  bool matches(item, Map matchState) =>
+      item is MapEntry &&
+      keyMatcher.matches(item.key, {}) &&
+      valueMatcher.matches(item.value, {});
+}