| // Copyright (c) 2014, 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"; |
| |
| // Test for type checks against tear-off closures generated in different |
| // optimization contexts. |
| // |
| // The type of a tear-off closure depends on the receiver when the method |
| // signature uses a type parameter of the method's class. This means that type |
| // checking needs to reference the receiver correctly, either during closure |
| // creation or during the type test. |
| |
| class A<T> { |
| const A(); |
| void add(T x) {} |
| T elementAt(int index) => index == 0 ? 42 as dynamic : 'string'; |
| |
| // This call get:elementAt has a known receiver type, so is is potentially |
| // eligible for a dummy receiver optimization. |
| getElementAt() => this.elementAt; |
| // Same for get:add. |
| getAdd() => this.add; |
| |
| toString() => 'A<$T>'; |
| } |
| |
| var getAddOfA = (a) => a.getAdd(); |
| var getElementAtOfA = (a) => a.getElementAt(); |
| |
| var getAdd1 = (a) => a.add; // receiver has unknown type here. |
| |
| var getAdd2 = (a) { |
| // Call needs to be indirect to avoid inlining. |
| if (a is A) return getAddOfA(a); |
| return a.add; |
| }; |
| |
| var getElementAt1 = (a) => a.elementAt; // receiver has unknown type here. |
| |
| var getElementAt2 = (a) { |
| // Call needs to be indirect to avoid inlining. |
| if (a is A) return getElementAtOfA(a); |
| return a.elementAt; |
| }; |
| |
| typedef void IntToVoid(int x); |
| typedef void StringToVoid(String x); |
| |
| typedef int IntToInt(int x); |
| typedef String IntToString(int x); |
| typedef T IntToT<T>(int x); |
| |
| var inscrutable; |
| |
| var checkers = { |
| 'IntToVoid': (x) => x is IntToVoid, |
| 'StringToVoid': (x) => x is StringToVoid, |
| 'IntToInt': (x) => x is IntToInt, |
| 'IntToString': (x) => x is IntToString, |
| 'IntToT<int>': (x) => x is IntToT<int>, |
| 'IntToT<String>': (x) => x is IntToT<String>, |
| }; |
| |
| var methods = { |
| 'getAdd1': (x) => getAdd1(x), |
| 'getAdd2': (x) => getAdd2(x), |
| 'getElementAt1': (x) => getElementAt1(x), |
| 'getElementAt2': (x) => getElementAt2(x), |
| }; |
| |
| main() { |
| inscrutable = (x) => x; |
| |
| getAdd1 = inscrutable(getAdd1); |
| getAdd2 = inscrutable(getAdd2); |
| getElementAt1 = inscrutable(getElementAt1); |
| getElementAt2 = inscrutable(getElementAt2); |
| getAddOfA = inscrutable(getAddOfA); |
| getElementAtOfA = inscrutable(getElementAtOfA); |
| |
| check(methodNames, objects, trueCheckNames) { |
| for (var trueCheckName in trueCheckNames) { |
| if (!checkers.containsKey(trueCheckName)) { |
| Expect.fail("unknown check '$trueCheckName'"); |
| } |
| } |
| |
| for (var object in objects) { |
| for (var methodName in methodNames) { |
| var methodFn = methods[methodName] as dynamic; |
| var description = '$object'; |
| checkers.forEach((checkName, checkFn) { |
| bool answer = trueCheckNames.contains(checkName); |
| Expect.equals(answer, checkFn(methodFn(object)), |
| '$methodName($description) is $checkName'); |
| }); |
| } |
| } |
| } |
| |
| var objectsDyn = [[], new A(), new A<dynamic>()]; |
| var objectsInt = [<int>[], new A<int>()]; |
| var objectsStr = [<String>[], new A<String>()]; |
| var objectsLst = [<List>[], new A<List>()]; |
| |
| var m = ['getAdd1', 'getAdd2']; |
| check(m, objectsDyn, ['IntToVoid', 'StringToVoid']); |
| check(m, objectsInt, ['IntToVoid']); |
| check(m, objectsStr, ['StringToVoid']); |
| check(m, objectsLst, []); |
| |
| m = ['getElementAt1', 'getElementAt2']; |
| check(m, objectsDyn, [ |
| 'IntToInt', |
| 'IntToString', |
| 'IntToVoid', |
| 'IntToT<int>', |
| 'IntToT<String>' |
| ]); |
| check(m, objectsInt, ['IntToInt', 'IntToVoid', 'IntToT<int>']); |
| check(m, objectsStr, ['IntToString', 'IntToVoid', 'IntToT<String>']); |
| check(m, objectsLst, ['IntToVoid']); |
| } |