-
Notifications
You must be signed in to change notification settings - Fork 197
Description
Reported by an internal user.
protobuf.dart/protobuf/lib/src/protobuf/pb_list.dart
Lines 41 to 46 in 87135ba
| @override | |
| void addAll(Iterable<E> iterable) { | |
| _checkModifiable('addAll'); | |
| iterable.forEach(_check); | |
| _wrappedList.addAll(iterable); | |
| } |
The line iterable.forEach(_check) traverses the iterable once. If the iterable has side effects, those will be run for the first time here.
Then the next line _wrappedList.addAll(iterable) will again traverse the iterable, causing the side effects to run again.
I don't know if we should call this a bug, but I think it's fair to say that it's a bit unexpected.
Two possible fixes:
-
Copy iterable elements to a list, then traverse the list many times:
void addAll(Iterable<E> iterable) { _checkModifiable('addAll'); List<E> list = iterable is List ? iterable : iterable.toList(); list.forEach(_check); _wrappedList.addAll(list); }
The downside is the function will now require allocating an intermediate list, and the list may be large in some uses.
-
Check as we add elements to the wrapped list:
void addAll(Iterable<E> iterable) { _checkModifiable('addAll'); for (final e in iterable) { _check(e); _wrappedList.add(e); } }
The downside is if a check fails the list will be updated. To avoid that, we could do:
void addAll(Iterable<E> iterable) { _checkModifiable('addAll'); var added = 0; for (final e in iterable) { try { _check(e); } catch (_) { _wrappedList.removeRange(_wrappedList.length - added, _wrappedList.length); rethrow; } _wrappedList.add(e); added += 1; } }