diff --git a/benchmark/pubspec.lock b/benchmark/pubspec.lock index 70b9a773c..99a2ce5c6 100644 --- a/benchmark/pubspec.lock +++ b/benchmark/pubspec.lock @@ -4,13 +4,13 @@ packages: analyzer: description: analyzer source: hosted - version: "0.15.7" + version: "0.18.0" angular: description: path: ".." relative: true source: path - version: "0.11.0" + version: "0.13.0" args: description: args source: hosted @@ -18,7 +18,7 @@ packages: barback: description: barback source: hosted - version: "0.13.0" + version: "0.14.0+3" benchmark_harness: description: benchmark_harness source: hosted @@ -30,17 +30,15 @@ packages: code_transformers: description: code_transformers source: hosted - version: "0.1.4+2" + version: "0.1.6" collection: description: collection source: hosted - version: "0.9.2" + version: "0.9.4" di: - description: - path: "../../di.dart" - relative: true - source: path - version: "2.0.0-alpha.9" + description: di + source: hosted + version: "2.0.1" html5lib: description: html5lib source: hosted @@ -48,27 +46,27 @@ packages: intl: description: intl source: hosted - version: "0.9.10" + version: "0.11.4" logging: description: logging source: hosted - version: "0.9.1+1" + version: "0.9.2" matcher: description: matcher source: hosted - version: "0.10.0" + version: "0.11.1" mock: description: mock source: hosted - version: "0.10.0+1" + version: "0.11.0+2" path: description: path source: hosted - version: "1.2.1" + version: "1.3.0" perf_api: description: perf_api source: hosted - version: "0.0.8" + version: "0.0.9" route_hierarchical: description: route_hierarchical source: hosted @@ -76,20 +74,28 @@ packages: source_maps: description: source_maps source: hosted - version: "0.9.0" + version: "0.9.4" + source_span: + description: source_span + source: hosted + version: "1.0.0" stack_trace: description: stack_trace source: hosted - version: "0.9.3+1" + version: "0.9.3+2" + typed_mock: + description: typed_mock + source: hosted + version: "0.0.4" unittest: description: unittest source: hosted - version: "0.10.1+2" + version: "0.11.0+4" utf: description: utf source: hosted - version: "0.9.0" + version: "0.9.0+1" web_components: description: web_components source: hosted - version: "0.3.3" + version: "0.3.5+1" diff --git a/lib/change_detection/prototype_map.dart b/lib/change_detection/prototype_map.dart index b324a12a1..3f279626c 100644 --- a/lib/change_detection/prototype_map.dart +++ b/lib/change_detection/prototype_map.dart @@ -1,39 +1,12 @@ part of angular.watch_group; -class PrototypeMap implements Map { +class PrototypeMap extends MicroMap { final Map prototype; - final Map self = new HashMap(); PrototypeMap(this.prototype); - void operator []=(name, value) { - self[name] = value; - } - V operator [](name) => self.containsKey(name) ? self[name] : prototype[name]; + V operator [](name) => containsKey(name) ? super[name] : prototype[name]; - bool get isEmpty => self.isEmpty && prototype.isEmpty; - bool get isNotEmpty => self.isNotEmpty || prototype.isNotEmpty; - // todo(vbe) include prototype keys ? - Iterable get keys => self.keys; - // todo(vbe) include prototype values ? - Iterable get values => self.values; - int get length => self.length; - - void forEach(fn) { - // todo(vbe) include prototype ? - self.forEach(fn); - } - V remove(key) => self.remove(key); - clear() => self.clear; - // todo(vbe) include prototype ? - bool containsKey(key) => self.containsKey(key); - // todo(vbe) include prototype ? - bool containsValue(key) => self.containsValue(key); - void addAll(map) { - self.addAll(map); - } - // todo(vbe) include prototype ? - V putIfAbsent(key, fn) => self.putIfAbsent(key, fn); - - toString() => self.toString(); + bool get isEmpty => _count == 0 && prototype.isEmpty; + bool get isNotEmpty => _count != 0 || prototype.isNotEmpty; } diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart index 49e3e7360..74cc3b9ce 100644 --- a/lib/change_detection/watch_group.dart +++ b/lib/change_detection/watch_group.dart @@ -1,6 +1,7 @@ library angular.watch_group; import 'package:angular/change_detection/change_detection.dart'; +import 'package:angular/collection/micro_map.dart'; import 'dart:collection'; part 'linked_list.dart'; @@ -54,7 +55,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList { final ChangeDetectorGroup<_Handler> _changeDetector; /** A cache for sharing sub expression watching. Watching `a` and `a.b` will * watch `a` only once. */ - final Map> _cache; + final MicroMap> _cache; final RootWatchGroup _rootGroup; /// STATS: Number of field watchers which are in use. @@ -120,7 +121,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList { : id = '', _rootGroup = null, _parentWatchGroup = null, - _cache = new HashMap>() + _cache = new MicroMap>() { _marker.watchGrp = this; _evalWatchTail = _evalWatchHead = _marker; @@ -301,7 +302,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList { this, _changeDetector.newGroup(), context == null ? this.context : context, - new HashMap>(), + new MicroMap>(), _rootGroup == null ? this : _rootGroup); _WatchGroupList._add(this, childGroup); var marker = childGroup._marker; diff --git a/lib/collection/micro_map.dart b/lib/collection/micro_map.dart new file mode 100644 index 000000000..90e0c5670 --- /dev/null +++ b/lib/collection/micro_map.dart @@ -0,0 +1,374 @@ +library angular.collection; + +import 'dart:collection'; + +const int MODE_ARRAY = 0; +const int MODE_MAP = 1; + +/** + * MicroMap is backed by fields until it reaches length 20. After that it + * delegates storage to a standard HashMap. + * Once micromap enters map mode (i.e. delegate map is constructed) it would + * not revert into fields mode (even after removals). + */ +class MicroMap implements Map { + + var _key0; var _obj0; + var _key1; var _obj1; + var _key2; var _obj2; + var _key3; var _obj3; + var _key4; var _obj4; + var _key5; var _obj5; + var _key6; var _obj6; + var _key7; var _obj7; + var _key8; var _obj8; + var _key9; var _obj9; + var _key10; var _obj10; + var _key11; var _obj11; + var _key12; var _obj12; + var _key13; var _obj13; + var _key14; var _obj14; + var _key15; var _obj15; + var _key16; var _obj16; + var _key17; var _obj17; + var _key18; var _obj18; + var _key19; var _obj19; + // Invariants: + // delegate != null -> _key*, _obj* = null, _count = 0 + // delegate == null -> 0 <= _count <= 20 + int _count; + Map _delegate; + + + MicroMap() { + _count = 0; + } + + bool containsValue(Object value) { + if (_delegate == null) { + if (_count == 0) return false; if(_obj0 == value) return true; + if (_count == 1) return false; if(_obj1 == value) return true; + if (_count == 2) return false; if(_obj2 == value) return true; + if (_count == 3) return false; if(_obj3 == value) return true; + if (_count == 4) return false; if(_obj4 == value) return true; + if (_count == 5) return false; if(_obj5 == value) return true; + if (_count == 6) return false; if(_obj6 == value) return true; + if (_count == 7) return false; if(_obj7 == value) return true; + if (_count == 8) return false; if(_obj8 == value) return true; + if (_count == 9) return false; if(_obj9 == value) return true; + if (_count == 10) return false; if(_obj10 == value) return true; + if (_count == 11) return false; if(_obj11 == value) return true; + if (_count == 12) return false; if(_obj12 == value) return true; + if (_count == 13) return false; if(_obj13 == value) return true; + if (_count == 14) return false; if(_obj14 == value) return true; + if (_count == 15) return false; if(_obj15 == value) return true; + if (_count == 16) return false; if(_obj16 == value) return true; + if (_count == 17) return false; if(_obj17 == value) return true; + if (_count == 18) return false; if(_obj18 == value) return true; + if (_count == 19) return false; if(_obj19 == value) return true; + if (_count == 20) return false; + } + return _delegate.containsValue(value); + } + + bool containsKey(Object key) { + if (_delegate == null) { + if (_count == 0) return false; if(_key0 == key) return true; + if (_count == 1) return false; if(_key1 == key) return true; + if (_count == 2) return false; if(_key2 == key) return true; + if (_count == 3) return false; if(_key3 == key) return true; + if (_count == 4) return false; if(_key4 == key) return true; + if (_count == 5) return false; if(_key5 == key) return true; + if (_count == 6) return false; if(_key6 == key) return true; + if (_count == 7) return false; if(_key7 == key) return true; + if (_count == 8) return false; if(_key8 == key) return true; + if (_count == 9) return false; if(_key9 == key) return true; + if (_count == 10) return false; if(_key10 == key) return true; + if (_count == 11) return false; if(_key11 == key) return true; + if (_count == 12) return false; if(_key12 == key) return true; + if (_count == 13) return false; if(_key13 == key) return true; + if (_count == 14) return false; if(_key14 == key) return true; + if (_count == 15) return false; if(_key15 == key) return true; + if (_count == 16) return false; if(_key16 == key) return true; + if (_count == 17) return false; if(_key17 == key) return true; + if (_count == 18) return false; if(_key18 == key) return true; + if (_count == 19) return false; if(_key19 == key) return true; + if (_count == 20) return false; + } + return _delegate.containsKey(key); + } + + V putIfAbsent(K key, V ifAbsent()) { + if (_delegate == null) { + if (_count == 0) { _count++; _key0 = key; return _obj0 = ifAbsent(); } else if (key == _key0) return _obj0; + if (_count == 1) { _count++; _key1 = key; return _obj1 = ifAbsent(); } else if (key == _key1) return _obj1; + if (_count == 2) { _count++; _key2 = key; return _obj2 = ifAbsent(); } else if (key == _key2) return _obj2; + if (_count == 3) { _count++; _key3 = key; return _obj3 = ifAbsent(); } else if (key == _key3) return _obj3; + if (_count == 4) { _count++; _key4 = key; return _obj4 = ifAbsent(); } else if (key == _key4) return _obj4; + if (_count == 5) { _count++; _key5 = key; return _obj5 = ifAbsent(); } else if (key == _key5) return _obj5; + if (_count == 6) { _count++; _key6 = key; return _obj6 = ifAbsent(); } else if (key == _key6) return _obj6; + if (_count == 7) { _count++; _key7 = key; return _obj7 = ifAbsent(); } else if (key == _key7) return _obj7; + if (_count == 8) { _count++; _key8 = key; return _obj8 = ifAbsent(); } else if (key == _key8) return _obj8; + if (_count == 9) { _count++; _key9 = key; return _obj9 = ifAbsent(); } else if (key == _key9) return _obj9; + if (_count == 10) { _count++; _key10 = key; return _obj10 = ifAbsent(); } else if (key == _key10) return _obj10; + if (_count == 11) { _count++; _key11 = key; return _obj11 = ifAbsent(); } else if (key == _key11) return _obj11; + if (_count == 12) { _count++; _key12 = key; return _obj12 = ifAbsent(); } else if (key == _key12) return _obj12; + if (_count == 13) { _count++; _key13 = key; return _obj13 = ifAbsent(); } else if (key == _key13) return _obj13; + if (_count == 14) { _count++; _key14 = key; return _obj14 = ifAbsent(); } else if (key == _key14) return _obj14; + if (_count == 15) { _count++; _key15 = key; return _obj15 = ifAbsent(); } else if (key == _key15) return _obj15; + if (_count == 16) { _count++; _key16 = key; return _obj16 = ifAbsent(); } else if (key == _key16) return _obj16; + if (_count == 17) { _count++; _key17 = key; return _obj17 = ifAbsent(); } else if (key == _key17) return _obj17; + if (_count == 18) { _count++; _key18 = key; return _obj18 = ifAbsent(); } else if (key == _key18) return _obj18; + if (_count == 19) { _count++; _key19 = key; return _obj19 = ifAbsent(); } else if (key == _key19) return _obj19; + if (_count == 20) _copyAllElementsToMap(); + } + return _delegate.putIfAbsent(key, ifAbsent); + } + + void addAll(Map other) { + other.forEach((K key, V value) => this[key] = value); + } + + void _fill(int idx, Object key, Object value) { + if (idx == 0) { _key0 = key; _obj0 = value; return; } + if (idx == 1) { _key1 = key; _obj1 = value; return; } + if (idx == 2) { _key2 = key; _obj2 = value; return; } + if (idx == 3) { _key3 = key; _obj3 = value; return; } + if (idx == 4) { _key4 = key; _obj4 = value; return; } + if (idx == 5) { _key5 = key; _obj5 = value; return; } + if (idx == 6) { _key6 = key; _obj6 = value; return; } + if (idx == 7) { _key7 = key; _obj7 = value; return; } + if (idx == 8) { _key8 = key; _obj8 = value; return; } + if (idx == 9) { _key9 = key; _obj9 = value; return; } + if (idx == 10) { _key10 = key; _obj10 = value; return; } + if (idx == 11) { _key11 = key; _obj11 = value; return; } + if (idx == 12) { _key12 = key; _obj12 = value; return; } + if (idx == 13) { _key13 = key; _obj13 = value; return; } + if (idx == 14) { _key14 = key; _obj14 = value; return; } + if (idx == 15) { _key15 = key; _obj15 = value; return; } + if (idx == 16) { _key16 = key; _obj16 = value; return; } + if (idx == 17) { _key17 = key; _obj17 = value; return; } + if (idx == 18) { _key18 = key; _obj18 = value; return; } + if (idx == 19) { _key19 = key; _obj19 = value; return; } + } + + V remove(Object key) { + if (_delegate == null) { + var value = null; + var seenIdx = -1; + if (_count == 0) { return null; } else if (key == _key0) { seenIdx = 0 ; value = _obj0; _obj0 = null;} + if (_count == 1) { if (seenIdx != -1) { if (seenIdx != 0) _fill(seenIdx, _key0, _obj0); _count--;} return value; } else if (key == _key1) { seenIdx = 1 ; value = _obj1; _obj1 = null;} + if (_count == 2) { if (seenIdx != -1) { if (seenIdx != 1) _fill(seenIdx, _key1, _obj1); _count--;} return value; } else if (key == _key2) { seenIdx = 2 ; value = _obj2; _obj2 = null;} + if (_count == 3) { if (seenIdx != -1) { if (seenIdx != 2) _fill(seenIdx, _key2, _obj2); _count--;} return value; } else if (key == _key3) { seenIdx = 3 ; value = _obj3; _obj3 = null;} + if (_count == 4) { if (seenIdx != -1) { if (seenIdx != 3) _fill(seenIdx, _key3, _obj3); _count--;} return value; } else if (key == _key4) { seenIdx = 4 ; value = _obj4; _obj4 = null;} + if (_count == 5) { if (seenIdx != -1) { if (seenIdx != 4) _fill(seenIdx, _key4, _obj4); _count--;} return value; } else if (key == _key5) { seenIdx = 5 ; value = _obj5; _obj5 = null;} + if (_count == 6) { if (seenIdx != -1) { if (seenIdx != 5) _fill(seenIdx, _key5, _obj5); _count--;} return value; } else if (key == _key6) { seenIdx = 6 ; value = _obj6; _obj6 = null;} + if (_count == 7) { if (seenIdx != -1) { if (seenIdx != 6) _fill(seenIdx, _key6, _obj6); _count--;} return value; } else if (key == _key7) { seenIdx = 7 ; value = _obj7; _obj7 = null;} + if (_count == 8) { if (seenIdx != -1) { if (seenIdx != 7) _fill(seenIdx, _key7, _obj7); _count--;} return value; } else if (key == _key8) { seenIdx = 8 ; value = _obj8; _obj8 = null;} + if (_count == 9) { if (seenIdx != -1) { if (seenIdx != 8) _fill(seenIdx, _key8, _obj8); _count--;} return value; } else if (key == _key9) { seenIdx = 9 ; value = _obj9; _obj9 = null;} + if (_count == 10) { if (seenIdx != -1) { if (seenIdx != 9) _fill(seenIdx, _key9, _obj9); _count--;} return value; } else if (key == _key10) { seenIdx = 10 ; value = _obj10; _obj10 = null;} + if (_count == 11) { if (seenIdx != -1) { if (seenIdx != 10) _fill(seenIdx, _key10, _obj10); _count--;} return value; } else if (key == _key11) { seenIdx = 11 ; value = _obj11; _obj11 = null;} + if (_count == 12) { if (seenIdx != -1) { if (seenIdx != 11) _fill(seenIdx, _key11, _obj11); _count--;} return value; } else if (key == _key12) { seenIdx = 12 ; value = _obj12; _obj12 = null;} + if (_count == 13) { if (seenIdx != -1) { if (seenIdx != 12) _fill(seenIdx, _key12, _obj12); _count--;} return value; } else if (key == _key13) { seenIdx = 13 ; value = _obj13; _obj13 = null;} + if (_count == 14) { if (seenIdx != -1) { if (seenIdx != 13) _fill(seenIdx, _key13, _obj13); _count--;} return value; } else if (key == _key14) { seenIdx = 14 ; value = _obj14; _obj14 = null;} + if (_count == 15) { if (seenIdx != -1) { if (seenIdx != 14) _fill(seenIdx, _key14, _obj14); _count--;} return value; } else if (key == _key15) { seenIdx = 15 ; value = _obj15; _obj15 = null;} + if (_count == 16) { if (seenIdx != -1) { if (seenIdx != 15) _fill(seenIdx, _key15, _obj15); _count--;} return value; } else if (key == _key16) { seenIdx = 16 ; value = _obj16; _obj16 = null;} + if (_count == 17) { if (seenIdx != -1) { if (seenIdx != 16) _fill(seenIdx, _key16, _obj16); _count--;} return value; } else if (key == _key17) { seenIdx = 17 ; value = _obj17; _obj17 = null;} + if (_count == 18) { if (seenIdx != -1) { if (seenIdx != 17) _fill(seenIdx, _key17, _obj17); _count--;} return value; } else if (key == _key18) { seenIdx = 18 ; value = _obj18; _obj18 = null;} + if (_count == 19) { if (seenIdx != -1) { if (seenIdx != 18) _fill(seenIdx, _key18, _obj18); _count--;} return value; } else if (key == _key19) { seenIdx = 19 ; value = _obj19; _obj19 = null;} + if (_count == 20) { if (seenIdx != -1) { if (seenIdx != 19) _fill(seenIdx, _key19, _obj19); _count--;} return value; } + return null; + } else { + return _delegate.remove(key); + } + } + + void clear() { + if (_delegate == null) { + _clearAllElements(); + } else { + _delegate.clear(); + } + } + + void forEach(void f(K key, V value)) { + if (_delegate == null) { + if (_count == 0) return; f(_key0 , _obj0 ); + if (_count == 1) return; f(_key1 , _obj1 ); + if (_count == 2) return; f(_key2 , _obj2 ); + if (_count == 3) return; f(_key3 , _obj3 ); + if (_count == 4) return; f(_key4 , _obj4 ); + if (_count == 5) return; f(_key5 , _obj5 ); + if (_count == 6) return; f(_key6 , _obj6 ); + if (_count == 7) return; f(_key7 , _obj7 ); + if (_count == 8) return; f(_key8 , _obj8 ); + if (_count == 9) return; f(_key9 , _obj9 ); + if (_count == 10) return; f(_key10, _obj10); + if (_count == 11) return; f(_key11, _obj11); + if (_count == 12) return; f(_key12, _obj12); + if (_count == 13) return; f(_key13, _obj13); + if (_count == 14) return; f(_key14, _obj14); + if (_count == 15) return; f(_key15, _obj15); + if (_count == 16) return; f(_key16, _obj16); + if (_count == 17) return; f(_key17, _obj17); + if (_count == 18) return; f(_key18, _obj18); + if (_count == 19) return; f(_key19, _obj19); + } else { + _delegate.forEach(f); + } + } + + Iterable get keys { + if (_delegate == null) _copyAllElementsToMap(); + return _delegate.keys; + } + + Iterable get values { + if (_delegate == null) _copyAllElementsToMap(); + return _delegate.values; + } + + int get length => _delegate == null ? _count : _delegate.length; + bool get isEmpty => _delegate == null ? _count == 0 : _delegate.isEmpty; + bool get isNotEmpty => _delegate == null ? _count != 0 : _delegate.isNotEmpty; + + V operator [](K key) { + if (_delegate == null) { + if (_count == 0) return null; if (key == _key0) return _obj0; + if (_count == 1) return null; if (key == _key1) return _obj1; + if (_count == 2) return null; if (key == _key2) return _obj2; + if (_count == 3) return null; if (key == _key3) return _obj3; + if (_count == 4) return null; if (key == _key4) return _obj4; + if (_count == 5) return null; if (key == _key5) return _obj5; + if (_count == 6) return null; if (key == _key6) return _obj6; + if (_count == 7) return null; if (key == _key7) return _obj7; + if (_count == 8) return null; if (key == _key8) return _obj8; + if (_count == 9) return null; if (key == _key9) return _obj9; + if (_count == 10) return null; if (key == _key10) return _obj10; + if (_count == 11) return null; if (key == _key11) return _obj11; + if (_count == 12) return null; if (key == _key12) return _obj12; + if (_count == 13) return null; if (key == _key13) return _obj13; + if (_count == 14) return null; if (key == _key14) return _obj14; + if (_count == 15) return null; if (key == _key15) return _obj15; + if (_count == 16) return null; if (key == _key16) return _obj16; + if (_count == 17) return null; if (key == _key17) return _obj17; + if (_count == 18) return null; if (key == _key18) return _obj18; + if (_count == 19) return null; if (key == _key19) return _obj19; + if (_count == 20) return null; + } else { + return _delegate[key]; + } + } + + void operator []=(K key, V value) { + if (_delegate == null) { + if (_count == 0) { _key0 = key; _obj0 = value; _count++; } else if (key == _key0) { _obj0 = value; } + else if (_count == 1) { _key1 = key; _obj1 = value; _count++; } else if (key == _key1) { _obj1 = value; } + else if (_count == 2) { _key2 = key; _obj2 = value; _count++; } else if (key == _key2) { _obj2 = value; } + else if (_count == 3) { _key3 = key; _obj3 = value; _count++; } else if (key == _key3) { _obj3 = value; } + else if (_count == 4) { _key4 = key; _obj4 = value; _count++; } else if (key == _key4) { _obj4 = value; } + else if (_count == 5) { _key5 = key; _obj5 = value; _count++; } else if (key == _key5) { _obj5 = value; } + else if (_count == 6) { _key6 = key; _obj6 = value; _count++; } else if (key == _key6) { _obj6 = value; } + else if (_count == 7) { _key7 = key; _obj7 = value; _count++; } else if (key == _key7) { _obj7 = value; } + else if (_count == 8) { _key8 = key; _obj8 = value; _count++; } else if (key == _key8) { _obj8 = value; } + else if (_count == 9) { _key9 = key; _obj9 = value; _count++; } else if (key == _key9) { _obj9 = value; } + else if (_count == 10) { _key10 = key; _obj10 = value; _count++; } else if (key == _key10) { _obj10 = value; } + else if (_count == 11) { _key11 = key; _obj11 = value; _count++; } else if (key == _key11) { _obj11 = value; } + else if (_count == 12) { _key12 = key; _obj12 = value; _count++; } else if (key == _key12) { _obj12 = value; } + else if (_count == 13) { _key13 = key; _obj13 = value; _count++; } else if (key == _key13) { _obj13 = value; } + else if (_count == 14) { _key14 = key; _obj14 = value; _count++; } else if (key == _key14) { _obj14 = value; } + else if (_count == 15) { _key15 = key; _obj15 = value; _count++; } else if (key == _key15) { _obj15 = value; } + else if (_count == 16) { _key16 = key; _obj16 = value; _count++; } else if (key == _key16) { _obj16 = value; } + else if (_count == 17) { _key17 = key; _obj17 = value; _count++; } else if (key == _key17) { _obj17 = value; } + else if (_count == 18) { _key18 = key; _obj18 = value; _count++; } else if (key == _key18) { _obj18 = value; } + else if (_count == 19) { _key19 = key; _obj19 = value; _count++; } else if (key == _key19) { _obj19 = value; } + else if (_count == 20) { + _copyAllElementsToMap(); + _delegate[key] = value; + } + } else { + _delegate[key] = value; + } + } + + int get mode { + return _delegate == null ? MODE_ARRAY : MODE_MAP; + } + + String toString() { + var elements = []; + if (_delegate == null) { + if (_count > 0) elements.add('${_key0}: ${_obj0}'); + if (_count > 1) elements.add('${_key1}: ${_obj1}'); + if (_count > 2) elements.add('${_key2}: ${_obj2}'); + if (_count > 3) elements.add('${_key3}: ${_obj3}'); + if (_count > 4) elements.add('${_key4}: ${_obj4}'); + if (_count > 5) elements.add('${_key5}: ${_obj5}'); + if (_count > 6) elements.add('${_key6}: ${_obj6}'); + if (_count > 7) elements.add('${_key7}: ${_obj7}'); + if (_count > 8) elements.add('${_key8}: ${_obj8}'); + if (_count > 9) elements.add('${_key9}: ${_obj9}'); + if (_count > 10) elements.add('${_key10}: ${_obj10}'); + if (_count > 11) elements.add('${_key11}: ${_obj11}'); + if (_count > 12) elements.add('${_key12}: ${_obj12}'); + if (_count > 13) elements.add('${_key13}: ${_obj13}'); + if (_count > 14) elements.add('${_key14}: ${_obj14}'); + if (_count > 15) elements.add('${_key15}: ${_obj15}'); + if (_count > 16) elements.add('${_key16}: ${_obj16}'); + if (_count > 17) elements.add('${_key17}: ${_obj17}'); + if (_count > 18) elements.add('${_key18}: ${_obj18}'); + if (_count > 19) elements.add('${_key19}: ${_obj19}'); + return '{${elements.join(', ')}}'; + } + return '$_delegate'; + } + + void _clearAllElements() { + _count = 0; + _key0 = null; _obj0 = null; + _key1 = null; _obj1 = null; + _key2 = null; _obj2 = null; + _key3 = null; _obj3 = null; + _key4 = null; _obj4 = null; + _key5 = null; _obj5 = null; + _key6 = null; _obj6 = null; + _key7 = null; _obj7 = null; + _key8 = null; _obj8 = null; + _key9 = null; _obj9 = null; + _key10 = null; _obj10 = null; + _key11 = null; _obj11 = null; + _key12 = null; _obj12 = null; + _key13 = null; _obj13 = null; + _key14 = null; _obj14 = null; + _key15 = null; _obj15 = null; + _key16 = null; _obj16 = null; + _key17 = null; _obj17 = null; + _key18 = null; _obj18 = null; + _key19 = null; _obj19 = null; + } + + void _copyAllElementsToMap() { + _delegate = new HashMap(); + if (_count > 0) _delegate[_key0] = _obj0; + if (_count > 1) _delegate[_key1] = _obj1; + if (_count > 2) _delegate[_key2] = _obj2; + if (_count > 3) _delegate[_key3] = _obj3; + if (_count > 4) _delegate[_key4] = _obj4; + if (_count > 5) _delegate[_key5] = _obj5; + if (_count > 6) _delegate[_key6] = _obj6; + if (_count > 7) _delegate[_key7] = _obj7; + if (_count > 8) _delegate[_key8] = _obj8; + if (_count > 9) _delegate[_key9] = _obj9; + if (_count > 10) _delegate[_key10] = _obj10; + if (_count > 11) _delegate[_key11] = _obj11; + if (_count > 12) _delegate[_key12] = _obj12; + if (_count > 13) _delegate[_key13] = _obj13; + if (_count > 14) _delegate[_key14] = _obj14; + if (_count > 15) _delegate[_key15] = _obj15; + if (_count > 16) _delegate[_key16] = _obj16; + if (_count > 17) _delegate[_key17] = _obj17; + if (_count > 18) _delegate[_key18] = _obj18; + if (_count > 19) _delegate[_key19] = _obj19; + _clearAllElements(); + } +} diff --git a/lib/core/module_internal.dart b/lib/core/module_internal.dart index 5b98206b0..d0c56a20d 100644 --- a/lib/core/module_internal.dart +++ b/lib/core/module_internal.dart @@ -21,6 +21,7 @@ import 'package:angular/change_detection/ast_parser.dart'; import 'package:angular/change_detection/change_detection.dart'; import 'package:angular/change_detection/dirty_checking_change_detector.dart'; import 'package:angular/core/formatter.dart'; +import 'package:angular/collection/micro_map.dart'; export 'package:angular/core/formatter.dart'; import 'package:angular/core/parser/utils.dart'; import 'package:angular/core/registry.dart'; @@ -45,7 +46,7 @@ class CoreModule extends Module { bind(ScopeStats); bind(ScopeStatsEmitter); bind(ScopeStatsConfig); - bind(Object, toValue: {}); // RootScope context + bind(Object, toValue: new MicroMap()); // RootScope context bind(Parser, toInstanceOf: DynamicParser); bind(ParserBackend, toInstanceOf: DynamicParserBackend); diff --git a/lib/core_dom/directive.dart b/lib/core_dom/directive.dart index 8738fe273..86be8898e 100644 --- a/lib/core_dom/directive.dart +++ b/lib/core_dom/directive.dart @@ -17,8 +17,8 @@ typedef void Mustache(bool hasObservers); class NodeAttrs { final dom.Element element; - Map> _observers; - final _mustacheAttrs = new HashMap(); + MicroMap> _observers; + final _mustacheAttrs = new MicroMap(); NodeAttrs(this.element); @@ -49,7 +49,7 @@ class NodeAttrs { * [:true:] */ observe(String attrName, notifyFn(String value)) { - if (_observers == null) _observers = new HashMap>(); + if (_observers == null) _observers = new MicroMap>(); _observers.putIfAbsent(attrName, () => <_AttributeChanged>[]) .add(notifyFn); diff --git a/lib/core_dom/module_internal.dart b/lib/core_dom/module_internal.dart index e599d30ae..78fb3fa25 100644 --- a/lib/core_dom/module_internal.dart +++ b/lib/core_dom/module_internal.dart @@ -11,6 +11,7 @@ import 'package:perf_api/perf_api.dart'; import 'package:angular/cache/module.dart'; +import 'package:angular/collection/micro_map.dart'; import 'package:angular/core/annotation.dart'; import 'package:angular/core/module_internal.dart'; import 'package:angular/core/parser/parser.dart'; diff --git a/lib/core_dom/selector.dart b/lib/core_dom/selector.dart index 1bb1b9977..7eb25d2ca 100644 --- a/lib/core_dom/selector.dart +++ b/lib/core_dom/selector.dart @@ -238,14 +238,14 @@ _addRefs(ElementBinderBuilder builder, List<_Directive> directives, dom.Node nod class _ElementSelector { final String _name; - final _elementMap = new HashMap>(); - final _elementPartialMap = new HashMap(); + final _elementMap = new MicroMap>(); + final _elementPartialMap = new MicroMap(); - final _classMap = new HashMap>(); - final _classPartialMap = new HashMap(); + final _classMap = new MicroMap>(); + final _classPartialMap = new MicroMap(); - final _attrValueMap = new HashMap>>(); - final _attrValuePartialMap = new HashMap>(); + final _attrValueMap = new MicroMap>>(); + final _attrValuePartialMap = new MicroMap>(); _ElementSelector(this._name); @@ -272,12 +272,12 @@ class _ElementSelector { } } else if ((name = part.attrName) != null) { if (terminal) { - elSelector._attrValueMap.putIfAbsent(name, () => new HashMap>()) + elSelector._attrValueMap.putIfAbsent(name, () => new MicroMap>()) .putIfAbsent(part.attrValue, () => []) .add(directive); } else { elSelector = elSelector._attrValuePartialMap - .putIfAbsent(name, () => new HashMap()) + .putIfAbsent(name, () => new MicroMap()) .putIfAbsent(part.attrValue, () => new _ElementSelector(name)); } } else { diff --git a/lib/directive/module.dart b/lib/directive/module.dart index 29b040ced..ccaba17e1 100644 --- a/lib/directive/module.dart +++ b/lib/directive/module.dart @@ -25,6 +25,7 @@ import 'package:angular/core/module_internal.dart'; import 'package:angular/core/parser/parser.dart'; import 'package:angular/core_dom/module_internal.dart'; import 'package:angular/core_dom/directive_injector.dart'; +import 'package:angular/collection/micro_map.dart'; import 'package:angular/utils.dart'; import 'package:angular/change_detection/watch_group.dart'; import 'package:angular/change_detection/change_detection.dart'; diff --git a/lib/directive/ng_repeat.dart b/lib/directive/ng_repeat.dart index 6bf161776..278640a01 100644 --- a/lib/directive/ng_repeat.dart +++ b/lib/directive/ng_repeat.dart @@ -106,7 +106,7 @@ class NgRepeat { if (trackByExpr != null) { Expression trackBy = _parser(trackByExpr); _generateId = ((key, value, index) { - final context = new HashMap() + final context = new MicroMap() ..[_valueIdentifier] = value ..[r'$index'] = index ..[r'$id'] = (obj) => obj; diff --git a/pubspec.lock b/pubspec.lock index 1660722b2..a9c731e02 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -12,7 +12,7 @@ packages: barback: description: barback source: hosted - version: "0.14.1+3" + version: "0.14.0+3" benchmark_harness: description: benchmark_harness source: hosted @@ -88,7 +88,7 @@ packages: stack_trace: description: stack_trace source: hosted - version: "1.0.2" + version: "0.9.3+2" typed_mock: description: typed_mock source: hosted diff --git a/test/collection/micro_map_spec.dart b/test/collection/micro_map_spec.dart new file mode 100644 index 000000000..55ab9919b --- /dev/null +++ b/test/collection/micro_map_spec.dart @@ -0,0 +1,216 @@ +library micro_map_spec; + +import '../_specs.dart'; +import 'package:angular/collection/micro_map.dart'; + +void main() { + insert(Map map, int start, int end) { + List alphabet = ['0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z']; + for(var i=start; i<=end; i++) map[i] = alphabet[i]; + } + describe('MicroMap', () { + it('should behave as a map', () { + MicroMap map = new MicroMap(); + map[1] = 'a'; + expect(map.length).toBe(1); + expect(map.toString()).toEqual('{1: a}'); + expect(map.mode).toEqual(MODE_ARRAY); + + map[2] = 'b'; + expect(map.length).toBe(2); + expect(map.toString()).toEqual('{1: a, 2: b}'); + expect(map.mode).toEqual(MODE_ARRAY); + + map[3] = 'c'; + expect(map.length).toBe(3); + expect(map.toString()).toEqual('{1: a, 2: b, 3: c}'); + expect(map.mode).toEqual(MODE_ARRAY); + + insert(map, 4, 21); + expect(map.length).toBe(21); + expect(map).toEqual({1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e', 6: 'f', 7: 'g', 8: 'h', 9: 'i', 10: 'j', 11: 'k', + 12: 'l', 13: 'm', 14: 'n', 15: 'o', 16: 'p', 17: 'q', 18: 'r', 19: 's', 20: 't', 21: 'u'}); + expect(map.mode).toEqual(MODE_MAP); + + map.remove(21); + expect(map.length).toBe(20); + expect(map.toString()).toEqual('{1: a, 2: b, 3: c, 4: d, 5: e, 6: f, 7: g, 8: h, 9: i, 10: j, 11: k,' + ' 12: l, 13: m, 14: n, 15: o, 16: p, 17: q, 18: r, 19: s, 20: t}'); + // once map mode has been hit it stays. + expect(map.mode).toEqual(MODE_MAP); + + map.remove(20); + map.remove(19); + map.remove(18); + map.remove(17); + map.remove(16); + expect(map.length).toBe(15); + expect(map.toString()).toEqual('{1: a, 2: b, 3: c, 4: d, 5: e, 6: f, 7: g, 8: h, 9: i, 10: j, 11: k,' + ' 12: l, 13: m, 14: n, 15: o}'); + // once map mode has been hit it stays. + expect(map.mode).toEqual(MODE_MAP); + }); + + describe('mode', () { + MicroMap map = new MicroMap(); + + beforeEach(() { + insert(map, 1, 20); + }); + + it('should switch to map mode', () { + map[21] = '21'; + expect(map.mode).toBe(MODE_MAP); + }); + }); + + describe('add elements', () { + MicroMap map; + + beforeEach(() { + map = new MicroMap(); + map[1] = '1'; + }); + + afterEach(() { + map = null; + }); + + it('should add element using bracket notation', () { + map[2] = 'nd'; + expect(map.length).toBe(2); + expect(map[2]).toEqual('nd'); + }); + + it('should add element which does not exist when using putIfAbsent', () { + map.putIfAbsent(2, () => 'second'); + expect(map.length).toBe(2); + expect(map[2]).toEqual('second'); + expect(map.mode).toBe(MODE_ARRAY); + }); + + it('should not add element which already exists when using putIfAbsent', () { + map.putIfAbsent(1, () => 'foo'); + expect(map[1]).toEqual('1'); + map[2] = '2'; + map[3] = '3'; + map.putIfAbsent(3, () => 'foo'); + expect(map[3]).toBe('3'); + }); + + it('should overwrite existing value', () { + map[1] = 'foo'; + expect(map.length).toBe(1); + expect(map.mode).toBe(MODE_ARRAY); + }); + + it('null key is valid', () { + map[null] = 'bar'; + expect(map.length).toBe(2); + }); + }); + + describe('add all elements', () { + MicroMap map; + + beforeEach(() { + map = new MicroMap(); + }); + + it('should add all elements', () { + Map other = {1 : 'first', 2: 'second', 3: 'third'}; + map.addAll(other); + expect(map.length).toBe(3); + expect(map[1]).toBe('first'); + expect(map[2]).toBe('second'); + expect(map[3]).toBe('third'); + expect(map.mode).toBe(MODE_ARRAY); + }); + + it('should add all elements', () { + Map other = {1 : 'first', 2: 'second', 3: 'third', 4:'fourth', 5:'fifth'}; + map.addAll(other); + expect(map.length).toBe(5); + expect(map[1]).toBe('first'); + expect(map[2]).toBe('second'); + expect(map[3]).toBe('third'); + expect(map[4]).toBe('fourth'); + expect(map[5]).toBe('fifth'); + expect(map.mode).toBe(MODE_ARRAY); + }); + + it('should add to existing elements', () { + insert(map, 1, 18); + Map other = {19: '19', 20: '20'}; + map.addAll(other); + expect(map.length).toBe(20); + expect(map.mode).toBe(MODE_ARRAY); + }); + + it('should add to existing elements', () { + insert(map, 1, 18); + Map other = {19: '19', 20: '20', 21: '21'}; + map.addAll(other); + expect(map.length).toBe(21); + expect(map.mode).toBe(MODE_MAP); + }); + + it('should overwrite elements with same key', () { + map[1] = 'first'; + map[2] = 'second'; + Map other = {1: 'foo', 3:'bar', 4:'baz'}; + map.addAll(other); + expect(map.length).toBe(4); + expect(map[1]).toBe('foo'); + expect(map[2]).toBe('second'); + expect(map[3]).toBe('bar'); + expect(map[4]).toBe('baz'); + expect(map.mode).toBe(MODE_ARRAY); + }); + }); + + describe('clear', () { + it('should length 0 after clearing', () { + MicroMap map = new MicroMap(); + insert(map, 0, 15); + map.clear(); + expect(map.length).toBe(0); + }); + }); + + describe('remove', () { + it('should return null when removing element which does not exist', () { + MicroMap map = new MicroMap(); + expect(map.remove(1)).toBeNull(); + }); + + it('should not get confused by a null key or value', () { + MicroMap map = new MicroMap(); + map[null] = null; + map[1] = 'foo'; + + expect(map.remove(null)).toBe(null); + expect(map.length).toBe(1); + expect(map[1]).toBe('foo'); + + expect(map.remove(null)).toBe(null); + expect(map.length).toBe(1); + }); + }); + + describe('iterables', () { + it('should return iterables of the right size', () { + MicroMap map = new MicroMap(); + map[2] = 3; + expect(map.keys).toEqual([2]); + }); + + it('should change mode when iterable is requested', () { + MicroMap map = new MicroMap(); + var iter = map.keys; + expect(map.mode).toEqual(MODE_MAP); + }); + }); + }); +}