|  | // Copyright (c) 2019, 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 "package:expect/expect.dart"; | 
|  | import "package:async_helper/async_helper.dart"; | 
|  |  | 
|  | // The >>> operator is (again) supported by Dart | 
|  | // (This test does not test int.operator>>>, such a test belongs in the corelib | 
|  | // test collection. Const uses of int.operator>>> is tested elsewhere as well). | 
|  |  | 
|  | /// Syntactically tricky coincidences containing >>> and >>>=. | 
|  | /// DO NOT FORMAT THIS FILE. There should not be a space between >>> and =. | 
|  | typedef F3<T extends List<List<int>>>= T Function(); | 
|  | typedef F4<T extends List<List<List<int>>>>= T Function(); | 
|  | typedef F5<T extends List<List<List<List<int>>>>>= T Function(); | 
|  | typedef F6<T extends List<List<List<List<List<int>>>>>>= T Function(); | 
|  | class E3<T extends List<List<int>>> {} | 
|  | class E4<T extends List<List<List<int>>>> {} | 
|  | class E5<T extends List<List<List<List<int>>>>> {} | 
|  | class E6<T extends List<List<List<List<List<int>>>>>> {} | 
|  |  | 
|  | main() { | 
|  | // >>> is an overridable operator. | 
|  | const c1 = C(1); | 
|  | const c2 = C(2); | 
|  | Expect.identical(c2, c1 >>> c2); | 
|  |  | 
|  | /// It combines to an assignment operator. | 
|  | C c = c1; | 
|  | c >>>= c2; | 
|  | Expect.identical(c2, c); | 
|  |  | 
|  | // Operand needs to have correct type for typed invocation. | 
|  | c1 // | 
|  | >>> 4 //# 01: compile-time error | 
|  | >>> "string" //# 02: compile-time error | 
|  | ; | 
|  | c // | 
|  | >>>= 4 //# 03: compile-time error | 
|  | ; | 
|  |  | 
|  | // Dynamic invocations are allowed, and check types at run-time. | 
|  | dynamic d = c1; | 
|  | Expect.identical(c2, d >>> c2); | 
|  | Expect.throws(() => d >>> 4); | 
|  |  | 
|  | // There is a symbol for >>>, both as constructed and literal. | 
|  | Expect.identical(const Symbol(">>>"), #>>>); | 
|  |  | 
|  | // No such method can catch dynamic invocations of >>>: | 
|  | dynamic nsm = NSM(); | 
|  | Invocation invocation = nsm >>> c2; | 
|  | Expect.isTrue(invocation.isMethod); | 
|  | Expect.isFalse(invocation.isAccessor); | 
|  | Expect.equals(#>>>, invocation.memberName); | 
|  | Expect.equals(1, invocation.positionalArguments.length); | 
|  | Expect.identical(c2, invocation.positionalArguments[0]); | 
|  | Expect.equals(0, invocation.namedArguments.length); | 
|  |  | 
|  | invocation = (nsm >>>= c2); | 
|  | Expect.isTrue(invocation.isMethod); | 
|  | Expect.isFalse(invocation.isAccessor); | 
|  | Expect.equals(#>>>, invocation.memberName); | 
|  | Expect.equals(1, invocation.positionalArguments.length); | 
|  | Expect.identical(c2, invocation.positionalArguments[0]); | 
|  | Expect.equals(0, invocation.namedArguments.length); | 
|  |  | 
|  | // And unimplemented interface methods. | 
|  | ShiftNSM shnsm = ShiftNSM(); | 
|  | invocation = shnsm >>> c2; | 
|  | Expect.isTrue(invocation.isMethod); | 
|  | Expect.isFalse(invocation.isAccessor); | 
|  | Expect.equals(#>>>, invocation.memberName); | 
|  | Expect.equals(1, invocation.positionalArguments.length); | 
|  | Expect.identical(c2, invocation.positionalArguments[0]); | 
|  | Expect.equals(0, invocation.namedArguments.length); | 
|  |  | 
|  | // If there is an interface, we must match it, even if the call | 
|  | // otherwise goes to noSuchMethod. | 
|  | shnsm // | 
|  | >>> 4 //# 04: compile-time error | 
|  | ; | 
|  |  | 
|  | /// A type error in the nSM return value is caught. | 
|  | dynamic badNSM = BadNSM(); | 
|  | Expect.throws(() => badNSM >>> "not an int", (e) => e != "Unreachable"); | 
|  | Expect.throws(() => badNSM >>> 4, (e) => e != "Unreachable"); | 
|  |  | 
|  | asyncStart(); | 
|  | () async { | 
|  | // Operands can be asynchronous. | 
|  | var fc1 = Future.value(c1); | 
|  | var fc2 = Future.value(c2); | 
|  | Expect.identical(c2, (await fc1) >>> (await fc2)); | 
|  | /// The operator itself can be async. | 
|  | var async = Async(); | 
|  | Expect.identical(c1, await (async >>> c1)); | 
|  |  | 
|  | var asyncStar = AsyncStar(); | 
|  | int count = 0; | 
|  | await for (var v in asyncStar >>> c1) { | 
|  | count++; | 
|  | Expect.identical(c1, v); | 
|  | } | 
|  | Expect.equals(1, count); | 
|  | asyncEnd(); | 
|  | }(); | 
|  |  | 
|  | { | 
|  | var syncStar = SyncStar(); | 
|  | int count = 0; | 
|  | for (var v in syncStar >>> c1) { | 
|  | count++; | 
|  | Expect.identical(c1, v); | 
|  | } | 
|  | Expect.equals(1, count); | 
|  | } | 
|  |  | 
|  | // >>> has same precedence as >> (and <<), is left associative. | 
|  | // Binds weaker than addition/multiplication, stronger than other bitwise | 
|  | // operators and comparisons. | 
|  | final a = Assoc("*"); | 
|  | Expect.equals("((~*)>>>(~*))", "${~a >>> ~a}"); | 
|  | Expect.equals("((*+*)>>>(*+*))", "${a + a >>> a + a}"); | 
|  | Expect.equals("((*/*)>>>(*/*))", "${a / a >>> a / a}"); | 
|  | Expect.equals("(((*>>*)>>>*)>>*)", "${a >> a >>> a >> a}"); | 
|  | Expect.equals("((*&(*>>>*))&*)", "${a & a >>> a & a}"); | 
|  | Expect.equals("((*|(*>>>*))|*)", "${a | a >>> a | a}"); | 
|  | Expect.equals("((*^(*>>>*))^*)", "${a ^ a >>> a ^ a}"); | 
|  | Expect.equals("(*<(*>>>*))", "${a < a >>> a}"); | 
|  | Expect.equals("((*>>>*)<*)", "${a >>> a < a}"); | 
|  |  | 
|  | var res = a; | 
|  | res >>>= a; | 
|  | res >>>= a; | 
|  | Expect.equals("((*>>>*)>>>*)", "$res"); | 
|  |  | 
|  | // Exercise the type declarations below. | 
|  | E3<List<List<int>>>(); | 
|  | E4<List<List<List<int>>>>(); | 
|  | E5<List<List<List<List<int>>>>>(); | 
|  | E6<List<List<List<List<List<int>>>>>>(); | 
|  | Expect.type<F3<List<List<int>>>>(() => <List<int>>[]); | 
|  | Expect.type<F4<List<List<List<int>>>>>(() => <List<List<int>>>[]); | 
|  | Expect.type<F5<List<List<List<List<int>>>>>>(() => <List<List<List<int>>>>[]); | 
|  | Expect.type<F6<List<List<List<List<List<int>>>>>>>( | 
|  | () => <List<List<List<List<int>>>>>[]); | 
|  | } | 
|  |  | 
|  | /// Class with a simple overridden `operator>>>`. | 
|  | class C { | 
|  | final int id; | 
|  | const C(this. id); | 
|  | C operator >>>(C other) => other; | 
|  | String toString() => "C($id)"; | 
|  | } | 
|  |  | 
|  | /// Invalid declarations of `>>>` operator. | 
|  | class Invalid { | 
|  | // Overridable operator must have exactly one required parameter. | 
|  | Object operator>>>() => null;  //# arg0: compile-time error | 
|  | Object operator>>>(v1, v2) => null;  //# arg2: compile-time error | 
|  | Object operator>>>([v1]) => null;  //# argOpt: compile-time error | 
|  | Object operator>>>({v1}) => null;  //# argNam: compile-time error | 
|  | } | 
|  |  | 
|  | /// Class with noSuchMethod and no `>>>` operator. | 
|  | class NSM { | 
|  | dynamic noSuchMethod(Invocation invocation) { | 
|  | return invocation; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Class with nSM and abstract `>>>` (implicit typed forwarder). | 
|  | class ShiftNSM extends NSM { | 
|  | dynamic operator>>>(C o); | 
|  | } | 
|  |  | 
|  | /// Class with nSM and abstract `>>>` where nSM returns wrong type. | 
|  | class BadNSM { | 
|  | int operator>>>(int n); | 
|  | dynamic noSuchMethod(Invocation i) { | 
|  | if (i.memberName == #>>>) { | 
|  | if (i.positionalArguments.first is! int) throw "Unreachable"; | 
|  | return "notAnInt"; | 
|  | } | 
|  | return super.noSuchMethod(i); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Class with an `async` implementation of `operator >>>` | 
|  | class Async { | 
|  | Future<C> operator >>>(C value) async => value; | 
|  | } | 
|  |  | 
|  | /// Class with an `async*` implementation of `operator >>>` | 
|  | class AsyncStar { | 
|  | Stream<C> operator >>>(C value) async* { | 
|  | yield value; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Class with a `sync*` implementation of `operator >>>` | 
|  | class SyncStar { | 
|  | Iterable<C> operator >>>(C value) sync* { | 
|  | yield value; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Helper class to record precedence and associativity of operators. | 
|  | class Assoc { | 
|  | final String ops; | 
|  | Assoc(this.ops); | 
|  | Assoc operator ~() => Assoc("(~${this})"); | 
|  | Assoc operator +(Assoc other) => Assoc("(${this}+$other)"); | 
|  | Assoc operator /(Assoc other) => Assoc("(${this}/$other)"); | 
|  | Assoc operator &(Assoc other) => Assoc("(${this}&$other)"); | 
|  | Assoc operator |(Assoc other) => Assoc("(${this}|$other)"); | 
|  | Assoc operator ^(Assoc other) => Assoc("(${this}^$other)"); | 
|  | Assoc operator >(Assoc other) => Assoc("(${this}>$other)"); | 
|  | Assoc operator >>(Assoc other) => Assoc("(${this}>>$other)"); | 
|  | Assoc operator >>>(Assoc other) => Assoc("(${this}>>>$other)"); | 
|  | Assoc operator <(Assoc other) => Assoc("(${this}<$other)"); | 
|  | Assoc operator >=(Assoc other) => Assoc("(${this}>=$other)"); | 
|  | Assoc operator <=(Assoc other) => Assoc("(${this}<=$other)"); | 
|  | String toString() => ops; | 
|  | } |