Skip to content

Commit a5baf4e

Browse files
osa1Commit Queue
authored and
Commit Queue
committed
[dart2wasm] Improve WasmListBase.{setRange,setAll}
- In `setRange`, use `array.copy` instruction when the iterable argument is another Wasm-array-backed list. (instead of only when the iterable and `this` are identical) - In `setRange`, add special case for `SubListIterable`. - In `setAll`, use unchecked reads from `iterable` when it's a Wasm-array-backed list. Also fix various error checking in `setAll` and `setRange`. `setAll` and `setRange` tests updated to test handling of different types of iterable arguments. CoreLibraryReviewExempt: adds internal method Change-Id: Ib3ed566018e929950eac4d1f3d41dcd406003f91 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/383860 Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]> Commit-Queue: Ömer Ağacan <[email protected]>
1 parent 00070c7 commit a5baf4e

File tree

4 files changed

+268
-151
lines changed

4 files changed

+268
-151
lines changed

sdk/lib/_internal/wasm/lib/list.dart

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -67,54 +67,116 @@ abstract class _ModifiableList<E> extends WasmListBase<E> {
6767
: super._withData(length, data);
6868

6969
@pragma('wasm:prefer-inline')
70+
@override
7071
void operator []=(int index, E value) {
7172
indexCheckWithName(index, _length, "[]=");
7273
_data[index] = value;
7374
}
7475

75-
// List interface.
76+
@override
7677
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
7778
RangeError.checkValidRange(start, end, this.length);
7879
int length = end - start;
7980
if (length == 0) return;
8081
RangeError.checkNotNegative(skipCount, "skipCount");
81-
if (identical(this, iterable)) {
82-
_data.copy(start, _data, skipCount, length);
83-
} else if (iterable is List<E>) {
84-
Lists.copy(iterable, skipCount, this, start, length);
85-
} else {
86-
Iterator<E> it = iterable.iterator;
87-
while (skipCount > 0) {
88-
if (!it.moveNext()) return;
89-
skipCount--;
82+
83+
// Look through `SubListIterable`s while still testing for the fast case as
84+
// first thing.
85+
while (true) {
86+
if (iterable is WasmListBase) {
87+
final iterableWasmList = unsafeCast<WasmListBase>(iterable);
88+
if (skipCount + length > iterableWasmList.length) {
89+
throw IterableElementError.tooFew();
90+
}
91+
_data.copy(start, iterableWasmList._data, skipCount, length);
92+
return;
9093
}
91-
for (int i = start; i < end; i++) {
92-
if (!it.moveNext()) return;
93-
_data[i] = it.current;
94+
95+
if (iterable is List) {
96+
final iterableList = unsafeCast<List<E>>(iterable);
97+
for (int i = skipCount, j = start; i < skipCount + length; i++, j++) {
98+
_data[j] = iterableList[i];
99+
}
100+
return;
94101
}
102+
103+
if (iterable is SubListIterable) {
104+
final listIterable = unsafeCast<SubListIterable<E>>(iterable);
105+
var sourceLength = listIterable.length;
106+
if (sourceLength - skipCount < length) {
107+
throw IterableElementError.tooFew();
108+
}
109+
iterable = SubListIterable.iterableOf(listIterable);
110+
skipCount += SubListIterable.startOf(listIterable);
111+
continue;
112+
}
113+
114+
break;
115+
}
116+
117+
Iterator<E> it = iterable.iterator;
118+
while (skipCount > 0) {
119+
if (!it.moveNext()) throw IterableElementError.tooFew();
120+
skipCount--;
121+
}
122+
for (int i = start; i < end; i++) {
123+
if (!it.moveNext()) throw IterableElementError.tooFew();
124+
_data[i] = it.current;
95125
}
96126
}
97127

128+
@override
98129
void setAll(int index, Iterable<E> iterable) {
99-
if (index < 0 || index > this.length) {
100-
throw RangeError.range(index, 0, this.length, "index");
101-
}
102-
List<E> iterableAsList;
103-
if (identical(this, iterable)) {
104-
iterableAsList = this;
105-
} else if (iterable is List<E>) {
106-
iterableAsList = iterable;
107-
} else {
108-
for (var value in iterable) {
109-
this[index++] = value;
130+
final length = this.length;
131+
132+
// index < 0 || index > length
133+
if (index.gtU(length)) {
134+
throw RangeError.range(index, 0, length, "index");
135+
}
136+
137+
if (iterable is WasmListBase) {
138+
final iterableWasmList = unsafeCast<WasmListBase>(iterable);
139+
final elementCount = iterableWasmList.length;
140+
141+
// Elements to copy = min(length - index, elementCount).
142+
int copyCount = length - index;
143+
if (copyCount > elementCount) {
144+
copyCount = elementCount;
145+
}
146+
147+
_data.copy(index, iterableWasmList._data, 0, copyCount);
148+
149+
if (elementCount > copyCount) {
150+
throw IndexError.withLength(length, length);
110151
}
152+
111153
return;
112154
}
113-
int length = iterableAsList.length;
114-
if (index + length > this.length) {
115-
throw RangeError.range(index + length, 0, this.length);
155+
156+
if (iterable is List) {
157+
final iterableList = unsafeCast<List<E>>(iterable);
158+
final elementCount = iterableList.length;
159+
160+
// Elements to copy = min(length - index, elementCount).
161+
int copyCount = length - index;
162+
if (copyCount > elementCount) {
163+
copyCount = elementCount;
164+
}
165+
166+
for (int i = 0, j = index; i < copyCount; i++, j++) {
167+
_data[j] = iterableList[i];
168+
}
169+
170+
if (elementCount > copyCount) {
171+
throw IndexError.withLength(length, length);
172+
}
173+
174+
return;
175+
}
176+
177+
for (var value in iterable) {
178+
this[index++] = value;
116179
}
117-
Lists.copy(iterableAsList, 0, this, index, length);
118180
}
119181

120182
@override

sdk/lib/internal/iterable.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,14 @@ base class SubListIterable<E> extends ListIterable<E> {
238238
/** If null, represents the length of the iterable. */
239239
final int? _endOrLength;
240240

241+
/// Returns `_iterable` for for internal code.
242+
static Iterable<E> iterableOf<E>(SubListIterable<E> subListIterable) =>
243+
subListIterable._iterable;
244+
245+
/// Returns `_start` for for internal code.
246+
static int startOf<E>(SubListIterable<E> subListIterable) =>
247+
subListIterable._start;
248+
241249
SubListIterable(this._iterable, this._start, this._endOrLength) {
242250
RangeError.checkNotNegative(_start, "start");
243251
int? endOrLength = _endOrLength;

tests/corelib/list_set_all_test.dart

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import "package:expect/expect.dart";
65
import "dart:collection";
76

8-
test(List<int> list, int index, Iterable<int> iterable) {
7+
import "package:expect/expect.dart";
8+
9+
void test(List<int> list, int index, Iterable<int> iterable) {
910
List copy = list.toList();
1011
list.setAll(index, iterable);
1112
Expect.equals(copy.length, list.length);
@@ -22,7 +23,7 @@ test(List<int> list, int index, Iterable<int> iterable) {
2223
}
2324

2425
class MyList<T> extends ListBase<T> {
25-
List list;
26+
final List<T> list;
2627
MyList(this.list);
2728
get length => list.length;
2829
set length(value) {
@@ -37,61 +38,55 @@ class MyList<T> extends ListBase<T> {
3738
toString() => list.toString();
3839
}
3940

40-
main() {
41-
test([1, 2, 3], 0, [4, 5]);
42-
test([1, 2, 3], 1, [4, 5]);
43-
test([1, 2, 3], 2, [4]);
44-
test([1, 2, 3], 3, []);
45-
test([1, 2, 3], 0, [4, 5].map((x) => x));
46-
test([1, 2, 3], 1, [4, 5].map((x) => x));
47-
test([1, 2, 3], 2, [4].map((x) => x));
48-
test([1, 2, 3], 3, [].map((x) => x));
49-
test([1, 2, 3], 0, const [4, 5]);
50-
test([1, 2, 3], 1, const [4, 5]);
51-
test([1, 2, 3], 2, const [4]);
52-
test([1, 2, 3], 3, const []);
53-
test([1, 2, 3], 0, new Iterable.generate(2, (x) => x + 4));
54-
test([1, 2, 3], 1, new Iterable.generate(2, (x) => x + 4));
55-
test([1, 2, 3], 2, new Iterable.generate(1, (x) => x + 4));
56-
test([1, 2, 3], 3, new Iterable.generate(0, (x) => x + 4));
57-
test([1, 2, 3].toList(growable: false), 0, [4, 5]);
58-
test([1, 2, 3].toList(growable: false), 1, [4, 5]);
59-
test([1, 2, 3].toList(growable: false), 2, [4]);
60-
test([1, 2, 3].toList(growable: false), 3, []);
61-
test([1, 2, 3].toList(growable: false), 0, [4, 5].map((x) => x));
62-
test([1, 2, 3].toList(growable: false), 1, [4, 5].map((x) => x));
63-
test([1, 2, 3].toList(growable: false), 2, [4].map((x) => x));
64-
test([1, 2, 3].toList(growable: false), 3, [].map((x) => x));
65-
test([1, 2, 3].toList(growable: false), 0, const [4, 5]);
66-
test([1, 2, 3].toList(growable: false), 1, const [4, 5]);
67-
test([1, 2, 3].toList(growable: false), 2, const [4]);
68-
test([1, 2, 3].toList(growable: false), 3, const []);
69-
test([1, 2, 3].toList(growable: false), 0,
70-
new Iterable.generate(2, (x) => x + 4));
71-
test([1, 2, 3].toList(growable: false), 1,
72-
new Iterable.generate(2, (x) => x + 4));
73-
test([1, 2, 3].toList(growable: false), 2,
74-
new Iterable.generate(1, (x) => x + 4));
75-
test([1, 2, 3].toList(growable: false), 3,
76-
new Iterable.generate(0, (x) => x + 4));
77-
test(new MyList([1, 2, 3]), 0, [4, 5]);
78-
test(new MyList([1, 2, 3]), 1, [4, 5]);
79-
test(new MyList([1, 2, 3]), 2, [4]);
80-
test(new MyList([1, 2, 3]), 3, []);
81-
test(new MyList([1, 2, 3]), 0, [4, 5].map((x) => x));
82-
test(new MyList([1, 2, 3]), 1, [4, 5].map((x) => x));
83-
test(new MyList([1, 2, 3]), 2, [4].map((x) => x));
84-
test(new MyList([1, 2, 3]), 3, [].map((x) => x));
41+
void main() {
42+
for (var makeIterable in iterableMakers) {
43+
test([1, 2, 3], 0, makeIterable([4, 5]));
44+
test([1, 2, 3], 1, makeIterable([4, 5]));
45+
test([1, 2, 3], 2, makeIterable([4]));
46+
test([1, 2, 3], 3, makeIterable([]));
47+
test([1, 2, 3], 0, makeIterable(const [4, 5]));
48+
test([1, 2, 3], 1, makeIterable(const [4, 5]));
49+
test([1, 2, 3], 2, makeIterable(const [4]));
50+
test([1, 2, 3], 3, makeIterable(const []));
51+
test([1, 2, 3].toList(growable: false), 0, makeIterable([4, 5]));
52+
test([1, 2, 3].toList(growable: false), 1, makeIterable([4, 5]));
53+
test([1, 2, 3].toList(growable: false), 2, makeIterable([4]));
54+
test([1, 2, 3].toList(growable: false), 3, makeIterable([]));
55+
test([1, 2, 3].toList(growable: false), 0, makeIterable(const [4, 5]));
56+
test([1, 2, 3].toList(growable: false), 1, makeIterable(const [4, 5]));
57+
test([1, 2, 3].toList(growable: false), 2, makeIterable(const [4]));
58+
test([1, 2, 3].toList(growable: false), 3, makeIterable(const []));
59+
test(MyList([1, 2, 3]), 0, makeIterable([4, 5]));
60+
test(MyList([1, 2, 3]), 1, makeIterable([4, 5]));
61+
test(MyList([1, 2, 3]), 2, makeIterable([4]));
62+
test(MyList([1, 2, 3]), 3, makeIterable([]));
8563

86-
Expect.throwsRangeError(() => test([1, 2, 3], -1, [4, 5]));
87-
Expect.throwsRangeError(
88-
() => test([1, 2, 3].toList(growable: false), -1, [4, 5]));
89-
Expect.throwsRangeError(() => test([1, 2, 3], 1, [4, 5, 6]));
90-
Expect.throwsRangeError(
91-
() => test([1, 2, 3].toList(growable: false), 1, [4, 5, 6]));
92-
Expect.throwsRangeError(() => test(new MyList([1, 2, 3]), -1, [4, 5]));
93-
Expect.throwsRangeError(() => test(new MyList([1, 2, 3]), 1, [4, 5, 6]));
94-
Expect.throwsUnsupportedError(() => test(const [1, 2, 3], 0, [4, 5]));
95-
Expect.throwsUnsupportedError(() => test(const [1, 2, 3], -1, [4, 5]));
96-
Expect.throwsUnsupportedError(() => test(const [1, 2, 3], 1, [4, 5, 6]));
64+
Expect.throwsRangeError(() => test([1, 2, 3], -1, makeIterable([4, 5])));
65+
Expect.throwsRangeError(() =>
66+
test([1, 2, 3].toList(growable: false), -1, makeIterable([4, 5])));
67+
Expect.throwsRangeError(() => test([1, 2, 3], 1, makeIterable([4, 5, 6])));
68+
Expect.throwsRangeError(() =>
69+
test([1, 2, 3].toList(growable: false), 1, makeIterable([4, 5, 6])));
70+
Expect.throwsRangeError(
71+
() => test(MyList([1, 2, 3]), -1, makeIterable([4, 5])));
72+
Expect.throwsRangeError(
73+
() => test(MyList([1, 2, 3]), 1, makeIterable([4, 5, 6])));
74+
Expect.throwsUnsupportedError(
75+
() => test(const [1, 2, 3], 0, makeIterable([4, 5])));
76+
Expect.throwsUnsupportedError(
77+
() => test(const [1, 2, 3], -1, makeIterable([4, 5])));
78+
Expect.throwsUnsupportedError(
79+
() => test(const [1, 2, 3], 1, makeIterable([4, 5, 6])));
80+
}
9781
}
82+
83+
// `setAll` implementations can have type tests and special cases to handle
84+
// different types of iterables differently, so we test with a few different
85+
// types of iterables.
86+
List<Iterable<int> Function(List<int>)> iterableMakers = [
87+
(list) => list,
88+
MyList.new,
89+
(list) => list.where((x) => true),
90+
(list) => list.map((x) => x),
91+
(list) => list.getRange(0, list.length),
92+
];

0 commit comments

Comments
 (0)