mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
[js_runtime] Use custom hashCode for GeneralConstantMap
Fixes #46580 Change-Id: Ida2b7df75415881973085f9afeacd9ee384fd910 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207160 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Mayank Patke <fishythefish@google.com>
This commit is contained in:
parent
a8f2262c10
commit
cfc8ad4e7f
3 changed files with 98 additions and 1 deletions
|
@ -173,13 +173,35 @@ class GeneralConstantMap<K, V> extends ConstantMap<K, V> {
|
|||
Map<K, V> _getMap() {
|
||||
LinkedHashMap<K, V>? backingMap = JS('LinkedHashMap|Null', r'#.$map', this);
|
||||
if (backingMap == null) {
|
||||
backingMap = JsLinkedHashMap<K, V>();
|
||||
backingMap = LinkedHashMap<K, V>(
|
||||
hashCode: _constantMapHashCode,
|
||||
// In legacy mode (--no-sound-null-safety), `null` keys are
|
||||
// permitted. In sound mode, `null` keys are permitted only if [K] is
|
||||
// nullable.
|
||||
isValidKey: JS_GET_FLAG('LEGACY') ? _typeTest<K?>() : _typeTest<K>());
|
||||
fillLiteralMap(_jsData, backingMap);
|
||||
JS('', r'#.$map = #', this, backingMap);
|
||||
}
|
||||
return backingMap;
|
||||
}
|
||||
|
||||
static int _constantMapHashCode(Object? key) {
|
||||
// Types are tested here one-by-one so that each call to get:hashCode can be
|
||||
// resolved differently.
|
||||
|
||||
// Some common primitives in a GeneralConstantMap.
|
||||
if (key is num) return key.hashCode; // One method on JSNumber.
|
||||
|
||||
// Specially handled known types.
|
||||
if (key is Symbol) return key.hashCode;
|
||||
if (key is Type) return key.hashCode;
|
||||
|
||||
// Everything else, including less common primitives.
|
||||
return identityHashCode(key);
|
||||
}
|
||||
|
||||
static bool Function(Object?) _typeTest<T>() => (Object? o) => o is T;
|
||||
|
||||
bool containsValue(Object? needle) {
|
||||
return _getMap().containsValue(needle);
|
||||
}
|
||||
|
|
21
tests/language/map/literal15_test.dart
Normal file
21
tests/language/map/literal15_test.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// Test the use of `null` keys in const maps.
|
||||
|
||||
library map_literal15_test;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void main() {
|
||||
var m1 = const <String, int>{null: 10, 'null': 20};
|
||||
// ^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MAP_KEY_TYPE_NOT_ASSIGNABLE
|
||||
// [cfe] The value 'null' can't be assigned to a variable of type 'String' because 'String' is not nullable.
|
||||
|
||||
var m2 = const <Comparable, int>{null: 10, 'null': 20};
|
||||
// ^^^^
|
||||
// [analyzer] COMPILE_TIME_ERROR.MAP_KEY_TYPE_NOT_ASSIGNABLE
|
||||
// [cfe] The value 'null' can't be assigned to a variable of type 'Comparable<dynamic>' because 'Comparable<dynamic>' is not nullable.
|
||||
}
|
54
tests/language_2/map/literal15_test.dart
Normal file
54
tests/language_2/map/literal15_test.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
// Test the use of `null` keys in const maps. In versions before 2.12, when
|
||||
// nullable types were introduced, types were nullable so it was legal to have
|
||||
// `null` keys in maps.
|
||||
|
||||
library map_literal15_test;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void test1() {
|
||||
var m1 = const <String, int>{null: 10, 'null': 20};
|
||||
Expect.isTrue(m1.containsKey(null));
|
||||
Expect.isTrue(m1.containsKey(undefined()));
|
||||
Expect.equals(10, m1[null]);
|
||||
Expect.equals(10, m1[undefined()]);
|
||||
Expect.isTrue(m1.containsKey('null'));
|
||||
Expect.equals(20, m1['null']);
|
||||
// The '.keys' carry the 'String' type
|
||||
Expect.type<Iterable<String>>(m1.keys);
|
||||
Expect.type<Iterable<Comparable>>(m1.keys);
|
||||
Expect.notType<Iterable<int>>(m1.keys);
|
||||
}
|
||||
|
||||
void test2() {
|
||||
var m2 = const <Comparable, int>{null: 10, 'null': 20};
|
||||
Expect.isTrue(m2.containsKey(null));
|
||||
Expect.isTrue(m2.containsKey(undefined()));
|
||||
Expect.equals(10, m2[null]);
|
||||
Expect.equals(10, m2[undefined()]);
|
||||
Expect.isTrue(m2.containsKey('null'));
|
||||
Expect.equals(20, m2['null']);
|
||||
// The '.keys' carry the 'Comparable' type
|
||||
Expect.notType<Iterable<String>>(m2.keys);
|
||||
Expect.type<Iterable<Comparable>>(m2.keys);
|
||||
Expect.notType<Iterable<int>>(m2.keys);
|
||||
}
|
||||
|
||||
main() {
|
||||
test1();
|
||||
test2();
|
||||
}
|
||||
|
||||
// Calling `undefined()` gives us a `null` that is implemented as JavaScript
|
||||
// `undefined` on dart2js.
|
||||
@pragma('dart2js:noInline')
|
||||
dynamic get undefined => _undefined;
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
void _undefined() {}
|
Loading…
Reference in a new issue