Specialized JavaScript map for num & string keys (+ generic dart.map)
diff --git a/lib/runtime/dart/_operations.js b/lib/runtime/dart/_operations.js
index 082a0f4..7dd6af2 100644
--- a/lib/runtime/dart/_operations.js
+++ b/lib/runtime/dart/_operations.js
@@ -275,8 +275,11 @@
* example `map()`.
*/
// TODO(jmesserly): this could be faster
- function map(values) {
- let map = collection.LinkedHashMap.new();
+ function map(values, keyType, valueType) {
+ let ctor = (keyType && valueType)
+ ? core.Map$(keyType, valueType)
+ : core.Map;
+ let map = ctor.new();
if (Array.isArray(values)) {
for (let i = 0, end = values.length - 1; i < end; i += 2) {
let key = values[i];
diff --git a/lib/runtime/dart/convert.js b/lib/runtime/dart/convert.js
index a535d9b..fd9c50e 100644
--- a/lib/runtime/dart/convert.js
+++ b/lib/runtime/dart/convert.js
@@ -644,7 +644,7 @@
let _FusedConverter = _FusedConverter$();
dart.defineLazyProperties(Encoding, {
get _nameToEncoding() {
- return dart.map({"iso_8859-1:1987": LATIN1, "iso-ir-100": LATIN1, "iso_8859-1": LATIN1, "iso-8859-1": LATIN1, latin1: LATIN1, l1: LATIN1, ibm819: LATIN1, cp819: LATIN1, csisolatin1: LATIN1, "iso-ir-6": ASCII, "ansi_x3.4-1968": ASCII, "ansi_x3.4-1986": ASCII, "iso_646.irv:1991": ASCII, "iso646-us": ASCII, "us-ascii": ASCII, us: ASCII, ibm367: ASCII, cp367: ASCII, csascii: ASCII, ascii: ASCII, csutf8: UTF8, "utf-8": UTF8});
+ return dart.map({"iso_8859-1:1987": LATIN1, "iso-ir-100": LATIN1, "iso_8859-1": LATIN1, "iso-8859-1": LATIN1, latin1: LATIN1, l1: LATIN1, ibm819: LATIN1, cp819: LATIN1, csisolatin1: LATIN1, "iso-ir-6": ASCII, "ansi_x3.4-1968": ASCII, "ansi_x3.4-1986": ASCII, "iso_646.irv:1991": ASCII, "iso646-us": ASCII, "us-ascii": ASCII, us: ASCII, ibm367: ASCII, cp367: ASCII, csascii: ASCII, ascii: ASCII, csutf8: UTF8, "utf-8": UTF8}, core.String, Encoding);
},
set _nameToEncoding(_) {}
});
diff --git a/lib/runtime/dart/js.js b/lib/runtime/dart/js.js
index db2f65e..48a06e1 100644
--- a/lib/runtime/dart/js.js
+++ b/lib/runtime/dart/js.js
@@ -116,7 +116,7 @@
args = core.List.from(args[dartx.map](_convertToJS));
let fn = this[_jsObject][method];
if (!(fn instanceof Function)) {
- dart.throw(new core.NoSuchMethodError(this[_jsObject], core.Symbol.new(dart.as(method, core.String)), args, dart.map()));
+ dart.throw(new core.NoSuchMethodError(this[_jsObject], core.Symbol.new(dart.as(method, core.String)), args, dart.map([], core.Symbol, dart.dynamic)));
}
return _convertToDart(fn.apply(this[_jsObject], args));
}
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
index 0ff3baa..bd3542d 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -3216,10 +3216,11 @@
// TODO(jmesserly): we can likely make these faster.
JS.Expression emitMap() {
var entries = node.entries;
+ if (entries.isEmpty && node.typeArguments == null) {
+ return js.call('dart.map()');
+ }
var mapArguments = null;
- if (entries.isEmpty) {
- mapArguments = [];
- } else if (entries.every((e) => e.key is StringLiteral)) {
+ if (entries.isNotEmpty && entries.every((e) => e.key is StringLiteral)) {
// Use JS object literal notation if possible, otherwise use an array.
// We could do this any time all keys are non-nullable String type.
// For now, support StringLiteral as the common non-nullable String case.
@@ -3236,8 +3237,14 @@
}
mapArguments = new JS.ArrayInitializer(values);
}
- // TODO(jmesserly): add generic types args.
- return js.call('dart.map(#)', [mapArguments]);
+ var args = [mapArguments];
+ if (node.typeArguments == null) {
+ return js.call('dart.map(#)', args);
+ } else {
+ args.addAll(node.typeArguments.arguments.map((a) => _emitTypeName(a.type)));
+ return js.call('dart.map(#, #, #)', args);
+ }
+
}
if (node.constKeyword != null) return _emitConst(emitMap);
return emitMap();
diff --git a/tool/input_sdk/lib/core/core.dart b/tool/input_sdk/lib/core/core.dart
index 4b11edc..4cef7f9 100644
--- a/tool/input_sdk/lib/core/core.dart
+++ b/tool/input_sdk/lib/core/core.dart
@@ -157,6 +157,7 @@
import "dart:_internal" as internal show Symbol;
import "dart:convert" show UTF8, LATIN1, Encoding;
import "dart:math" show Random; // Used by List.shuffle.
+import 'dart:_foreign_helper' show JS;
part "annotations.dart";
part "bool.dart";
diff --git a/tool/input_sdk/lib/core/map.dart b/tool/input_sdk/lib/core/map.dart
index c011a20..b29bb44 100644
--- a/tool/input_sdk/lib/core/map.dart
+++ b/tool/input_sdk/lib/core/map.dart
@@ -4,6 +4,147 @@
part of dart.core;
+const _NULL_KEY = "__dart_jsMap_null";
+const _PROTOTYPE_KEY = "__dart_jsMap_prototype";
+
+final _GLOBAL =
+ JS('', '(typeof window === "undefined" ? global : window)');
+
+final _Object_keys = JS('', '#["Object"].keys', _GLOBAL);
+final _String = JS('', '#.String', _GLOBAL);
+final _Number = JS('', '#.Number', _GLOBAL);
+
+class JsMap<K, V> implements Map<K, V> {
+ //static final JsObject _Object = context['Object'];
+ var _jsMap = JS('', 'new Map()');
+
+ final _keyCaster;
+
+ JsMap(this._keyCaster);
+
+ get _jsKeys => JS('', '#(#)', _Object_keys, _jsMap);
+
+ List<K> get _keysList {
+ var jsMap = _jsMap;
+ var jsKeys = _jsKeys;
+ var result = <K>[];
+ for (var i = 0, n = JS('int', '#.length', jsKeys); i < n; i++) {
+ result.add(JS('K', '#[#]', jsKeys, i));
+ }
+ return result;
+ }
+
+ _mangleKey(key) {
+ // assert(key is num || key is String);
+ if (key == null) {
+ return _NULL_KEY;
+ } else if (key == 'prototype') {
+ return _PROTOTYPE_KEY;
+ } else {
+ return key;
+ }
+ }
+
+ _demangleKey(key) {
+ if (key == _PROTOTYPE_KEY) {
+ return 'prototype';
+ } else if (key == _NULL_KEY) {
+ return null;
+ } else {
+ return JS('', '#(#)', _keyCaster, key);
+ }
+ }
+
+ @override
+ V operator [](Object key) {
+ return JS('V', '#[#]', _jsMap, _mangleKey(key));
+ }
+
+ @override
+ void operator []=(K key, V value) {
+ JS('', '#[#] = #', _jsMap, _mangleKey(key), value);
+ }
+
+ @override
+ void addAll(Map<K, V> other) {
+ other.forEach((K key, V value) {
+ this[key] = value;
+ });
+ }
+
+ @override
+ void clear() {
+ _jsMap = JS('', 'new Map()');
+ }
+
+ @override
+ bool containsKey(Object key) {
+ return JS('bool', '# in #', _mangleKey(key), _jsMap);
+ }
+
+ @override
+ bool containsValue(Object value) {
+ for (var key in _keysList) {
+ var v = JS('V', '#[#]', _jsMap, key);
+ if (value == v) return true;
+ }
+ return false;
+ }
+
+ @override
+ void forEach(void f(K key, V value)) {
+ for (var key in _keysList) {
+ V value = JS('V', '#[#]', _jsMap, key);
+ f(_demangleKey(key), value);
+ }
+ }
+
+ @override
+ bool get isEmpty => JS('bool', '#.length == 0', _jsKeys);
+
+ @override
+ bool get isNotEmpty => !isEmpty;
+
+ // TODO: implement keys
+ @override
+ Iterable<K> get keys {
+ var result = <K>[];
+ for (var key in _keysList) {
+ result.add(_demangleKey(key));
+ }
+ return result;
+ }
+
+ // TODO: implement length
+ @override
+ int get length => _keysList.length;
+
+ @override
+ V putIfAbsent(K key, V ifAbsent()) {
+ var prop = _mangleKey(key);
+ return JS('V', '# in # ? #[#] : (#[#] = #())',
+ prop, _jsMap, _jsMap, prop, _jsMap, prop, ifAbsent);
+ }
+
+ @override
+ V remove(Object key) {
+ var prop = _mangleKey(key);
+ V value = JS('V', '#[#]', _jsMap, prop);
+ JS('', 'delete #[#]', _jsMap, prop);
+ return value;
+ }
+
+ // TODO: implement values
+ @override
+ Iterable<V> get values {
+ var result = <V>[];
+ for (var key in _keysList) {
+ result.add(JS('V', '#[#]', _jsMap, key));
+ }
+ return result;
+ }
+}
+
/**
* An collection of key-value pairs, from which you retrieve a value
* using its associated key.
@@ -35,7 +176,16 @@
* `operator==` and `hashCode`, and it allows null as a key.
* It iterates in key insertion order.
*/
- factory Map() = LinkedHashMap<K, V>;
+ //factory Map() = LinkedHashMap<K, V>;
+ factory Map() {
+ if (JS('', 'K && (K.name === "int" || K.name === "num" || K.name === "double")')) {
+ return new JsMap<K, V>(_Number);
+ } else if (JS('', 'K && (K.name === "String")')) {
+ return new JsMap<K, V>(_String);
+ } else {
+ return new LinkedHashMap<K, V>();
+ }
+ }
/**
* Creates a [LinkedHashMap] instance that contains all key-value pairs of