Make Iterable be the default implementation of itself.

Makes `Iterable` a mixin class with a `const` constructor,
and `IterableBase` and `IterableMixin` (to-be-deprecated) aliases
for it.

Combining everything in one place avoids (some) code
duplication.

Also gave the methods a quick overhaul for formatting,
removing uses of `late` and unnecessary element accesses,
and some general tweaks.

CoreLibraryReviewExempt: Refactoring of redundant types.
Tested: Refactoring. If existing tests work, it works.
Change-Id: Ie49a88f713d386d2d118c53606a71bdd50e1eb11
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287600
Reviewed-by: Brian Quinlan <bquinlan@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2023-04-04 21:45:19 +00:00 committed by Lasse Nielsen
parent 23b73d7ca4
commit 5573ce7b84
42 changed files with 396 additions and 579 deletions

View file

@ -21,7 +21,9 @@
- **Breaking Change**: Non-`mixin` classes in the platform libraries
can no longer be mixed in, unless they are explicitly marked as `mixin class`.
The following existing classes have been made mixin classes:
* `IterableMixin`
* `Iterable`
* `IterableMixin` (now alias for `Iterable`)
* `IterableBase` (now alias for `Iterable`)
* `ListMixin`
* `SetMixin`
* `MapMixin`

View file

@ -180,14 +180,14 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
return <core::int>[].{core::Iterable::iterator}{core::Iterator<core::int>};
@ -201,9 +201,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
return o is{ForNonNullableByDefault} self::WithEquals && (o{self::WithEquals} as{ForNonNullableByDefault} self::WithEquals).{self::WithEquals::i}{core::int} =={core::num::==}{(core::Object) → core::bool} this.{self::WithEquals::i}{core::int};
}
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
return <core::String>[].{core::Iterable::iterator}{core::Iterator<core::String>};
@ -383,7 +383,6 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///const_collections.dart:
- CustomIterable. (from org-dartlang-testcase:///const_collections.dart:79:9)
- IterableBase. (from org-dartlang-sdk:///sdk/lib/collection/iterable.dart)
- Iterable. (from org-dartlang-sdk:///sdk/lib/core/iterable.dart)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
- WithEquals. (from org-dartlang-testcase:///const_collections.dart:72:9)

View file

@ -180,14 +180,14 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
return core::_GrowableList::•<core::int>(0).{core::Iterable::iterator}{core::Iterator<core::int>};
@ -201,9 +201,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
return o is{ForNonNullableByDefault} self::WithEquals && o{self::WithEquals}.{self::WithEquals::i}{core::int} =={core::num::==}{(core::Object) → core::bool} this.{self::WithEquals::i}{core::int};
}
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
return core::_GrowableList::•<core::String>(0).{core::Iterable::iterator}{core::Iterator<core::String>};
@ -383,7 +383,6 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///const_collections.dart:
- CustomIterable. (from org-dartlang-testcase:///const_collections.dart:79:9)
- IterableBase. (from org-dartlang-sdk:///sdk/lib/collection/iterable.dart)
- Iterable. (from org-dartlang-sdk:///sdk/lib/core/iterable.dart)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
- WithEquals. (from org-dartlang-testcase:///const_collections.dart:72:9)

View file

@ -180,14 +180,14 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
return <core::int>[].{core::Iterable::iterator}{core::Iterator<core::int>};
@ -201,9 +201,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
return o is{ForNonNullableByDefault} self::WithEquals && (o{self::WithEquals} as{ForNonNullableByDefault} self::WithEquals).{self::WithEquals::i}{core::int} =={core::num::==}{(core::Object) → core::bool} this.{self::WithEquals::i}{core::int};
}
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
return <core::String>[].{core::Iterable::iterator}{core::Iterator<core::String>};
@ -383,7 +383,6 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///const_collections.dart:
- CustomIterable. (from org-dartlang-testcase:///const_collections.dart:79:9)
- IterableBase. (from org-dartlang-sdk:///sdk/lib/collection/iterable.dart)
- Iterable. (from org-dartlang-sdk:///sdk/lib/core/iterable.dart)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
- WithEquals. (from org-dartlang-testcase:///const_collections.dart:72:9)

View file

@ -180,14 +180,14 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
return <core::int>[].{core::Iterable::iterator}{core::Iterator<core::int>};
@ -201,9 +201,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
return o is{ForNonNullableByDefault} self::WithEquals && (o{self::WithEquals} as{ForNonNullableByDefault} self::WithEquals).{self::WithEquals::i}{core::int} =={core::num::==}{(core::Object) → core::bool} this.{self::WithEquals::i}{core::int};
}
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
return <core::String>[].{core::Iterable::iterator}{core::Iterator<core::String>};
@ -383,7 +383,6 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///const_collections.dart:
- CustomIterable. (from org-dartlang-testcase:///const_collections.dart:79:9)
- IterableBase. (from org-dartlang-sdk:///sdk/lib/collection/iterable.dart)
- Iterable. (from org-dartlang-sdk:///sdk/lib/core/iterable.dart)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
- WithEquals. (from org-dartlang-testcase:///const_collections.dart:72:9)

View file

@ -37,14 +37,13 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
;
@ -57,9 +56,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
operator ==(core::Object o) → core::bool
;
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
;

View file

@ -180,14 +180,14 @@ library /*isNonNullableByDefault*/;
// ^
//
import self as self;
import "dart:collection" as col;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:collection";
class ConstIterable extends col::IterableBase<core::int> /*hasConstConstructor*/ {
class ConstIterable extends core::Iterable<core::int> /*hasConstConstructor*/ {
const constructor •() → self::ConstIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::int>
return core::_GrowableList::•<core::int>(0).{core::Iterable::iterator}{core::Iterator<core::int>};
@ -201,9 +201,9 @@ class WithEquals extends core::Object /*hasConstConstructor*/ {
return o is{ForNonNullableByDefault} self::WithEquals && o{self::WithEquals}.{self::WithEquals::i}{core::int} =={core::num::==}{(core::Object) → core::bool} this.{self::WithEquals::i}{core::int};
}
}
class CustomIterable extends col::IterableBase<core::String> /*hasConstConstructor*/ {
class CustomIterable extends core::Iterable<core::String> /*hasConstConstructor*/ {
const constructor •() → self::CustomIterable
: super col::IterableBase::•()
: super core::Iterable::•()
;
get iterator() → core::Iterator<core::String>
return core::_GrowableList::•<core::String>(0).{core::Iterable::iterator}{core::Iterator<core::String>};
@ -383,7 +383,6 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///const_collections.dart:
- CustomIterable. (from org-dartlang-testcase:///const_collections.dart:79:9)
- IterableBase. (from org-dartlang-sdk:///sdk/lib/collection/iterable.dart)
- Iterable. (from org-dartlang-sdk:///sdk/lib/core/iterable.dart)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart)
- WithEquals. (from org-dartlang-testcase:///const_collections.dart:72:9)

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -12,7 +12,7 @@ library /*isNonNullableByDefault*/;
//
// class C extends Iterable<Object> {
// ^
// sdk/lib/core/iterable.dart:148:19: Context: 'Iterable.iterator' is defined here.
// sdk/lib/core/iterable.dart:169:19: Context: 'Iterable.iterator' is defined here.
// Iterator<E> get iterator;
// ^^^^^^^^
//

View file

@ -514,7 +514,7 @@ library from "org-dartlang-test:///main.dart" as main {
return dart.core::Comparable::compare(a as{ForNonNullableByDefault} dart.core::Comparable<dynamic>, b as{ForNonNullableByDefault} dart.core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(dart.core::List<dart.core::Object?> list) → dart.core::String
return dart.collection::IterableBase::iterableToFullString(list, "[", "]");
return dart.core::Iterable::iterableToFullString(list, "[", "]");
}
class WithListMixin extends main::_WithListMixin&Object&ListMixin {
field dart.core::int length = 2;

View file

@ -514,7 +514,7 @@ library from "org-dartlang-test:///main.dart" as main {
return dart.core::Comparable::compare(a as{ForNonNullableByDefault} dart.core::Comparable<dynamic>, b as{ForNonNullableByDefault} dart.core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(dart.core::List<dart.core::Object?> list) → dart.core::String
return dart.collection::IterableBase::iterableToFullString(list, "[", "]");
return dart.core::Iterable::iterableToFullString(list, "[", "]");
}
class WithListMixin extends main::_WithListMixin&Object&ListMixin {
field dart.core::int length = 2;

View file

@ -589,7 +589,7 @@ abstract class _CustomList&Object&ListMixin<E extends core::Object? = dynamic> e
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class CustomList<E extends core::Object? = dynamic> extends cac::_CustomList&Object&ListMixin<cac::CustomList::E%> {
final field core::List<cac::CustomList::E%> list;

View file

@ -589,7 +589,7 @@ abstract class _CustomList&Object&ListMixin<E extends core::Object? = dynamic> e
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class CustomList<E extends core::Object? = dynamic> extends cac::_CustomList&Object&ListMixin<cac::CustomList::E%> {
final field core::List<cac::CustomList::E%> list;

View file

@ -2,6 +2,7 @@ library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:_internal" as _in;
import "dart:collection";
@ -134,12 +135,12 @@ abstract class _MyMap&Object&MapMixin<K extends core::Object? = dynamic, V exten
method /* from org-dartlang-sdk:///sdk/lib/collection/maps.dart */ toString() → core::String
return col::MapBase::mapToString(this);
static method /* from org-dartlang-sdk:///sdk/lib/collection/maps.dart */ mapToString(core::Map<core::Object?, core::Object?> m) → core::String {
if(col::_isToStringVisiting(m)) {
if(_in::isToStringVisiting(m)) {
return "{...}";
}
core::StringBuffer result = new core::StringBuffer::•();
try {
col::_toStringVisiting.{core::List::add}(m){(core::Object) → void};
_in::toStringVisiting.{core::List::add}(m){(core::Object) → void};
result.{core::StringBuffer::write}("{"){(core::Object?) → void};
core::bool first = true;
m.{core::Map::forEach}((core::Object? k, core::Object? v) → void {
@ -154,8 +155,8 @@ abstract class _MyMap&Object&MapMixin<K extends core::Object? = dynamic, V exten
result.{core::StringBuffer::write}("}"){(core::Object?) → void};
}
finally {
assert(core::identical(col::_toStringVisiting.{core::Iterable::last}{core::Object}, m));
col::_toStringVisiting.{core::List::removeLast}(){() → core::Object};
assert(core::identical(_in::toStringVisiting.{core::Iterable::last}{core::Object}, m));
_in::toStringVisiting.{core::List::removeLast}(){() → core::Object};
}
return result.{core::StringBuffer::toString}(){() → core::String};
}

View file

@ -2,6 +2,7 @@ library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "dart:collection" as col;
import "dart:_internal" as _in;
import "dart:collection";
@ -134,12 +135,12 @@ abstract class _MyMap&Object&MapMixin<K extends core::Object? = dynamic, V exten
method /* from org-dartlang-sdk:///sdk/lib/collection/maps.dart */ toString() → core::String
return col::MapBase::mapToString(this);
static method /* from org-dartlang-sdk:///sdk/lib/collection/maps.dart */ mapToString(core::Map<core::Object?, core::Object?> m) → core::String {
if(col::_isToStringVisiting(m)) {
if(_in::isToStringVisiting(m)) {
return "{...}";
}
core::StringBuffer result = new core::StringBuffer::•();
try {
col::_toStringVisiting.{core::List::add}(m){(core::Object) → void};
_in::toStringVisiting.{core::List::add}(m){(core::Object) → void};
result.{core::StringBuffer::write}("{"){(core::Object?) → void};
core::bool first = true;
m.{core::Map::forEach}((core::Object? k, core::Object? v) → void {
@ -154,8 +155,8 @@ abstract class _MyMap&Object&MapMixin<K extends core::Object? = dynamic, V exten
result.{core::StringBuffer::write}("}"){(core::Object?) → void};
}
finally {
assert(core::identical(col::_toStringVisiting.{core::Iterable::last}{core::Object}, m));
col::_toStringVisiting.{core::List::removeLast}(){() → core::Object};
assert(core::identical(_in::toStringVisiting.{core::Iterable::last}{core::Object}, m));
_in::toStringVisiting.{core::List::removeLast}(){() → core::Object};
}
return result.{core::StringBuffer::toString}(){() → core::String};
}

View file

@ -518,7 +518,7 @@ abstract class _A&Object&ListMixin extends core::Object implements col::ListBase
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class A extends self::_A&Object&ListMixin {
field core::int count = 0;

View file

@ -518,7 +518,7 @@ abstract class _A&Object&ListMixin extends core::Object implements col::ListBase
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class A extends self::_A&Object&ListMixin {
field core::int count = 0;

View file

@ -518,7 +518,7 @@ abstract class _NegativeLengthList&Object&ListMixin<E extends core::Object? = dy
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class NegativeLengthList<E extends core::Object? = dynamic> extends self::_NegativeLengthList&Object&ListMixin<self::NegativeLengthList::E%> {
final field core::List<self::NegativeLengthList::E%> _list;

View file

@ -518,7 +518,7 @@ abstract class _NegativeLengthList&Object&ListMixin<E extends core::Object? = dy
return core::Comparable::compare(a as{ForNonNullableByDefault} core::Comparable<dynamic>, b as{ForNonNullableByDefault} core::Comparable<dynamic>);
}
static method /* from org-dartlang-sdk:///sdk/lib/collection/list.dart */ listToString(core::List<core::Object?> list) → core::String
return col::IterableBase::iterableToFullString(list, "[", "]");
return core::Iterable::iterableToFullString(list, "[", "]");
}
class NegativeLengthList<E extends core::Object? = dynamic> extends self::_NegativeLengthList&Object&ListMixin<self::NegativeLengthList::E%> {
final field core::List<self::NegativeLengthList::E%> _list;

View file

@ -36,7 +36,7 @@ List<String> stops = [];
// THIS TEST DEPENDS ON SPECIFIC LINE NUMBERS IN UNRELATED PLATFORM LIBRARIES
// STAYING FIXED. VERY FRAGILE. SHOULD BE FIXED OR REMOVED.
List<String> expected = [
"set.dart:142:23 (sdk_break_with_mixin_test.dart:20:5)",
"set.dart:142:21 (sdk_break_with_mixin_test.dart:20:5)",
];
var tests = <IsolateTest>[

View file

@ -35,7 +35,7 @@ class MySet extends Object with SetMixin {
List<String> stops = [];
List<String> expected = [
"set.dart:142:23 (sdk_break_with_mixin_test.dart:21:5)",
"set.dart:142:21 (sdk_break_with_mixin_test.dart:21:5)",
];
var tests = <IsolateTest>[

View file

@ -53,7 +53,7 @@ class DartIterator<E> implements Iterator<E> {
}
/// Used to compile `sync*`.
class SyncIterable<E> extends IterableBase<E> {
class SyncIterable<E> extends Iterable<E> {
final Function() _initGenerator;
SyncIterable(this._initGenerator);

View file

@ -230,7 +230,7 @@ class _MatchImplementation implements RegExpMatch {
}
}
class _AllMatchesIterable extends IterableBase<RegExpMatch> {
class _AllMatchesIterable extends Iterable<RegExpMatch> {
final JSSyntaxRegExp _re;
final String _string;
final int _start;

View file

@ -18,8 +18,6 @@ import 'dart:_foreign_helper' show JS, JS_GET_FLAG;
import 'dart:_async_await_error_codes' as async_error_codes;
import "dart:collection" show IterableBase;
@patch
class _AsyncRun {
@patch
@ -686,7 +684,7 @@ _SyncStarIterable<T> _makeSyncStarIterable<T>(body) {
/// An Iterable corresponding to a sync* method.
///
/// Each invocation of a sync* method will return a new instance of this class.
class _SyncStarIterable<T> extends IterableBase<T> {
class _SyncStarIterable<T> extends Iterable<T> {
// This is a function that will return a helper function that does the
// iteration of the sync*.
//

View file

@ -230,7 +230,7 @@ class _MatchImplementation implements RegExpMatch {
}
}
class _AllMatchesIterable extends IterableBase<RegExpMatch> {
class _AllMatchesIterable extends Iterable<RegExpMatch> {
final JSSyntaxRegExp _re;
final String _string;
final int _start;

View file

@ -42,7 +42,6 @@ import "dart:async" show Completer, DeferredLoadException, Future, Timer, Zone;
import "dart:collection"
show
HashMap,
IterableBase,
LinkedHashMap,
LinkedList,
LinkedListEntry,

View file

@ -323,7 +323,7 @@ class _RegExp implements RegExp {
Int32List(_initialBacktrackingStackSize);
}
class _AllMatchesIterable extends IterableBase<RegExpMatch> {
class _AllMatchesIterable extends Iterable<RegExpMatch> {
final _RegExp _re;
final String _str;
final int _start;

View file

@ -35,7 +35,6 @@ import 'dart:_js_helper' show JS, JSSyntaxRegExp, quoteStringForRegExp;
import "dart:collection"
show
HashMap,
IterableBase,
LinkedHashMap,
LinkedList,
LinkedListEntry,

View file

@ -219,7 +219,7 @@ class _MatchImplementation implements RegExpMatch {
}
}
class _AllMatchesIterable extends IterableBase<RegExpMatch> {
class _AllMatchesIterable extends Iterable<RegExpMatch> {
final JSSyntaxRegExp _re;
final String _string;
final int _start;

View file

@ -7,396 +7,12 @@ part of dart.collection;
/// This [Iterable] mixin implements all [Iterable] members except `iterator`.
///
/// All other methods are implemented in terms of `iterator`.
abstract mixin class IterableMixin<E> implements Iterable<E> {
// This class has methods copied verbatim into:
// - IterableBase
// - SetMixin
// If changing a method here, also change the other copies.
Iterable<R> cast<R>() => Iterable.castFrom<E, R>(this);
Iterable<T> map<T>(T toElement(E element)) =>
MappedIterable<E, T>(this, toElement);
Iterable<E> where(bool test(E element)) => WhereIterable<E>(this, test);
Iterable<T> whereType<T>() => WhereTypeIterable<T>(this);
Iterable<T> expand<T>(Iterable<T> toElements(E element)) =>
ExpandIterable<E, T>(this, toElements);
Iterable<E> followedBy(Iterable<E> other) {
// Type workaround because IterableMixin<E> doesn't promote
// to EfficientLengthIterable<E>.
Iterable<E> self = this;
if (self is EfficientLengthIterable<E>) {
return FollowedByIterable<E>.firstEfficient(self, other);
}
return FollowedByIterable<E>(this, other);
}
bool contains(Object? element) {
for (E e in this) {
if (e == element) return true;
}
return false;
}
void forEach(void action(E element)) {
for (E element in this) action(element);
}
E reduce(E combine(E value, E element)) {
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) {
throw IterableElementError.noElement();
}
E value = iterator.current;
while (iterator.moveNext()) {
value = combine(value, iterator.current);
}
return value;
}
T fold<T>(T initialValue, T combine(T previousValue, E element)) {
var value = initialValue;
for (E element in this) value = combine(value, element);
return value;
}
bool every(bool test(E element)) {
for (E element in this) {
if (!test(element)) return false;
}
return true;
}
String join([String separator = ""]) {
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) return "";
StringBuffer buffer = StringBuffer();
if (separator == null || separator == "") {
do {
buffer.write("${iterator.current}");
} while (iterator.moveNext());
} else {
buffer.write("${iterator.current}");
while (iterator.moveNext()) {
buffer.write(separator);
buffer.write("${iterator.current}");
}
}
return buffer.toString();
}
bool any(bool test(E element)) {
for (E element in this) {
if (test(element)) return true;
}
return false;
}
List<E> toList({bool growable = true}) =>
List<E>.from(this, growable: growable);
Set<E> toSet() => Set<E>.from(this);
int get length {
assert(this is! EfficientLengthIterable);
int count = 0;
Iterator it = iterator;
while (it.moveNext()) {
count++;
}
return count;
}
bool get isEmpty => !iterator.moveNext();
bool get isNotEmpty => !isEmpty;
Iterable<E> take(int count) {
return TakeIterable<E>(this, count);
}
Iterable<E> takeWhile(bool test(E value)) {
return TakeWhileIterable<E>(this, test);
}
Iterable<E> skip(int count) {
return SkipIterable<E>(this, count);
}
Iterable<E> skipWhile(bool test(E value)) {
return SkipWhileIterable<E>(this, test);
}
E get first {
Iterator<E> it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
return it.current;
}
E get last {
Iterator<E> it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
E result;
do {
result = it.current;
} while (it.moveNext());
return result;
}
E get single {
Iterator<E> it = iterator;
if (!it.moveNext()) throw IterableElementError.noElement();
E result = it.current;
if (it.moveNext()) throw IterableElementError.tooMany();
return result;
}
E firstWhere(bool test(E value), {E Function()? orElse}) {
for (E element in this) {
if (test(element)) return element;
}
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
E lastWhere(bool test(E value), {E Function()? orElse}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
result = element;
foundMatching = true;
}
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
E singleWhere(bool test(E element), {E Function()? orElse}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
if (foundMatching) {
throw IterableElementError.tooMany();
}
result = element;
foundMatching = true;
}
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
E elementAt(int index) {
checkNotNullable(index, "index");
RangeError.checkNotNegative(index, "index");
int elementIndex = 0;
for (E element in this) {
if (index == elementIndex) return element;
elementIndex++;
}
throw IndexError.withLength(index, elementIndex,
indexable: this, name: "index");
}
String toString() => IterableBase.iterableToShortString(this, '(', ')');
}
// @Deprecated("Use Iterable instead")
typedef IterableMixin<E> = Iterable<E>;
/// Base class for implementing [Iterable].
///
/// This class implements all methods of [Iterable], except [Iterable.iterator],
/// in terms of `iterator`.
abstract class IterableBase<E> extends Iterable<E> {
const IterableBase();
/// Convert an `Iterable` to a string like [IterableBase.toString].
///
/// Allows using other delimiters than '(' and ')'.
///
/// Handles circular references where converting one of the elements
/// to a string ends up converting [iterable] to a string again.
static String iterableToShortString(Iterable iterable,
[String leftDelimiter = '(', String rightDelimiter = ')']) {
if (_isToStringVisiting(iterable)) {
if (leftDelimiter == "(" && rightDelimiter == ")") {
// Avoid creating a new string in the "common" case.
return "(...)";
}
return "$leftDelimiter...$rightDelimiter";
}
List<String> parts = <String>[];
_toStringVisiting.add(iterable);
try {
_iterablePartsToStrings(iterable, parts);
} finally {
assert(identical(_toStringVisiting.last, iterable));
_toStringVisiting.removeLast();
}
return (StringBuffer(leftDelimiter)
..writeAll(parts, ", ")
..write(rightDelimiter))
.toString();
}
/// Converts an `Iterable` to a string.
///
/// Converts each elements to a string, and separates the results by ", ".
/// Then wraps the result in [leftDelimiter] and [rightDelimiter].
///
/// Unlike [iterableToShortString], this conversion doesn't omit any
/// elements or puts any limit on the size of the result.
///
/// Handles circular references where converting one of the elements
/// to a string ends up converting [iterable] to a string again.
static String iterableToFullString(Iterable iterable,
[String leftDelimiter = '(', String rightDelimiter = ')']) {
if (_isToStringVisiting(iterable)) {
return "$leftDelimiter...$rightDelimiter";
}
StringBuffer buffer = StringBuffer(leftDelimiter);
_toStringVisiting.add(iterable);
try {
buffer.writeAll(iterable, ", ");
} finally {
assert(identical(_toStringVisiting.last, iterable));
_toStringVisiting.removeLast();
}
buffer.write(rightDelimiter);
return buffer.toString();
}
}
/// A collection used to identify cyclic lists during toString() calls.
final List<Object> _toStringVisiting = [];
/// Check if we are currently visiting `o` in a toString call.
bool _isToStringVisiting(Object o) {
for (int i = 0; i < _toStringVisiting.length; i++) {
if (identical(o, _toStringVisiting[i])) return true;
}
return false;
}
/// Convert elements of [iterable] to strings and store them in [parts].
void _iterablePartsToStrings(Iterable<Object?> iterable, List<String> parts) {
/*
* This is the complicated part of [iterableToShortString].
* It is extracted as a separate function to avoid having too much code
* inside the try/finally.
*/
/// Try to stay below this many characters.
const int lengthLimit = 80;
/// Always at least this many elements at the start.
const int headCount = 3;
/// Always at least this many elements at the end.
const int tailCount = 2;
/// Stop iterating after this many elements. Iterables can be infinite.
const int maxCount = 100;
// Per entry length overhead. It's for ", " for all after the first entry,
// and for "(" and ")" for the initial entry. By pure luck, that's the same
// number.
const int overhead = 2;
const int ellipsisSize = 3; // "...".length.
int length = 0;
int count = 0;
Iterator<Object?> it = iterable.iterator;
// Initial run of elements, at least headCount, and then continue until
// passing at most lengthLimit characters.
while (length < lengthLimit || count < headCount) {
if (!it.moveNext()) return;
String next = "${it.current}";
parts.add(next);
length += next.length + overhead;
count++;
}
String penultimateString;
String ultimateString;
// Find last two elements. One or more of them may already be in the
// parts array. Include their length in `length`.
if (!it.moveNext()) {
if (count <= headCount + tailCount) return;
ultimateString = parts.removeLast();
penultimateString = parts.removeLast();
} else {
Object? penultimate = it.current;
count++;
if (!it.moveNext()) {
if (count <= headCount + 1) {
parts.add("$penultimate");
return;
}
ultimateString = "$penultimate";
penultimateString = parts.removeLast();
length += ultimateString.length + overhead;
} else {
Object? ultimate = it.current;
count++;
// Then keep looping, keeping the last two elements in variables.
assert(count < maxCount);
while (it.moveNext()) {
penultimate = ultimate;
ultimate = it.current;
count++;
if (count > maxCount) {
// If we haven't found the end before maxCount, give up.
// This cannot happen in the code above because each entry
// increases length by at least two, so there is no way to
// visit more than ~40 elements before this loop.
// Remove any surplus elements until length, including ", ...)",
// is at most lengthLimit.
while (length > lengthLimit - ellipsisSize - overhead &&
count > headCount) {
length -= parts.removeLast().length + overhead;
count--;
}
parts.add("...");
return;
}
}
penultimateString = "$penultimate";
ultimateString = "$ultimate";
length += ultimateString.length + penultimateString.length + 2 * overhead;
}
}
// If there is a gap between the initial run and the last two,
// prepare to add an ellipsis.
String? elision;
if (count > parts.length + tailCount) {
elision = "...";
length += ellipsisSize + overhead;
}
// If the last two elements were very long, and we have more than
// headCount elements in the initial run, drop some to make room for
// the last two.
while (length > lengthLimit && parts.length > headCount) {
length -= parts.removeLast().length + overhead;
if (elision == null) {
elision = "...";
length += ellipsisSize + overhead;
}
}
if (elision != null) {
parts.add(elision);
}
parts.add(penultimateString);
parts.add(ultimateString);
}
// @Deprecated("Use Iterable instead")
typedef IterableBase<E> = Iterable<E>;

View file

@ -109,14 +109,14 @@ abstract mixin class MapBase<K, V> implements Map<K, V> {
String toString() => mapToString(this);
static String mapToString(Map<Object?, Object?> m) {
// Reuses the list in IterableBase for detecting toString cycles.
if (_isToStringVisiting(m)) {
// Reuses the list used by Iterable for detecting toString cycles.
if (isToStringVisiting(m)) {
return '{...}';
}
var result = StringBuffer();
try {
_toStringVisiting.add(m);
toStringVisiting.add(m);
result.write('{');
bool first = true;
m.forEach((Object? k, Object? v) {
@ -130,8 +130,8 @@ abstract mixin class MapBase<K, V> implements Map<K, V> {
});
result.write('}');
} finally {
assert(identical(_toStringVisiting.last, m));
_toStringVisiting.removeLast();
assert(identical(toStringVisiting.last, m));
toStringVisiting.removeLast();
}
return result.toString();

View file

@ -483,7 +483,7 @@ final class DoubleLinkedQueue<E> extends Iterable<E> implements Queue<E> {
return _DoubleLinkedQueueIterator<E>(this);
}
String toString() => IterableBase.iterableToFullString(this, '{', '}');
String toString() => Iterable.iterableToFullString(this, '{', '}');
}
class _DoubleLinkedQueueIterator<E> implements Iterator<E> {
@ -803,7 +803,7 @@ final class ListQueue<E> extends ListIterable<E> implements Queue<E> {
}
}
String toString() => IterableBase.iterableToFullString(this, "{", "}");
String toString() => Iterable.iterableToFullString(this, "{", "}");
// Queue interface.

View file

@ -133,6 +133,7 @@ abstract mixin class SetBase<E> implements Set<E> {
// Copied from Iterable.
// Should be inherited if we had multi-level mixins.
Iterable<E> where(bool f(E element)) => WhereIterable<E>(this, f);
Iterable<T> expand<T>(Iterable<T> f(E element)) =>
@ -170,17 +171,20 @@ abstract mixin class SetBase<E> implements Set<E> {
String join([String separator = ""]) {
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) return "";
StringBuffer buffer = StringBuffer();
if (separator == null || separator == "") {
var first = iterator.current.toString();
if (!iterator.moveNext()) return first;
var buffer = StringBuffer(first);
// TODO(51681): Drop null check when de-supporting pre-2.12 code.
if (separator == null || separator.isEmpty) {
do {
buffer.write(iterator.current);
} while (iterator.moveNext());
} else {
buffer.write(iterator.current);
while (iterator.moveNext()) {
buffer.write(separator);
buffer.write(iterator.current);
}
do {
buffer
..write(separator)
..write(iterator.current);
} while (iterator.moveNext());
}
return buffer.toString();
}
@ -237,45 +241,47 @@ abstract mixin class SetBase<E> implements Set<E> {
}
E lastWhere(bool test(E value), {E Function()? orElse}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
result = element;
foundMatching = true;
var iterator = this.iterator;
E result;
do {
if (!iterator.moveNext()) {
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
result = iterator.current;
} while (!test(result));
while (iterator.moveNext()) {
var current = iterator.current;
if (test(current)) result = current;
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
return result;
}
E singleWhere(bool test(E value), {E Function()? orElse}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
if (foundMatching) {
throw IterableElementError.tooMany();
}
result = element;
foundMatching = true;
var iterator = this.iterator;
E result;
do {
if (!iterator.moveNext()) {
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
result = iterator.current;
} while (!test(result));
while (iterator.moveNext()) {
if (test(iterator.current)) throw IterableElementError.tooMany();
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
return result;
}
E elementAt(int index) {
checkNotNullable(index, "index");
RangeError.checkNotNegative(index, "index");
int elementIndex = 0;
for (E element in this) {
if (index == elementIndex) return element;
elementIndex++;
var iterator = this.iterator;
var skipCount = index;
while (iterator.moveNext()) {
if (skipCount == 0) return iterator.current;
skipCount--;
}
throw IndexError.withLength(index, elementIndex,
throw IndexError.withLength(index, index - skipCount,
indexable: this, name: "index");
}

View file

@ -947,7 +947,7 @@ class _SplayTreeMapEntryIterator<K, V>
/// * [HashSet] the order of the objects in the iterations is not guaranteed.
/// * [LinkedHashSet] objects stored based on insertion order.
final class SplayTreeSet<E> extends _SplayTree<E, _SplayTreeSetNode<E>>
with IterableMixin<E>, SetMixin<E> {
with Iterable<E>, SetMixin<E> {
_SplayTreeSetNode<E>? _root;
Comparator<E> _compare;
@ -1188,5 +1188,5 @@ final class SplayTreeSet<E> extends _SplayTree<E, _SplayTreeSetNode<E>>
Set<E> toSet() => _clone();
String toString() => IterableBase.iterableToFullString(this, '{', '}');
String toString() => Iterable.iterableToFullString(this, '{', '}');
}

View file

@ -81,9 +81,19 @@ part of dart.core;
/// For methods like [map] and [where], the returned iterable will execute the
/// argument function on every iteration, so those functions should also not
/// have side effects.
abstract class Iterable<E> {
// TODO(lrn): When we allow forwarding const constructors through
// mixin applications, make this class implement [IterableMixin].
///
/// The `Iterable` declaration provides a default implementation,
/// which can be extended or mixed in to implement the `Iterable` interface.
/// It implements every member other than the [iterator] getter,
/// using the [Iterator] provided by [iterator].
/// An implementation of the `Iterable` interface should
/// provide a more efficient implementation of the members of `Iterable`
/// when it can do so.
abstract mixin class Iterable<E> {
// This class has methods copied verbatim into:
// - SetMixin
// If changing a method here, also change other copies.
const Iterable();
/// Creates an `Iterable` which generates its elements dynamically.
@ -100,7 +110,19 @@ abstract class Iterable<E> {
/// As an `Iterable`, `Iterable.generate(n, generator))` is equivalent to
/// `const [0, ..., n - 1].map(generator)`.
factory Iterable.generate(int count, [E generator(int index)?]) {
// Always OK to omit generator when count is zero.
if (count <= 0) return EmptyIterable<E>();
if (generator == null) {
// If generator is omitted, we generate integers.
// If `E` does not allow integers, it's an error.
Function id = _GeneratorIterable._id;
if (id is! E Function(int)) {
throw ArgumentError(
"Generator must be supplied or element type must allow integers",
"generator");
}
generator = id;
}
return _GeneratorIterable<E>(count, generator);
}
@ -119,8 +141,7 @@ abstract class Iterable<E> {
static Iterable<T> castFrom<S, T>(Iterable<S> source) =>
CastIterable<S, T>(source);
/// Returns a new `Iterator` that allows iterating the elements of this
/// `Iterable`.
/// A new `Iterator` that allows iterating the elements of this `Iterable`.
///
/// Iterable classes may specify the iteration order of their elements
/// (for example [List] always iterate in index order),
@ -147,7 +168,7 @@ abstract class Iterable<E> {
/// break iteration.
Iterator<E> get iterator;
/// Provides a view of this iterable as an iterable of [R] instances.
/// A view of this iterable as an iterable of [R] instances.
///
/// If this iterable only contains instances of [R], all operations
/// will work correctly. If any operation tries to access an element
@ -155,9 +176,9 @@ abstract class Iterable<E> {
///
/// When the returned iterable creates a new object that depends on
/// the type [R], e.g., from [toList], it will have exactly the type [R].
Iterable<R> cast<R>() => Iterable.castFrom<E, R>(this);
Iterable<R> cast<R>() => CastIterable<E, R>(this);
/// Returns the lazy concatenation of this iterable and [other].
/// Creates the lazy concatenation of this iterable and [other].
///
/// The returned iterable will provide the same elements as this iterable,
/// and, after that, the elements of [other], in the same order as in the
@ -216,7 +237,7 @@ abstract class Iterable<E> {
/// ```
Iterable<T> map<T>(T toElement(E e)) => MappedIterable<E, T>(this, toElement);
/// Returns a new lazy [Iterable] with all elements that satisfy the
/// Creates a new lazy [Iterable] with all elements that satisfy the
/// predicate [test].
///
/// The matching elements have the same order in the returned iterable
@ -238,7 +259,7 @@ abstract class Iterable<E> {
/// ```
Iterable<E> where(bool test(E element)) => WhereIterable<E>(this, test);
/// Returns a new lazy [Iterable] with all elements that have type [T].
/// Creates a new lazy [Iterable] with all elements that have type [T].
///
/// The matching elements have the same order in the returned iterable
/// as they have in [iterator].
@ -421,17 +442,20 @@ abstract class Iterable<E> {
String join([String separator = ""]) {
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) return "";
StringBuffer buffer = StringBuffer();
if (separator == null || separator == "") {
var first = iterator.current.toString();
if (!iterator.moveNext()) return first;
var buffer = StringBuffer(first);
// TODO(51681): Drop null check when de-supporting pre-2.12 code.
if (separator == null || separator.isEmpty) {
do {
buffer.write(iterator.current.toString());
} while (iterator.moveNext());
} else {
buffer.write(iterator.current.toString());
while (iterator.moveNext()) {
buffer.write(separator);
buffer.write(iterator.current.toString());
}
do {
buffer
..write(separator)
..write(iterator.current.toString());
} while (iterator.moveNext());
}
return buffer.toString();
}
@ -466,9 +490,8 @@ abstract class Iterable<E> {
/// final valuesList =
/// planets.values.toList(growable: false); // [Mercury, Venus, Mars]
/// ```
List<E> toList({bool growable = true}) {
return List<E>.of(this, growable: growable);
}
List<E> toList({bool growable = true}) =>
List<E>.of(this, growable: growable);
/// Creates a [Set] containing the same elements as this iterable.
///
@ -485,15 +508,16 @@ abstract class Iterable<E> {
/// ```
Set<E> toSet() => Set<E>.of(this);
/// Returns the number of elements in [this].
/// The number of elements in [this].
///
/// Counting all elements may involve iterating through all elements and can
/// therefore be slow.
/// Some iterables have a more efficient way to find the number of elements.
/// These *must* override the default implementation of `length`.
int get length {
assert(this is! EfficientLengthIterable);
int count = 0;
Iterator it = iterator;
Iterator<Object?> it = iterator;
while (it.moveNext()) {
count++;
}
@ -524,7 +548,7 @@ abstract class Iterable<E> {
/// ```
bool get isNotEmpty => !isEmpty;
/// Returns a lazy iterable of the [count] first elements of this iterable.
/// Creates a lazy iterable of the [count] first elements of this iterable.
///
/// The returned `Iterable` may contain fewer than `count` elements, if `this`
/// contains fewer than `count` elements.
@ -540,11 +564,9 @@ abstract class Iterable<E> {
/// final result = numbers.take(4); // (1, 2, 3, 5)
/// final takeAll = numbers.take(100); // (1, 2, 3, 5, 6, 7)
/// ```
Iterable<E> take(int count) {
return TakeIterable<E>(this, count);
}
Iterable<E> take(int count) => TakeIterable<E>(this, count);
/// Returns a lazy iterable of the leading elements satisfying [test].
/// Creates a lazy iterable of the leading elements satisfying [test].
///
/// The filtering happens lazily. Every new iterator of the returned
/// iterable starts iterating over the elements of `this`.
@ -561,11 +583,9 @@ abstract class Iterable<E> {
/// result = numbers.takeWhile((x) => x != 4); // (1, 2, 3, 5, 6, 7)
/// result = numbers.takeWhile((x) => x.isOdd); // (1)
/// ```
Iterable<E> takeWhile(bool test(E value)) {
return TakeWhileIterable<E>(this, test);
}
Iterable<E> takeWhile(bool test(E value)) => TakeWhileIterable<E>(this, test);
/// Returns an [Iterable] that provides all but the first [count] elements.
/// Creates an [Iterable] that provides all but the first [count] elements.
///
/// When the returned iterable is iterated, it starts iterating over `this`,
/// first skipping past the initial [count] elements.
@ -586,11 +606,9 @@ abstract class Iterable<E> {
/// ```
///
/// The [count] must not be negative.
Iterable<E> skip(int count) {
return SkipIterable<E>(this, count);
}
Iterable<E> skip(int count) => SkipIterable<E>(this, count);
/// Returns an `Iterable` that skips leading elements while [test] is satisfied.
/// Creates an `Iterable` that skips leading elements while [test] is satisfied.
///
/// The filtering happens lazily. Every new [Iterator] of the returned
/// iterable iterates over all elements of `this`.
@ -609,11 +627,9 @@ abstract class Iterable<E> {
/// result = numbers.skipWhile((x) => x != 4); // ()
/// result = numbers.skipWhile((x) => x.isOdd); // (2, 3, 5, 6, 7)
/// ```
Iterable<E> skipWhile(bool test(E value)) {
return SkipWhileIterable<E>(this, test);
}
Iterable<E> skipWhile(bool test(E value)) => SkipWhileIterable<E>(this, test);
/// Returns the first element.
/// The first element.
///
/// Throws a [StateError] if `this` is empty.
/// Otherwise returns the first element in the iteration order,
@ -626,7 +642,7 @@ abstract class Iterable<E> {
return it.current;
}
/// Returns the last element.
/// The last element.
///
/// Throws a [StateError] if `this` is empty.
/// Otherwise may iterate through the elements and returns the last one
@ -657,7 +673,7 @@ abstract class Iterable<E> {
return result;
}
/// Returns the first element that satisfies the given predicate [test].
/// The first element that satisfies the given predicate [test].
///
/// Iterates through elements and returns the first to satisfy [test].
///
@ -681,7 +697,7 @@ abstract class Iterable<E> {
throw IterableElementError.noElement();
}
/// Returns the last element that satisfies the given predicate [test].
/// The last element that satisfies the given predicate [test].
///
/// An iterable that can access its elements directly may check its
/// elements in any order (for example a list starts by checking the
@ -703,20 +719,25 @@ abstract class Iterable<E> {
/// function is returned.
/// If [orElse] is omitted, it defaults to throwing a [StateError].
E lastWhere(bool test(E element), {E orElse()?}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
result = element;
foundMatching = true;
var iterator = this.iterator;
// Potential result during first loop.
E result;
do {
if (!iterator.moveNext()) {
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
result = iterator.current;
} while (!test(result));
// Now `result` is actual result, unless a later one is found.
while (iterator.moveNext()) {
var current = iterator.current;
if (test(current)) result = current;
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
return result;
}
/// Returns the single element that satisfies [test].
/// The single element that satisfies [test].
///
/// Checks elements to see if `test(element)` returns true.
/// If exactly one element satisfies [test], that element is returned.
@ -740,20 +761,19 @@ abstract class Iterable<E> {
/// result = numbers.singleWhere((element) => element == 2); // Throws Error.
/// ```
E singleWhere(bool test(E element), {E orElse()?}) {
late E result;
bool foundMatching = false;
for (E element in this) {
if (test(element)) {
if (foundMatching) {
throw IterableElementError.tooMany();
}
result = element;
foundMatching = true;
var iterator = this.iterator;
E result;
do {
if (!iterator.moveNext()) {
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
result = iterator.current;
} while (!test(result));
while (iterator.moveNext()) {
if (test(iterator.current)) throw IterableElementError.tooMany();
}
if (foundMatching) return result;
if (orElse != null) return orElse();
throw IterableElementError.noElement();
return result;
}
/// Returns the [index]th element.
@ -773,12 +793,13 @@ abstract class Iterable<E> {
/// ```
E elementAt(int index) {
RangeError.checkNotNegative(index, "index");
int elementIndex = 0;
for (E element in this) {
if (index == elementIndex) return element;
elementIndex++;
var iterator = this.iterator;
var skipCount = index;
while (iterator.moveNext()) {
if (skipCount == 0) return iterator.current;
skipCount--;
}
throw IndexError.withLength(index, elementIndex,
throw IndexError.withLength(index, index - skipCount,
indexable: this, name: "index");
}
@ -796,10 +817,69 @@ abstract class Iterable<E> {
/// The conversion may omit calling `toString` on some elements if they
/// are known to not occur in the output, and it may stop iterating after
/// a hundred elements.
String toString() => IterableBase.iterableToShortString(this, '(', ')');
String toString() => iterableToShortString(this, '(', ')');
/// Convert an `Iterable` to a string like [Iterable.toString].
///
/// Allows using other delimiters than '(' and ')'.
///
/// Handles circular references where converting one of the elements
/// to a string ends up converting [iterable] to a string again.
static String iterableToShortString(Iterable iterable,
[String leftDelimiter = '(', String rightDelimiter = ')']) {
if (isToStringVisiting(iterable)) {
if (leftDelimiter == "(" && rightDelimiter == ")") {
// Avoid creating a new string in the "common" case.
return "(...)";
}
return "$leftDelimiter...$rightDelimiter";
}
List<String> parts = <String>[];
toStringVisiting.add(iterable);
try {
_iterablePartsToStrings(iterable, parts);
} finally {
assert(identical(toStringVisiting.last, iterable));
toStringVisiting.removeLast();
}
return (StringBuffer(leftDelimiter)
..writeAll(parts, ", ")
..write(rightDelimiter))
.toString();
}
/// Converts an `Iterable` to a string.
///
/// Converts each elements to a string, and separates the results by ", ".
/// Then wraps the result in [leftDelimiter] and [rightDelimiter].
///
/// Unlike [iterableToShortString], this conversion doesn't omit any
/// elements or puts any limit on the size of the result.
///
/// Handles circular references where converting one of the elements
/// to a string ends up converting [iterable] to a string again.
static String iterableToFullString(Iterable iterable,
[String leftDelimiter = '(', String rightDelimiter = ')']) {
if (isToStringVisiting(iterable)) {
return "$leftDelimiter...$rightDelimiter";
}
StringBuffer buffer = StringBuffer(leftDelimiter);
toStringVisiting.add(iterable);
try {
buffer.writeAll(iterable, ", ");
} finally {
assert(identical(toStringVisiting.last, iterable));
toStringVisiting.removeLast();
}
buffer.write(rightDelimiter);
return buffer.toString();
}
}
class _GeneratorIterable<E> extends ListIterable<E> {
// Methods have efficient implementations from `ListIterable`,
// based on `length` and `elementAt`.
/// The length of the generated iterable.
final int length;
@ -807,12 +887,7 @@ class _GeneratorIterable<E> extends ListIterable<E> {
final E Function(int) _generator;
/// Creates the generated iterable.
///
/// If [generator] is `null`, it is checked that `int` is assignable to [E].
_GeneratorIterable(this.length, E generator(int index)?)
: // The `as` below is used as check to make sure that `int` is assignable
// to [E].
_generator = generator ?? (_id as E Function(int));
_GeneratorIterable(this.length, this._generator);
E elementAt(int index) {
IndexError.check(index, length, indexable: this);
@ -822,3 +897,116 @@ class _GeneratorIterable<E> extends ListIterable<E> {
/// Helper function used as default _generator function.
static int _id(int n) => n;
}
/// Convert elements of [iterable] to strings and store them in [parts].
void _iterablePartsToStrings(Iterable<Object?> iterable, List<String> parts) {
// This is the complicated part of [iterableToShortString].
// It is extracted as a separate function to avoid having too much code
// inside the try/finally.
// Try to stay below this many characters.
const int lengthLimit = 80;
// Always at least this many elements at the start.
const int headCount = 3;
// Always at least this many elements at the end.
const int tailCount = 2;
// Stop iterating after this many elements. Iterables can be infinite.
const int maxCount = 100;
// Per entry length overhead. It's for ", " for all after the first entry,
// and for "(" and ")" for the initial entry. By pure luck, that's the same
// number.
const int overhead = 2;
const int ellipsisSize = 3; // "...".length.
int length = 0;
int count = 0;
Iterator<Object?> it = iterable.iterator;
// Initial run of elements, at least headCount, and then continue until
// passing at most lengthLimit characters.
while (length < lengthLimit || count < headCount) {
if (!it.moveNext()) return;
String next = "${it.current}";
parts.add(next);
length += next.length + overhead;
count++;
}
String penultimateString;
String ultimateString;
// Find last two elements. One or more of them may already be in the
// parts array. Include their length in `length`.
if (!it.moveNext()) {
if (count <= headCount + tailCount) return;
ultimateString = parts.removeLast();
penultimateString = parts.removeLast();
} else {
Object? penultimate = it.current;
count++;
if (!it.moveNext()) {
if (count <= headCount + 1) {
parts.add("$penultimate");
return;
}
ultimateString = "$penultimate";
penultimateString = parts.removeLast();
length += ultimateString.length + overhead;
} else {
Object? ultimate = it.current;
count++;
// Then keep looping, keeping the last two elements in variables.
assert(count < maxCount);
while (it.moveNext()) {
penultimate = ultimate;
ultimate = it.current;
count++;
if (count > maxCount) {
// If we haven't found the end before maxCount, give up.
// This cannot happen in the code above because each entry
// increases length by at least two, so there is no way to
// visit more than ~40 elements before this loop.
// Remove any surplus elements until length, including ", ...)",
// is at most lengthLimit.
while (length > lengthLimit - ellipsisSize - overhead &&
count > headCount) {
length -= parts.removeLast().length + overhead;
count--;
}
parts.add("...");
return;
}
}
penultimateString = "$penultimate";
ultimateString = "$ultimate";
length += ultimateString.length + penultimateString.length + 2 * overhead;
}
}
// If there is a gap between the initial run and the last two,
// prepare to add an ellipsis.
String? elision;
if (count > parts.length + tailCount) {
elision = "...";
length += ellipsisSize + overhead;
}
// If the last two elements were very long, and we have more than
// headCount elements in the initial run, drop some to make room for
// the last two.
while (length > lengthLimit && parts.length > headCount) {
length -= parts.removeLast().length + overhead;
if (elision == null) {
elision = "...";
length += ellipsisSize + overhead;
}
}
if (elision != null) {
parts.add(elision);
}
parts.add(penultimateString);
parts.add(ultimateString);
}

View file

@ -1040,3 +1040,16 @@ class DoubleLinkedQueueEntry<E> {
/// through scheduled events or timers, which would put the object into
/// an inconsistent state if simply being copied.
const vmIsolateUnsendable = pragma("vm:isolate-unsendable");
// Helpers used to detect cycles in collection `toString`s.
/// A collection used to identify cyclic lists during `toString` calls.
final List<Object> toStringVisiting = [];
/// Check if we are currently visiting [object] in a `toString` call.
bool isToStringVisiting(Object object) {
for (int i = 0; i < toStringVisiting.length; i++) {
if (identical(object, toStringVisiting[i])) return true;
}
return false;
}

View file

@ -5,7 +5,7 @@
part of dart._internal;
/// A rudimentary linked list.
class LinkedList<T extends LinkedListEntry<T>> extends IterableBase<T> {
class LinkedList<T extends LinkedListEntry<T>> extends Iterable<T> {
T get first => _first as T;
T? _first;

View file

@ -5,7 +5,7 @@
part of dart._vmservice;
/// Set like contains which automatically generated String ids for its items
class NamedLookup<E extends Object> extends Object with IterableMixin<E> {
class NamedLookup<E extends Object> extends Iterable<E> {
final IdGenerator _generator;
final _elements = <String, E>{};
final _ids = <E, String>{};