mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 10:48:25 +00:00
optimize maps/sets in DDC, fixes #29865
- string/int keys use identity maps - construct the correct type rather than relying on factory constructors when possible - checks if key has identity semantics and use that when possible - avoid helper calls/casts/etc in implementations - use native ES6 Map/Set key order tracking - optimized constructor for map literals - fixes #30466, const maps are now canonicalized correctly Change-Id: Id44acf3d9bf8cb153a4755aa28df08fe91bfc633 Reviewed-on: https://dart-review.googlesource.com/8167 Reviewed-by: Vijay Menon <vsm@google.com>
This commit is contained in:
parent
082bbdb6a7
commit
3a79c35f3c
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -128,7 +128,7 @@ class CodeGenerator extends Object
|
|||
/// The type provider from the current Analysis [context].
|
||||
final TypeProvider types;
|
||||
|
||||
final LibraryElement dartCoreLibrary;
|
||||
final LibraryElement coreLibrary;
|
||||
final LibraryElement dartJSLibrary;
|
||||
|
||||
/// The dart:async `StreamIterator<>` type.
|
||||
|
@ -153,6 +153,10 @@ class CodeGenerator extends Object
|
|||
final ClassElement stringClass;
|
||||
final ClassElement functionClass;
|
||||
final ClassElement privateSymbolClass;
|
||||
final InterfaceType linkedHashMapImplType;
|
||||
final InterfaceType identityHashMapImplType;
|
||||
final InterfaceType linkedHashSetImplType;
|
||||
final InterfaceType identityHashSetImplType;
|
||||
|
||||
ConstFieldVisitor _constants;
|
||||
|
||||
|
@ -216,7 +220,7 @@ class CodeGenerator extends Object
|
|||
_jsNumber = _getLibrary(c, 'dart:_interceptors').getType('JSNumber'),
|
||||
interceptorClass =
|
||||
_getLibrary(c, 'dart:_interceptors').getType('Interceptor'),
|
||||
dartCoreLibrary = _getLibrary(c, 'dart:core'),
|
||||
coreLibrary = _getLibrary(c, 'dart:core'),
|
||||
boolClass = _getLibrary(c, 'dart:core').getType('bool'),
|
||||
intClass = _getLibrary(c, 'dart:core').getType('int'),
|
||||
doubleClass = _getLibrary(c, 'dart:core').getType('double'),
|
||||
|
@ -227,6 +231,14 @@ class CodeGenerator extends Object
|
|||
functionClass = _getLibrary(c, 'dart:core').getType('Function'),
|
||||
privateSymbolClass =
|
||||
_getLibrary(c, 'dart:_internal').getType('PrivateSymbol'),
|
||||
linkedHashMapImplType =
|
||||
_getLibrary(c, 'dart:_js_helper').getType('LinkedMap').type,
|
||||
identityHashMapImplType =
|
||||
_getLibrary(c, 'dart:_js_helper').getType('IdentityMap').type,
|
||||
linkedHashSetImplType =
|
||||
_getLibrary(c, 'dart:collection').getType('_HashSet').type,
|
||||
identityHashSetImplType =
|
||||
_getLibrary(c, 'dart:collection').getType('_IdentityHashSet').type,
|
||||
dartJSLibrary = _getLibrary(c, 'dart:js') {
|
||||
typeRep = new JSTypeRep(rules, types);
|
||||
}
|
||||
|
@ -363,7 +375,7 @@ class CodeGenerator extends Object
|
|||
}
|
||||
|
||||
// Add implicit dart:core dependency so it is first.
|
||||
emitLibraryName(dartCoreLibrary);
|
||||
emitLibraryName(coreLibrary);
|
||||
|
||||
// Visit each compilation unit and emit its code.
|
||||
//
|
||||
|
@ -510,8 +522,8 @@ class CodeGenerator extends Object
|
|||
}
|
||||
|
||||
String coreModuleName;
|
||||
if (!_libraries.containsKey(dartCoreLibrary)) {
|
||||
coreModuleName = _libraryToModule(dartCoreLibrary);
|
||||
if (!_libraries.containsKey(coreLibrary)) {
|
||||
coreModuleName = _libraryToModule(coreLibrary);
|
||||
}
|
||||
modules.forEach((module, libraries) {
|
||||
// Generate import directives.
|
||||
|
@ -610,11 +622,12 @@ class CodeGenerator extends Object
|
|||
for (var declaration in unit.declarations) {
|
||||
if (declaration is TopLevelVariableDeclaration) {
|
||||
inferNullableTypes(declaration);
|
||||
if (isInternalSdk &&
|
||||
(declaration.variables.isFinal || declaration.variables.isConst)) {
|
||||
_emitInternalSdkFields(declaration.variables.variables);
|
||||
} else {
|
||||
(fields ??= []).addAll(declaration.variables.variables);
|
||||
var lazyFields = declaration.variables.variables;
|
||||
if (isInternalSdk) {
|
||||
lazyFields = _emitInternalSdkFields(lazyFields);
|
||||
}
|
||||
if (lazyFields.isNotEmpty) {
|
||||
(fields ??= []).addAll(lazyFields);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -2189,12 +2202,11 @@ class CodeGenerator extends Object
|
|||
}
|
||||
emitSignature('Constructor', constructors);
|
||||
}
|
||||
|
||||
// Add static property dart._runtimeType to Object.
|
||||
// All other Dart classes will (statically) inherit this property.
|
||||
if (classElem == objectClass) {
|
||||
body.add(_callHelperStatement('tagComputed(#, () => #.#);',
|
||||
[className, emitLibraryName(dartCoreLibrary), 'Type']));
|
||||
[className, emitLibraryName(coreLibrary), 'Type']));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2777,7 +2789,7 @@ class CodeGenerator extends Object
|
|||
var isSync = !(element.isAsynchronous || element.isGenerator);
|
||||
var formals = _emitFormalParameterList(parameters, destructure: isSync);
|
||||
var typeFormals = _emitTypeFormals(type.typeFormals);
|
||||
formals.insertAll(0, typeFormals);
|
||||
if (_reifyGeneric(element)) formals.insertAll(0, typeFormals);
|
||||
|
||||
JS.Block code = isSync
|
||||
? _emitFunctionBody(element, parameters, body)
|
||||
|
@ -3786,8 +3798,14 @@ class CodeGenerator extends Object
|
|||
}
|
||||
|
||||
List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) {
|
||||
// add no reify generic check here: if (node.function)
|
||||
// node is Identifier
|
||||
var function = node.function;
|
||||
if (function is Identifier && !_reifyGeneric(function.staticElement)) {
|
||||
return null;
|
||||
}
|
||||
return _emitFunctionTypeArguments(
|
||||
node.function.staticType, node.staticInvokeType, node.typeArguments);
|
||||
function.staticType, node.staticInvokeType, node.typeArguments);
|
||||
}
|
||||
|
||||
/// If `g` is a generic function type, and `f` is an instantiation of it,
|
||||
|
@ -4194,17 +4212,30 @@ class CodeGenerator extends Object
|
|||
|
||||
/// Treat dart:_runtime fields as safe to eagerly evaluate.
|
||||
// TODO(jmesserly): it'd be nice to avoid this special case.
|
||||
void _emitInternalSdkFields(List<VariableDeclaration> fields) {
|
||||
List<VariableDeclaration> _emitInternalSdkFields(
|
||||
List<VariableDeclaration> fields) {
|
||||
var lazyFields = <VariableDeclaration>[];
|
||||
for (var field in fields) {
|
||||
// Skip our magic undefined constant.
|
||||
var element = field.element as TopLevelVariableElement;
|
||||
if (element.name == 'undefined') continue;
|
||||
_moduleItems.add(annotate(
|
||||
js.statement('# = #;',
|
||||
[_emitTopLevelName(field.element), _visitInitializer(field)]),
|
||||
field,
|
||||
field.element));
|
||||
|
||||
var init = field.initializer;
|
||||
if (init == null ||
|
||||
init is Literal ||
|
||||
_isJSInvocation(init) ||
|
||||
init is InstanceCreationExpression &&
|
||||
isSdkInternalRuntime(init.staticElement.library)) {
|
||||
_moduleItems.add(annotate(
|
||||
js.statement('# = #;',
|
||||
[_emitTopLevelName(field.element), _visitInitializer(field)]),
|
||||
field,
|
||||
field.element));
|
||||
} else {
|
||||
lazyFields.add(field);
|
||||
}
|
||||
}
|
||||
return lazyFields;
|
||||
}
|
||||
|
||||
JS.Expression _visitInitializer(VariableDeclaration node) {
|
||||
|
@ -4280,31 +4311,56 @@ class CodeGenerator extends Object
|
|||
SimpleIdentifier name,
|
||||
ArgumentList argumentList,
|
||||
bool isConst) {
|
||||
if (element == null) {
|
||||
return _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}'
|
||||
'.${name?.name ?? '<unnamed>'}');
|
||||
}
|
||||
|
||||
var classElem = element.enclosingElement;
|
||||
if (_isObjectLiteral(classElem)) {
|
||||
return _emitObjectLiteral(argumentList);
|
||||
}
|
||||
|
||||
JS.Expression emitNew() {
|
||||
JS.Expression ctor;
|
||||
bool isFactory = false;
|
||||
bool isNative = false;
|
||||
if (element == null) {
|
||||
ctor = _throwUnsafe('unresolved constructor: ${type?.name ?? '<null>'}'
|
||||
'.${name?.name ?? '<unnamed>'}');
|
||||
} else {
|
||||
ctor = _emitConstructorName(element, type);
|
||||
isFactory = element.isFactory;
|
||||
var classElem = element.enclosingElement;
|
||||
isNative = _isJSNative(classElem);
|
||||
}
|
||||
var args = _emitArgumentList(argumentList);
|
||||
if (argumentList.arguments.isEmpty && element.library.isInSdk) {
|
||||
// Skip the slow SDK factory constructors when possible.
|
||||
switch (classElem.name) {
|
||||
case 'Map':
|
||||
case 'HashMap':
|
||||
case 'LinkedHashMap':
|
||||
if (element.name == '') {
|
||||
return js.call('new #.new()', _emitMapImplType(type));
|
||||
} else if (element.name == 'identity') {
|
||||
return js.call(
|
||||
'new #.new()', _emitMapImplType(type, identity: true));
|
||||
}
|
||||
break;
|
||||
case 'Set':
|
||||
case 'HashSet':
|
||||
case 'LinkedHashSet':
|
||||
if (element.name == '') {
|
||||
return js.call('new #.new()', _emitSetImplType(type));
|
||||
} else if (element.name == 'identity') {
|
||||
return js.call(
|
||||
'new #.new()', _emitSetImplType(type, identity: true));
|
||||
}
|
||||
break;
|
||||
case 'List':
|
||||
if (element.name == '' && type is InterfaceType) {
|
||||
return _emitList(type.typeArguments[0], []);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Native factory constructors are JS constructors - use new here.
|
||||
return isFactory && !isNative
|
||||
var ctor = _emitConstructorName(element, type);
|
||||
return element.isFactory && !_isJSNative(classElem)
|
||||
? new JS.Call(ctor, args)
|
||||
: new JS.New(ctor, args);
|
||||
}
|
||||
|
||||
if (element != null && _isObjectLiteral(element.enclosingElement)) {
|
||||
return _emitObjectLiteral(argumentList);
|
||||
}
|
||||
if (isConst) return _emitConst(emitNew);
|
||||
return emitNew();
|
||||
return isConst ? _emitConst(emitNew) : emitNew();
|
||||
}
|
||||
|
||||
bool _isObjectLiteral(Element classElem) {
|
||||
|
@ -5499,23 +5555,41 @@ class CodeGenerator extends Object
|
|||
|
||||
@override
|
||||
visitMapLiteral(MapLiteral node) {
|
||||
var isConst = node.constKeyword != null;
|
||||
// TODO(jmesserly): we can likely make these faster.
|
||||
JS.Expression emitMap() {
|
||||
var entries = node.entries;
|
||||
var type = node.staticType as InterfaceType;
|
||||
|
||||
var elements = <JS.Expression>[];
|
||||
for (var e in entries) {
|
||||
elements.add(_visit(e.key));
|
||||
elements.add(_visit(e.value));
|
||||
emitEntries() {
|
||||
var entries = <JS.Expression>[];
|
||||
for (var e in node.entries) {
|
||||
entries.add(_visit(e.key));
|
||||
entries.add(_visit(e.value));
|
||||
}
|
||||
var args = type.typeArguments.map(_emitType).toList();
|
||||
args.add(new JS.ArrayInitializer(elements));
|
||||
return _callHelper(isConst ? 'constMap(#)' : 'map(#)', [args]);
|
||||
return new JS.ArrayInitializer(entries);
|
||||
}
|
||||
|
||||
return isConst ? _cacheConst(emitMap) : emitMap();
|
||||
if (node.constKeyword == null) {
|
||||
var mapType = _emitMapImplType(node.staticType);
|
||||
if (node.entries.isEmpty) {
|
||||
return js.call('new #.new()', [mapType]);
|
||||
}
|
||||
return js.call('new #.from(#)', [mapType, emitEntries()]);
|
||||
}
|
||||
var typeArgs = (node.staticType as InterfaceType).typeArguments;
|
||||
return _cacheConst(() => _callHelper('constMap(#, #, #)',
|
||||
[_emitType(typeArgs[0]), _emitType(typeArgs[1]), emitEntries()]));
|
||||
}
|
||||
|
||||
JS.Expression _emitMapImplType(InterfaceType type, {bool identity}) {
|
||||
var typeArgs = type.typeArguments;
|
||||
if (typeArgs.isEmpty) return _emitType(type);
|
||||
identity ??= isPrimitiveType(typeArgs[0]);
|
||||
type = identity ? identityHashMapImplType : linkedHashMapImplType;
|
||||
return _emitType(type.instantiate(typeArgs));
|
||||
}
|
||||
|
||||
JS.Expression _emitSetImplType(InterfaceType type, {bool identity}) {
|
||||
var typeArgs = type.typeArguments;
|
||||
if (typeArgs.isEmpty) return _emitType(type);
|
||||
identity ??= isPrimitiveType(typeArgs[0]);
|
||||
type = identity ? identityHashSetImplType : linkedHashSetImplType;
|
||||
return _emitType(type.instantiate(typeArgs));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -6138,6 +6212,13 @@ bool _isDeferredLoadLibrary(Expression target, SimpleIdentifier name) {
|
|||
bool _annotatedNullCheck(Element e) =>
|
||||
e != null && findAnnotation(e, isNullCheckAnnotation) != null;
|
||||
|
||||
bool _reifyGeneric(Element e) =>
|
||||
e == null ||
|
||||
!e.library.isInSdk ||
|
||||
findAnnotation(
|
||||
e, (a) => isBuiltinAnnotation(a, '_js_helper', 'NoReifyGeneric')) ==
|
||||
null;
|
||||
|
||||
final _friendlyOperatorName = {
|
||||
'<': 'lessThan',
|
||||
'>': 'greaterThan',
|
||||
|
|
|
@ -47,7 +47,7 @@ bool isPublicJSAnnotation(DartObjectImpl value) =>
|
|||
bool isJSAnonymousAnnotation(DartObjectImpl value) =>
|
||||
_isJsLibType('_Anonymous', value.type.element);
|
||||
|
||||
bool _isBuiltinAnnotation(
|
||||
bool isBuiltinAnnotation(
|
||||
DartObjectImpl value, String libraryName, String annotationName) {
|
||||
var e = value?.type?.element;
|
||||
if (e?.name != annotationName) return false;
|
||||
|
@ -59,22 +59,22 @@ bool _isBuiltinAnnotation(
|
|||
/// Whether [value] is a `@JSExportName` (internal annotation used in SDK
|
||||
/// instead of `@JS` from `package:js`).
|
||||
bool isJSExportNameAnnotation(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
|
||||
isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
|
||||
|
||||
bool isJsName(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_js_helper', 'JSName');
|
||||
isBuiltinAnnotation(value, '_js_helper', 'JSName');
|
||||
|
||||
bool isJsPeerInterface(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_js_helper', 'JsPeerInterface');
|
||||
isBuiltinAnnotation(value, '_js_helper', 'JsPeerInterface');
|
||||
|
||||
bool isNativeAnnotation(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_js_helper', 'Native');
|
||||
isBuiltinAnnotation(value, '_js_helper', 'Native');
|
||||
|
||||
bool isNotNullAnnotation(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_js_helper', 'NotNull');
|
||||
isBuiltinAnnotation(value, '_js_helper', 'NotNull');
|
||||
|
||||
bool isNullCheckAnnotation(DartObjectImpl value) =>
|
||||
_isBuiltinAnnotation(value, '_js_helper', 'NullCheck');
|
||||
isBuiltinAnnotation(value, '_js_helper', 'NullCheck');
|
||||
|
||||
/// Returns the name value of the `JSExportName` annotation (when compiling
|
||||
/// the SDK), or `null` if there's none. This is used to control the name
|
||||
|
|
|
@ -24,7 +24,7 @@ import 'property_model.dart';
|
|||
// TODO(vsm): Revisit whether we really need this when we get
|
||||
// better non-nullability in the type system.
|
||||
abstract class NullableTypeInference {
|
||||
LibraryElement get dartCoreLibrary;
|
||||
LibraryElement get coreLibrary;
|
||||
VirtualFieldModel get virtualFields;
|
||||
|
||||
InterfaceType getImplementationType(DartType type);
|
||||
|
@ -74,7 +74,7 @@ abstract class NullableTypeInference {
|
|||
}
|
||||
}
|
||||
|
||||
if (e.name == 'identical' && identical(e.library, dartCoreLibrary)) {
|
||||
if (e.name == 'identical' && identical(e.library, coreLibrary)) {
|
||||
return true;
|
||||
}
|
||||
// If this is a method call, check to see whether it is to a final
|
||||
|
@ -87,7 +87,7 @@ abstract class NullableTypeInference {
|
|||
DartType targetType = container.type;
|
||||
InterfaceType implType = getImplementationType(targetType);
|
||||
if (implType != null) {
|
||||
MethodElement method = implType.lookUpMethod(e.name, dartCoreLibrary);
|
||||
MethodElement method = implType.lookUpMethod(e.name, coreLibrary);
|
||||
if (method != null) e = method;
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ abstract class NullableTypeInference {
|
|||
var targetType = container.type;
|
||||
var implType = getImplementationType(targetType);
|
||||
if (implType != null) {
|
||||
var getter = implType.lookUpGetter(name, dartCoreLibrary);
|
||||
var getter = implType.lookUpGetter(name, coreLibrary);
|
||||
if (getter != null) element = getter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,5 +419,10 @@ class ClassPropertyModel {
|
|||
if (!type.isObject) {
|
||||
_collectNativeMembers(element.supertype, members);
|
||||
}
|
||||
if (element.isEnum) {
|
||||
// TODO(jmesserly): analyzer does not create the synthetic element
|
||||
// for the enum's `toString()` method, so we'll use the one on Object.
|
||||
members.add('toString');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,7 @@ import 'dart:_js_helper'
|
|||
checkInt,
|
||||
getRuntimeType,
|
||||
getTraceFromException,
|
||||
JsLinkedHashMap,
|
||||
LinkedMap,
|
||||
JSSyntaxRegExp,
|
||||
NoInline,
|
||||
notNull,
|
||||
|
@ -390,7 +390,7 @@ class Map<K, V> {
|
|||
}
|
||||
|
||||
@patch
|
||||
factory Map() = JsLinkedHashMap<K, V>.es6;
|
||||
factory Map() = LinkedMap<K, V>;
|
||||
}
|
||||
|
||||
@patch
|
||||
|
|
|
@ -22,6 +22,14 @@ class NotNull {
|
|||
|
||||
const notNull = const NotNull();
|
||||
|
||||
/// Marks a generic function or static method API to be not reified.
|
||||
/// ****CAUTION******
|
||||
/// This is currently unchecked, and hence should be used very carefully for
|
||||
/// internal SDK APIs only.
|
||||
class NoReifyGeneric {
|
||||
const NoReifyGeneric();
|
||||
}
|
||||
|
||||
/// Tells the development compiler to check a variable for null at its
|
||||
/// declaration point, and then to assume that the variable is non-null
|
||||
/// from that point forward.
|
||||
|
|
195
pkg/dev_compiler/tool/input_sdk/private/custom_hash_map.dart
Normal file
195
pkg/dev_compiler/tool/input_sdk/private/custom_hash_map.dart
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
part of dart._js_helper;
|
||||
|
||||
class CustomKeyHashMap<K, V> extends CustomHashMap<K, V> {
|
||||
final _Predicate<Object> _validKey;
|
||||
CustomKeyHashMap(_Equality<K> equals, _Hasher<K> hashCode, this._validKey)
|
||||
: super(equals, hashCode);
|
||||
|
||||
@override
|
||||
@notNull
|
||||
bool containsKey(Object key) {
|
||||
if (!_validKey(key)) return false;
|
||||
return super.containsKey(key);
|
||||
}
|
||||
|
||||
@override
|
||||
V operator [](Object key) {
|
||||
if (!_validKey(key)) return null;
|
||||
return super[key];
|
||||
}
|
||||
|
||||
@override
|
||||
V remove(Object key) {
|
||||
if (!_validKey(key)) return null;
|
||||
return super.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomHashMap<K, V> extends InternalMap<K, V> {
|
||||
/// The backing store for this map.
|
||||
@notNull
|
||||
final _map = JS('', 'new Map()');
|
||||
|
||||
/// Our map used to map keys onto the canonical key that is stored in [_map].
|
||||
@notNull
|
||||
final _keyMap = JS('', 'new Map()');
|
||||
|
||||
// We track the number of modifications done to the key set of the
|
||||
// hash map to be able to throw when the map is modified while being
|
||||
// iterated over.
|
||||
//
|
||||
// Value cycles after 2^30 modifications so that modification counts are
|
||||
// always unboxed (Smi) values. Modification detection will be missed if you
|
||||
// make exactly some multiple of 2^30 modifications between advances of an
|
||||
// iterator.
|
||||
@notNull
|
||||
int _modifications = 0;
|
||||
|
||||
final _Equality<K> _equals;
|
||||
final _Hasher<K> _hashCode;
|
||||
|
||||
CustomHashMap(this._equals, this._hashCode);
|
||||
|
||||
@notNull
|
||||
int get length => JS('int', '#.size', _map);
|
||||
|
||||
@notNull
|
||||
bool get isEmpty => JS('bool', '#.size == 0', _map);
|
||||
|
||||
@notNull
|
||||
bool get isNotEmpty => JS('bool', '#.size != 0', _map);
|
||||
|
||||
Iterable<K> get keys => new _JSMapIterable<K>(this, true);
|
||||
Iterable<V> get values => new _JSMapIterable<V>(this, false);
|
||||
|
||||
@notNull
|
||||
bool containsKey(Object key) {
|
||||
if (key is K) {
|
||||
var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, _hashCode(key));
|
||||
if (buckets != null) {
|
||||
var equals = _equals;
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (equals(k, key)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool containsValue(Object value) {
|
||||
for (var v in JS('', '#.values()', _map)) {
|
||||
if (value == v) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void addAll(Map<K, V> other) {
|
||||
other.forEach((K key, V value) {
|
||||
this[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
V operator [](Object key) {
|
||||
if (key is K) {
|
||||
var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, _hashCode(key));
|
||||
if (buckets != null) {
|
||||
var equals = _equals;
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (equals(k, key)) return JS('', '#.get(#)', _map, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void operator []=(K key, V value) {
|
||||
var keyMap = _keyMap;
|
||||
var hash = JS('int', '# & 0x3ffffff', _hashCode(key));
|
||||
var buckets = JS('', '#.get(#)', keyMap, hash);
|
||||
if (buckets == null) {
|
||||
JS('', '#.set(#, [#])', keyMap, hash, key);
|
||||
} else {
|
||||
var equals = _equals;
|
||||
for (int i = 0, n = JS('int', '#.length', buckets);;) {
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (equals(k, key)) {
|
||||
key = k;
|
||||
break;
|
||||
}
|
||||
if (++i >= n) {
|
||||
JS('', '#.push(#)', buckets, key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
JS('', '#.set(#, #)', _map, key, value);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
|
||||
V putIfAbsent(K key, V ifAbsent()) {
|
||||
var keyMap = _keyMap;
|
||||
var hash = JS('int', '# & 0x3ffffff', _hashCode(key));
|
||||
var buckets = JS('', '#.get(#)', keyMap, hash);
|
||||
if (buckets == null) {
|
||||
JS('', '#.set(#, [#])', keyMap, hash, key);
|
||||
} else {
|
||||
var equals = _equals;
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (equals(k, key)) return JS('', '#.get(#)', _map, k);
|
||||
}
|
||||
JS('', '#.push(#)', buckets, key);
|
||||
}
|
||||
V value = ifAbsent();
|
||||
JS('', '#.set(#, #)', _map, key, value);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
return value;
|
||||
}
|
||||
|
||||
V remove(Object key) {
|
||||
if (key is K) {
|
||||
var hash = JS('int', '# & 0x3ffffff', _hashCode(key));
|
||||
var keyMap = _keyMap;
|
||||
var buckets = JS('', '#.get(#)', keyMap, hash);
|
||||
if (buckets == null) return null; // not found
|
||||
var equals = _equals;
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (equals(k, key)) {
|
||||
if (n == 1) {
|
||||
JS('', '#.delete(#)', keyMap, hash);
|
||||
} else {
|
||||
JS('', '#.splice(#, 1)', buckets, i);
|
||||
}
|
||||
var map = _map;
|
||||
V value = JS('', '#.get(#)', map, k);
|
||||
JS('', '#.delete(#)', map, k);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
var map = _map;
|
||||
if (JS('int', '#.size', map) > 0) {
|
||||
JS('', '#.clear()', map);
|
||||
JS('', '#.clear()', _keyMap);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
String toString() => Maps.mapToString(this);
|
||||
}
|
||||
|
||||
typedef bool _Equality<K>(K a, K b);
|
||||
typedef int _Hasher<K>(K object);
|
||||
typedef bool _Predicate<T>(T value);
|
|
@ -350,6 +350,7 @@ void _installPropertiesForGlobalObject(jsProto) {
|
|||
_installPropertiesForObject(jsProto);
|
||||
// Use JS toString for JS objects, rather than the Dart one.
|
||||
JS('', '#[dartx.toString] = function() { return this.toString(); }', jsProto);
|
||||
identityEquals ??= JS('', '#[dartx._equals]', jsProto);
|
||||
}
|
||||
|
||||
final _extensionMap = JS('', 'new Map()');
|
||||
|
@ -522,3 +523,6 @@ final isStream = JS('', 'Symbol("_is_Stream")');
|
|||
|
||||
/// The well known symbol for testing `is StreamSubscription`
|
||||
final isStreamSubscription = JS('', 'Symbol("_is_StreamSubscription")');
|
||||
|
||||
/// The default `operator ==` that calls [identical].
|
||||
var identityEquals;
|
||||
|
|
|
@ -542,31 +542,6 @@ notNull(x) {
|
|||
return x;
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a dart:collection LinkedHashMap.
|
||||
///
|
||||
/// For a map with string keys an object literal can be used, for example
|
||||
/// `map({'hi': 1, 'there': 2})`.
|
||||
///
|
||||
/// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will
|
||||
/// create a map with keys [1, 3] and values [2, 4]. Each key-value pair
|
||||
/// should be adjacent entries in the array.
|
||||
///
|
||||
/// For a map with no keys the function can be called with no arguments, for
|
||||
/// example `map()`.
|
||||
///
|
||||
// TODO(jmesserly): this could be faster
|
||||
// TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed.
|
||||
// TODO(jmesserly): move this to classes for consistency with list literals?
|
||||
map<K, V>(JSArray elements) {
|
||||
var map = new JsLinkedHashMap<K, V>();
|
||||
if (elements == null) return map;
|
||||
for (var i = 0, end = elements.length - 1; i < end; i += 2) {
|
||||
map[JS('', '#[#]', elements, i)] = JS('', '#[#]', elements, i + 1);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The global constant map table.
|
||||
final constantMaps = JS('', 'new Map()');
|
||||
|
||||
|
@ -585,7 +560,7 @@ constMap<K, V>(JSArray elements) {
|
|||
map = lookupNonTerminal(map, K);
|
||||
var result = JS('', '#.get(#)', map, V);
|
||||
if (result != null) return result;
|
||||
result = new ImmutableMap<K, V>(elements);
|
||||
result = new ImmutableMap<K, V>.from(elements);
|
||||
JS('', '#.set(#, #)', map, V, result);
|
||||
return result;
|
||||
}
|
||||
|
@ -735,6 +710,8 @@ const_(obj) => JS('', '''(() => {
|
|||
// Right now we use the (name,value) pairs in sequence, which prevents
|
||||
// an object with incorrect field values being returned, but won't
|
||||
// canonicalize correctly if key order is different.
|
||||
//
|
||||
// See issue https://github.com/dart-lang/sdk/issues/30876
|
||||
for (let i = 0; i < count; i++) {
|
||||
let name = names[i];
|
||||
map = lookupNonTerminal(map, name);
|
||||
|
@ -799,6 +776,17 @@ int hashCode(obj) {
|
|||
return obj == null ? 0 : JS('int', '#[#]', obj, extensionSymbol('hashCode'));
|
||||
}
|
||||
|
||||
hashKey(k) {
|
||||
if (k == null) return 0;
|
||||
switch (JS('String', 'typeof #', k)) {
|
||||
case "object":
|
||||
case "function":
|
||||
return JS('int', '#[#] & 0x3ffffff', k, extensionSymbol('hashCode'));
|
||||
}
|
||||
// For primitive types we can store the key directly in an ES6 Map.
|
||||
return k;
|
||||
}
|
||||
|
||||
@JSExportName('toString')
|
||||
String _toString(obj) {
|
||||
if (obj == null) return "null";
|
||||
|
@ -834,6 +822,7 @@ String str(strings, @rest values) => JS('', '''(() => {
|
|||
|
||||
final identityHashCode_ = JS('', 'Symbol("_identityHashCode")');
|
||||
|
||||
/// Adapts a Dart `get iterator` into a JS `[Symbol.iterator]`.
|
||||
// TODO(jmesserly): instead of an adaptor, we could compile Dart iterators
|
||||
// natively implementing the JS iterator protocol. This would allow us to
|
||||
// optimize them a bit.
|
||||
|
|
131
pkg/dev_compiler/tool/input_sdk/private/identity_hash_map.dart
Normal file
131
pkg/dev_compiler/tool/input_sdk/private/identity_hash_map.dart
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
part of dart._js_helper;
|
||||
|
||||
class IdentityMap<K, V> extends InternalMap<K, V> {
|
||||
final _map = JS('', 'new Map()');
|
||||
|
||||
// We track the number of modifications done to the key set of the
|
||||
// hash map to be able to throw when the map is modified while being
|
||||
// iterated over.
|
||||
//
|
||||
// Value cycles after 2^30 modifications so that modification counts are
|
||||
// always unboxed (Smi) values. Modification detection will be missed if you
|
||||
// make exactly some multiple of 2^30 modifications between advances of an
|
||||
// iterator.
|
||||
@notNull
|
||||
int _modifications = 0;
|
||||
|
||||
IdentityMap();
|
||||
IdentityMap.from(JSArray entries) {
|
||||
var map = _map;
|
||||
for (int i = 0, n = JS('int', '#.length', entries); i < n; i += 2) {
|
||||
JS('', '#.set(#[#], #[#])', map, entries, i, entries, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int get length => JS('int', '#.size', _map);
|
||||
bool get isEmpty => JS('bool', '#.size == 0', _map);
|
||||
bool get isNotEmpty => JS('bool', '#.size != 0', _map);
|
||||
|
||||
Iterable<K> get keys => new _JSMapIterable<K>(this, true);
|
||||
Iterable<V> get values => new _JSMapIterable<V>(this, false);
|
||||
|
||||
bool containsKey(Object key) {
|
||||
return JS('bool', '#.has(#)', _map, key);
|
||||
}
|
||||
|
||||
bool containsValue(Object value) {
|
||||
for (var v in JS('', '#.values()', _map)) {
|
||||
if (v == value) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void addAll(Map<K, V> other) {
|
||||
if (other.isNotEmpty) {
|
||||
var map = _map;
|
||||
other.forEach((key, value) {
|
||||
JS('', '#.set(#, #)', map, key, value);
|
||||
});
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
V operator [](Object key) {
|
||||
return JS('', '#.get(#)', _map, key);
|
||||
}
|
||||
|
||||
void operator []=(K key, V value) {
|
||||
JS('', '#.set(#, #)', _map, key, value);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
|
||||
V putIfAbsent(K key, V ifAbsent()) {
|
||||
if (JS('bool', '#.has(#)', _map, key)) return JS('', '#.get(#)', _map, key);
|
||||
V value = ifAbsent();
|
||||
JS('', '#.set(#, #)', _map, key, value);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
return value;
|
||||
}
|
||||
|
||||
V remove(Object key) {
|
||||
V value = JS('', '#.get(#)', _map, key);
|
||||
if (JS('bool', '#.delete(#)', _map, key)) {
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (JS('int', '#.size', _map) > 0) {
|
||||
JS('', '#.clear()', _map);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
String toString() => Maps.mapToString(this);
|
||||
}
|
||||
|
||||
class _JSMapIterable<E> extends EfficientLengthIterable<E> {
|
||||
final InternalMap _map;
|
||||
@notNull
|
||||
final bool _isKeys;
|
||||
_JSMapIterable(this._map, this._isKeys);
|
||||
|
||||
int get length => _map.length;
|
||||
bool get isEmpty => _map.isEmpty;
|
||||
|
||||
@JSExportName('Symbol.iterator')
|
||||
_jsIterator() {
|
||||
var map = _map;
|
||||
var iterator =
|
||||
JS('', '# ? #.keys() : #.values()', _isKeys, map._map, map._map);
|
||||
int modifications = map._modifications;
|
||||
return JS(
|
||||
'',
|
||||
'''{
|
||||
next() {
|
||||
if (# != #) {
|
||||
throw #;
|
||||
}
|
||||
return #.next();
|
||||
}
|
||||
}''',
|
||||
modifications,
|
||||
map._modifications,
|
||||
new ConcurrentModificationError(map),
|
||||
iterator);
|
||||
}
|
||||
|
||||
Iterator<E> get iterator => new DartIterator<E>(_jsIterator());
|
||||
|
||||
bool contains(Object element) =>
|
||||
_isKeys ? _map.containsKey(element) : _map.containsValue(element);
|
||||
|
||||
void forEach(void f(E element)) {
|
||||
for (var entry in this) f(entry);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import 'dart:collection';
|
|||
|
||||
import 'dart:_debugger' show stackTraceMapper;
|
||||
|
||||
import 'dart:_foreign_helper' show JS, JS_STRING_CONCAT;
|
||||
import 'dart:_foreign_helper' show JS, JS_STRING_CONCAT, JSExportName;
|
||||
|
||||
import 'dart:_interceptors';
|
||||
import 'dart:_internal'
|
||||
|
@ -19,6 +19,8 @@ import 'dart:_runtime' as dart;
|
|||
|
||||
part 'annotations.dart';
|
||||
part 'linked_hash_map.dart';
|
||||
part 'identity_hash_map.dart';
|
||||
part 'custom_hash_map.dart';
|
||||
part 'native_helper.dart';
|
||||
part 'regexp_helper.dart';
|
||||
part 'string_helper.dart';
|
||||
|
@ -30,9 +32,22 @@ class _Patch {
|
|||
|
||||
const _Patch patch = const _Patch();
|
||||
|
||||
/// Marks the internal map in dart2js, so that internal libraries can is-check
|
||||
// them.
|
||||
abstract class InternalMap<K, V> implements Map<K, V> {}
|
||||
/// Adapts a JS `[Symbol.iterator]` to a Dart `get iterator`.
|
||||
///
|
||||
/// This is the inverse of `JsIterator`, for classes where we can more
|
||||
/// efficiently obtain a JS iterator instead of a Dart one.
|
||||
class DartIterator<E> implements Iterator<E> {
|
||||
DartIterator(jsIterator) {
|
||||
JS('', 'this._current = null');
|
||||
JS('', 'this._jsIterator = #', jsIterator);
|
||||
}
|
||||
E get current => JS('', 'this._current');
|
||||
bool moveNext() {
|
||||
var next = JS('', 'this._jsIterator.next()');
|
||||
JS('', 'this._current = #.value', next);
|
||||
return JS('bool', '!#.done', next);
|
||||
}
|
||||
}
|
||||
|
||||
class Primitives {
|
||||
/// Isolate-unique ID for caching [JsClosureMirror.function].
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
||||
// Copyright (c) 2017, 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.
|
||||
|
||||
|
@ -7,452 +7,264 @@
|
|||
|
||||
part of dart._js_helper;
|
||||
|
||||
// DDC-specific, just use Object-backed maps
|
||||
//const _USE_ES6_MAPS = const bool.fromEnvironment("dart2js.use.es6.maps");
|
||||
|
||||
class JsLinkedHashMap<K, V> implements LinkedHashMap<K, V>, InternalMap<K, V> {
|
||||
/// Our internal LinkedHashMaps.
|
||||
abstract class InternalMap<K, V> implements LinkedHashMap<K, V> {
|
||||
@notNull
|
||||
int _length = 0;
|
||||
get _map;
|
||||
|
||||
// The hash map contents are divided into three parts: one part for
|
||||
// string keys, one for numeric keys, and one for the rest. String
|
||||
// and numeric keys map directly to their linked cells, but the rest
|
||||
// of the entries are stored in bucket lists of the form:
|
||||
//
|
||||
// [cell-0, cell-1, ...]
|
||||
//
|
||||
// where all keys in the same bucket share the same hash code.
|
||||
var _strings;
|
||||
var _nums;
|
||||
var _rest;
|
||||
@notNull
|
||||
int get _modifications;
|
||||
|
||||
// The keys and values are stored in cells that are linked together
|
||||
// to form a double linked list.
|
||||
LinkedHashMapCell<K, V> _first;
|
||||
LinkedHashMapCell<K, V> _last;
|
||||
void forEach(void action(K key, V value)) {
|
||||
int modifications = _modifications;
|
||||
for (var entry in JS('Iterable', '#.entries()', _map)) {
|
||||
action(JS('', '#[0]', entry), JS('', '#[1]', entry));
|
||||
if (modifications != _modifications) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A linked hash map implementation based on ES6 Map.
|
||||
///
|
||||
/// Items that can use identity semantics are stored directly in the backing
|
||||
/// map.
|
||||
///
|
||||
/// Items that have a custom equality/hashCode are first canonicalized by
|
||||
/// looking up the canonical key by its hashCode.
|
||||
class LinkedMap<K, V> extends InternalMap<K, V> {
|
||||
/// The backing store for this map.
|
||||
///
|
||||
/// Keys that use identity equality are stored directly. For other types of
|
||||
/// keys, we first look them up (by hashCode) in the [_keyMap] map, then
|
||||
/// we lookup the key in this map.
|
||||
@notNull
|
||||
final _map = JS('', 'new Map()');
|
||||
|
||||
/// Items that use custom equality semantics.
|
||||
///
|
||||
/// This maps from the item's hashCode to the canonical key, which is then
|
||||
/// used to lookup the item in [_map]. Keeping the data in our primary backing
|
||||
/// map gives us the ordering semantics requred by [LinkedHashMap], while
|
||||
/// also providing convenient access to keys/values.
|
||||
@notNull
|
||||
final _keyMap = JS('', 'new Map()');
|
||||
|
||||
// We track the number of modifications done to the key set of the
|
||||
// hash map to be able to throw when the map is modified while being
|
||||
// iterated over.
|
||||
//
|
||||
// Value cycles after 2^30 modifications so that modification counts are
|
||||
// always unboxed (Smi) values. Modification detection will be missed if you
|
||||
// make exactly some multiple of 2^30 modifications between advances of an
|
||||
// iterator.
|
||||
@notNull
|
||||
int _modifications = 0;
|
||||
|
||||
// static bool get _supportsEs6Maps {
|
||||
// return JS('returns:bool;depends:none;effects:none;throws:never;gvn:true',
|
||||
// 'typeof Map != "undefined"');
|
||||
// }
|
||||
LinkedMap();
|
||||
|
||||
JsLinkedHashMap();
|
||||
|
||||
/// If ES6 Maps are available returns a linked hash-map backed by an ES6 Map.
|
||||
// @ForceInline()
|
||||
factory JsLinkedHashMap.es6() {
|
||||
// return (_USE_ES6_MAPS && JsLinkedHashMap._supportsEs6Maps)
|
||||
// ? new Es6LinkedHashMap<K, V>()
|
||||
// : new JsLinkedHashMap<K, V>();
|
||||
return new JsLinkedHashMap<K, V>();
|
||||
/// Called by generated code for a map literal.
|
||||
LinkedMap.from(JSArray entries) {
|
||||
var map = _map;
|
||||
var keyMap = _keyMap;
|
||||
for (int i = 0, n = JS('int', '#.length', entries); i < n; i += 2) {
|
||||
K key = JS('', '#[#]', entries, i);
|
||||
V value = JS('', '#[#]', entries, i + 1);
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
key = putLinkedMapKey(key, keyMap);
|
||||
}
|
||||
JS('', '#.set(#, #)', map, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@notNull
|
||||
int get length => _length;
|
||||
@notNull
|
||||
bool get isEmpty => _length == 0;
|
||||
@notNull
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
int get length => JS('int', '#.size', _map);
|
||||
|
||||
Iterable<K> get keys {
|
||||
return new LinkedHashMapKeyIterable<K>(this);
|
||||
}
|
||||
@notNull
|
||||
bool get isEmpty => JS('bool', '#.size == 0', _map);
|
||||
|
||||
Iterable<V> get values {
|
||||
return new MappedIterable<K, V>(keys, (each) => this[each]);
|
||||
}
|
||||
@notNull
|
||||
bool get isNotEmpty => JS('bool', '#.size != 0', _map);
|
||||
|
||||
Iterable<K> get keys => new _JSMapIterable<K>(this, true);
|
||||
Iterable<V> get values => new _JSMapIterable<V>(this, false);
|
||||
|
||||
@notNull
|
||||
bool containsKey(Object key) {
|
||||
if (_isStringKey(key)) {
|
||||
var strings = _strings;
|
||||
if (strings == null) return false;
|
||||
return _containsTableEntry(strings, key);
|
||||
} else if (_isNumericKey(key)) {
|
||||
var nums = _nums;
|
||||
if (nums == null) return false;
|
||||
return _containsTableEntry(nums, key);
|
||||
} else {
|
||||
return internalContainsKey(key);
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
@notNull
|
||||
var k = key;
|
||||
var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, k.hashCode);
|
||||
if (buckets != null) {
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
k = JS('', '#[#]', buckets, i);
|
||||
if (k == key) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@notNull
|
||||
bool internalContainsKey(Object key) {
|
||||
var rest = _rest;
|
||||
if (rest == null) return false;
|
||||
var bucket = _getBucket(rest, key);
|
||||
return internalFindBucketIndex(bucket, key) >= 0;
|
||||
return JS('bool', '#.has(#)', _map, key);
|
||||
}
|
||||
|
||||
bool containsValue(Object value) {
|
||||
return keys.any((each) => this[each] == value);
|
||||
for (var v in JS('', '#.values()', _map)) {
|
||||
if (v == value) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void addAll(Map<K, V> other) {
|
||||
var map = _map;
|
||||
int length = JS('', '#.size', map);
|
||||
other.forEach((K key, V value) {
|
||||
this[key] = value;
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
key = putLinkedMapKey(key, _keyMap);
|
||||
}
|
||||
JS('', '#.set(#, #)', _map, key, value);
|
||||
});
|
||||
if (length != JS('int', '#.size', map)) {
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
V operator [](Object key) {
|
||||
if (_isStringKey(key)) {
|
||||
var strings = _strings;
|
||||
if (strings == null) return null;
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _getTableCell(strings, key);
|
||||
return (cell == null) ? null : cell.hashMapCellValue;
|
||||
} else if (_isNumericKey(key)) {
|
||||
var nums = _nums;
|
||||
if (nums == null) return null;
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _getTableCell(nums, key);
|
||||
return (cell == null) ? null : cell.hashMapCellValue;
|
||||
} else {
|
||||
return internalGet(key);
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
@notNull
|
||||
var k = key;
|
||||
var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, k.hashCode);
|
||||
if (buckets != null) {
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
k = JS('', '#[#]', buckets, i);
|
||||
if (k == key) return JS('', '#.get(#)', _map, k);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
V internalGet(Object key) {
|
||||
var rest = _rest;
|
||||
if (rest == null) return null;
|
||||
var bucket = _getBucket(rest, key);
|
||||
int index = internalFindBucketIndex(bucket, key);
|
||||
if (index < 0) return null;
|
||||
LinkedHashMapCell/*<K, V>*/ cell = JS('var', '#[#]', bucket, index);
|
||||
return cell.hashMapCellValue;
|
||||
return JS('', '#.get(#)', _map, key);
|
||||
}
|
||||
|
||||
void operator []=(K key, V value) {
|
||||
if (_isStringKey(key)) {
|
||||
var strings = _strings;
|
||||
if (strings == null) _strings = strings = _newHashTable();
|
||||
_addHashTableEntry(strings, key, value);
|
||||
} else if (_isNumericKey(key)) {
|
||||
var nums = _nums;
|
||||
if (nums == null) _nums = nums = _newHashTable();
|
||||
_addHashTableEntry(nums, key, value);
|
||||
} else {
|
||||
internalSet(key, value);
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
key = putLinkedMapKey(key, _keyMap);
|
||||
}
|
||||
}
|
||||
|
||||
void internalSet(K key, V value) {
|
||||
var rest = _rest;
|
||||
if (rest == null) _rest = rest = _newHashTable();
|
||||
var hash = internalComputeHashCode(key);
|
||||
var bucket = _getTableBucket(rest, hash);
|
||||
if (bucket == null) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _newLinkedCell(key, value);
|
||||
_setTableEntry(rest, hash, JS('var', '[#]', cell));
|
||||
} else {
|
||||
int index = internalFindBucketIndex(bucket, key);
|
||||
if (index >= 0) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = JS('var', '#[#]', bucket, index);
|
||||
cell.hashMapCellValue = value;
|
||||
} else {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _newLinkedCell(key, value);
|
||||
JS('void', '#.push(#)', bucket, cell);
|
||||
}
|
||||
var map = _map;
|
||||
int length = JS('', '#.size', map);
|
||||
JS('', '#.set(#, #)', map, key, value);
|
||||
// TODO(jmesserly): can we remove this? Identity maps do not try to be as
|
||||
// precise. We do pay a performance cost to support it.
|
||||
if (length != JS('int', '#.size', map)) {
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
V putIfAbsent(K key, V ifAbsent()) {
|
||||
if (containsKey(key)) return this[key];
|
||||
var map = _map;
|
||||
if (key == null) {
|
||||
key = null;
|
||||
if (JS('bool', '#.has(null)', map)) return JS('', '#.get(null)', map);
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
@notNull
|
||||
K k = key;
|
||||
var hash = JS('int', '# & 0x3ffffff', k.hashCode);
|
||||
var buckets = JS('', '#.get(#)', _keyMap, hash);
|
||||
if (buckets == null) {
|
||||
JS('', '#.set(#, [#])', _keyMap, hash, key);
|
||||
} else {
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
k = JS('', '#[#]', buckets, i);
|
||||
if (k == key) return JS('', '#.get(#)', map, k);
|
||||
}
|
||||
JS('', '#.push(#)', buckets, key);
|
||||
}
|
||||
} else if (JS('bool', '#.has(#)', map, key)) {
|
||||
return JS('', '#.get(#)', map, key);
|
||||
}
|
||||
V value = ifAbsent();
|
||||
this[key] = value;
|
||||
JS('', '#.set(#, #)', map, key, value);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
return value;
|
||||
}
|
||||
|
||||
V remove(Object key) {
|
||||
if (_isStringKey(key)) {
|
||||
return _removeHashTableEntry(_strings, key);
|
||||
} else if (_isNumericKey(key)) {
|
||||
return _removeHashTableEntry(_nums, key);
|
||||
} else {
|
||||
return internalRemove(key);
|
||||
if (key == null) {
|
||||
key = null;
|
||||
} else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'),
|
||||
dart.identityEquals)) {
|
||||
@notNull
|
||||
var k = key;
|
||||
var hash = JS('int', '# & 0x3ffffff', k.hashCode);
|
||||
var buckets = JS('', '#.get(#)', _keyMap, hash);
|
||||
if (buckets == null) return null; // not found
|
||||
for (int i = 0, n = JS('int', '#.length', buckets);;) {
|
||||
k = JS('', '#[#]', buckets, i);
|
||||
if (k == key) {
|
||||
key = k;
|
||||
if (n == 1) {
|
||||
JS('', '#.delete(#)', _keyMap, hash);
|
||||
} else {
|
||||
JS('', '#.splice(#, 1)', buckets, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (++i >= n) return null; // not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
V internalRemove(Object key) {
|
||||
var rest = _rest;
|
||||
if (rest == null) return null;
|
||||
var bucket = _getBucket(rest, key);
|
||||
int index = internalFindBucketIndex(bucket, key);
|
||||
if (index < 0) return null;
|
||||
// Use splice to remove the [cell] element at the index and
|
||||
// unlink the cell before returning its value.
|
||||
LinkedHashMapCell/*<K, V>*/ cell =
|
||||
JS('var', '#.splice(#, 1)[0]', bucket, index);
|
||||
_unlinkCell(cell);
|
||||
// TODO(kasperl): Consider getting rid of the bucket list when
|
||||
// the length reaches zero.
|
||||
return cell.hashMapCellValue;
|
||||
var map = _map;
|
||||
V value = JS('', '#.get(#)', map, key);
|
||||
if (JS('bool', '#.delete(#)', map, key)) {
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (_length > 0) {
|
||||
_strings = _nums = _rest = _first = _last = null;
|
||||
_length = 0;
|
||||
_modified();
|
||||
var map = _map;
|
||||
if (JS('int', '#.size', map) > 0) {
|
||||
JS('', '#.clear()', map);
|
||||
JS('', '#.clear()', _keyMap);
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
void forEach(void action(K key, V value)) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _first;
|
||||
int modifications = _modifications;
|
||||
while (cell != null) {
|
||||
action(cell.hashMapCellKey, cell.hashMapCellValue);
|
||||
if (modifications != _modifications) {
|
||||
throw new ConcurrentModificationError(this);
|
||||
}
|
||||
cell = cell._next;
|
||||
}
|
||||
}
|
||||
|
||||
void _addHashTableEntry(var table, K key, V value) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _getTableCell(table, key);
|
||||
if (cell == null) {
|
||||
_setTableEntry(table, key, _newLinkedCell(key, value));
|
||||
} else {
|
||||
cell.hashMapCellValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
V _removeHashTableEntry(var table, Object key) {
|
||||
if (table == null) return null;
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _getTableCell(table, key);
|
||||
if (cell == null) return null;
|
||||
_unlinkCell(cell);
|
||||
_deleteTableEntry(table, key);
|
||||
return cell.hashMapCellValue;
|
||||
}
|
||||
|
||||
void _modified() {
|
||||
// Value cycles after 2^30 modifications so that modification counts are
|
||||
// always unboxed (Smi) values. Modification detection will be missed if you
|
||||
// make exactly some multiple of 2^30 modifications between advances of an
|
||||
// iterator.
|
||||
_modifications = (_modifications + 1) & 0x3ffffff;
|
||||
}
|
||||
|
||||
// Create a new cell and link it in as the last one in the list.
|
||||
LinkedHashMapCell/*<K, V>*/ _newLinkedCell(K key, V value) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell =
|
||||
new LinkedHashMapCell/*<K, V>*/(key, value);
|
||||
if (_first == null) {
|
||||
_first = _last = cell;
|
||||
} else {
|
||||
LinkedHashMapCell/*<K, V>*/ last = _last;
|
||||
cell._previous = last;
|
||||
_last = last._next = cell;
|
||||
}
|
||||
_length++;
|
||||
_modified();
|
||||
return cell;
|
||||
}
|
||||
|
||||
// Unlink the given cell from the linked list of cells.
|
||||
void _unlinkCell(LinkedHashMapCell/*<K, V>*/ cell) {
|
||||
LinkedHashMapCell/*<K, V>*/ previous = cell._previous;
|
||||
LinkedHashMapCell/*<K, V>*/ next = cell._next;
|
||||
if (previous == null) {
|
||||
assert(cell == _first);
|
||||
_first = next;
|
||||
} else {
|
||||
previous._next = next;
|
||||
}
|
||||
if (next == null) {
|
||||
assert(cell == _last);
|
||||
_last = previous;
|
||||
} else {
|
||||
next._previous = previous;
|
||||
}
|
||||
_length--;
|
||||
_modified();
|
||||
}
|
||||
|
||||
@notNull
|
||||
static bool _isStringKey(var key) {
|
||||
return key is String;
|
||||
}
|
||||
|
||||
@notNull
|
||||
static bool _isNumericKey(var key) {
|
||||
// Only treat unsigned 30-bit integers as numeric keys. This way,
|
||||
// we avoid converting them to strings when we use them as keys in
|
||||
// the JavaScript hash table object.
|
||||
return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key);
|
||||
}
|
||||
|
||||
int internalComputeHashCode(var key) {
|
||||
// We force the hash codes to be unsigned 30-bit integers to avoid
|
||||
// issues with problematic keys like '__proto__'. Another option
|
||||
// would be to throw an exception if the hash code isn't a number.
|
||||
return JS('int', '# & 0x3ffffff', key.hashCode);
|
||||
}
|
||||
|
||||
List<dynamic/*=LinkedHashMapCell<K, V>*/ > _getBucket(var table, var key) {
|
||||
var hash = internalComputeHashCode(key);
|
||||
return _getTableBucket(table, hash);
|
||||
}
|
||||
|
||||
@notNull
|
||||
int internalFindBucketIndex(var bucket, var key) {
|
||||
if (bucket == null) return -1;
|
||||
int length = JS('int', '#.length', bucket);
|
||||
for (int i = 0; i < length; i++) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = JS('var', '#[#]', bucket, i);
|
||||
if (cell.hashMapCellKey == key) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
String toString() => Maps.mapToString(this);
|
||||
|
||||
/*=LinkedHashMapCell<K, V>*/ _getTableCell(var table, var key) {
|
||||
return JS('var', '#[#]', table, key);
|
||||
}
|
||||
|
||||
/*=List<LinkedHashMapCell<K, V>>*/ _getTableBucket(var table, var key) {
|
||||
return JS('var', '#[#]', table, key);
|
||||
}
|
||||
|
||||
void _setTableEntry(var table, var key, var value) {
|
||||
assert(value != null);
|
||||
JS('void', '#[#] = #', table, key, value);
|
||||
}
|
||||
|
||||
void _deleteTableEntry(var table, var key) {
|
||||
JS('void', 'delete #[#]', table, key);
|
||||
}
|
||||
|
||||
@notNull
|
||||
bool _containsTableEntry(var table, var key) {
|
||||
LinkedHashMapCell/*<K, V>*/ cell = _getTableCell(table, key);
|
||||
return cell != null;
|
||||
}
|
||||
|
||||
_newHashTable() {
|
||||
// Create a new JavaScript object to be used as a hash table. Use
|
||||
// Object.create to avoid the properties on Object.prototype
|
||||
// showing up as entries.
|
||||
var table = JS('var', 'Object.create(null)');
|
||||
// Attempt to force the hash table into 'dictionary' mode by
|
||||
// adding a property to it and deleting it again.
|
||||
var temporaryKey = '<non-identifier-key>';
|
||||
_setTableEntry(table, temporaryKey, table);
|
||||
_deleteTableEntry(table, temporaryKey);
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
class Es6LinkedHashMap<K, V> extends JsLinkedHashMap<K, V> {
|
||||
@override
|
||||
/*=LinkedHashMapCell<K, V>*/ _getTableCell(var table, var key) {
|
||||
return JS('var', '#.get(#)', table, key);
|
||||
@NoReifyGeneric()
|
||||
K putLinkedMapKey<K>(@notNull K key, keyMap) {
|
||||
var hash = JS('int', '# & 0x3ffffff', key.hashCode);
|
||||
var buckets = JS('', '#.get(#)', keyMap, hash);
|
||||
if (buckets == null) {
|
||||
JS('', '#.set(#, [#])', keyMap, hash, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@override
|
||||
/*=List<LinkedHashMapCell<K, V>>*/ _getTableBucket(var table, var key) {
|
||||
return JS('var', '#.get(#)', table, key);
|
||||
}
|
||||
|
||||
@override
|
||||
void _setTableEntry(var table, var key, var value) {
|
||||
JS('void', '#.set(#, #)', table, key, value);
|
||||
}
|
||||
|
||||
@override
|
||||
void _deleteTableEntry(var table, var key) {
|
||||
JS('void', '#.delete(#)', table, key);
|
||||
}
|
||||
|
||||
@override
|
||||
bool _containsTableEntry(var table, var key) {
|
||||
return JS('bool', '#.has(#)', table, key);
|
||||
}
|
||||
|
||||
@override
|
||||
_newHashTable() {
|
||||
return JS('var', 'new Map()');
|
||||
for (int i = 0, n = JS('int', '#.length', buckets); i < n; i++) {
|
||||
@notNull
|
||||
K k = JS('', '#[#]', buckets, i);
|
||||
if (k == key) return k;
|
||||
}
|
||||
JS('', '#.push(#)', buckets, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
class LinkedHashMapCell<K, V> {
|
||||
final dynamic/*=K*/ hashMapCellKey;
|
||||
dynamic/*=V*/ hashMapCellValue;
|
||||
|
||||
LinkedHashMapCell/*<K, V>*/ _next;
|
||||
LinkedHashMapCell/*<K, V>*/ _previous;
|
||||
|
||||
LinkedHashMapCell(this.hashMapCellKey, this.hashMapCellValue);
|
||||
}
|
||||
|
||||
class LinkedHashMapKeyIterable<E> extends EfficientLengthIterable<E> {
|
||||
final dynamic/*=JsLinkedHashMap<E, dynamic>*/ _map;
|
||||
LinkedHashMapKeyIterable(this._map);
|
||||
|
||||
int get length => _map._length;
|
||||
bool get isEmpty => _map._length == 0;
|
||||
|
||||
Iterator<E> get iterator {
|
||||
return new LinkedHashMapKeyIterator<E>(_map, _map._modifications);
|
||||
}
|
||||
|
||||
bool contains(Object element) {
|
||||
return _map.containsKey(element);
|
||||
}
|
||||
|
||||
void forEach(void f(E element)) {
|
||||
LinkedHashMapCell/*<E, dynamic>*/ cell = _map._first;
|
||||
int modifications = _map._modifications;
|
||||
while (cell != null) {
|
||||
f(cell.hashMapCellKey);
|
||||
if (modifications != _map._modifications) {
|
||||
throw new ConcurrentModificationError(_map);
|
||||
}
|
||||
cell = cell._next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LinkedHashMapKeyIterator<E> implements Iterator<E> {
|
||||
final dynamic/*=JsLinkedHashMap<E, dynamic>*/ _map;
|
||||
final int _modifications;
|
||||
LinkedHashMapCell/*<E, dynamic>*/ _cell;
|
||||
E _current;
|
||||
|
||||
LinkedHashMapKeyIterator(this._map, this._modifications) {
|
||||
_cell = _map._first;
|
||||
}
|
||||
|
||||
E get current => _current;
|
||||
|
||||
bool moveNext() {
|
||||
if (_modifications != _map._modifications) {
|
||||
throw new ConcurrentModificationError(_map);
|
||||
} else if (_cell == null) {
|
||||
_current = null;
|
||||
return false;
|
||||
} else {
|
||||
_current = _cell.hashMapCellKey;
|
||||
_cell = _cell._next;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImmutableMap<K, V> extends JsLinkedHashMap<K, V> {
|
||||
ImmutableMap(JSArray elements) {
|
||||
if (elements == null) return;
|
||||
for (var i = 0, end = elements.length - 1; i < end; i += 2) {
|
||||
super[JS('', '#[#]', elements, i)] = JS('', '#[#]', elements, i + 1);
|
||||
}
|
||||
}
|
||||
class ImmutableMap<K, V> extends LinkedMap<K, V> {
|
||||
ImmutableMap.from(JSArray entries) : super.from(entries);
|
||||
|
||||
void operator []=(Object key, Object value) {
|
||||
throw _unsupported();
|
||||
|
|
|
@ -22,6 +22,9 @@ string_static_test: MissingCompileTimeError
|
|||
[ !$checked && $compiler != dartdevc && $runtime != none ]
|
||||
null_nosuchmethod_test: RuntimeError # needs Dart 2 or checked mode
|
||||
|
||||
[ $compiler != dartdevc && $runtime != none ]
|
||||
map_keys2_test: RuntimeError # needs Dart 2 is checks
|
||||
|
||||
[ (!$checked && $runtime == vm) || (!$checked && $compiler == dart2js) || $compiler == precompiler ]
|
||||
int_parse_radix_test/badTypes: RuntimeError # wrong exception returned
|
||||
|
||||
|
@ -105,7 +108,6 @@ iterable_to_list_test/*: RuntimeError
|
|||
list_concurrent_modify_test: RuntimeError # DDC uses ES6 array iterators so it does not issue this
|
||||
nan_infinity_test/01: RuntimeError # Issue 29921
|
||||
main_test: RuntimeError # Issue 29921
|
||||
map_keys2_test: RuntimeError # Issue 29921
|
||||
null_nosuchmethod_test/01: RuntimeError # DDC checks type before too many arguments, so TypeError instead of NSM
|
||||
regexp/bol-with-multiline_test: RuntimeError # Issue 29921
|
||||
regexp/invalid-range-in-class_test: RuntimeError # Issue 29921
|
||||
|
@ -364,7 +366,6 @@ big_integer_parsed_mul_div_vm_test: Pass, Timeout # --no_intrinsify
|
|||
string_trimlr_test/02: RuntimeError # Issue 29060
|
||||
iterable_to_set_test: RuntimeError # is-checks do not implement strong mode type system
|
||||
|
||||
|
||||
[ $compiler == precompiler || $compiler == app_jit ]
|
||||
string_trimlr_test/02: RuntimeError # Issue 29060
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ testWithHashMap() {
|
|||
var otherMap = new HashMap.from(map);
|
||||
Expect.isTrue(otherMap is Map);
|
||||
Expect.isTrue(otherMap is HashMap);
|
||||
Expect.isTrue(otherMap is! LinkedHashMap);
|
||||
var i = 1;
|
||||
for (var val in map.values) {
|
||||
Expect.equals(i++, val);
|
||||
|
|
|
@ -15,16 +15,16 @@ main() {
|
|||
var map6 = new Map<String, bool>();
|
||||
|
||||
Expect.isTrue(map1.keys is Iterable<String>);
|
||||
Expect.isTrue(map1.keys is Iterable<bool>);
|
||||
Expect.isFalse(map1.keys is Iterable<bool>);
|
||||
|
||||
Expect.isTrue(map2.keys is Iterable<String>);
|
||||
Expect.isTrue(map2.keys is Iterable<bool>);
|
||||
Expect.isFalse(map2.keys is Iterable<String>);
|
||||
Expect.isFalse(map2.keys is Iterable<bool>);
|
||||
|
||||
Expect.isTrue(map3.keys is Iterable<String>);
|
||||
Expect.isTrue(map3.keys is Iterable<bool>);
|
||||
Expect.isFalse(map3.keys is Iterable<bool>);
|
||||
|
||||
Expect.isTrue(map4.keys is Iterable<String>);
|
||||
Expect.isTrue(map4.keys is Iterable<bool>);
|
||||
Expect.isFalse(map4.keys is Iterable<String>);
|
||||
Expect.isFalse(map4.keys is Iterable<bool>);
|
||||
|
||||
Expect.isTrue(map5.keys is Iterable<String>);
|
||||
Expect.isFalse(map5.keys is Iterable<bool>);
|
||||
|
|
|
@ -122,6 +122,19 @@ void main() {
|
|||
testUnmodifiableMap(new UnmodifiableMapView({1: 37}));
|
||||
testUnmodifiableMap(new UnmodifiableMapBaseMap([1, 37]));
|
||||
|
||||
testTypeAnnotations(new HashMap());
|
||||
testTypeAnnotations(new LinkedHashMap());
|
||||
testTypeAnnotations(new HashMap(equals: identical));
|
||||
testTypeAnnotations(new LinkedHashMap(equals: identical));
|
||||
testTypeAnnotations(new HashMap(
|
||||
equals: (int a, int b) => a == b,
|
||||
hashCode: (int a) => a.hashCode,
|
||||
isValidKey: (a) => a is int));
|
||||
testTypeAnnotations(new LinkedHashMap(
|
||||
equals: (int a, int b) => a == b,
|
||||
hashCode: (int a) => a.hashCode,
|
||||
isValidKey: (a) => a is int));
|
||||
|
||||
testFrom();
|
||||
}
|
||||
|
||||
|
@ -501,9 +514,12 @@ void testNumericKeys(Map map) {
|
|||
}
|
||||
|
||||
void testNaNKeys(Map map) {
|
||||
Object nan = double.NAN;
|
||||
// Skip this test on platforms that use native-JS NaN semantics for speed.
|
||||
if (!identical(nan, nan)) return;
|
||||
|
||||
Expect.isTrue(map.isEmpty);
|
||||
// Test NaN.
|
||||
var nan = double.NAN;
|
||||
Expect.isFalse(map.containsKey(nan));
|
||||
Expect.equals(null, map[nan]);
|
||||
|
||||
|
@ -993,3 +1009,20 @@ void testFrom() {
|
|||
expectMap(superMap, new SplayTreeMap<Super, Super>.from(interfaceMap));
|
||||
expectMap(superMap, new SplayTreeMap<Interface, Interface>.from(superMap));
|
||||
}
|
||||
|
||||
void testTypeAnnotations(Map<int, int> map) {
|
||||
map[0] = 100;
|
||||
map[999] = 101;
|
||||
map[0x800000000] = 102;
|
||||
map[0x20000000000000] = 103;
|
||||
Expect.isFalse(map.containsKey("not an it"));
|
||||
Expect.isNull(map.remove("not an it"));
|
||||
|
||||
testLength(4, map);
|
||||
Expect.equals(101, map.remove(999));
|
||||
testLength(3, map);
|
||||
Expect.equals(102, map.remove(0x800000000));
|
||||
testLength(2, map);
|
||||
Expect.equals(103, map.remove(0x20000000000000));
|
||||
testLength(1, map);
|
||||
}
|
||||
|
|
|
@ -178,8 +178,8 @@ branch_canonicalization_test: RuntimeError # Issue 29920
|
|||
call_closurization_test: RuntimeError # Issue 29920
|
||||
call_test: RuntimeError
|
||||
canonical_const2_test: RuntimeError # Ints and doubles are unified.
|
||||
compile_time_constant_d_test: RuntimeError # Issue 29920
|
||||
compile_time_constant_e_test: RuntimeError # Issue 30466
|
||||
compile_time_constant_d_test: RuntimeError # Issue 30876
|
||||
compile_time_constant_e_test: RuntimeError # Issue 30876
|
||||
const_switch_test/02: RuntimeError # Issue 29920
|
||||
const_switch_test/04: RuntimeError # Ints and doubles are unified.
|
||||
constructor12_test: RuntimeError # Issue 29920
|
||||
|
|
|
@ -8,9 +8,9 @@ import 'dart:async';
|
|||
|
||||
main() {
|
||||
// Unique object.
|
||||
var baz = new Object();
|
||||
var baz = new Mimic(new Object());
|
||||
// Not so unique object that thinks it's the same as baz.
|
||||
var mimic = new Mimic(baz);
|
||||
var mimic = new Mimic(baz.original);
|
||||
|
||||
// runGuarded calls run, captures the synchronous error (if any) and
|
||||
// gives that one to handleUncaughtError.
|
||||
|
@ -44,7 +44,7 @@ main() {
|
|||
Expect.equals(499, forked[#foo]);
|
||||
Expect.listEquals([], forked["bar"]);
|
||||
Expect.equals("baz", forked[baz]);
|
||||
Expect.isNull(Zone.current[mimic]);
|
||||
Expect.equals("baz", forked[mimic]);
|
||||
Expect.equals("zero!", forked[0]);
|
||||
Expect.equals("zero!", forked[0.0]); // Lookup uses equality.
|
||||
Expect.equals("zero!", forked[-0.0]);
|
||||
|
@ -57,7 +57,7 @@ main() {
|
|||
Expect.equals(499, Zone.current[#foo]);
|
||||
Expect.listEquals([], Zone.current["bar"]);
|
||||
Expect.equals("baz", Zone.current[baz]);
|
||||
Expect.isNull(Zone.current[mimic]);
|
||||
Expect.equals("baz", Zone.current[mimic]);
|
||||
Expect.equals("zero!", Zone.current[0]);
|
||||
Expect.equals("zero!", Zone.current[0.0]); // Lookup uses equality.
|
||||
Expect.equals("zero!", Zone.current[-0.0]);
|
||||
|
@ -84,7 +84,7 @@ main() {
|
|||
Expect.equals(499, Zone.current[#foo]);
|
||||
Expect.listEquals([42], Zone.current["bar"]);
|
||||
Expect.equals("baz", Zone.current[baz]);
|
||||
Expect.isNull(Zone.current[mimic]);
|
||||
Expect.equals("baz", Zone.current[mimic]);
|
||||
Expect.equals("zero!", Zone.current[0]);
|
||||
Expect.equals(baz, Zone.current[null]);
|
||||
Expect.isNull(Zone.current["qux"]);
|
||||
|
@ -129,7 +129,7 @@ main() {
|
|||
Expect.equals(499, forked[#foo]);
|
||||
Expect.listEquals([42], forked["bar"]);
|
||||
Expect.equals("baz", forked[baz]);
|
||||
Expect.isNull(Zone.current[mimic]);
|
||||
Expect.equals("baz", forked[mimic]);
|
||||
Expect.equals("zero!", forked[0]);
|
||||
Expect.equals("zero!", forked[0.0]); // Lookup uses equality.
|
||||
Expect.equals("zero!", forked[-0.0]);
|
||||
|
|
Loading…
Reference in a new issue