diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart index 21bc20d352c..6a116744d44 100644 --- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart +++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart @@ -553,10 +553,14 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) { var originalTarget = JS('!', '# === void 0', obj) ? f : obj; callNSM(@notNull String errorMessage) { + var name = displayName; + if (name is String && name.isEmpty) { + name = ''; + } return noSuchMethod( originalTarget, InvocationImpl( - displayName, + name, JS>('!', '#', args), namedArguments: named, // Repeated the default value here in JS to preserve the historic diff --git a/tests/dartdevc/no_such_method_errors_test.dart b/tests/dartdevc/no_such_method_errors_test.dart index a7e71749e39..1dc87b6e0c7 100644 --- a/tests/dartdevc/no_such_method_errors_test.dart +++ b/tests/dartdevc/no_such_method_errors_test.dart @@ -41,6 +41,22 @@ class A { 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) { @@ -55,6 +71,15 @@ Function fieldArity1 = (int val) { 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(); } @@ -63,6 +88,19 @@ int? x; void main() { group('Dynamic call of', () { + test('instance of class with a `call()` method', () { + dynamic d = B(); + Expect.throws( + () => d(), + (error) => error.toString().contains( + "NoSuchMethodError: 'call'\n" + "Dynamic call with 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()`. @@ -193,6 +231,8 @@ void main() { // Compiled as `dgcall()` and throws from `checkAndCall()`. expectThrowsNSMWithExactError( () => instantiatedTearoff(), + // 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. " @@ -277,6 +317,22 @@ void main() { (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), ); }); + test('class instance getter that returns tearoff', () { + Expect.throws( + () => A().getterArity1Tearoff(), + (error) => error.toString().contains( + "NoSuchMethodError: 'getterArity1Tearoff'", + ), + ); + }); + test('class instance field that stores a tearoff', () { + Expect.throws( + () => A().fieldArity1Tearoff(), + (error) => error.toString().contains( + "NoSuchMethodError: 'fieldArity1Tearoff'", + ), + ); + }); test('class instance generic method', () { dynamic instanceOfA = A(); Expect.throws( @@ -297,10 +353,14 @@ void main() { dynamic tearoff = A().genericArity2; Expect.throws( () => tearoff(10), - (error) => error.toString().contains("NoSuchMethodError: 'result'"), + (error) => error.toString().contains( + // TODO(60654): Improve error message to include the actual name of + // the method. + "NoSuchMethodError: 'result'", + ), ); }); - test('class instance field', () { + test('class instance field that stores a closure', () { dynamic instanceOfA = A(); Expect.throws( () => instanceOfA.fieldArity1(), @@ -308,6 +368,14 @@ void main() { error.toString().contains("NoSuchMethodError: 'fieldArity1'"), ); }); + test('class instance getter that stores a closure', () { + dynamic instanceOfA = A(); + Expect.throws( + () => instanceOfA.getterArity1(), + (error) => + error.toString().contains("NoSuchMethodError: 'getterArity1'"), + ); + }); test('class static method tearoff', () { dynamic tearoff = A.staticArity1; Expect.throws( @@ -329,7 +397,11 @@ void main() { dynamic tearoff = A.staticGenericArity2; Expect.throws( () => tearoff(10), - (error) => error.toString().contains("NoSuchMethodError: 'result'"), + (error) => error.toString().contains( + // TODO(60654): Improve error message to include the actual name of + // the method. + "NoSuchMethodError: 'result'", + ), ); }); test('top level method tearoff', () { @@ -352,13 +424,39 @@ void main() { dynamic tearoff = genericArity2; Expect.throws( () => tearoff(10), - (error) => error.toString().contains("NoSuchMethodError: 'result'"), + (error) => error.toString().contains( + // TODO(60654): Improve error message to include the actual name of + // the method. + "NoSuchMethodError: 'result'", + ), ); }); - test('top level field', () { + test('top level field storing a closure', () { Expect.throws( () => fieldArity1(), - (error) => error.toString().contains("NoSuchMethodError: ''"), + (error) => error.toString().contains( + "NoSuchMethodError: ''", + ), + ); + }); + test('top level getter that returns a closure', () { + Expect.throws( + () => getterArity1(), + (error) => error.toString().contains( + "NoSuchMethodError: ''", + ), + ); + }); + test('top level field storing a tearoff', () { + Expect.throws( + () => fieldArity1Tearoff(), + (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), + ); + }); + test('top level getter that returns a tearoff', () { + Expect.throws( + () => getterArity1Tearoff(), + (error) => error.toString().contains("NoSuchMethodError: 'arity1'"), ); }); });