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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart

Issue 12827018: Add a new implementation of HashMap that uses JS objects for its (multiple) hash tables. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Prefer local variable. Created 7 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Patch file for dart:collection classes.
6 import 'dart:_foreign_helper' show JS;
7
8 patch class HashMap<K, V> {
9 // BUG(9368): We're currently unable to pass the type arguments to
10 // the construction of the JSMap instance.
11 patch factory HashMap() => new JSMap();
12 }
13
14 class JSMap<K, V> implements HashMap<K, V> {
15 int _length = 0;
16 var _strings, _nums, _rest;
ahe 2013/03/22 16:18:31 Each of these should be on their own line, and eac
17 List _keys;
18
19 int get length => _length;
20 bool get isEmpty => _length == 0;
21
22 Iterable<K> get keys {
23 return new JSMapKeyIterable<K>(this);
24 }
25
26 Iterable<V> get values {
27 return keys.map((each) => this[each]);
28 }
29
30 bool containsKey(K key) {
31 if (key is String && key != '__proto__') {
32 var strings = _strings;
ahe 2013/03/22 16:18:31 Would this work: var strings = JS('JavaScriptInde
33 if (strings == null) return false;
34 var probe = JS('var', '#[#]', strings, key);
35 return JS('bool', '# != null', probe);
ahe 2013/03/22 16:18:31 Why not simply: return probe != null;
36 } else if (key is num) {
37 var nums = _nums;
38 if (nums == null) return false;
39 var probe = JS('var', '#[#]', nums, key);
40 return JS('bool', '# != null', probe);
41 } else {
42 var rest = _rest;
43 if (rest == null) return false;
44 var hash = key.hashCode;
45 var bucket = JS('var', '#[#]', rest, hash);
ahe 2013/03/22 16:18:31 Do you know if open addressing would be better in
46 if (bucket != null) {
47 int bucketLength = JS('int', '#.length', bucket);
ahe 2013/03/22 16:18:31 Could you tell compiler that bucket is a List and
48 for (int i = 0; i < bucketLength; i += 2) {
49 if (JS('var', '#[#]', bucket, i) == key) {
50 return true;
51 }
52 }
53 }
54 return false;
55 }
56 }
57
58 bool containsValue(V value) {
59 return _computeKeys().any((each) => this[each] == value);
ahe 2013/03/22 16:18:31 This looks slow. Wouldn't this always be faster:
60 }
61
62 V operator[](K key) {
63 if (key is String && key != '__proto__') {
64 var strings = _strings;
65 if (strings == null) return null;
66 var probe = JS('var', '#[#]', strings, key);
67 return JS('bool', '# === #', probe, strings) ? null : probe;
ahe 2013/03/22 16:18:31 You're using _strings as a sentinel object? Comme
68 } else if (key is num) {
69 var nums = _nums;
70 if (nums == null) return null;
71 var probe = JS('var', '#[#]', nums, key);
72 return JS('bool', '# === #', probe, nums) ? null : probe;
73 } else {
74 var rest = _rest;
75 if (rest == null) return null;
76 var hash = JS('num', 'Number(#)', key.hashCode);
ahe 2013/03/22 16:18:31 Shouldn't you throw some kind of exception if hash
77 var bucket = JS('var', '#[#]', rest, hash);
78 if (bucket != null) {
79 int bucketLength = JS('int', '#.length', bucket);
80 for (int i = 0; i < bucketLength; i += 2) {
81 if (JS('var', '#[#]', bucket, i) == key) {
82 return JS('var', '#[# + 1]', bucket, i);
83 }
84 }
85 }
86 return null;
87 }
88 }
89
90 void operator[]=(K key, V value) {
91 if (key is String && key != '__proto__') {
92 var strings = _strings;
93 if (strings == null) {
94 _strings = strings = _newHashTable();
95 _length++;
96 _keys = null;
ahe 2013/03/22 16:18:31 This might be more readable: addedNewKey = true;
97 } else if (JS('bool', '#[#] == null', strings, key)) {
98 _length++;
99 _keys = null;
100 }
101 if (value == null) value = strings;
102 JS('void', '#[#] = #', strings, key, value);
103 } else if (key is num) {
104 var nums = _nums;
105 if (nums == null) {
106 _nums = nums = _newHashTable();
107 _length++;
108 _keys = null;
109 } else if (JS('bool', '#[#] == null', nums, key)) {
110 _length++;
111 _keys = null;
112 }
113 if (value == null) value = nums;
114 JS('void', '#[#] = #', nums, key, value);
115 } else {
116 var rest = _rest;
117 var hash = JS('num', 'Number(#)', key.hashCode);
ahe 2013/03/22 16:18:31 Exception?
118 var bucket;
119 if (rest == null) {
120 _rest = rest = _newHashTable();
121 } else {
122 bucket = JS('var', '#[#]', rest, hash);
123 }
124 if (bucket == null) {
125 JS('void', '#[#] = [#, #]', rest, hash, key, value);
126 _length++;
127 _keys = null;
128 } else {
129 int bucketLength = JS('int', '#.length', bucket);
130 for (int i = 0; i < bucketLength; i += 2) {
131 if (JS('var', '#[#]', bucket, i) == key) {
132 JS('void', '#[# + 1] = #', bucket, i, value);
133 return;
134 }
135 }
136 JS('void', '#.push(#, #)', bucket, key, value);
137 _length++;
138 _keys = null;
139 }
140 }
141 }
142
143 V remove(K key) {
144 if (key is String && key != '__proto__') {
145 var strings = _strings;
146 if (strings == null) return null;
147 var probe = JS('var', '#[#]', strings, key);
148 if (probe == null) return null;
149 JS('void', 'delete #[#]', strings, key);
150 _length--;
151 _keys = null;
152 return JS('bool', '# === #', probe, strings) ? null : probe;
153 } else if (key is num) {
154 var nums = _nums;
155 if (nums == null) return null;
156 var probe = JS('var', '#[#]', nums, key);
157 if (probe == null) return null;
158 JS('void', 'delete #[#]', nums, key);
159 _length--;
160 _keys = null;
161 return JS('bool', '# === #', probe, nums) ? null : probe;
162 } else {
163 var rest = _rest;
164 if (rest == null) return null;
165 var hash = JS('num', 'Number(#)', key.hashCode);
166 var bucket = JS('var', '#[#]', rest, hash);
167 if (bucket == null) return null;
168 int bucketLength = JS('int', '#.length', bucket);
169 for (int i = 0; i < bucketLength; i += 2) {
170 if (JS('var', '#[#]', bucket, i) == key) {
171 var value = JS('var', '#[# + 1]', bucket, i);
172 int last = bucketLength - 2;
173 if (i != last) {
174 JS('void', '#[#] = #[#]', bucket, i, bucket, last);
175 JS('void', '#[# + 1] = #[# + 1]', bucket, i, bucket, last);
176 }
177 JS('void', '#.length = #', bucket, last);
178 _length--;
179 _keys = null;
180 return value;
181 }
182 }
183 return null;
184 }
185 }
186
187 void clear() {
188 if (_length > 0) {
189 _strings = _nums = _rest = _keys = null;
190 _length = 0;
191 }
192 }
193
194 void forEach(void f(K key, V value)) {
195 List keys = _computeKeys();
196 for (int i = 0, length = keys.length; i < length; i++) {
197 var key = keys[i];
198 f(key, this[key]);
199 if (!identical(keys, _keys)) {
200 throw new ConcurrentModificationError(this);
ahe 2013/03/22 16:18:31 This means there are situations where a concurrent
201 }
202 }
203 }
204
205 V putIfAbsent(K key, V ifAbsent()) {
206 if (containsKey(key)) return this[key];
207 V value = ifAbsent();
208 this[key] = value;
209 return value;
210 }
211
212 void addAll(Map<K, V> other) {
213 other.forEach((K key, V value) {
214 this[key] = value;
215 });
216 }
217
218 String toString() => Maps.mapToString(this);
219
220 List _computeKeys() {
221 if (_keys != null) return _keys;
222
223 List result = new List(_length);
224 int index = 0;
225 var strings = _strings;
226 if (strings != null) {
227 var names = JS('var', 'Object.getOwnPropertyNames(#)', strings);
ahe 2013/03/22 16:18:31 There is a comment in the emitter about an Opera b
ahe 2013/03/22 16:18:31 If the type of the JS expression is '=List' can't
228 int length = JS('int', '#.length', names);
229 for (int i = 0; i < length; i++) {
230 String key = JS('String', '#[#]', names, i);
231 result[index++] = key;
232 }
233 }
234
235 var nums = _nums;
236 if (_nums != null) {
237 var names = JS('var', 'Object.getOwnPropertyNames(#)', nums);
ahe 2013/03/22 16:18:31 Use =List?
238 int length = JS('int', '#.length', names);
239 for (int i = 0; i < length; i++) {
240 num key = JS('num', 'Number(#[#])', names, i);
ahe 2013/03/22 16:18:31 A comment explaining why Number is necessary would
241 result[index++] = key;
242 }
243 }
244
245 var rest = _rest;
246 if (rest != null) {
247 var names = JS('var', 'Object.getOwnPropertyNames(#)', rest);
ahe 2013/03/22 16:18:31 Use =List?
248 int length = JS('int', '#.length', names);
249 for (int i = 0; i < length; i++) {
250 var key = JS('String', '#[#]', names, i);
251 var bucket = JS('var', '#[#]', rest, key);
252 int bucketLength = JS('int', '#.length', bucket);
253 for (int i = 0; i < bucketLength; i += 2) {
254 var key = JS('var', '#[#]', bucket, i);
255 result[index++] = key;
256 }
257 }
258 }
259 return _keys = result;
260 }
261
262 _newHashTable() {
263 var table = JS('var', 'Object.create(null)');
264 var temporaryKey = '<non-identifier-key>';
265 JS('void', '#[#] = #', table, temporaryKey, table);
266 JS('void', 'delete #[#]', table, temporaryKey);
ahe 2013/03/22 16:18:31 Add a comment explaining the purpose of <non-ident
267 return table;
268 }
269 }
270
271
272 class JSMapKeyIterable<E> extends Iterable<E> {
273 final JSMap _map;
274 JSMapKeyIterable(this._map);
275
276 int get length => _map._length;
277 bool get isEmpty => _map._length == 0;
278
279 Iterator<E> get iterator {
280 return new JSMapKeyIterator<E>(_map, _map._computeKeys());
281 }
282
283 void forEach(void f(E element)) {
284 List keys = _map._computeKeys();
285 for (int i = 0, length = keys.length; i < length; i++) {
286 f(keys[i]);
287 if (!identical(keys, _map._keys)) {
288 throw new ConcurrentModificationError(_map);
289 }
290 }
291 }
292 }
293
294 class JSMapKeyIterator<E> implements Iterator<E> {
295 final JSMap _map;
296 final List _keys;
297 int _offset = 0;
298 E _current;
299
300 JSMapKeyIterator(this._map, this._keys);
301
302 E get current => _current;
303
304 bool moveNext() {
305 if (!identical(_keys, _map._keys)) {
306 throw new ConcurrentModificationError(_map);
307 } else if (_offset >= _keys.length) {
308 _current = null;
309 return false;
310 } else {
311 _current = _keys[_offset++];
312 return true;
313 }
314 }
315 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698