|  | // 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:expect/legacy/minitest.dart'; // ignore: deprecated_member_use_from_same_package | 
|  |  | 
|  | /// This test is currently written as a white box test to ensure we don't | 
|  | /// regress on any of the existing code paths that lead to NoSuchMethod errors | 
|  | /// from dynamic calls. The test cases written with the knowledge of what the | 
|  | /// generated code will look like. | 
|  |  | 
|  | // TODO(52190): Improve the NSM errors to make them more helpful. | 
|  |  | 
|  | expectThrowsNSMWithExactError( | 
|  | void Function() computation, | 
|  | String expectedErrorMessage, | 
|  | ) => Expect.throws<NoSuchMethodError>(computation, (error) { | 
|  | Expect.equals(expectedErrorMessage, error.toString()); | 
|  | return true; | 
|  | }); | 
|  |  | 
|  | class A { | 
|  | String arity1(int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | } | 
|  |  | 
|  | void genericArity2<T, S>() => print('$T, $S'); | 
|  |  | 
|  | static String staticArity1(int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | } | 
|  |  | 
|  | static void staticGenericArity2<T, S>() => print('$T, $S'); | 
|  |  | 
|  | Function? nullField; | 
|  |  | 
|  | Function fieldArity1 = (int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | }; | 
|  |  | 
|  | Function get getterArity1 => (int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | }; | 
|  |  | 
|  | Function fieldArity1Tearoff = A.staticArity1; | 
|  |  | 
|  | Function get getterArity1Tearoff => A.staticArity1; | 
|  | } | 
|  |  | 
|  | class B { | 
|  | String call(int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | String arity1(int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | } | 
|  |  | 
|  | void genericArity2<T, S>() => print('$T, $S'); | 
|  |  | 
|  | Function fieldArity1 = (int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | }; | 
|  |  | 
|  | Function get getterArity1 => (int val) { | 
|  | val += 10; | 
|  | return val.toString(); | 
|  | }; | 
|  |  | 
|  | Function fieldArity1Tearoff = arity1; | 
|  |  | 
|  | Function get getterArity1Tearoff => arity1; | 
|  |  | 
|  | String requiredNamedArity1({required bool fosse}) { | 
|  | return fosse.toString(); | 
|  | } | 
|  |  | 
|  | int? x; | 
|  |  | 
|  | void main() { | 
|  | group('Dynamic call of', () { | 
|  | test('instance of class with a `call()` method', () { | 
|  | dynamic d = B(); | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => d(), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: 'call'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Missing positional arguments. Expected: 1 Actual: 0\n" | 
|  | "Receiver: Instance of 'B'\n" | 
|  | "Arguments: []", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | dynamic instanceOfA = A(); | 
|  | test('instance of a class with no `call()` method', () { | 
|  | // Compiled as `dcall()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => instanceOfA(), | 
|  | "NoSuchMethodError: 'call'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Object has no instance method 'call'.\n" | 
|  | "Receiver: ${Error.safeToString(instanceOfA)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | group('null', () { | 
|  | // TODO(49628): These should actually throw NoSuchMethodError with a | 
|  | // helpful error message. | 
|  | group('value', () { | 
|  | dynamic nullVal = null; | 
|  | test('without type arguments', () { | 
|  | // Compiled as `dcall()`. | 
|  | Expect.throws(() => nullVal()); | 
|  | }); | 
|  | test('passing type arguments', () { | 
|  | // Compiled as `dgcall()`. | 
|  | Expect.throws(() => nullVal<String, bool>()); | 
|  | }); | 
|  | }); | 
|  | group('instance field', () { | 
|  | test('without type arguments', () { | 
|  | // Compiled as `dsend()`. | 
|  | Expect.throws(() => instanceOfA.nullField()); | 
|  | }); | 
|  | test('passing type arguments', () { | 
|  | // Compiled as `dgsend()`. | 
|  | Expect.throws(() => instanceOfA.nullField<String, bool>()); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | group('class instance members that do not exist', () { | 
|  | group('method', () { | 
|  | test('without passing type arguments', () { | 
|  | // Compiled as `dsend()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => instanceOfA.doesNotExist(), | 
|  | "NoSuchMethodError: 'doesNotExist'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Tried to invoke `null` like a method.\n" | 
|  | "Receiver: ${Error.safeToString(instanceOfA)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | test('passing type arguments', () { | 
|  | // Compiled as `dgsend()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => instanceOfA.doesNotExist<String, bool>(), | 
|  | "NoSuchMethodError: 'doesNotExist'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Tried to invoke `null` like a method.\n" | 
|  | "Receiver: ${Error.safeToString(instanceOfA)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | }); | 
|  | test('setter', () { | 
|  | // Compiled as `dput()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => instanceOfA.doesNotExist = 10, | 
|  | "NoSuchMethodError: 'doesNotExist='\n" | 
|  | "method not found\n" | 
|  | "Receiver: ${Error.safeToString(instanceOfA)}\n" | 
|  | "Arguments: [10]", | 
|  | ); | 
|  | }); | 
|  | test('getter', () { | 
|  | // Compiled as `dload()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => x = instanceOfA.doesNotExist, | 
|  | "NoSuchMethodError: 'doesNotExist'\n" | 
|  | "method not found\n" | 
|  | "Receiver: ${Error.safeToString(instanceOfA)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | }); | 
|  | group('tearoff', () { | 
|  | // The code path for throwing NoSuchMethodErrors because of the incorrect | 
|  | // type arguments is shared by all forms of dynamic invocations. Simply | 
|  | // using tearoffs to trigger each form of the error. | 
|  | dynamic arity1Tearoff = arity1; | 
|  | dynamic genericArity2Tearoff = genericArity2; | 
|  | dynamic instantiatedTearoff = genericArity2<int, String>; | 
|  | test('passing unexpected type arguments', () { | 
|  | // Compiled as `dgcall()` and throws from `checkAndCall()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => arity1Tearoff<bool>(42), | 
|  | "NoSuchMethodError: 'arity1'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Incorrect number of type arguments. " | 
|  | "Expected: 0 Actual: 1\n" | 
|  | "Receiver: ${Error.safeToString(arity1Tearoff)}\n" | 
|  | "Arguments: [42]", | 
|  | ); | 
|  | }); | 
|  | test('passing too many type arguments', () { | 
|  | // Compiled as `dgcall()` and throws from `checkAndCall()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => genericArity2Tearoff<int, double, String>(), | 
|  | "NoSuchMethodError: 'genericArity2'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Incorrect number of type arguments. " | 
|  | "Expected: 2 Actual: 3\n" | 
|  | "Receiver: ${Error.safeToString(genericArity2Tearoff)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | test('passing too few type arguments', () { | 
|  | // Compiled as `dgcall()` and throws from `checkAndCall()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => genericArity2Tearoff<int>(), | 
|  | "NoSuchMethodError: 'genericArity2'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Incorrect number of type arguments. " | 
|  | "Expected: 2 Actual: 1\n" | 
|  | "Receiver: ${Error.safeToString(genericArity2Tearoff)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | test('already instantiated and passing type arguments ', () { | 
|  | // Compiled as `dgcall()` and throws from `checkAndCall()`. | 
|  | expectThrowsNSMWithExactError( | 
|  | () => instantiatedTearoff<int, double>(), | 
|  | // TODO(60654): Improve error message to include the actual name of | 
|  | // the method. | 
|  | "NoSuchMethodError: 'result'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Incorrect number of type arguments. " | 
|  | "Expected: 0 Actual: 2\n" | 
|  | "Receiver: ${Error.safeToString(instantiatedTearoff)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | }); | 
|  | group('`Function.apply()`', () { | 
|  | Function arity1Tearoff = arity1; | 
|  | Function requiredNamedArity1Tearoff = requiredNamedArity1; | 
|  | // The code path for throwing NoSuchMethodErrors because of the wrong | 
|  | // number of arguments or incorrect named arguments is shared by all | 
|  | // forms of dynamic invocations. Function.apply used here for simplicity. | 
|  | // Argument errors generated from `_argumentErrors()`. | 
|  | test('passing too many arguments', () { | 
|  | expectThrowsNSMWithExactError( | 
|  | () => Function.apply(arity1Tearoff, [42, false]), | 
|  | "NoSuchMethodError: 'arity1'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Too many positional arguments. " | 
|  | "Expected: 1 Actual: 2\n" | 
|  | "Receiver: ${Error.safeToString(arity1Tearoff)}\n" | 
|  | "Arguments: [42, false]", | 
|  | ); | 
|  | }); | 
|  | test('passing too few arguments', () { | 
|  | expectThrowsNSMWithExactError( | 
|  | () => Function.apply(arity1Tearoff, []), | 
|  | "NoSuchMethodError: 'arity1'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Missing positional arguments. " | 
|  | "Expected: 1 Actual: 0\n" | 
|  | "Receiver: ${Error.safeToString(arity1Tearoff)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | test('passing unexpected named argument', () { | 
|  | expectThrowsNSMWithExactError( | 
|  | () => Function.apply(requiredNamedArity1Tearoff, null, { | 
|  | #fosse: true, | 
|  | #cello: true, | 
|  | }), | 
|  | "NoSuchMethodError: 'requiredNamedArity1'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Unexpected named argument 'cello'.\n" | 
|  | "Receiver: ${Error.safeToString(requiredNamedArity1Tearoff)}\n" | 
|  | "Arguments: [fosse: true, cello: true]", | 
|  | ); | 
|  | }); | 
|  | test('missing required named argument', () { | 
|  | // Missing required named arguments are not an error when running | 
|  | // without sound null safety. | 
|  | if (hasUnsoundNullSafety) return; | 
|  | expectThrowsNSMWithExactError( | 
|  | () => Function.apply(requiredNamedArity1Tearoff, null), | 
|  | "NoSuchMethodError: 'requiredNamedArity1'\n" | 
|  | "Dynamic call failed.\n" | 
|  | "Missing required named arguments: fosse.\n" | 
|  | "Receiver: ${Error.safeToString(requiredNamedArity1Tearoff)}\n" | 
|  | "Arguments: []", | 
|  | ); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | group('Descriptors appearing in `NoSuchMethodError` message for ', () { | 
|  | // Some extra tests for the names that appear in all the forms of dynamic | 
|  | // calls. All of these tests pass wrong number of arguments just to trigger | 
|  | // the error message. | 
|  | test('class instance method', () { | 
|  | dynamic instanceOfA = A(); | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => instanceOfA.arity1(), | 
|  | (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), | 
|  | ); | 
|  | }); | 
|  | test('class instance method tearoff', () { | 
|  | dynamic tearoff = A().arity1; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(), | 
|  | (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), | 
|  | ); | 
|  | }); | 
|  | test('class instance getter that returns tearoff', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => A().getterArity1Tearoff(), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: 'getterArity1Tearoff'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('class instance field that stores a tearoff', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => A().fieldArity1Tearoff(), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: 'fieldArity1Tearoff'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('class instance generic method', () { | 
|  | dynamic instanceOfA = A(); | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => instanceOfA.genericArity2(10), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'genericArity2'"), | 
|  | ); | 
|  | }); | 
|  | test('class instance generic method tearoff', () { | 
|  | dynamic tearoff = A().genericArity2; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'genericArity2'"), | 
|  | ); | 
|  | }); | 
|  | test('class instance generic method tearoff instantiated', () { | 
|  | dynamic tearoff = A().genericArity2<int, String>; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => error.toString().contains( | 
|  | // TODO(60654): Improve error message to include the actual name of | 
|  | // the method. | 
|  | "NoSuchMethodError: 'result'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('class instance field that stores a closure', () { | 
|  | dynamic instanceOfA = A(); | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => instanceOfA.fieldArity1(), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'fieldArity1'"), | 
|  | ); | 
|  | }); | 
|  | test('class instance getter that stores a closure', () { | 
|  | dynamic instanceOfA = A(); | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => instanceOfA.getterArity1(), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'getterArity1'"), | 
|  | ); | 
|  | }); | 
|  | test('class static method tearoff', () { | 
|  | dynamic tearoff = A.staticArity1; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'staticArity1'"), | 
|  | ); | 
|  | }); | 
|  | test('class static generic method tearoff', () { | 
|  | dynamic tearoff = A.staticGenericArity2; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: 'staticGenericArity2'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('class static generic method tearoff instantiated', () { | 
|  | dynamic tearoff = A.staticGenericArity2<int, double>; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => error.toString().contains( | 
|  | // TODO(60654): Improve error message to include the actual name of | 
|  | // the method. | 
|  | "NoSuchMethodError: 'result'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('top level method tearoff', () { | 
|  | dynamic tearoff = A.staticArity1; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'staticArity1'"), | 
|  | ); | 
|  | }); | 
|  | test('top level generic method tearoff', () { | 
|  | dynamic tearoff = genericArity2; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => | 
|  | error.toString().contains("NoSuchMethodError: 'genericArity2'"), | 
|  | ); | 
|  | }); | 
|  | test('top level generic method tearoff instantiated', () { | 
|  | dynamic tearoff = genericArity2<int, String>; | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => tearoff(10), | 
|  | (error) => error.toString().contains( | 
|  | // TODO(60654): Improve error message to include the actual name of | 
|  | // the method. | 
|  | "NoSuchMethodError: 'result'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('top level field storing a closure', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => fieldArity1(), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: '<anonymous closure>'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('top level getter that returns a closure', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => getterArity1(), | 
|  | (error) => error.toString().contains( | 
|  | "NoSuchMethodError: '<anonymous closure>'", | 
|  | ), | 
|  | ); | 
|  | }); | 
|  | test('top level field storing a tearoff', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => fieldArity1Tearoff(), | 
|  | (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), | 
|  | ); | 
|  | }); | 
|  | test('top level getter that returns a tearoff', () { | 
|  | Expect.throws<NoSuchMethodError>( | 
|  | () => getterArity1Tearoff(), | 
|  | (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), | 
|  | ); | 
|  | }); | 
|  | }); | 
|  | } |