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

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/js_array.dart

Issue 1212513002: sdk files reorganization to make dart2js a proper package (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renamed Created 5 years, 5 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of _interceptors;
6
7 /**
8 * The interceptor class for [List]. The compiler recognizes this
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
11 * argument added to each member.
12 */
13 class JSArray<E> extends Interceptor implements List<E>, JSIndexable {
14
15 const JSArray();
16
17 /**
18 * Returns a fresh JavaScript Array, marked as fixed-length.
19 *
20 * [length] must be a non-negative integer.
21 */
22 factory JSArray.fixed(int length) {
23 // Explicit type test is necessary to guard against JavaScript conversions
24 // in unchecked mode.
25 if ((length is !int) || (length < 0)) {
26 throw new ArgumentError("Length must be a non-negative integer: $length");
27 }
28 return new JSArray<E>.markFixed(JS('', 'new Array(#)', length));
29 }
30
31 /**
32 * Returns a fresh growable JavaScript Array of zero length length.
33 */
34 factory JSArray.emptyGrowable() => new JSArray<E>.markGrowable(JS('', '[]'));
35
36 /**
37 * Returns a fresh growable JavaScript Array with initial length.
38 *
39 * [validatedLength] must be a non-negative integer.
40 */
41 factory JSArray.growable(int length) {
42 // Explicit type test is necessary to guard against JavaScript conversions
43 // in unchecked mode.
44 if ((length is !int) || (length < 0)) {
45 throw new ArgumentError("Length must be a non-negative integer: $length");
46 }
47 return new JSArray<E>.markGrowable(JS('', 'new Array(#)', length));
48 }
49
50 /**
51 * Constructor for adding type parameters to an existing JavaScript Array.
52 * The compiler specially recognizes this constructor.
53 *
54 * var a = new JSArray<int>.typed(JS('JSExtendableArray', '[]'));
55 * a is List<int> --> true
56 * a is List<String> --> false
57 *
58 * Usually either the [JSArray.markFixed] or [JSArray.markGrowable]
59 * constructors is used instead.
60 *
61 * The input must be a JavaScript Array. The JS form is just a re-assertion
62 * to help type analysis when the input type is sloppy.
63 */
64 factory JSArray.typed(allocation) => JS('JSArray', '#', allocation);
65
66 factory JSArray.markFixed(allocation) =>
67 JS('JSFixedArray', '#', markFixedList(new JSArray<E>.typed(allocation)));
68
69 factory JSArray.markGrowable(allocation) =>
70 JS('JSExtendableArray', '#', new JSArray<E>.typed(allocation));
71
72 static List markFixedList(List list) {
73 // Functions are stored in the hidden class and not as properties in
74 // the object. We never actually look at the value, but only want
75 // to know if the property exists.
76 JS('void', r'#.fixed$length = Array', list);
77 return JS('JSFixedArray', '#', list);
78 }
79
80 static List markUnmodifiableList(List list) {
81 // Functions are stored in the hidden class and not as properties in
82 // the object. We never actually look at the value, but only want
83 // to know if the property exists.
84 JS('void', r'#.fixed$length = Array', list);
85 JS('void', r'#.immutable$list = Array', list);
86 return JS('JSUnmodifiableArray', '#', list);
87 }
88
89 checkMutable(reason) {
90 if (this is !JSMutableArray) {
91 throw new UnsupportedError(reason);
92 }
93 }
94
95 checkGrowable(reason) {
96 if (this is !JSExtendableArray) {
97 throw new UnsupportedError(reason);
98 }
99 }
100
101 void add(E value) {
102 checkGrowable('add');
103 JS('void', r'#.push(#)', this, value);
104 }
105
106 E removeAt(int index) {
107 checkGrowable('removeAt');
108 if (index is !int) throw argumentErrorValue(index);
109 if (index < 0 || index >= length) {
110 throw new RangeError.value(index);
111 }
112 return JS('var', r'#.splice(#, 1)[0]', this, index);
113 }
114
115 void insert(int index, E value) {
116 checkGrowable('insert');
117 if (index is !int) throw argumentErrorValue(index);
118 if (index < 0 || index > length) {
119 throw new RangeError.value(index);
120 }
121 JS('void', r'#.splice(#, 0, #)', this, index, value);
122 }
123
124 void insertAll(int index, Iterable<E> iterable) {
125 checkGrowable('insertAll');
126 RangeError.checkValueInInterval(index, 0, this.length, "index");
127 if (iterable is! EfficientLength) {
128 iterable = iterable.toList();
129 }
130 int insertionLength = iterable.length;
131 this.length += insertionLength;
132 int end = index + insertionLength;
133 this.setRange(end, this.length, this, index);
134 this.setRange(index, end, iterable);
135 }
136
137 void setAll(int index, Iterable<E> iterable) {
138 checkMutable('setAll');
139 RangeError.checkValueInInterval(index, 0, this.length, "index");
140 for (var element in iterable) {
141 this[index++] = element;
142 }
143 }
144
145 E removeLast() {
146 checkGrowable('removeLast');
147 if (length == 0) throw new RangeError.value(-1);
148 return JS('var', r'#.pop()', this);
149 }
150
151 bool remove(Object element) {
152 checkGrowable('remove');
153 for (int i = 0; i < this.length; i++) {
154 if (this[i] == element) {
155 JS('var', r'#.splice(#, 1)', this, i);
156 return true;
157 }
158 }
159 return false;
160 }
161
162 /**
163 * Removes elements matching [test] from [this] List.
164 */
165 void removeWhere(bool test(E element)) {
166 checkGrowable('removeWhere');
167 _removeWhere(test, true);
168 }
169
170 void retainWhere(bool test(E element)) {
171 checkGrowable('retainWhere');
172 _removeWhere(test, false);
173 }
174
175 void _removeWhere(bool test(E element), bool removeMatching) {
176 // Performed in two steps, to avoid exposing an inconsistent state
177 // to the [test] function. First the elements to retain are found, and then
178 // the original list is updated to contain those elements.
179
180 // TODO(sra): Replace this algorthim with one that retains a list of ranges
181 // to be removed. Most real uses remove 0, 1 or a few clustered elements.
182
183 List retained = [];
184 int end = this.length;
185 for (int i = 0; i < end; i++) {
186 // TODO(22407): Improve bounds check elimination to allow this JS code to
187 // be replaced by indexing.
188 var element = JS('', '#[#]', this, i);
189 // !test() ensures bool conversion in checked mode.
190 if (!test(element) == removeMatching) {
191 retained.add(element);
192 }
193 if (this.length != end) throw new ConcurrentModificationError(this);
194 }
195 if (retained.length == end) return;
196 this.length = retained.length;
197 for (int i = 0; i < retained.length; i++) {
198 this[i] = retained[i];
199 }
200 }
201
202 Iterable<E> where(bool f(E element)) {
203 return new WhereIterable<E>(this, f);
204 }
205
206 Iterable expand(Iterable f(E element)) {
207 return new ExpandIterable<E, dynamic>(this, f);
208 }
209
210 void addAll(Iterable<E> collection) {
211 checkGrowable('addAll');
212 for (E e in collection) {
213 JS('void', r'#.push(#)', this, e);
214 }
215 }
216
217 void clear() {
218 length = 0;
219 }
220
221 void forEach(void f(E element)) {
222 int end = this.length;
223 for (int i = 0; i < end; i++) {
224 // TODO(22407): Improve bounds check elimination to allow this JS code to
225 // be replaced by indexing.
226 var element = JS('', '#[#]', this, i);
227 f(element);
228 if (this.length != end) throw new ConcurrentModificationError(this);
229 }
230 }
231
232 Iterable map(f(E element)) {
233 return new MappedListIterable(this, f);
234 }
235
236 String join([String separator = ""]) {
237 var list = new List(this.length);
238 for (int i = 0; i < this.length; i++) {
239 list[i] = "${this[i]}";
240 }
241 return JS('String', "#.join(#)", list, separator);
242 }
243
244 Iterable<E> take(int n) {
245 return new SubListIterable<E>(this, 0, n);
246 }
247
248 Iterable<E> takeWhile(bool test(E value)) {
249 return new TakeWhileIterable<E>(this, test);
250 }
251
252 Iterable<E> skip(int n) {
253 return new SubListIterable<E>(this, n, null);
254 }
255
256 Iterable<E> skipWhile(bool test(E value)) {
257 return new SkipWhileIterable<E>(this, test);
258 }
259
260 E reduce(E combine(E previousValue, E element)) {
261 int length = this.length;
262 if (length == 0) throw IterableElementError.noElement();
263 E value = this[0];
264 for (int i = 1; i < length; i++) {
265 // TODO(22407): Improve bounds check elimination to allow this JS code to
266 // be replaced by indexing.
267 var element = JS('', '#[#]', this, i);
268 value = combine(value, element);
269 if (length != this.length) throw new ConcurrentModificationError(this);
270 }
271 return value;
272 }
273
274 fold(var initialValue, combine(var previousValue, E element)) {
275 var value = initialValue;
276 int length = this.length;
277 for (int i = 0; i < length; i++) {
278 // TODO(22407): Improve bounds check elimination to allow this JS code to
279 // be replaced by indexing.
280 var element = JS('', '#[#]', this, i);
281 value = combine(value, element);
282 if (this.length != length) throw new ConcurrentModificationError(this);
283 }
284 return value;
285 }
286
287 E firstWhere(bool test(E value), {E orElse()}) {
288 var end = this.length;
289 for (int i = 0; i < end; ++i) {
290 // TODO(22407): Improve bounds check elimination to allow this JS code to
291 // be replaced by indexing.
292 var element = JS('', '#[#]', this, i);
293 if (test(element)) return element;
294 if (this.length != end) throw new ConcurrentModificationError(this);
295 }
296 if (orElse != null) return orElse();
297 throw IterableElementError.noElement();
298 }
299
300 E lastWhere(bool test(E element), { E orElse() }) {
301 int length = this.length;
302 for (int i = length - 1; i >= 0; i--) {
303 // TODO(22407): Improve bounds check elimination to allow this JS code to
304 // be replaced by indexing.
305 var element = JS('', '#[#]', this, i);
306 if (test(element)) return element;
307 if (length != this.length) {
308 throw new ConcurrentModificationError(this);
309 }
310 }
311 if (orElse != null) return orElse();
312 throw IterableElementError.noElement();
313 }
314
315 E singleWhere(bool test(E element)) {
316 int length = this.length;
317 E match = null;
318 bool matchFound = false;
319 for (int i = 0; i < length; i++) {
320 // TODO(22407): Improve bounds check elimination to allow this JS code to
321 // be replaced by indexing.
322 var element = JS('', '#[#]', this, i);
323 if (test(element)) {
324 if (matchFound) {
325 throw IterableElementError.tooMany();
326 }
327 matchFound = true;
328 match = element;
329 }
330 if (length != this.length) {
331 throw new ConcurrentModificationError(this);
332 }
333 }
334 if (matchFound) return match;
335 throw IterableElementError.noElement();
336 }
337
338 E elementAt(int index) {
339 return this[index];
340 }
341
342 List<E> sublist(int start, [int end]) {
343 checkNull(start); // TODO(ahe): This is not specified but co19 tests it.
344 if (start is !int) throw argumentErrorValue(start);
345 if (start < 0 || start > length) {
346 throw new RangeError.range(start, 0, length);
347 }
348 if (end == null) {
349 end = length;
350 } else {
351 if (end is !int) throw argumentErrorValue(end);
352 if (end < start || end > length) {
353 throw new RangeError.range(end, start, length);
354 }
355 }
356 if (start == end) return <E>[];
357 return new JSArray<E>.markGrowable(
358 JS('', r'#.slice(#, #)', this, start, end));
359 }
360
361
362 Iterable<E> getRange(int start, int end) {
363 RangeError.checkValidRange(start, end, this.length);
364 return new SubListIterable<E>(this, start, end);
365 }
366
367 E get first {
368 if (length > 0) return this[0];
369 throw IterableElementError.noElement();
370 }
371
372 E get last {
373 if (length > 0) return this[length - 1];
374 throw IterableElementError.noElement();
375 }
376
377 E get single {
378 if (length == 1) return this[0];
379 if (length == 0) throw IterableElementError.noElement();
380 throw IterableElementError.tooMany();
381 }
382
383 void removeRange(int start, int end) {
384 checkGrowable('removeRange');
385 RangeError.checkValidRange(start, end, this.length);
386 int deleteCount = end - start;
387 JS('', '#.splice(#, #)', this, start, deleteCount);
388 }
389
390 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
391 checkMutable('set range');
392
393 RangeError.checkValidRange(start, end, this.length);
394 int length = end - start;
395 if (length == 0) return;
396 RangeError.checkNotNegative(skipCount, "skipCount");
397
398 List otherList;
399 int otherStart;
400 // TODO(floitsch): Make this accept more.
401 if (iterable is List) {
402 otherList = iterable;
403 otherStart = skipCount;
404 } else {
405 otherList = iterable.skip(skipCount).toList(growable: false);
406 otherStart = 0;
407 }
408 if (otherStart + length > otherList.length) {
409 throw IterableElementError.tooFew();
410 }
411 if (otherStart < start) {
412 // Copy backwards to ensure correct copy if [from] is this.
413 // TODO(sra): If [from] is the same Array as [this], we can copy without
414 // type annotation checks on the stores.
415 for (int i = length - 1; i >= 0; i--) {
416 // Use JS to avoid bounds check (the bounds check elimination
417 // optimzation is too weak). The 'E' type annotation is a store type
418 // check - we can't rely on iterable, it could be List<dynamic>.
419 E element = otherList[otherStart + i];
420 JS('', '#[#] = #', this, start + i, element);
421 }
422 } else {
423 for (int i = 0; i < length; i++) {
424 E element = otherList[otherStart + i];
425 JS('', '#[#] = #', this, start + i, element);
426 }
427 }
428 }
429
430 void fillRange(int start, int end, [E fillValue]) {
431 checkMutable('fill range');
432 RangeError.checkValidRange(start, end, this.length);
433 for (int i = start; i < end; i++) {
434 // Store is safe since [fillValue] type has been checked as parameter.
435 JS('', '#[#] = #', this, i, fillValue);
436 }
437 }
438
439 void replaceRange(int start, int end, Iterable<E> replacement) {
440 checkGrowable('replace range');
441 RangeError.checkValidRange(start, end, this.length);
442 if (replacement is! EfficientLength) {
443 replacement = replacement.toList();
444 }
445 int removeLength = end - start;
446 int insertLength = replacement.length;
447 if (removeLength >= insertLength) {
448 int delta = removeLength - insertLength;
449 int insertEnd = start + insertLength;
450 int newLength = this.length - delta;
451 this.setRange(start, insertEnd, replacement);
452 if (delta != 0) {
453 this.setRange(insertEnd, newLength, this, end);
454 this.length = newLength;
455 }
456 } else {
457 int delta = insertLength - removeLength;
458 int newLength = this.length + delta;
459 int insertEnd = start + insertLength; // aka. end + delta.
460 this.length = newLength;
461 this.setRange(insertEnd, newLength, this, end);
462 this.setRange(start, insertEnd, replacement);
463 }
464 }
465
466 bool any(bool test(E element)) {
467 int end = this.length;
468 for (int i = 0; i < end; i++) {
469 // TODO(22407): Improve bounds check elimination to allow this JS code to
470 // be replaced by indexing.
471 var element = JS('', '#[#]', this, i);
472 if (test(element)) return true;
473 if (this.length != end) throw new ConcurrentModificationError(this);
474 }
475 return false;
476 }
477
478 bool every(bool test(E element)) {
479 int end = this.length;
480 for (int i = 0; i < end; i++) {
481 // TODO(22407): Improve bounds check elimination to allow this JS code to
482 // be replaced by indexing.
483 var element = JS('', '#[#]', this, i);
484 if (!test(element)) return false;
485 if (this.length != end) throw new ConcurrentModificationError(this);
486 }
487 return true;
488 }
489
490 Iterable<E> get reversed => new ReversedListIterable<E>(this);
491
492 void sort([int compare(E a, E b)]) {
493 checkMutable('sort');
494 Sort.sort(this, compare == null ? Comparable.compare : compare);
495 }
496
497 void shuffle([Random random]) {
498 checkMutable('shuffle');
499 if (random == null) random = new Random();
500 int length = this.length;
501 while (length > 1) {
502 int pos = random.nextInt(length);
503 length -= 1;
504 var tmp = this[length];
505 this[length] = this[pos];
506 this[pos] = tmp;
507 }
508 }
509
510 int indexOf(Object element, [int start = 0]) {
511 if (start >= this.length) {
512 return -1;
513 }
514 if (start < 0) {
515 start = 0;
516 }
517 for (int i = start; i < this.length; i++) {
518 if (this[i] == element) {
519 return i;
520 }
521 }
522 return -1;
523 }
524
525 int lastIndexOf(Object element, [int startIndex]) {
526 if (startIndex == null) {
527 startIndex = this.length - 1;
528 } else {
529 if (startIndex < 0) {
530 return -1;
531 }
532 if (startIndex >= this.length) {
533 startIndex = this.length - 1;
534 }
535 }
536 for (int i = startIndex; i >= 0; i--) {
537 if (this[i] == element) {
538 return i;
539 }
540 }
541 return -1;
542 }
543
544 bool contains(Object other) {
545 for (int i = 0; i < length; i++) {
546 if (this[i] == other) return true;
547 }
548 return false;
549 }
550
551 bool get isEmpty => length == 0;
552
553 bool get isNotEmpty => !isEmpty;
554
555 String toString() => ListBase.listToString(this);
556
557 List<E> toList({ bool growable: true }) =>
558 growable
559 ? _toListGrowable()
560 : _toListFixed();
561
562 List<E> _toListGrowable() =>
563 new JSArray<E>.markGrowable(JS('', '#.slice()', this));
564
565 List<E> _toListFixed() =>
566 new JSArray<E>.markFixed(JS('', '#.slice()', this));
567
568 Set<E> toSet() => new Set<E>.from(this);
569
570 Iterator<E> get iterator => new ArrayIterator<E>(this);
571
572 int get hashCode => Primitives.objectHashCode(this);
573
574 int get length => JS('JSUInt32', r'#.length', this);
575
576 void set length(int newLength) {
577 checkGrowable('set length');
578 if (newLength is !int) {
579 throw new ArgumentError.value(newLength, 'newLength');
580 }
581 // TODO(sra): Remove this test and let JavaScript throw an error.
582 if (newLength < 0) {
583 throw new RangeError.range(newLength, 0, null, 'newLength');
584 }
585 // JavaScript with throw a RangeError for numbers that are too big. The
586 // message does not contain the value.
587 JS('void', r'#.length = #', this, newLength);
588 }
589
590 E operator [](int index) {
591 if (index is !int) throw diagnoseIndexError(this, index);
592 if (index >= length || index < 0) throw diagnoseIndexError(this, index);
593 return JS('var', '#[#]', this, index);
594 }
595
596 void operator []=(int index, E value) {
597 checkMutable('indexed set');
598 if (index is !int) throw diagnoseIndexError(this, index);
599 if (index >= length || index < 0) throw diagnoseIndexError(this, index);
600 JS('void', r'#[#] = #', this, index, value);
601 }
602
603 Map<int, E> asMap() {
604 return new ListMapView<E>(this);
605 }
606 }
607
608 /**
609 * Dummy subclasses that allow the backend to track more precise
610 * information about arrays through their type. The CPA type inference
611 * relies on the fact that these classes do not override [] nor []=.
612 *
613 * These classes are really a fiction, and can have no methods, since
614 * getInterceptor always returns JSArray. We should consider pushing the
615 * 'isGrowable' and 'isMutable' checks into the getInterceptor implementation so
616 * these classes can have specialized implementations. Doing so will challenge
617 * many assuptions in the JS backend.
618 */
619 class JSMutableArray<E> extends JSArray<E> implements JSMutableIndexable {}
620 class JSFixedArray<E> extends JSMutableArray<E> {}
621 class JSExtendableArray<E> extends JSMutableArray<E> {}
622 class JSUnmodifiableArray<E> extends JSArray<E> {} // Already is JSIndexable.
623
624
625 /// An [Iterator] that iterates a JSArray.
626 ///
627 class ArrayIterator<E> implements Iterator<E> {
628 final JSArray<E> _iterable;
629 final int _length;
630 int _index;
631 E _current;
632
633 ArrayIterator(JSArray<E> iterable)
634 : _iterable = iterable, _length = iterable.length, _index = 0;
635
636 E get current => _current;
637
638 bool moveNext() {
639 int length = _iterable.length;
640
641 // We have to do the length check even on fixed length Arrays. If we can
642 // inline moveNext() we might be able to GVN the length and eliminate this
643 // check on known fixed length JSArray.
644 if (_length != length) {
645 throw new ConcurrentModificationError(_iterable);
646 }
647
648 if (_index >= length) {
649 _current = null;
650 return false;
651 }
652 _current = _iterable[_index];
653 _index++;
654 return true;
655 }
656 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698