| 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 |