| 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 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 if (JS('bool', r'#.fixed$length', this)) { | 57 if (JS('bool', r'#.fixed$length', this)) { |
| 58 throw new UnsupportedError(reason); | 58 throw new UnsupportedError(reason); |
| 59 } | 59 } |
| 60 } | 60 } |
| 61 | 61 |
| 62 void add(E value) { | 62 void add(E value) { |
| 63 checkGrowable('add'); | 63 checkGrowable('add'); |
| 64 JS('void', r'#.push(#)', this, value); | 64 JS('void', r'#.push(#)', this, value); |
| 65 } | 65 } |
| 66 | 66 |
| 67 E removeAt(int index) { | 67 E removeAt(@nullCheck int index) { |
| 68 checkGrowable('removeAt'); | 68 checkGrowable('removeAt'); |
| 69 if (index is! int) throw argumentErrorValue(index); | |
| 70 if (index < 0 || index >= length) { | 69 if (index < 0 || index >= length) { |
| 71 throw new RangeError.value(index); | 70 throw new RangeError.value(index); |
| 72 } | 71 } |
| 73 return JS('-dynamic', r'#.splice(#, 1)[0]', this, index); | 72 return JS('-dynamic', r'#.splice(#, 1)[0]', this, index); |
| 74 } | 73 } |
| 75 | 74 |
| 76 void insert(int index, E value) { | 75 void insert(@nullCheck int index, E value) { |
| 77 checkGrowable('insert'); | 76 checkGrowable('insert'); |
| 78 if (index is! int) throw argumentErrorValue(index); | |
| 79 if (index < 0 || index > length) { | 77 if (index < 0 || index > length) { |
| 80 throw new RangeError.value(index); | 78 throw new RangeError.value(index); |
| 81 } | 79 } |
| 82 JS('void', r'#.splice(#, 0, #)', this, index, value); | 80 JS('void', r'#.splice(#, 0, #)', this, index, value); |
| 83 } | 81 } |
| 84 | 82 |
| 85 void insertAll(int index, Iterable<E> iterable) { | 83 void insertAll(@nullCheck int index, Iterable<E> iterable) { |
| 86 checkGrowable('insertAll'); | 84 checkGrowable('insertAll'); |
| 87 RangeError.checkValueInInterval(index, 0, this.length, "index"); | 85 RangeError.checkValueInInterval(index, 0, this.length, "index"); |
| 88 if (iterable is! EfficientLengthIterable) { | 86 if (iterable is! EfficientLengthIterable) { |
| 89 iterable = iterable.toList(); | 87 iterable = iterable.toList(); |
| 90 } | 88 } |
| 89 @nullCheck |
| 91 int insertionLength = iterable.length; | 90 int insertionLength = iterable.length; |
| 92 this.length += insertionLength; | 91 this.length += insertionLength; |
| 93 int end = index + insertionLength; | 92 int end = index + insertionLength; |
| 94 this.setRange(end, this.length, this, index); | 93 this.setRange(end, this.length, this, index); |
| 95 this.setRange(index, end, iterable); | 94 this.setRange(index, end, iterable); |
| 96 } | 95 } |
| 97 | 96 |
| 98 void setAll(int index, Iterable<E> iterable) { | 97 void setAll(@nullCheck int index, Iterable<E> iterable) { |
| 99 checkMutable('setAll'); | 98 checkMutable('setAll'); |
| 100 RangeError.checkValueInInterval(index, 0, this.length, "index"); | 99 RangeError.checkValueInInterval(index, 0, this.length, "index"); |
| 101 for (var element in iterable) { | 100 for (var element in iterable) { |
| 102 this[index++] = element; | 101 this[index++] = element; |
| 103 } | 102 } |
| 104 } | 103 } |
| 105 | 104 |
| 106 E removeLast() { | 105 E removeLast() { |
| 107 checkGrowable('removeLast'); | 106 checkGrowable('removeLast'); |
| 108 if (length == 0) throw diagnoseIndexError(this, -1); | 107 if (length == 0) throw diagnoseIndexError(this, -1); |
| 109 return JS('var', r'#.pop()', this); | 108 return JS('var', r'#.pop()', this); |
| 110 } | 109 } |
| 111 | 110 |
| 112 bool remove(Object element) { | 111 bool remove(Object element) { |
| 113 checkGrowable('remove'); | 112 checkGrowable('remove'); |
| 114 for (int i = 0; i < this.length; i++) { | 113 var length = this.length; |
| 114 for (int i = 0; i < length; i++) { |
| 115 if (this[i] == element) { | 115 if (this[i] == element) { |
| 116 JS('var', r'#.splice(#, 1)', this, i); | 116 JS('var', r'#.splice(#, 1)', this, i); |
| 117 return true; | 117 return true; |
| 118 } | 118 } |
| 119 } | 119 } |
| 120 return false; | 120 return false; |
| 121 } | 121 } |
| 122 | 122 |
| 123 /** | 123 /** |
| 124 * Removes elements matching [test] from [this] List. | 124 * Removes elements matching [test] from [this] List. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 139 // the original list is updated to contain those elements. | 139 // the original list is updated to contain those elements. |
| 140 | 140 |
| 141 // TODO(sra): Replace this algorithm with one that retains a list of ranges | 141 // TODO(sra): Replace this algorithm with one that retains a list of ranges |
| 142 // to be removed. Most real uses remove 0, 1 or a few clustered elements. | 142 // to be removed. Most real uses remove 0, 1 or a few clustered elements. |
| 143 | 143 |
| 144 List retained = []; | 144 List retained = []; |
| 145 int end = this.length; | 145 int end = this.length; |
| 146 for (int i = 0; i < end; i++) { | 146 for (int i = 0; i < end; i++) { |
| 147 // TODO(22407): Improve bounds check elimination to allow this JS code to | 147 // TODO(22407): Improve bounds check elimination to allow this JS code to |
| 148 // be replaced by indexing. | 148 // be replaced by indexing. |
| 149 var element = JS('', '#[#]', this, i); | 149 E element = JS('-dynamic', '#[#]', this, i); |
| 150 // !test() ensures bool conversion in checked mode. | 150 // !test() ensures bool conversion in checked mode. |
| 151 if (!test(element) == removeMatching) { | 151 if (!test(element) == removeMatching) { |
| 152 retained.add(element); | 152 retained.add(element); |
| 153 } | 153 } |
| 154 if (this.length != end) throw new ConcurrentModificationError(this); | 154 if (this.length != end) throw new ConcurrentModificationError(this); |
| 155 } | 155 } |
| 156 if (retained.length == end) return; | 156 if (retained.length == end) return; |
| 157 this.length = retained.length; | 157 this.length = retained.length; |
| 158 for (int i = 0; i < retained.length; i++) { | 158 @nullCheck |
| 159 this[i] = retained[i]; | 159 var length = retained.length; |
| 160 for (int i = 0; i < length; i++) { |
| 161 JS('', '#[#] = #[#]', this, i, retained, i); |
| 160 } | 162 } |
| 161 } | 163 } |
| 162 | 164 |
| 163 Iterable<E> where(bool f(E element)) { | 165 Iterable<E> where(bool f(E element)) { |
| 164 return new WhereIterable<E>(this, f); | 166 return new WhereIterable<E>(this, f); |
| 165 } | 167 } |
| 166 | 168 |
| 167 Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) { | 169 Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) { |
| 168 return new ExpandIterable<E, dynamic/*=T*/ >(this, f); | 170 return new ExpandIterable<E, dynamic/*=T*/ >(this, f); |
| 169 } | 171 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 191 f(element); | 193 f(element); |
| 192 if (this.length != end) throw new ConcurrentModificationError(this); | 194 if (this.length != end) throw new ConcurrentModificationError(this); |
| 193 } | 195 } |
| 194 } | 196 } |
| 195 | 197 |
| 196 Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E element)) { | 198 Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E element)) { |
| 197 return new MappedListIterable<E, T>(this, f); | 199 return new MappedListIterable<E, T>(this, f); |
| 198 } | 200 } |
| 199 | 201 |
| 200 String join([String separator = ""]) { | 202 String join([String separator = ""]) { |
| 201 var list = new List(this.length); | 203 var length = this.length; |
| 202 for (int i = 0; i < this.length; i++) { | 204 var list = new List(length); |
| 205 for (int i = 0; i < length; i++) { |
| 203 list[i] = "${this[i]}"; | 206 list[i] = "${this[i]}"; |
| 204 } | 207 } |
| 205 return JS('String', "#.join(#)", list, separator); | 208 return JS('String', "#.join(#)", list, separator); |
| 206 } | 209 } |
| 207 | 210 |
| 208 Iterable<E> take(int n) { | 211 Iterable<E> take(int n) { |
| 209 return new SubListIterable<E>(this, 0, n); | 212 return new SubListIterable<E>(this, 0, n); |
| 210 } | 213 } |
| 211 | 214 |
| 212 Iterable<E> takeWhile(bool test(E value)) { | 215 Iterable<E> takeWhile(bool test(E value)) { |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 throw IterableElementError.noElement(); | 281 throw IterableElementError.noElement(); |
| 279 } | 282 } |
| 280 | 283 |
| 281 E singleWhere(bool test(E element)) { | 284 E singleWhere(bool test(E element)) { |
| 282 int length = this.length; | 285 int length = this.length; |
| 283 E match = null; | 286 E match = null; |
| 284 bool matchFound = false; | 287 bool matchFound = false; |
| 285 for (int i = 0; i < length; i++) { | 288 for (int i = 0; i < length; i++) { |
| 286 // TODO(22407): Improve bounds check elimination to allow this JS code to | 289 // TODO(22407): Improve bounds check elimination to allow this JS code to |
| 287 // be replaced by indexing. | 290 // be replaced by indexing. |
| 288 var element = JS('', '#[#]', this, i); | 291 E element = JS('-dynamic', '#[#]', this, i); |
| 289 if (test(element)) { | 292 if (test(element)) { |
| 290 if (matchFound) { | 293 if (matchFound) { |
| 291 throw IterableElementError.tooMany(); | 294 throw IterableElementError.tooMany(); |
| 292 } | 295 } |
| 293 matchFound = true; | 296 matchFound = true; |
| 294 match = element; | 297 match = element; |
| 295 } | 298 } |
| 296 if (length != this.length) { | 299 if (length != this.length) { |
| 297 throw new ConcurrentModificationError(this); | 300 throw new ConcurrentModificationError(this); |
| 298 } | 301 } |
| 299 } | 302 } |
| 300 if (matchFound) return match; | 303 if (matchFound) return match; |
| 301 throw IterableElementError.noElement(); | 304 throw IterableElementError.noElement(); |
| 302 } | 305 } |
| 303 | 306 |
| 304 E elementAt(int index) { | 307 E elementAt(int index) { |
| 305 return this[index]; | 308 return this[index]; |
| 306 } | 309 } |
| 307 | 310 |
| 308 List<E> sublist(int start, [int end]) { | 311 List<E> sublist(@nullCheck int start, [int end]) { |
| 309 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
| 310 if (start is! int) throw argumentErrorValue(start); | |
| 311 if (start < 0 || start > length) { | 312 if (start < 0 || start > length) { |
| 312 throw new RangeError.range(start, 0, length, "start"); | 313 throw new RangeError.range(start, 0, length, "start"); |
| 313 } | 314 } |
| 314 if (end == null) { | 315 if (end == null) { |
| 315 end = length; | 316 end = length; |
| 316 } else { | 317 } else { |
| 317 if (end is! int) throw argumentErrorValue(end); | 318 @notNull |
| 318 if (end < start || end > length) { | 319 var _end = end; |
| 320 if (_end < start || _end > length) { |
| 319 throw new RangeError.range(end, start, length, "end"); | 321 throw new RangeError.range(end, start, length, "end"); |
| 320 } | 322 } |
| 321 } | 323 } |
| 322 if (start == end) return <E>[]; | 324 if (start == end) return <E>[]; |
| 323 return new JSArray<E>.of(JS('', r'#.slice(#, #)', this, start, end)); | 325 return new JSArray<E>.of(JS('', r'#.slice(#, #)', this, start, end)); |
| 324 } | 326 } |
| 325 | 327 |
| 326 Iterable<E> getRange(int start, int end) { | 328 Iterable<E> getRange(int start, int end) { |
| 327 RangeError.checkValidRange(start, end, this.length); | 329 RangeError.checkValidRange(start, end, this.length); |
| 328 return new SubListIterable<E>(this, start, end); | 330 return new SubListIterable<E>(this, start, end); |
| 329 } | 331 } |
| 330 | 332 |
| 331 E get first { | 333 E get first { |
| 332 if (length > 0) return this[0]; | 334 if (length > 0) return this[0]; |
| 333 throw IterableElementError.noElement(); | 335 throw IterableElementError.noElement(); |
| 334 } | 336 } |
| 335 | 337 |
| 336 E get last { | 338 E get last { |
| 337 if (length > 0) return this[length - 1]; | 339 if (length > 0) return this[length - 1]; |
| 338 throw IterableElementError.noElement(); | 340 throw IterableElementError.noElement(); |
| 339 } | 341 } |
| 340 | 342 |
| 341 E get single { | 343 E get single { |
| 342 if (length == 1) return this[0]; | 344 if (length == 1) return this[0]; |
| 343 if (length == 0) throw IterableElementError.noElement(); | 345 if (length == 0) throw IterableElementError.noElement(); |
| 344 throw IterableElementError.tooMany(); | 346 throw IterableElementError.tooMany(); |
| 345 } | 347 } |
| 346 | 348 |
| 347 void removeRange(int start, int end) { | 349 void removeRange(@nullCheck int start, @nullCheck int end) { |
| 348 checkGrowable('removeRange'); | 350 checkGrowable('removeRange'); |
| 349 RangeError.checkValidRange(start, end, this.length); | 351 RangeError.checkValidRange(start, end, this.length); |
| 350 int deleteCount = end - start; | 352 int deleteCount = end - start; |
| 351 JS('', '#.splice(#, #)', this, start, deleteCount); | 353 JS('', '#.splice(#, #)', this, start, deleteCount); |
| 352 } | 354 } |
| 353 | 355 |
| 354 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | 356 void setRange(@nullCheck int start, @nullCheck int end, Iterable<E> iterable, |
| 357 [@nullCheck int skipCount = 0]) { |
| 355 checkMutable('set range'); | 358 checkMutable('set range'); |
| 356 | 359 |
| 357 RangeError.checkValidRange(start, end, this.length); | 360 RangeError.checkValidRange(start, end, this.length); |
| 358 int length = end - start; | 361 int length = end - start; |
| 359 if (length == 0) return; | 362 if (length == 0) return; |
| 360 RangeError.checkNotNegative(skipCount, "skipCount"); | 363 RangeError.checkNotNegative(skipCount, "skipCount"); |
| 361 | 364 |
| 362 List/*<E>*/ otherList; | 365 List<E> otherList; |
| 363 int otherStart; | 366 int otherStart = 0; |
| 364 // TODO(floitsch): Make this accept more. | 367 // TODO(floitsch): Make this accept more. |
| 365 if (iterable is List) { | 368 if (iterable is List<E>) { |
| 366 otherList = iterable; | 369 otherList = iterable; |
| 367 otherStart = skipCount; | 370 otherStart = skipCount; |
| 368 } else { | 371 } else { |
| 369 otherList = iterable.skip(skipCount).toList(growable: false); | 372 otherList = iterable.skip(skipCount).toList(growable: false); |
| 370 otherStart = 0; | 373 otherStart = 0; |
| 371 } | 374 } |
| 372 if (otherStart + length > otherList.length) { | 375 if (otherStart + length > otherList.length) { |
| 373 throw IterableElementError.tooFew(); | 376 throw IterableElementError.tooFew(); |
| 374 } | 377 } |
| 375 if (otherStart < start) { | 378 if (otherStart < start) { |
| 376 // Copy backwards to ensure correct copy if [from] is this. | 379 // Copy backwards to ensure correct copy if [from] is this. |
| 377 // TODO(sra): If [from] is the same Array as [this], we can copy without | 380 // TODO(sra): If [from] is the same Array as [this], we can copy without |
| 378 // type annotation checks on the stores. | 381 // type annotation checks on the stores. |
| 379 for (int i = length - 1; i >= 0; i--) { | 382 for (int i = length - 1; i >= 0; i--) { |
| 380 // Use JS to avoid bounds check (the bounds check elimination | 383 // Use JS to avoid bounds check (the bounds check elimination |
| 381 // optimzation is too weak). The 'E' type annotation is a store type | 384 // optimzation is too weak). The 'E' type annotation is a store type |
| 382 // check - we can't rely on iterable, it could be List<dynamic>. | 385 // check - we can't rely on iterable, it could be List<dynamic>. |
| 383 E element = otherList[otherStart + i]; | 386 E element = otherList[otherStart + i]; |
| 384 JS('', '#[#] = #', this, start + i, element); | 387 JS('', '#[#] = #', this, start + i, element); |
| 385 } | 388 } |
| 386 } else { | 389 } else { |
| 387 for (int i = 0; i < length; i++) { | 390 for (int i = 0; i < length; i++) { |
| 388 E element = otherList[otherStart + i]; | 391 E element = otherList[otherStart + i]; |
| 389 JS('', '#[#] = #', this, start + i, element); | 392 JS('', '#[#] = #', this, start + i, element); |
| 390 } | 393 } |
| 391 } | 394 } |
| 392 } | 395 } |
| 393 | 396 |
| 394 void fillRange(int start, int end, [E fillValue]) { | 397 void fillRange(@nullCheck int start, @nullCheck int end, [E fillValue]) { |
| 395 checkMutable('fill range'); | 398 checkMutable('fill range'); |
| 396 RangeError.checkValidRange(start, end, this.length); | 399 RangeError.checkValidRange(start, end, this.length); |
| 397 for (int i = start; i < end; i++) { | 400 for (int i = start; i < end; i++) { |
| 398 // Store is safe since [fillValue] type has been checked as parameter. | 401 // Store is safe since [fillValue] type has been checked as parameter. |
| 399 JS('', '#[#] = #', this, i, fillValue); | 402 JS('', '#[#] = #', this, i, fillValue); |
| 400 } | 403 } |
| 401 } | 404 } |
| 402 | 405 |
| 403 void replaceRange(int start, int end, Iterable<E> replacement) { | 406 void replaceRange( |
| 407 @nullCheck int start, @nullCheck int end, Iterable<E> replacement) { |
| 404 checkGrowable('replace range'); | 408 checkGrowable('replace range'); |
| 405 RangeError.checkValidRange(start, end, this.length); | 409 RangeError.checkValidRange(start, end, this.length); |
| 406 if (replacement is! EfficientLengthIterable) { | 410 if (replacement is! EfficientLengthIterable) { |
| 407 replacement = replacement.toList(); | 411 replacement = replacement.toList(); |
| 408 } | 412 } |
| 409 int removeLength = end - start; | 413 int removeLength = end - start; |
| 414 @nullCheck |
| 410 int insertLength = replacement.length; | 415 int insertLength = replacement.length; |
| 411 if (removeLength >= insertLength) { | 416 if (removeLength >= insertLength) { |
| 412 int delta = removeLength - insertLength; | 417 int delta = removeLength - insertLength; |
| 413 int insertEnd = start + insertLength; | 418 int insertEnd = start + insertLength; |
| 414 int newLength = this.length - delta; | 419 int newLength = this.length - delta; |
| 415 this.setRange(start, insertEnd, replacement); | 420 this.setRange(start, insertEnd, replacement); |
| 416 if (delta != 0) { | 421 if (delta != 0) { |
| 417 this.setRange(insertEnd, newLength, this, end); | 422 this.setRange(insertEnd, newLength, this, end); |
| 418 this.length = newLength; | 423 this.length = newLength; |
| 419 } | 424 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 437 if (this.length != end) throw new ConcurrentModificationError(this); | 442 if (this.length != end) throw new ConcurrentModificationError(this); |
| 438 } | 443 } |
| 439 return false; | 444 return false; |
| 440 } | 445 } |
| 441 | 446 |
| 442 bool every(bool test(E element)) { | 447 bool every(bool test(E element)) { |
| 443 int end = this.length; | 448 int end = this.length; |
| 444 for (int i = 0; i < end; i++) { | 449 for (int i = 0; i < end; i++) { |
| 445 // TODO(22407): Improve bounds check elimination to allow this JS code to | 450 // TODO(22407): Improve bounds check elimination to allow this JS code to |
| 446 // be replaced by indexing. | 451 // be replaced by indexing. |
| 447 var/*=E*/ element = JS('', '#[#]', this, i); | 452 E element = JS('-dynamic', '#[#]', this, i); |
| 448 if (!test(element)) return false; | 453 if (!test(element)) return false; |
| 449 if (this.length != end) throw new ConcurrentModificationError(this); | 454 if (this.length != end) throw new ConcurrentModificationError(this); |
| 450 } | 455 } |
| 451 return true; | 456 return true; |
| 452 } | 457 } |
| 453 | 458 |
| 454 Iterable<E> get reversed => new ReversedListIterable<E>(this); | 459 Iterable<E> get reversed => new ReversedListIterable<E>(this); |
| 455 | 460 |
| 456 void sort([int compare(E a, E b)]) { | 461 void sort([int compare(E a, E b)]) { |
| 457 checkMutable('sort'); | 462 checkMutable('sort'); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 468 int length = this.length; | 473 int length = this.length; |
| 469 while (length > 1) { | 474 while (length > 1) { |
| 470 int pos = random.nextInt(length); | 475 int pos = random.nextInt(length); |
| 471 length -= 1; | 476 length -= 1; |
| 472 var tmp = this[length]; | 477 var tmp = this[length]; |
| 473 this[length] = this[pos]; | 478 this[length] = this[pos]; |
| 474 this[pos] = tmp; | 479 this[pos] = tmp; |
| 475 } | 480 } |
| 476 } | 481 } |
| 477 | 482 |
| 478 int indexOf(Object element, [int start = 0]) { | 483 int indexOf(Object element, [@nullCheck int start = 0]) { |
| 479 if (start >= this.length) { | 484 int length = this.length; |
| 485 if (start >= length) { |
| 480 return -1; | 486 return -1; |
| 481 } | 487 } |
| 482 if (start < 0) { | 488 if (start < 0) { |
| 483 start = 0; | 489 start = 0; |
| 484 } | 490 } |
| 485 for (int i = start; i < this.length; i++) { | 491 for (int i = start; i < length; i++) { |
| 486 if (this[i] == element) { | 492 if (this[i] == element) { |
| 487 return i; | 493 return i; |
| 488 } | 494 } |
| 489 } | 495 } |
| 490 return -1; | 496 return -1; |
| 491 } | 497 } |
| 492 | 498 |
| 493 int lastIndexOf(Object element, [int startIndex]) { | 499 int lastIndexOf(Object element, [int _startIndex]) { |
| 494 if (startIndex == null) { | 500 @notNull |
| 501 int startIndex = _startIndex ?? this.length - 1; |
| 502 if (startIndex >= this.length) { |
| 495 startIndex = this.length - 1; | 503 startIndex = this.length - 1; |
| 496 } else { | 504 } else if (startIndex < 0) { |
| 497 if (startIndex < 0) { | 505 return -1; |
| 498 return -1; | |
| 499 } | |
| 500 if (startIndex >= this.length) { | |
| 501 startIndex = this.length - 1; | |
| 502 } | |
| 503 } | 506 } |
| 504 for (int i = startIndex; i >= 0; i--) { | 507 for (int i = startIndex; i >= 0; i--) { |
| 505 if (this[i] == element) { | 508 if (this[i] == element) { |
| 506 return i; | 509 return i; |
| 507 } | 510 } |
| 508 } | 511 } |
| 509 return -1; | 512 return -1; |
| 510 } | 513 } |
| 511 | 514 |
| 512 bool contains(Object other) { | 515 bool contains(Object other) { |
| 516 var length = this.length; |
| 513 for (int i = 0; i < length; i++) { | 517 for (int i = 0; i < length; i++) { |
| 514 if (this[i] == other) return true; | 518 E element = JS('-dynamic | Null', '#[#]', this, i); |
| 519 if (element == other) return true; |
| 515 } | 520 } |
| 516 return false; | 521 return false; |
| 517 } | 522 } |
| 518 | 523 |
| 524 @notNull |
| 519 bool get isEmpty => length == 0; | 525 bool get isEmpty => length == 0; |
| 520 | 526 |
| 527 @notNull |
| 521 bool get isNotEmpty => !isEmpty; | 528 bool get isNotEmpty => !isEmpty; |
| 522 | 529 |
| 523 String toString() => ListBase.listToString(this); | 530 String toString() => ListBase.listToString(this); |
| 524 | 531 |
| 525 List<E> toList({bool growable: true}) { | 532 List<E> toList({@nullCheck bool growable: true}) { |
| 526 var list = JS('', '#.slice()', this); | 533 var list = JS('', '#.slice()', this); |
| 527 if (!growable) markFixedList(list); | 534 if (!growable) markFixedList(list); |
| 528 return new JSArray<E>.of(list); | 535 return new JSArray<E>.of(list); |
| 529 } | 536 } |
| 530 | 537 |
| 531 Set<E> toSet() => new Set<E>.from(this); | 538 Set<E> toSet() => new Set<E>.from(this); |
| 532 | 539 |
| 533 Iterator<E> get iterator => new ArrayIterator<E>(this); | 540 Iterator<E> get iterator => new ArrayIterator<E>(this); |
| 534 | 541 |
| 535 int get hashCode => Primitives.objectHashCode(this); | 542 int get hashCode => Primitives.objectHashCode(this); |
| 536 | 543 |
| 544 @notNull |
| 537 bool operator ==(other) => identical(this, other); | 545 bool operator ==(other) => identical(this, other); |
| 538 | 546 |
| 547 @notNull |
| 539 int get length => JS('int', r'#.length', this); | 548 int get length => JS('int', r'#.length', this); |
| 540 | 549 |
| 541 void set length(int newLength) { | 550 void set length(@nullCheck int newLength) { |
| 542 checkGrowable('set length'); | 551 checkGrowable('set length'); |
| 543 if (newLength is! int) { | |
| 544 throw new ArgumentError.value(newLength, 'newLength'); | |
| 545 } | |
| 546 // TODO(sra): Remove this test and let JavaScript throw an error. | 552 // TODO(sra): Remove this test and let JavaScript throw an error. |
| 547 if (newLength < 0) { | 553 if (newLength < 0) { |
| 548 throw new RangeError.range(newLength, 0, null, 'newLength'); | 554 throw new RangeError.range(newLength, 0, null, 'newLength'); |
| 549 } | 555 } |
| 550 // JavaScript with throw a RangeError for numbers that are too big. The | 556 // JavaScript with throw a RangeError for numbers that are too big. The |
| 551 // message does not contain the value. | 557 // message does not contain the value. |
| 552 JS('void', r'#.length = #', this, newLength); | 558 JS('void', r'#.length = #', this, newLength); |
| 553 } | 559 } |
| 554 | 560 |
| 555 E operator [](int index) { | 561 E operator [](int index) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 class JSFixedArray<E> extends JSMutableArray<E> {} | 602 class JSFixedArray<E> extends JSMutableArray<E> {} |
| 597 | 603 |
| 598 class JSExtendableArray<E> extends JSMutableArray<E> {} | 604 class JSExtendableArray<E> extends JSMutableArray<E> {} |
| 599 | 605 |
| 600 class JSUnmodifiableArray<E> extends JSArray<E> {} // Already is JSIndexable. | 606 class JSUnmodifiableArray<E> extends JSArray<E> {} // Already is JSIndexable. |
| 601 | 607 |
| 602 /// An [Iterator] that iterates a JSArray. | 608 /// An [Iterator] that iterates a JSArray. |
| 603 /// | 609 /// |
| 604 class ArrayIterator<E> implements Iterator<E> { | 610 class ArrayIterator<E> implements Iterator<E> { |
| 605 final JSArray<E> _iterable; | 611 final JSArray<E> _iterable; |
| 612 @notNull |
| 606 final int _length; | 613 final int _length; |
| 614 @notNull |
| 607 int _index; | 615 int _index; |
| 608 E _current; | 616 E _current; |
| 609 | 617 |
| 610 ArrayIterator(JSArray<E> iterable) | 618 ArrayIterator(JSArray<E> iterable) |
| 611 : _iterable = iterable, | 619 : _iterable = iterable, |
| 612 _length = iterable.length, | 620 _length = iterable.length, |
| 613 _index = 0; | 621 _index = 0; |
| 614 | 622 |
| 615 E get current => _current; | 623 E get current => _current; |
| 616 | 624 |
| 617 bool moveNext() { | 625 bool moveNext() { |
| 626 @notNull |
| 618 int length = _iterable.length; | 627 int length = _iterable.length; |
| 619 | 628 |
| 620 // We have to do the length check even on fixed length Arrays. If we can | 629 // We have to do the length check even on fixed length Arrays. If we can |
| 621 // inline moveNext() we might be able to GVN the length and eliminate this | 630 // inline moveNext() we might be able to GVN the length and eliminate this |
| 622 // check on known fixed length JSArray. | 631 // check on known fixed length JSArray. |
| 623 if (_length != length) { | 632 if (_length != length) { |
| 624 throw throwConcurrentModificationError(_iterable); | 633 throw throwConcurrentModificationError(_iterable); |
| 625 } | 634 } |
| 626 | 635 |
| 627 if (_index >= length) { | 636 if (_index >= length) { |
| 628 _current = null; | 637 _current = null; |
| 629 return false; | 638 return false; |
| 630 } | 639 } |
| 631 _current = _iterable[_index]; | 640 _current = _iterable[_index]; |
| 632 _index++; | 641 _index++; |
| 633 return true; | 642 return true; |
| 634 } | 643 } |
| 635 } | 644 } |
| OLD | NEW |