|  | // Copyright (c) 2024, 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 'dart:collection'; | 
|  |  | 
|  | import 'package:expect/expect.dart'; | 
|  |  | 
|  | /// Bug in dev-compiler's [putIfAbsent](https://dartbug.com/47852). | 
|  | void originalError() { | 
|  | final map = {}; | 
|  | final key = DateTime.now(); // Overrides Object.==/hashCode | 
|  | bool wasAbsent = false; | 
|  | map.putIfAbsent(key, () { | 
|  | wasAbsent = true; | 
|  | Expect.isFalse( | 
|  | map.containsKey(key), | 
|  | 'containsKey should be false in putIfAbsent', | 
|  | ); | 
|  | return key; | 
|  | }); | 
|  | Expect.isTrue(wasAbsent); | 
|  | } | 
|  |  | 
|  | enum AnEnum { element1, element2 } | 
|  |  | 
|  | class Dumb { | 
|  | final Object field; | 
|  | Dumb(this.field); | 
|  |  | 
|  | // Dumb hashCode to stress same-bucket paths. | 
|  | int get hashCode => 0; | 
|  | bool operator ==(Object other) => other is Dumb && this.field == other.field; | 
|  |  | 
|  | String toString() => 'Dumb($field)'; | 
|  | } | 
|  |  | 
|  | // Test keys. These instances of classes that do and don't override `==` and | 
|  | // `hashCode`, and a variety of primitive values since we generally don't know | 
|  | // whether they have overrides. | 
|  | final keys = [ | 
|  | 123, | 
|  | 3.14, | 
|  | 10.0, // int or double depending on platform. | 
|  | 'aString', | 
|  | #someSymbol, | 
|  | true, | 
|  | false, | 
|  | null, | 
|  | AnEnum.element1, | 
|  | AnEnum.element2, | 
|  | DateTime.now(), | 
|  | Dumb(1), | 
|  | Dumb(2), | 
|  | Dumb(3), | 
|  | Object(), | 
|  | const [1], | 
|  | const {1}, | 
|  | const {'x'}, | 
|  | // const maps have different implementation types depending on keys. | 
|  | const {'x': 1}, | 
|  | const {123: 1}, | 
|  | const {AnEnum.element1: 1, #foo: 2}, | 
|  | ]; | 
|  |  | 
|  | void testMap(Map<Object?, Object?> map) { | 
|  | for (final key in keys) { | 
|  | bool wasAbsent = false; | 
|  | map.putIfAbsent(key, () { | 
|  | wasAbsent = true; | 
|  | Expect.isFalse( | 
|  | map.containsKey(key), | 
|  | 'containsKey should be false in putIfAbsent. key = $key', | 
|  | ); | 
|  | return key; | 
|  | }); | 
|  | Expect.isTrue(wasAbsent); | 
|  | Expect.isTrue(map.containsKey(key), 'Key was not added. key = $key'); | 
|  | } | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | originalError(); | 
|  |  | 
|  | testMap({}); | 
|  | testMap(Map()); // Should be same as `{}`. | 
|  | testMap(HashMap()); | 
|  | testMap(LinkedHashMap()); // Should be same as `{}`. | 
|  |  | 
|  | testMap(Map.identity()); | 
|  | testMap(HashMap.identity()); | 
|  | testMap(LinkedHashMap.identity()); // Should be same as `Map.identity()`. | 
|  |  | 
|  | // Custom maps: | 
|  | testMap( | 
|  | HashMap( | 
|  | equals: (k1, k2) => k2 == k1, | 
|  | hashCode: (key) => key.hashCode + 1, | 
|  | isValidKey: (_) => true, | 
|  | ), | 
|  | ); | 
|  | testMap( | 
|  | LinkedHashMap( | 
|  | equals: (k1, k2) => k2 == k1, | 
|  | hashCode: (key) => key.hashCode + 1, | 
|  | isValidKey: (_) => true, | 
|  | ), | 
|  | ); | 
|  | } |