-
Notifications
You must be signed in to change notification settings - Fork 194
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; } }