OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 part of dart._interceptors; | 5 part of dart._interceptors; |
6 | 6 |
7 /** | 7 /** |
8 * The interceptor class for [List]. The compiler recognizes this | 8 * The interceptor class for [List]. The compiler recognizes this |
9 * class as an interceptor, and changes references to [:this:] to | 9 * class as an interceptor, and changes references to [:this:] to |
10 * actually use the receiver of the method, which is generated as an extra | 10 * actually use the receiver of the method, which is generated as an extra |
11 * argument added to each member. | 11 * argument added to each member. |
12 */ | 12 */ |
13 @JsPeerInterface(name: 'Array') | 13 @JsPeerInterface(name: 'Array') |
14 class JSArray<E> implements List<E>, JSIndexable<E> { | 14 class JSArray<E> implements List<E>, JSIndexable<E> { |
15 | |
16 const JSArray(); | 15 const JSArray(); |
17 | 16 |
18 /** | 17 /** |
19 * Constructor for adding type parameters to an existing JavaScript Array. | 18 * Constructor for adding type parameters to an existing JavaScript Array. |
20 */ | 19 */ |
21 factory JSArray.typed(allocation) => | 20 factory JSArray.typed(allocation) => |
22 // TODO(jmesserly): skip this when E is dynamic and Object. | 21 // TODO(jmesserly): skip this when E is dynamic and Object. |
23 JS('-dynamic', 'dart.list(#, #)', allocation, E); | 22 JS('-dynamic', 'dart.list(#, #)', allocation, E); |
24 | 23 |
25 /** | 24 /** |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 } | 66 } |
68 } | 67 } |
69 | 68 |
70 void add(E value) { | 69 void add(E value) { |
71 checkGrowable('add'); | 70 checkGrowable('add'); |
72 JS('void', r'#.push(#)', this, value); | 71 JS('void', r'#.push(#)', this, value); |
73 } | 72 } |
74 | 73 |
75 E removeAt(int index) { | 74 E removeAt(int index) { |
76 checkGrowable('removeAt'); | 75 checkGrowable('removeAt'); |
77 if (index is !int) throw argumentErrorValue(index); | 76 if (index is! int) throw argumentErrorValue(index); |
78 if (index < 0 || index >= length) { | 77 if (index < 0 || index >= length) { |
79 throw new RangeError.value(index); | 78 throw new RangeError.value(index); |
80 } | 79 } |
81 return JS('-dynamic', r'#.splice(#, 1)[0]', this, index); | 80 return JS('-dynamic', r'#.splice(#, 1)[0]', this, index); |
82 } | 81 } |
83 | 82 |
84 void insert(int index, E value) { | 83 void insert(int index, E value) { |
85 checkGrowable('insert'); | 84 checkGrowable('insert'); |
86 if (index is !int) throw argumentErrorValue(index); | 85 if (index is! int) throw argumentErrorValue(index); |
87 if (index < 0 || index > length) { | 86 if (index < 0 || index > length) { |
88 throw new RangeError.value(index); | 87 throw new RangeError.value(index); |
89 } | 88 } |
90 JS('void', r'#.splice(#, 0, #)', this, index, value); | 89 JS('void', r'#.splice(#, 0, #)', this, index, value); |
91 } | 90 } |
92 | 91 |
93 void insertAll(int index, Iterable<E> iterable) { | 92 void insertAll(int index, Iterable<E> iterable) { |
94 checkGrowable('insertAll'); | 93 checkGrowable('insertAll'); |
95 RangeError.checkValueInInterval(index, 0, this.length, "index"); | 94 RangeError.checkValueInInterval(index, 0, this.length, "index"); |
96 if (iterable is! EfficientLengthIterable) { | 95 if (iterable is! EfficientLengthIterable) { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 for (int i = 0; i < retained.length; i++) { | 165 for (int i = 0; i < retained.length; i++) { |
167 this[i] = retained[i]; | 166 this[i] = retained[i]; |
168 } | 167 } |
169 } | 168 } |
170 | 169 |
171 Iterable<E> where(bool f(E element)) { | 170 Iterable<E> where(bool f(E element)) { |
172 return new WhereIterable<E>(this, f); | 171 return new WhereIterable<E>(this, f); |
173 } | 172 } |
174 | 173 |
175 Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) { | 174 Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) { |
176 return new ExpandIterable<E, dynamic/*=T*/>(this, f); | 175 return new ExpandIterable<E, dynamic/*=T*/ >(this, f); |
177 } | 176 } |
178 | 177 |
179 void addAll(Iterable<E> collection) { | 178 void addAll(Iterable<E> collection) { |
180 int i = this.length; | 179 int i = this.length; |
181 checkGrowable('addAll'); | 180 checkGrowable('addAll'); |
182 for (E e in collection) { | 181 for (E e in collection) { |
183 assert(i == this.length || (throw new ConcurrentModificationError(this))); | 182 assert(i == this.length || (throw new ConcurrentModificationError(this))); |
184 i++; | 183 i++; |
185 JS('void', r'#.push(#)', this, e); | 184 JS('void', r'#.push(#)', this, e); |
186 } | 185 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 for (int i = 1; i < length; i++) { | 235 for (int i = 1; i < length; i++) { |
237 // TODO(22407): Improve bounds check elimination to allow this JS code to | 236 // TODO(22407): Improve bounds check elimination to allow this JS code to |
238 // be replaced by indexing. | 237 // be replaced by indexing. |
239 var/*=E*/ element = JS('', '#[#]', this, i); | 238 var/*=E*/ element = JS('', '#[#]', this, i); |
240 value = combine(value, element); | 239 value = combine(value, element); |
241 if (length != this.length) throw new ConcurrentModificationError(this); | 240 if (length != this.length) throw new ConcurrentModificationError(this); |
242 } | 241 } |
243 return value; | 242 return value; |
244 } | 243 } |
245 | 244 |
246 /*=T*/ fold/*<T>*/(/*=T*/ initialValue, /*=T*/ combine(/*=T*/ previousValue, E
element)) { | 245 /*=T*/ fold/*<T>*/( |
| 246 /*=T*/ initialValue, /*=T*/ combine(/*=T*/ previousValue, E element)) { |
247 var/*=T*/ value = initialValue; | 247 var/*=T*/ value = initialValue; |
248 int length = this.length; | 248 int length = this.length; |
249 for (int i = 0; i < length; i++) { | 249 for (int i = 0; i < length; i++) { |
250 // TODO(22407): Improve bounds check elimination to allow this JS code to | 250 // TODO(22407): Improve bounds check elimination to allow this JS code to |
251 // be replaced by indexing. | 251 // be replaced by indexing. |
252 var/*=E*/ element = JS('', '#[#]', this, i); | 252 var/*=E*/ element = JS('', '#[#]', this, i); |
253 value = combine(value, element); | 253 value = combine(value, element); |
254 if (this.length != length) throw new ConcurrentModificationError(this); | 254 if (this.length != length) throw new ConcurrentModificationError(this); |
255 } | 255 } |
256 return value; | 256 return value; |
257 } | 257 } |
258 | 258 |
259 E firstWhere(bool test(E value), {E orElse()}) { | 259 E firstWhere(bool test(E value), {E orElse()}) { |
260 int end = this.length; | 260 int end = this.length; |
261 for (int i = 0; i < end; ++i) { | 261 for (int i = 0; i < end; ++i) { |
262 // TODO(22407): Improve bounds check elimination to allow this JS code to | 262 // TODO(22407): Improve bounds check elimination to allow this JS code to |
263 // be replaced by indexing. | 263 // be replaced by indexing. |
264 var/*=E*/ element = JS('', '#[#]', this, i); | 264 var/*=E*/ element = JS('', '#[#]', this, i); |
265 if (test(element)) return element; | 265 if (test(element)) return element; |
266 if (this.length != end) throw new ConcurrentModificationError(this); | 266 if (this.length != end) throw new ConcurrentModificationError(this); |
267 } | 267 } |
268 if (orElse != null) return orElse(); | 268 if (orElse != null) return orElse(); |
269 throw IterableElementError.noElement(); | 269 throw IterableElementError.noElement(); |
270 } | 270 } |
271 | 271 |
272 E lastWhere(bool test(E element), { E orElse() }) { | 272 E lastWhere(bool test(E element), {E orElse()}) { |
273 int length = this.length; | 273 int length = this.length; |
274 for (int i = length - 1; i >= 0; i--) { | 274 for (int i = length - 1; i >= 0; i--) { |
275 // TODO(22407): Improve bounds check elimination to allow this JS code to | 275 // TODO(22407): Improve bounds check elimination to allow this JS code to |
276 // be replaced by indexing. | 276 // be replaced by indexing. |
277 var/*=E*/ element = JS('', '#[#]', this, i); | 277 var/*=E*/ element = JS('', '#[#]', this, i); |
278 if (test(element)) return element; | 278 if (test(element)) return element; |
279 if (length != this.length) { | 279 if (length != this.length) { |
280 throw new ConcurrentModificationError(this); | 280 throw new ConcurrentModificationError(this); |
281 } | 281 } |
282 } | 282 } |
(...skipping 23 matching lines...) Expand all Loading... |
306 if (matchFound) return match; | 306 if (matchFound) return match; |
307 throw IterableElementError.noElement(); | 307 throw IterableElementError.noElement(); |
308 } | 308 } |
309 | 309 |
310 E elementAt(int index) { | 310 E elementAt(int index) { |
311 return this[index]; | 311 return this[index]; |
312 } | 312 } |
313 | 313 |
314 List<E> sublist(int start, [int end]) { | 314 List<E> sublist(int start, [int end]) { |
315 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | 315 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. |
316 if (start is !int) throw argumentErrorValue(start); | 316 if (start is! int) throw argumentErrorValue(start); |
317 if (start < 0 || start > length) { | 317 if (start < 0 || start > length) { |
318 throw new RangeError.range(start, 0, length, "start"); | 318 throw new RangeError.range(start, 0, length, "start"); |
319 } | 319 } |
320 if (end == null) { | 320 if (end == null) { |
321 end = length; | 321 end = length; |
322 } else { | 322 } else { |
323 if (end is !int) throw argumentErrorValue(end); | 323 if (end is! int) throw argumentErrorValue(end); |
324 if (end < start || end > length) { | 324 if (end < start || end > length) { |
325 throw new RangeError.range(end, start, length, "end"); | 325 throw new RangeError.range(end, start, length, "end"); |
326 } | 326 } |
327 } | 327 } |
328 if (start == end) return <E>[]; | 328 if (start == end) return <E>[]; |
329 return new JSArray<E>.typed( | 329 return new JSArray<E>.typed(JS('', r'#.slice(#, #)', this, start, end)); |
330 JS('', r'#.slice(#, #)', this, start, end)); | |
331 } | 330 } |
332 | 331 |
333 | |
334 Iterable<E> getRange(int start, int end) { | 332 Iterable<E> getRange(int start, int end) { |
335 RangeError.checkValidRange(start, end, this.length); | 333 RangeError.checkValidRange(start, end, this.length); |
336 return new SubListIterable<E>(this, start, end); | 334 return new SubListIterable<E>(this, start, end); |
337 } | 335 } |
338 | 336 |
339 E get first { | 337 E get first { |
340 if (length > 0) return this[0]; | 338 if (length > 0) return this[0]; |
341 throw IterableElementError.noElement(); | 339 throw IterableElementError.noElement(); |
342 } | 340 } |
343 | 341 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
421 int insertEnd = start + insertLength; | 419 int insertEnd = start + insertLength; |
422 int newLength = this.length - delta; | 420 int newLength = this.length - delta; |
423 this.setRange(start, insertEnd, replacement); | 421 this.setRange(start, insertEnd, replacement); |
424 if (delta != 0) { | 422 if (delta != 0) { |
425 this.setRange(insertEnd, newLength, this, end); | 423 this.setRange(insertEnd, newLength, this, end); |
426 this.length = newLength; | 424 this.length = newLength; |
427 } | 425 } |
428 } else { | 426 } else { |
429 int delta = insertLength - removeLength; | 427 int delta = insertLength - removeLength; |
430 int newLength = this.length + delta; | 428 int newLength = this.length + delta; |
431 int insertEnd = start + insertLength; // aka. end + delta. | 429 int insertEnd = start + insertLength; // aka. end + delta. |
432 this.length = newLength; | 430 this.length = newLength; |
433 this.setRange(insertEnd, newLength, this, end); | 431 this.setRange(insertEnd, newLength, this, end); |
434 this.setRange(start, insertEnd, replacement); | 432 this.setRange(start, insertEnd, replacement); |
435 } | 433 } |
436 } | 434 } |
437 | 435 |
438 bool any(bool test(E element)) { | 436 bool any(bool test(E element)) { |
439 int end = this.length; | 437 int end = this.length; |
440 for (int i = 0; i < end; i++) { | 438 for (int i = 0; i < end; i++) { |
441 // TODO(22407): Improve bounds check elimination to allow this JS code to | 439 // TODO(22407): Improve bounds check elimination to allow this JS code to |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
523 } | 521 } |
524 return false; | 522 return false; |
525 } | 523 } |
526 | 524 |
527 bool get isEmpty => length == 0; | 525 bool get isEmpty => length == 0; |
528 | 526 |
529 bool get isNotEmpty => !isEmpty; | 527 bool get isNotEmpty => !isEmpty; |
530 | 528 |
531 String toString() => ListBase.listToString(this); | 529 String toString() => ListBase.listToString(this); |
532 | 530 |
533 List<E> toList({ bool growable: true }) { | 531 List<E> toList({bool growable: true}) { |
534 var list = JS('', '#.slice()', this); | 532 var list = JS('', '#.slice()', this); |
535 if (!growable) markFixedList(list); | 533 if (!growable) markFixedList(list); |
536 return new JSArray<E>.typed(list); | 534 return new JSArray<E>.typed(list); |
537 } | 535 } |
538 | 536 |
539 Set<E> toSet() => new Set<E>.from(this); | 537 Set<E> toSet() => new Set<E>.from(this); |
540 | 538 |
541 Iterator<E> get iterator => new ArrayIterator<E>(this); | 539 Iterator<E> get iterator => new ArrayIterator<E>(this); |
542 | 540 |
543 int get hashCode => Primitives.objectHashCode(this); | 541 int get hashCode => Primitives.objectHashCode(this); |
544 | 542 |
545 int get length => JS('int', r'#.length', this); | 543 int get length => JS('int', r'#.length', this); |
546 | 544 |
547 void set length(int newLength) { | 545 void set length(int newLength) { |
548 checkGrowable('set length'); | 546 checkGrowable('set length'); |
549 if (newLength is !int) { | 547 if (newLength is! int) { |
550 throw new ArgumentError.value(newLength, 'newLength'); | 548 throw new ArgumentError.value(newLength, 'newLength'); |
551 } | 549 } |
552 // TODO(sra): Remove this test and let JavaScript throw an error. | 550 // TODO(sra): Remove this test and let JavaScript throw an error. |
553 if (newLength < 0) { | 551 if (newLength < 0) { |
554 throw new RangeError.range(newLength, 0, null, 'newLength'); | 552 throw new RangeError.range(newLength, 0, null, 'newLength'); |
555 } | 553 } |
556 // JavaScript with throw a RangeError for numbers that are too big. The | 554 // JavaScript with throw a RangeError for numbers that are too big. The |
557 // message does not contain the value. | 555 // message does not contain the value. |
558 JS('void', r'#.length = #', this, newLength); | 556 JS('void', r'#.length = #', this, newLength); |
559 } | 557 } |
560 | 558 |
561 E operator [](int index) { | 559 E operator [](int index) { |
562 if (index is !int) throw diagnoseIndexError(this, index); | 560 if (index is! int) throw diagnoseIndexError(this, index); |
563 if (index >= length || index < 0) throw diagnoseIndexError(this, index); | 561 if (index >= length || index < 0) throw diagnoseIndexError(this, index); |
564 return JS('var', '#[#]', this, index); | 562 return JS('var', '#[#]', this, index); |
565 } | 563 } |
566 | 564 |
567 void operator []=(int index, E value) { | 565 void operator []=(int index, E value) { |
568 checkMutable('indexed set'); | 566 checkMutable('indexed set'); |
569 if (index is !int) throw diagnoseIndexError(this, index); | 567 if (index is! int) throw diagnoseIndexError(this, index); |
570 if (index >= length || index < 0) throw diagnoseIndexError(this, index); | 568 if (index >= length || index < 0) throw diagnoseIndexError(this, index); |
571 JS('void', r'#[#] = #', this, index, value); | 569 JS('void', r'#[#] = #', this, index, value); |
572 } | 570 } |
573 | 571 |
574 Map<int, E> asMap() { | 572 Map<int, E> asMap() { |
575 return new ListMapView<E>(this); | 573 return new ListMapView<E>(this); |
576 } | 574 } |
577 } | 575 } |
578 | 576 |
579 /** | 577 /** |
580 * Dummy subclasses that allow the backend to track more precise | 578 * Dummy subclasses that allow the backend to track more precise |
581 * information about arrays through their type. The CPA type inference | 579 * information about arrays through their type. The CPA type inference |
582 * relies on the fact that these classes do not override [] nor []=. | 580 * relies on the fact that these classes do not override [] nor []=. |
583 * | 581 * |
584 * These classes are really a fiction, and can have no methods, since | 582 * These classes are really a fiction, and can have no methods, since |
585 * getInterceptor always returns JSArray. We should consider pushing the | 583 * getInterceptor always returns JSArray. We should consider pushing the |
586 * 'isGrowable' and 'isMutable' checks into the getInterceptor implementation so | 584 * 'isGrowable' and 'isMutable' checks into the getInterceptor implementation so |
587 * these classes can have specialized implementations. Doing so will challenge | 585 * these classes can have specialized implementations. Doing so will challenge |
588 * many assuptions in the JS backend. | 586 * many assuptions in the JS backend. |
589 */ | 587 */ |
590 class JSMutableArray<E> extends JSArray<E> {} | 588 class JSMutableArray<E> extends JSArray<E> {} |
| 589 |
591 class JSFixedArray<E> extends JSMutableArray<E> {} | 590 class JSFixedArray<E> extends JSMutableArray<E> {} |
| 591 |
592 class JSExtendableArray<E> extends JSMutableArray<E> {} | 592 class JSExtendableArray<E> extends JSMutableArray<E> {} |
| 593 |
593 class JSUnmodifiableArray<E> extends JSArray<E> {} // Already is JSIndexable. | 594 class JSUnmodifiableArray<E> extends JSArray<E> {} // Already is JSIndexable. |
594 | 595 |
595 | |
596 /// An [Iterator] that iterates a JSArray. | 596 /// An [Iterator] that iterates a JSArray. |
597 /// | 597 /// |
598 class ArrayIterator<E> implements Iterator<E> { | 598 class ArrayIterator<E> implements Iterator<E> { |
599 final JSArray<E> _iterable; | 599 final JSArray<E> _iterable; |
600 final int _length; | 600 final int _length; |
601 int _index; | 601 int _index; |
602 E _current; | 602 E _current; |
603 | 603 |
604 ArrayIterator(JSArray<E> iterable) | 604 ArrayIterator(JSArray<E> iterable) |
605 : _iterable = iterable, _length = iterable.length, _index = 0; | 605 : _iterable = iterable, |
| 606 _length = iterable.length, |
| 607 _index = 0; |
606 | 608 |
607 E get current => _current; | 609 E get current => _current; |
608 | 610 |
609 bool moveNext() { | 611 bool moveNext() { |
610 int length = _iterable.length; | 612 int length = _iterable.length; |
611 | 613 |
612 // We have to do the length check even on fixed length Arrays. If we can | 614 // We have to do the length check even on fixed length Arrays. If we can |
613 // inline moveNext() we might be able to GVN the length and eliminate this | 615 // inline moveNext() we might be able to GVN the length and eliminate this |
614 // check on known fixed length JSArray. | 616 // check on known fixed length JSArray. |
615 if (_length != length) { | 617 if (_length != length) { |
616 throw throwConcurrentModificationError(_iterable); | 618 throw throwConcurrentModificationError(_iterable); |
617 } | 619 } |
618 | 620 |
619 if (_index >= length) { | 621 if (_index >= length) { |
620 _current = null; | 622 _current = null; |
621 return false; | 623 return false; |
622 } | 624 } |
623 _current = _iterable[_index]; | 625 _current = _iterable[_index]; |
624 _index++; | 626 _index++; |
625 return true; | 627 return true; |
626 } | 628 } |
627 } | 629 } |
OLD | NEW |