|  | // Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'dart:collection'; | 
|  | import 'package:expect/expect.dart'; | 
|  |  | 
|  | class ThrowingCurrentIterator implements Iterator<int> { | 
|  | int _remaining; | 
|  | ThrowingCurrentIterator(this._remaining); | 
|  |  | 
|  | bool moveNext() => _remaining-- > 0; | 
|  | int get current => throw 'current'; | 
|  | } | 
|  |  | 
|  | class ThrowingCurrent extends IterableBase<int> { | 
|  | final int _length; | 
|  | ThrowingCurrent(this._length); | 
|  | Iterator<int> get iterator => ThrowingCurrentIterator(_length); | 
|  | } | 
|  |  | 
|  | Iterable<int> f1() sync* { | 
|  | yield* ThrowingCurrent(5); | 
|  | yield* ThrowingCurrent(5); | 
|  | } | 
|  |  | 
|  | Iterable<int> f2() sync* { | 
|  | yield* f1(); | 
|  | yield* f1(); | 
|  | } | 
|  |  | 
|  | main() { | 
|  | // `IterableBase.length` uses only `moveNext()`. | 
|  | Expect.equals(5, ThrowingCurrent(5).length); | 
|  |  | 
|  | // The spec dictates that `yield*` calls `moveNext()` and `current` for each | 
|  | // element in order to add the value to the iterable associated with the | 
|  | // generator. | 
|  | final i1 = f1().iterator; | 
|  | Expect.throws(() => i1.moveNext()); | 
|  |  | 
|  | // Further calls to `moveNext()` must return false (17.15). | 
|  | Expect.isFalse(i1.moveNext()); | 
|  |  | 
|  | // Same tests but for nested `yield*`. | 
|  | final i2 = f2().iterator; | 
|  | Expect.throws(() => i2.moveNext()); | 
|  | Expect.isFalse(i2.moveNext()); | 
|  |  | 
|  | // Slightly surprising consequence of the specified behavior. | 
|  | Expect.throws(() => f1().length); | 
|  | Expect.throws(() => f2().length); | 
|  | } |