| // 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. | 
 |  | 
 | // SharedOptions=--enable-experiment=triple-shift | 
 |  | 
 | 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; | 
 | } |