Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(266)

Side by Side Diff: runtime/lib/convert_patch.dart

Issue 181543004: Optimize VM JSON parser for memory use. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add more tests. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | sdk/lib/core/iterable.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import "dart:typed_data"; 5 import "dart:typed_data";
6 import "dart:collection" show HashMap, LinkedHashMap, Maps, IterableBase;
7 import "dart:_internal" show SubListIterable, EfficientLength;
6 8
7 // JSON conversion. 9 // JSON conversion.
8 10
9 patch _parseJson(String json, reviver(var key, var value)) { 11 patch _parseJson(String json, reviver(var key, var value)) {
10 _BuildJsonListener listener; 12 _BuildJsonListener listener;
11 if (reviver == null) { 13 if (reviver == null) {
12 listener = new _BuildJsonListener(); 14 listener = new _BuildJsonListener();
13 } else { 15 } else {
14 listener = new _ReviverJsonListener(reviver); 16 listener = new _ReviverJsonListener(reviver);
15 } 17 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 * started. If the container is a [Map], there is also a current [key] 53 * started. If the container is a [Map], there is also a current [key]
52 * which is also stored on the stack. 54 * which is also stored on the stack.
53 */ 55 */
54 List stack = []; 56 List stack = [];
55 /** The current [Map] or [List] being built. */ 57 /** The current [Map] or [List] being built. */
56 var currentContainer; 58 var currentContainer;
57 /** The most recently read property key. */ 59 /** The most recently read property key. */
58 String key; 60 String key;
59 /** The most recently read value. */ 61 /** The most recently read value. */
60 var value; 62 var value;
63 /** Cache for reusable hidden classes of objects. Start out in multi-mode. */
64 static _JsonTransitionMap staticCache =
65 new _JsonMultiTransitionMap(const _JsonHiddenClass.empty(),
66 new HashMap());
67 // Counts how many transitions have been added to the cache.
68 // Cache is cleared when reaching the max size.
69 static int staticCacheSize = 0;
70 static const int MAX_STATIC_CACHE_SIZE = 512;
61 71
62 /** Pushes the currently active container (and key, if a [Map]). */ 72 /** Pushes the currently active container (and key, if a [Map]). */
63 void pushContainer() { 73 void pushContainer() {
64 if (currentContainer is Map) stack.add(key); 74 if (key != null)
srdjan 2014/03/03 15:12:54 Curly braces missing?
floitsch 2014/03/03 15:36:44 something's not right here.
75 if (currentContainer is _JsonObjectBuilder) stack.add(key);
65 stack.add(currentContainer); 76 stack.add(currentContainer);
66 } 77 }
67 78
68 /** Pops the top container from the [stack], including a key if applicable. */ 79 /** Pops the top container from the [stack], including a key if applicable. */
69 void popContainer() { 80 void popContainer() {
70 value = currentContainer; 81 value = currentContainer;
71 currentContainer = stack.removeLast(); 82 currentContainer = stack.removeLast();
72 if (currentContainer is Map) key = stack.removeLast(); 83 if (currentContainer is _JsonObjectBuilder) key = stack.removeLast();
73 } 84 }
74 85
75 void handleString(String value) { this.value = value; } 86 void handleString(String value) { this.value = value; }
76 void handleNumber(num value) { this.value = value; } 87 void handleNumber(num value) { this.value = value; }
77 void handleBool(bool value) { this.value = value; } 88 void handleBool(bool value) { this.value = value; }
78 void handleNull() { this.value = null; } 89 void handleNull() { this.value = null; }
79 90
80 void beginObject() { 91 void beginObject() {
81 pushContainer(); 92 pushContainer();
82 currentContainer = {}; 93 currentContainer = new _JsonObjectBuilder(staticCache);
83 } 94 }
84 95
85 void propertyName() { 96 void propertyName() {
86 key = value; 97 key = value;
87 value = null; 98 value = null;
88 } 99 }
89 100
90 void propertyValue() { 101 void propertyValue() {
91 Map map = currentContainer; 102 _JsonObjectBuilder builder = currentContainer;
92 map[key] = value; 103 builder.add(key, value);
93 key = value = null; 104 key = value = null;
94 } 105 }
95 106
96 void endObject() { 107 void endObject() {
108 _JsonObjectBuilder builder = currentContainer;
109 currentContainer = builder.toMap();
110 staticCacheSize += builder.transitionsAdded;
97 popContainer(); 111 popContainer();
98 } 112 }
99 113
100 void beginArray() { 114 void beginArray() {
101 pushContainer(); 115 pushContainer();
102 currentContainer = []; 116 currentContainer = [];
103 } 117 }
104 118
105 void arrayElement() { 119 void arrayElement() {
106 List list = currentContainer; 120 List list = currentContainer;
107 currentContainer.add(value); 121 currentContainer.add(value);
108 value = null; 122 value = null;
109 } 123 }
110 124
111 void endArray() { 125 void endArray() {
112 popContainer(); 126 popContainer();
113 } 127 }
114 128
115 /** Read out the final result of parsing a JSON string. */ 129 /** Read out the final result of parsing a JSON string. */
116 get result { 130 get result {
117 assert(currentContainer == null); 131 assert(currentContainer == null);
132 if (staticCacheSize > MAX_STATIC_CACHE_SIZE) {
133 _JsonMultiTransitionMap cache = staticCache;
134 cache.mapping.clear();
135 staticCacheSize = 0;
136 }
118 return value; 137 return value;
119 } 138 }
120 } 139 }
121 140
122 class _ReviverJsonListener extends _BuildJsonListener { 141 class _ReviverJsonListener extends _BuildJsonListener {
123 final _Reviver reviver; 142 final _Reviver reviver;
124 _ReviverJsonListener(reviver(key, value)) : this.reviver = reviver; 143 _ReviverJsonListener(reviver(key, value)) : this.reviver = reviver;
125 144
126 void arrayElement() { 145 void arrayElement() {
127 List list = currentContainer; 146 List list = currentContainer;
128 value = reviver(list.length, value); 147 value = reviver(list.length, value);
129 super.arrayElement(); 148 super.arrayElement();
130 } 149 }
131 150
132 void propertyValue() { 151 void propertyValue() {
133 value = reviver(key, value); 152 value = reviver(key, value);
134 super.propertyValue(); 153 super.propertyValue();
135 } 154 }
136 155
137 get result { 156 get result {
138 return reviver(null, value); 157 return reviver(null, value);
139 } 158 }
140 } 159 }
141 160
142 class _JsonParser { 161 class _JsonParser {
143 // A simple non-recursive state-based parser for JSON. 162 // A simple non-recursive state-based parser for JSON.
144 // 163 //
145 // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON 164 // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON
146 // and strings also in OBJECT_EMPTY, OBJECT_COMMA. 165 // and strings also in OBJECT_EMPTY, OBJECT_COMMA.
147 // VALUE STRING : , } ] Transitions to 166 // VALUE STRING : , } ] f to
srdjan 2014/03/03 15:12:54 ?
floitsch 2014/03/03 15:36:44 ?
148 // EMPTY X X -> END 167 // EMPTY X X -> END
149 // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop 168 // ARRAY_EMPTY X X @ -> ARRAY_VALUE / pop
150 // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop 169 // ARRAY_VALUE @ @ -> ARRAY_COMMA / pop
151 // ARRAY_COMMA X X -> ARRAY_VALUE 170 // ARRAY_COMMA X X -> ARRAY_VALUE
152 // OBJECT_EMPTY X @ -> OBJECT_KEY / pop 171 // OBJECT_EMPTY X @ -> OBJECT_KEY / pop
153 // OBJECT_KEY @ -> OBJECT_COLON 172 // OBJECT_KEY @ -> OBJECT_COLON
154 // OBJECT_COLON X X -> OBJECT_VALUE 173 // OBJECT_COLON X X -> OBJECT_VALUE
155 // OBJECT_VALUE @ @ -> OBJECT_COMMA / pop 174 // OBJECT_VALUE @ @ -> OBJECT_COMMA / pop
156 // OBJECT_COMMA X -> OBJECT_KEY 175 // OBJECT_COMMA X -> OBJECT_KEY
157 // END 176 // END
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 int sliceEnd = position + 20; 569 int sliceEnd = position + 20;
551 if (sliceEnd > source.length) { 570 if (sliceEnd > source.length) {
552 slice = "'${source.substring(position)}'"; 571 slice = "'${source.substring(position)}'";
553 } else { 572 } else {
554 slice = "'${source.substring(position, sliceEnd)}...'"; 573 slice = "'${source.substring(position, sliceEnd)}...'";
555 } 574 }
556 throw new FormatException("Unexpected character at $position: $slice"); 575 throw new FormatException("Unexpected character at $position: $slice");
557 } 576 }
558 } 577 }
559 578
579 /*
580 * JSON Map
581 *
582 * A map with hidden class structure.
583 *
584 * When building maps, don't use a linked hashmap directly.
585 * Instead use a "hidden class" map that keeps the hash structure
586 * in a separate sharable structure representation, and only the
587 * data in the actual map.
588 * Basically, use a map of string->index, and a list of values,
589 * and share the map between all objects with the same structure.
590 *
591 * JSON maps are expected to preserve order, so the hidden classes
592 * maintain the order of the keys.
593 *
594 * The maps will be a delegating map that points to the hidden class
595 * (itself a "map") except that all modifying operations makes the
596 * hidden class replace itself with a linked hash map.
597 */
598
599 /**
600 * A transition cache that shows transitions from one hidden class
601 * to another.
602 */
603 class _JsonTransitionMap {
604 _JsonHiddenClass get hiddenClass;
605 /** See if there is a transition from this class with [key] as key. */
606 _JsonTransitionMap lookup(String key);
607 /** Add a new transition from this class to a new one. */
608 _JsonTransitionMap addAlternative(String key, _JsonTransitionMap targetMap);
609 /** Update the transition map that is linked by a given key. */
610 void update(String key, _JsonTransitionMap map);
611 }
612
613 class _JsonLeafTransitionMap implements _JsonTransitionMap {
614 final _JsonHiddenClass hiddenClass;
615 _JsonLeafTransitionMap(this.hiddenClass);
616 _JsonTransitionMap lookup(String key) => null;
617 _JsonTransitionMap addAlternative(String key, _JsonTransitionMap targetMap) {
618 return new _JsonSingletonTransitionMap(hiddenClass, key, targetMap);
619 }
620 void update(String key, _JsonTransitionMap map) {
621 assert(false); // Must not be called.
622 }
623 }
624
625 class _JsonSingletonTransitionMap implements _JsonTransitionMap {
626 final _JsonHiddenClass hiddenClass;
627 final String key;
628 _JsonTransitionMap next;
629 _JsonSingletonTransitionMap(this.hiddenClass, this.key, this.next);
630
631 _JsonTransitionMap lookup(String key) {
632 if (this.key == key) return next;
633 return null;
634 }
635
636 _JsonTransitionMap addAlternative(String key, _JsonTransitionMap targetMap) {
637 Map mapping = new HashMap();
638 mapping[this.key] = next;
639 mapping[key] = targetMap;
640 return new _JsonMultiTransitionMap(hiddenClass, mapping);
641 }
642
643 void update(String key, _JsonTransitionMap map) {
644 assert(this.key == key);
645 next = map;
646 }
647 }
648
649 class _JsonMultiTransitionMap implements _JsonTransitionMap {
650 final _JsonHiddenClass hiddenClass;
651 final Map mapping;
652 _JsonMultiTransitionMap(this.hiddenClass, this.mapping);
653 _JsonTransitionMap lookup(String key) => mapping[key];
654 _JsonTransitionMap addAlternative(String key, _JsonTransitionMap targetMap) {
655 assert(!mapping.containsKey(key));
656 mapping[key] = targetMap;
657 return this;
658 }
659 void update(String key, _JsonTransitionMap map) {
660 assert(mapping.containsKey(key));
661 mapping[key] = map;
662 }
663 }
664
665 /**
666 * A JSON Object builder that keeps a hidden class for keys and a list of
667 * values.
668 *
669 * When the object is complete, it can be extracted as a `Map` using `toMap`.
670 *
671 */
672 class _JsonObjectBuilder {
673 int transitionsAdded = 0;
674 _JsonTransitionMap parentMap;
675 String previousKey;
676 _JsonTransitionMap currentMap;
677
678 final List values = [];
679
680 _JsonObjectBuilder(this.currentMap);
681
682 Object toMap() {
683 return currentMap.hiddenClass.asMap(values);
684 }
685
686 /**
687 * Add a property to the object being built.
688 *
689 * If the key is already in the object, its value is just overwritten.
690 * Otherwise the hidden class is transitioned to one with the new key
691 * and the result is added at the end.
692 */
693 void add(String key, var value) {
694 int index = currentMap.hiddenClass.lookup(key);
695 if (index >= 0) {
696 values[index] = value;
697 } else {
698 _JsonTransitionMap nextMap = currentMap.lookup(key);
699 if (nextMap == null) {
700 _JsonHiddenClass nextClass = currentMap.hiddenClass.addKey(key);
701 nextMap = new _JsonLeafTransitionMap(nextClass);
702 currentMap = currentMap.addAlternative(key, nextMap);
703 if (parentMap != null) {
704 parentMap.update(previousKey, currentMap);
705 }
706 transitionsAdded++;
707 }
708 parentMap = currentMap;
709 previousKey = key;
710 currentMap = nextMap;
711
712 values.add(value);
713 }
714 }
715 }
716
717 /**
718 * A "hidden class" is a mapping from string key to integer index.
719 *
720 * A map using a class will have a list of values for each index in the
721 * hidden class.
722 */
723 abstract class _JsonHiddenClass {
724 const _JsonHiddenClass();
725 const factory _JsonHiddenClass.empty() = _JsonEmptyHiddenClass;
726 int lookup(String key);
727 Map toMap(List values) {
728 Map map = new LinkedHashMap<String, dynamic>();
729 addToMap(map, values);
730 return map;
731 }
732 Iterator<String> get keyIterator;
733 void addToMap(Map<String,dynamic> map, List values);
734 int get length;
735
736 _JsonHiddenClass addKey(String key);
737
738 Map<String, dynamic> asMap(List values) {
739 return new _JsonHiddenClassMap(this, values).wrapper;
740 }
741 }
742
743 class _JsonEmptyHiddenClass extends _JsonHiddenClass {
744 const _JsonEmptyHiddenClass();
745 int lookup(String key) => -1;
746 Map toMap(List values) => new LinkedHashMap<String, dynamic>();
747 Iterator<String> get keyIterator => const[];
748 void addToMap(Map map, List values) {}
749 int get length => 0;
750 _JsonHiddenClass addKey(String key) {
751 return new _JsonSmallHiddenClass(<String>[key], 1);
752 }
753 }
754
755 /**
756 * A hidden class for a JSON object that maps keys to value indices.
757 *
758 * This is intended for small objects. Looking up a key is done using
759 * linear search.
760 */
761 class _JsonSmallHiddenClass extends _JsonHiddenClass {
762 final List keys;
763 final int length; // `keys` may contain more elements than length.
floitsch 2014/03/03 15:36:44 Point to `addKey` where we add a new key for the n
764 _JsonSmallHiddenClass(this.keys, this.length);
765 int lookup(String key) {
766 for (int i = 0; i < length; i++) {
767 if (keys[i] == key) return i;
768 }
769 return -1;
770 }
771
772 Iterator<String> get keyIterator => keys.take(length).iterator;
773
774 void addToMap(Map map, List values) {
775 for (int i = 0; i < length; i++) {
776 map[keys[i]] = values[i];
777 }
778 }
779
780 _JsonHiddenClass addKey(String key) {
781 const int MAX_SMALL_CLASS = 4;
floitsch 2014/03/03 15:36:44 I would go higher, but that's just my gut-reaction
782 if (length == MAX_SMALL_CLASS) {
783 Map map = new LinkedHashMap<String,int>();
784 for (int i = 0; i < length; i++) map[keys[i]] = i;
785 map[key] = length;
786 return new _JsonMediumHiddenClass(map, length + 1);
787 }
788 // TODO(lrn): Add an implementation for larger key lists that doesn't use
789 // linear search. Switch to using that implementation here if length is
790 // above a threshold.
791 var newKeys;
792 if (keys.length > length) {
793 newKeys = keys.sublist(0, length);
794 } else {
795 newKeys = keys;
floitsch 2014/03/03 15:36:44 Add comment that we are sharing the list here.
796 }
797 newKeys.add(key);
798 return new _JsonSmallHiddenClass(newKeys, length + 1);
799 }
800 }
801
802 /**
803 * A hidden class that uses a [LinkedHashMap] to store the key-to-index mapping.
804 *
805 * This introduces the same overhead as a normal map, so if the hidden class
806 * is only used once, it's just an overhead.
807 */
808 class _JsonMediumHiddenClass extends _JsonHiddenClass {
809 final LinkedHashMap<String, int> keys;
810 final int length; // `keys` may contain more elements than length.
floitsch 2014/03/03 15:36:44 ditto. point to `addKey`.
811 _JsonMediumHiddenClass(this.keys, this.length);
812
813 int lookup(String key) {
814 int index = keys[key];
815 if (index == null || index >= length) return -1;
816 return index;
817 }
818
819 Iterator<String> get keyIterator => keys.keys.take(length).iterator;
820
821 void addToMap(Map map, List values) {
822 int i = 0;
823 assert(length != 0);
824 for (String key in keys.keys) {
825 map[key] = values[i];
826 i++;
827 if (i == length) break;
828 }
829 }
830
831 _JsonHiddenClass addKey(String key) {
832 // TODO(lrn): Add an implementation for larger key lists that doesn't use
833 // linear search. Switch to using that implementation here if length is
834 // above a threshold.
835 var newKeys;
836 if (keys.length > length) {
837 newKeys = new HashMap<String,int>();
838 keys.forEach((String key, int value) {
839 if (value < length) newKeys[key] = value;
840 });
841 } else {
842 newKeys = keys;
843 }
844 newKeys[key] = length;
845 return new _JsonMediumHiddenClass(newKeys, length + 1);
846 }
847 }
848
849
850 /**
851 * A map based on a hidden class.
852 *
853 * The hidden class translates string keys to integer indices, and the
854 * values are stored at those indices in [values].
855 * The idea is that the hidden class can be shared between multiple similar
856 * objects, reducing the memory overhead of the map created by decoding a
857 * JSON Object. This only works when there are more than one object with
858 * the same structure.
859 *
860 * This object is hidden behind the [_JsonMapWrapper].
861 *
862 * Any attempt to write to the map will make it convert itself to a
863 * [LinkedHashMap] with the same values, and make the wrapper delegate to that
864 * map instead.
865 */
866 class _JsonHiddenClassMap implements Map {
867 final _JsonHiddenClass hiddenClass;
868 final List mapValues;
869 bool modified = false;
870 _JsonMapWrapper wrapper;
871
872 _JsonHiddenClassMap(this.hiddenClass, this.mapValues) {
873 wrapper = new _JsonMapWrapper(this);
874 }
875
876 Map convertToMap() {
877 modified = true;
878 Map map = hiddenClass.toMap(mapValues);
879 wrapper._delegate = map;
880 return map;
881 }
882
883 bool containsValue(Object value) {
884 for (int i = 0; i < mapValues.length; i++) {
885 if (mapValues[i] == value) return true;
886 }
887 return false;
888 }
889
890 bool containsKey(Object key) => hiddenClass.lookup(key) >= 0;
891
892 operator [](Object key) {
893 int index = hiddenClass.lookup(key);
894 if (index < 0) return null;
895 return mapValues[index];
896 }
897
898 void operator []=(String key, var value) {
899 int index = hiddenClass.lookup(key);
900 if (index >= 0) {
901 mapValues[index] = value;
902 } else {
903 convertToMap()[key] = value;
904 }
905 }
906
907 putIfAbsent(String key, ifAbsent()) {
908 int index = hiddenClass.lookup(key);
909 if (index >= 0) {
910 return mapValues[index];
911 }
912 return convertToMap().putIfAbsent(key, ifAbsent);
913 }
914
915 void addAll(Map<String, dynamic> other) {
916 Iterator values = other.iterator;
floitsch 2014/03/03 15:36:44 Maps don't have iterators.
917 if (!values.moveNext()) returm
floitsch 2014/03/03 15:36:44 You could also just ask, if other.length == 0, con
918 Map map = convertToMap();
919 do {
920 map.add(values.current);
floitsch 2014/03/03 15:36:44 map doesn't have "add".
921 } while (values.moveNext());
922 }
923
924 remove(Object key) {
925 int index = hiddenClass.lookup(key);
926 if (index < 0) return null;
927 return convertToMap().remove(key);
928 }
929
930 void clear() {
931 modified = true;
932 wrapper._delegate = new LinkedHashMap<String, dynamic>();
933 }
934
935 void forEach(void f(String key, var value)) {
936 Iterator keys = hiddenClass.keyIterator;
937 for (int i = 0; i < mapValues.length; i++) {
938 keys.moveNext();
939 String key = keys.current;
940 f(key, mapValues[i]);
941 if (modified) throw new ConcurrentModificationError(wrapper);
942 }
943 }
944
945 Iterable<String> get keys => new _JsonHiddenClassMapKeyIterable(this);
946
947 Iterable get valueIterator => new _JsonHiddenClassMapValueIterable(this);
948
949 int get length => mapValues.length;
950
951 bool get isEmpty => mapValues.length == 0;
952
953 bool get isNotEmpty => mapValues.length != 0;
954
955 String toString() => Maps.mapToString(this);
956 }
957
958 abstract class _JsonHiddenClassMapIterable<T> extends IterableBase<T>
959 implements EfficientLength {
960 _JsonHiddenClassMap _map;
961 _JsonHiddenClassMapIterable(this._map);
962 int get length => _map.length;
963 bool get isEmpty => _map.isEmpty;
964 bool get isNotEmpty => _map.isNotEmpty;
965 }
966
967 class _JsonHiddenClassMapKeyIterable
968 extends _JsonHiddenClassMapIterable<String> {
969 _JsonHiddenClassMapKeyIterable(_JsonHiddenClassMap map) : super(map);
970 Iterator get iterator => new _JsonHiddenClassMapKeyIterator(_map);
971 }
972
973 class _JsonHiddenClassMapValueIterable extends _JsonHiddenClassMapIterable {
974 _JsonHiddenClassMapValueIterable(_JsonHiddenClassMap map) : super(map);
975 Iterator get iterator => new _JsonHiddenClassMapValueIterator(_map);
976 }
977
978 class _JsonHiddenClassMapKeyIterator implements Iterator<String> {
979 Iterator _keys;
980 _JsonHiddenClassMap _map;
981 _JsonHiddenClassMapKeyIterator(_JsonHiddenClassMap map)
982 : _map = map, _keys = map.hiddenClass.keyIterator;
983 bool moveNext() {
984 if (_map.modified) throw new ConcurrentModificationError(_map.wrapper);
985 return _keys.moveNext();
986 }
987 String get current => _keys.current;
988 }
989
990 class _JsonHiddenClassMapValueIterator implements Iterator {
991 int _index = 0;
992 var _current;
993 _JsonHiddenClassMap _map;
994 _JsonHiddenClassMapKeyIterator(_JsonHiddenClassMap map)
995 : _map = map;
996 bool moveNext() {
997 if (_map.modified) throw new ConcurrentModificationError(_map.wrapper);
998 if (_index == _map.mapValues.length) {
999 _current = false;
1000 return false;
1001 }
1002 _current = _map.mapValues[_index++];
1003 return true;
1004 }
1005 get current => _current;
1006 }
1007
1008 /**
1009 * Delegating map wrapper.
1010 *
1011 * Used to have a "copy on write" map implementation optimized for reading,
floitsch 2014/03/03 15:36:44 s/Used to have/Has/
1012 * which converts itself to a [LinkedHashMap] on any write operation by
1013 * creating the hash map and writing it to [_delegate].
1014 *
1015 * This is the only object that the JSON decoder's user sees.
1016 */
1017 class _JsonMapWrapper implements Map<String, dynamic> {
1018 Map _delegate;
1019
1020 _JsonMapWrapper(this._delegate);
1021
1022 bool containsValue(Object value) => _delegate.containsValue(value);
1023
1024 bool containsKey(Object key) => _delegate.containsKey(key);
1025
1026 operator [](Object key) => _delegate[key];
1027
1028 void operator []=(String key, var value) { _delegate[key] = value; }
1029
1030 putIfAbsent(String key, ifAbsent()) => _delegate.putIfAbsent(key, ifAbsent);
1031
1032 void addAll(Map<String, dynamic> other) => _delegate.addAll(other);
1033
1034 remove(Object key) => _delegate.remove(key);
1035
1036 void clear() { _delegate.clear(); }
1037
1038 void forEach(void f(String key, var value)) { _delegate.forEach(f); }
1039
1040 Iterable<String> get keys => _delegate.keys;
1041
1042 Iterable get values => _delegate.values;
1043
1044 int get length => _delegate.length;
1045
1046 bool get isEmpty => _delegate.isEmpty;
1047
1048 bool get isNotEmpty => _delegate.isNotEmpty;
1049
1050 String toString() => _delegate.toString();
1051 }
1052
560 // UTF-8 conversion. 1053 // UTF-8 conversion.
561 1054
562 patch class _Utf8Encoder { 1055 patch class _Utf8Encoder {
563 /* patch */ static List<int> _createBuffer(int size) => new Uint8List(size); 1056 /* patch */ static List<int> _createBuffer(int size) => new Uint8List(size);
564 } 1057 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/core/iterable.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698