| 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 _interceptors; | 5 part of _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 @JsName(name: 'Array') | 13 @JsPeerInterface(name: 'Array') |
| 14 class JSArray<E> implements List<E>, JSIndexable { | 14 class JSArray<E> implements List<E>, JSIndexable { |
| 15 | 15 |
| 16 const JSArray(); | 16 const JSArray(); |
| 17 | 17 |
| 18 /** | 18 /** |
| 19 * Returns a fresh JavaScript Array, marked as fixed-length. | 19 * Constructor for adding type parameters to an existing JavaScript Array. |
| 20 * | |
| 21 * [length] must be a non-negative integer. | |
| 22 */ | 20 */ |
| 23 factory JSArray.fixed(int length) { | 21 factory JSArray.typed(allocation) => |
| 24 // Explicit type test is necessary to guard against JavaScript conversions | 22 // TODO(jmesserly): skip this when E is dynamic and Object. |
| 25 // in unchecked mode. | 23 JS('-dynamic', 'dart.list(#, #)', allocation, E); |
| 26 if ((length is !int) || (length < 0)) { | |
| 27 throw new ArgumentError("Length must be a non-negative integer: $length"); | |
| 28 } | |
| 29 return new JSArray<E>.markFixed(JS('', 'new Array(#)', length)); | |
| 30 } | |
| 31 | 24 |
| 32 /** | 25 // TODO(jmesserly): consider a fixed array subclass instead. |
| 33 * Returns a fresh growable JavaScript Array of zero length length. | 26 factory JSArray.markFixed(allocation) => |
| 34 */ | 27 new JSArray<E>.typed(markFixedList(allocation)); |
| 35 factory JSArray.emptyGrowable() => new JSArray<E>.markGrowable(JS('', '[]')); | |
| 36 | 28 |
| 37 /** | 29 factory JSArray.markGrowable(allocation) = JSArray.typed; |
| 38 * Returns a fresh growable JavaScript Array with initial length. | |
| 39 * | |
| 40 * [validatedLength] must be a non-negative integer. | |
| 41 */ | |
| 42 factory JSArray.growable(int length) { | |
| 43 // Explicit type test is necessary to guard against JavaScript conversions | |
| 44 // in unchecked mode. | |
| 45 if ((length is !int) || (length < 0)) { | |
| 46 throw new ArgumentError("Length must be a non-negative integer: $length"); | |
| 47 } | |
| 48 return new JSArray<E>.markGrowable(JS('', 'new Array(#)', length)); | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Constructor for adding type parameters to an existing JavaScript Array. | |
| 53 * The compiler specially recognizes this constructor. | |
| 54 * | |
| 55 * var a = new JSArray<int>.typed(JS('JSExtendableArray', '[]')); | |
| 56 * a is List<int> --> true | |
| 57 * a is List<String> --> false | |
| 58 * | |
| 59 * Usually either the [JSArray.markFixed] or [JSArray.markGrowable] | |
| 60 * constructors is used instead. | |
| 61 * | |
| 62 * The input must be a JavaScript Array. The JS form is just a re-assertion | |
| 63 * to help type analysis when the input type is sloppy. | |
| 64 */ | |
| 65 factory JSArray.typed(allocation) => JS('JSArray', '#', allocation); | |
| 66 | |
| 67 factory JSArray.markFixed(allocation) => | |
| 68 JS('JSFixedArray', '#', markFixedList(new JSArray<E>.typed(allocation))); | |
| 69 | |
| 70 factory JSArray.markGrowable(allocation) => | |
| 71 JS('JSExtendableArray', '#', new JSArray<E>.typed(allocation)); | |
| 72 | 30 |
| 73 static List markFixedList(List list) { | 31 static List markFixedList(List list) { |
| 74 // Functions are stored in the hidden class and not as properties in | 32 // Functions are stored in the hidden class and not as properties in |
| 75 // the object. We never actually look at the value, but only want | 33 // the object. We never actually look at the value, but only want |
| 76 // to know if the property exists. | 34 // to know if the property exists. |
| 77 JS('void', r'#.fixed$length = Array', list); | 35 JS('void', r'#.fixed$length = Array', list); |
| 78 return JS('JSFixedArray', '#', list); | 36 return list; |
| 79 } | |
| 80 | |
| 81 checkMutable(reason) { | |
| 82 if (this is !JSMutableArray) { | |
| 83 throw new UnsupportedError(reason); | |
| 84 } | |
| 85 } | 37 } |
| 86 | 38 |
| 87 checkGrowable(reason) { | 39 checkGrowable(reason) { |
| 88 if (JS('bool', r'#.fixed$length', this)) { | 40 if (JS('bool', r'#.fixed$length', this)) { |
| 89 throw new UnsupportedError(reason); | 41 throw new UnsupportedError(reason); |
| 90 } | 42 } |
| 91 } | 43 } |
| 92 | 44 |
| 93 void add(E value) { | 45 void add(E value) { |
| 94 checkGrowable('add'); | 46 checkGrowable('add'); |
| 95 JS('void', r'#.push(#)', this, value); | 47 JS('void', r'#.push(#)', this, value); |
| 96 } | 48 } |
| 97 | 49 |
| 98 E removeAt(int index) { | 50 E removeAt(int index) { |
| 99 if (index is !int) throw new ArgumentError(index); | 51 if (index is !int) throw new ArgumentError(index); |
| 100 if (index < 0 || index >= length) { | 52 if (index < 0 || index >= length) { |
| 101 throw new RangeError.value(index); | 53 throw new RangeError.value(index); |
| 102 } | 54 } |
| 103 checkGrowable('removeAt'); | 55 checkGrowable('removeAt'); |
| 104 return JS('var', r'#.splice(#, 1)[0]', this, index); | 56 return JS('-dynamic', r'#.splice(#, 1)[0]', this, index); |
| 105 } | 57 } |
| 106 | 58 |
| 107 void insert(int index, E value) { | 59 void insert(int index, E value) { |
| 108 if (index is !int) throw new ArgumentError(index); | 60 if (index is !int) throw new ArgumentError(index); |
| 109 if (index < 0 || index > length) { | 61 if (index < 0 || index > length) { |
| 110 throw new RangeError.value(index); | 62 throw new RangeError.value(index); |
| 111 } | 63 } |
| 112 checkGrowable('insert'); | 64 checkGrowable('insert'); |
| 113 JS('void', r'#.splice(#, 0, #)', this, index, value); | 65 JS('void', r'#.splice(#, 0, #)', this, index, value); |
| 114 } | 66 } |
| 115 | 67 |
| 116 void insertAll(int index, Iterable<E> iterable) { | 68 void insertAll(int index, Iterable<E> iterable) { |
| 117 checkGrowable('insertAll'); | 69 checkGrowable('insertAll'); |
| 118 IterableMixinWorkaround.insertAllList(this, index, iterable); | 70 IterableMixinWorkaround.insertAllList(this, index, iterable); |
| 119 } | 71 } |
| 120 | 72 |
| 121 void setAll(int index, Iterable<E> iterable) { | 73 void setAll(int index, Iterable<E> iterable) { |
| 122 checkMutable('setAll'); | |
| 123 IterableMixinWorkaround.setAllList(this, index, iterable); | 74 IterableMixinWorkaround.setAllList(this, index, iterable); |
| 124 } | 75 } |
| 125 | 76 |
| 126 E removeLast() { | 77 E removeLast() { |
| 127 checkGrowable('removeLast'); | 78 checkGrowable('removeLast'); |
| 128 if (length == 0) throw new RangeError.value(-1); | 79 if (length == 0) throw new RangeError.value(-1); |
| 129 return JS('var', r'#.pop()', this); | 80 return JS('var', r'#.pop()', this); |
| 130 } | 81 } |
| 131 | 82 |
| 132 bool remove(Object element) { | 83 bool remove(Object element) { |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 } | 189 } |
| 239 if (end == null) { | 190 if (end == null) { |
| 240 end = length; | 191 end = length; |
| 241 } else { | 192 } else { |
| 242 if (end is !int) throw new ArgumentError(end); | 193 if (end is !int) throw new ArgumentError(end); |
| 243 if (end < start || end > length) { | 194 if (end < start || end > length) { |
| 244 throw new RangeError.range(end, start, length); | 195 throw new RangeError.range(end, start, length); |
| 245 } | 196 } |
| 246 } | 197 } |
| 247 if (start == end) return <E>[]; | 198 if (start == end) return <E>[]; |
| 248 return new JSArray<E>.markGrowable( | 199 return new JSArray<E>.typed( |
| 249 JS('', r'#.slice(#, #)', this, start, end)); | 200 JS('', r'#.slice(#, #)', this, start, end)); |
| 250 } | 201 } |
| 251 | 202 |
| 252 | 203 |
| 253 Iterable<E> getRange(int start, int end) { | 204 Iterable<E> getRange(int start, int end) { |
| 254 return new IterableMixinWorkaround<E>().getRangeList(this, start, end); | 205 return new IterableMixinWorkaround<E>().getRangeList(this, start, end); |
| 255 } | 206 } |
| 256 | 207 |
| 257 E get first { | 208 E get first { |
| 258 if (length > 0) return this[0]; | 209 if (length > 0) return this[0]; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 281 } | 232 } |
| 282 Lists.copy(this, | 233 Lists.copy(this, |
| 283 end, | 234 end, |
| 284 this, | 235 this, |
| 285 start, | 236 start, |
| 286 receiverLength - end); | 237 receiverLength - end); |
| 287 this.length = receiverLength - (end - start); | 238 this.length = receiverLength - (end - start); |
| 288 } | 239 } |
| 289 | 240 |
| 290 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | 241 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { |
| 291 checkMutable('set range'); | |
| 292 IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); | 242 IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); |
| 293 } | 243 } |
| 294 | 244 |
| 295 void fillRange(int start, int end, [E fillValue]) { | 245 void fillRange(int start, int end, [E fillValue]) { |
| 296 checkMutable('fill range'); | |
| 297 IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); | 246 IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); |
| 298 } | 247 } |
| 299 | 248 |
| 300 void replaceRange(int start, int end, Iterable<E> iterable) { | 249 void replaceRange(int start, int end, Iterable<E> iterable) { |
| 301 checkGrowable('removeRange'); | |
| 302 IterableMixinWorkaround.replaceRangeList(this, start, end, iterable); | 250 IterableMixinWorkaround.replaceRangeList(this, start, end, iterable); |
| 303 } | 251 } |
| 304 | 252 |
| 305 bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f); | 253 bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f); |
| 306 | 254 |
| 307 bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f); | 255 bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f); |
| 308 | 256 |
| 309 Iterable<E> get reversed => | 257 Iterable<E> get reversed => |
| 310 new IterableMixinWorkaround<E>().reversedList(this); | 258 new IterableMixinWorkaround<E>().reversedList(this); |
| 311 | 259 |
| 312 void sort([int compare(E a, E b)]) { | 260 void sort([int compare(E a, E b)]) { |
| 313 checkMutable('sort'); | |
| 314 IterableMixinWorkaround.sortList(this, compare); | 261 IterableMixinWorkaround.sortList(this, compare); |
| 315 } | 262 } |
| 316 | 263 |
| 317 void shuffle([Random random]) { | 264 void shuffle([Random random]) { |
| 318 IterableMixinWorkaround.shuffleList(this, random); | 265 IterableMixinWorkaround.shuffleList(this, random); |
| 319 } | 266 } |
| 320 | 267 |
| 321 int indexOf(Object element, [int start = 0]) { | 268 int indexOf(Object element, [int start = 0]) { |
| 322 return IterableMixinWorkaround.indexOfList(this, element, start); | 269 return IterableMixinWorkaround.indexOfList(this, element, start); |
| 323 } | 270 } |
| 324 | 271 |
| 325 int lastIndexOf(Object element, [int start]) { | 272 int lastIndexOf(Object element, [int start]) { |
| 326 return IterableMixinWorkaround.lastIndexOfList(this, element, start); | 273 return IterableMixinWorkaround.lastIndexOfList(this, element, start); |
| 327 } | 274 } |
| 328 | 275 |
| 329 bool contains(Object other) { | 276 bool contains(Object other) { |
| 330 for (int i = 0; i < length; i++) { | 277 for (int i = 0; i < length; i++) { |
| 331 if (this[i] == other) return true; | 278 if (this[i] == other) return true; |
| 332 } | 279 } |
| 333 return false; | 280 return false; |
| 334 } | 281 } |
| 335 | 282 |
| 336 bool get isEmpty => length == 0; | 283 bool get isEmpty => length == 0; |
| 337 | 284 |
| 338 bool get isNotEmpty => !isEmpty; | 285 bool get isNotEmpty => !isEmpty; |
| 339 | 286 |
| 340 String toString() => ListBase.listToString(this); | 287 String toString() => ListBase.listToString(this); |
| 341 | 288 |
| 342 List<E> toList({ bool growable: true }) { | 289 List<E> toList({ bool growable: true }) { |
| 343 if (growable) { | 290 var list = JS('', '#.slice()', this); |
| 344 return new JSArray<E>.markGrowable(JS('', '#.slice()', this)); | 291 if (!growable) markFixedList(list); |
| 345 } else { | 292 return new JSArray<E>.typed(list); |
| 346 return new JSArray<E>.markFixed(JS('', '#.slice()', this)); | |
| 347 } | |
| 348 } | 293 } |
| 349 | 294 |
| 350 Set<E> toSet() => new Set<E>.from(this); | 295 Set<E> toSet() => new Set<E>.from(this); |
| 351 | 296 |
| 352 Iterator<E> get iterator => new ListIterator<E>(this); | 297 Iterator<E> get iterator => new ListIterator<E>(this); |
| 353 | 298 |
| 354 int get hashCode => Primitives.objectHashCode(this); | 299 int get hashCode => Primitives.objectHashCode(this); |
| 355 | 300 |
| 356 int get length => JS('JSUInt32', r'#.length', this); | 301 int get length => JS('JSUInt32', r'#.length', this); |
| 357 | 302 |
| 358 void set length(int newLength) { | 303 void set length(int newLength) { |
| 359 if (newLength is !int) throw new ArgumentError(newLength); | 304 if (newLength is !int) throw new ArgumentError(newLength); |
| 360 if (newLength < 0) throw new RangeError.value(newLength); | 305 if (newLength < 0) throw new RangeError.value(newLength); |
| 361 checkGrowable('set length'); | 306 checkGrowable('set length'); |
| 362 JS('void', r'#.length = #', this, newLength); | 307 JS('void', r'#.length = #', this, newLength); |
| 363 } | 308 } |
| 364 | 309 |
| 365 E operator [](int index) { | 310 E operator [](int index) { |
| 366 if (index is !int) throw new ArgumentError(index); | 311 if (index is !int) throw new ArgumentError(index); |
| 367 if (index >= length || index < 0) throw new RangeError.value(index); | 312 if (index >= length || index < 0) throw new RangeError.value(index); |
| 368 return JS('var', '#[#]', this, index); | 313 return JS('var', '#[#]', this, index); |
| 369 } | 314 } |
| 370 | 315 |
| 371 void operator []=(int index, E value) { | 316 void operator []=(int index, E value) { |
| 372 checkMutable('indexed set'); | |
| 373 if (index is !int) throw new ArgumentError(index); | 317 if (index is !int) throw new ArgumentError(index); |
| 374 if (index >= length || index < 0) throw new RangeError.value(index); | 318 if (index >= length || index < 0) throw new RangeError.value(index); |
| 375 JS('void', r'#[#] = #', this, index, value); | 319 JS('void', r'#[#] = #', this, index, value); |
| 376 } | 320 } |
| 377 | 321 |
| 378 Map<int, E> asMap() { | 322 Map<int, E> asMap() { |
| 379 return new IterableMixinWorkaround<E>().asMapList(this); | 323 return new IterableMixinWorkaround<E>().asMapList(this); |
| 380 } | 324 } |
| 381 } | 325 } |
| 382 | 326 |
| 383 /** | 327 /** |
| 384 * Dummy subclasses that allow the backend to track more precise | 328 * Dummy subclasses that allow the backend to track more precise |
| 385 * information about arrays through their type. The CPA type inference | 329 * information about arrays through their type. The CPA type inference |
| 386 * relies on the fact that these classes do not override [] nor []=. | 330 * relies on the fact that these classes do not override [] nor []=. |
| 387 */ | 331 */ |
| 388 class JSMutableArray<E> extends JSArray<E> implements JSMutableIndexable {} | 332 class JSMutableArray<E> extends JSArray<E> implements JSMutableIndexable {} |
| 389 class JSFixedArray<E> extends JSMutableArray<E> {} | 333 class JSFixedArray<E> extends JSMutableArray<E> {} |
| 390 class JSExtendableArray<E> extends JSMutableArray<E> {} | 334 class JSExtendableArray<E> extends JSMutableArray<E> {} |
| OLD | NEW |