[dartdevc] Add top level function names to NSM error messages

These are now represented as named functions. This will also help the
debug tools give more information.

Tested with three applications and adding the names caused a code size increases
of less than 1%.

|Total App JS size | Increase |
|------------------|----------|
|  24.7 MB         |   0.06%  |
|  242  MB         |   0.14%  |
|  327  MB         |   0.19%  |

Fixes: #37118
Change-Id: I89a259215ceb8b9e559a190dbd521c923d4c55c2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105546
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index 1dc1e73..d5a4880 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -2630,7 +2630,10 @@
 
     var element = resolutionMap.elementDeclaredByFunctionDeclaration(node);
     var nameExpr = _emitTopLevelName(element);
-    body.add(js.statement('# = #', [nameExpr, fn]));
+    body.add(js.statement('# = #', [
+      nameExpr,
+      js_ast.NamedFunction(js_ast.TemporaryId(element.name), fn)
+    ]));
     // Function types of top-level/static functions are only needed when
     // dart:mirrors is enabled.
     // TODO(jmesserly): do we even need this for mirrors, since statics are not
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 5351443..9971f7c 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -2396,7 +2396,8 @@
     }
 
     var nameExpr = _emitTopLevelName(p);
-    body.add(js.statement('# = #', [nameExpr, fn]));
+    body.add(js.statement('# = #',
+        [nameExpr, js_ast.NamedFunction(js_ast.TemporaryId(p.name.name), fn)]));
     // Function types of top-level/static functions are only needed when
     // dart:mirrors is enabled.
     // TODO(jmesserly): do we even need this for mirrors, since statics are not
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 0c287ae..c34bca0 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
@@ -274,6 +274,7 @@
       originalTarget = f;
       $f = ${bindCall(f, _canonicalMember(f, 'call'))};
       $ftype = null;
+      $displayName = "call";
     }
     if ($f == null) return callNSM(
         "Dynamic call of object has no instance method 'call'.");
@@ -325,8 +326,8 @@
   return callNSM(errorMessage);
 })()''');
 
-dcall(f, args, [@undefined named]) =>
-    _checkAndCall(f, null, JS('', 'void 0'), null, args, named, 'call');
+dcall(f, args, [@undefined named]) => _checkAndCall(
+    f, null, JS('', 'void 0'), null, args, named, JS('', 'f.name'));
 
 dgcall(f, typeArgs, args, [@undefined named]) =>
     _checkAndCall(f, null, JS('', 'void 0'), typeArgs, args, named, 'call');
diff --git a/tests/compiler/dartdevc_native/no_such_method_errors_test.dart b/tests/compiler/dartdevc_native/no_such_method_errors_test.dart
new file mode 100644
index 0000000..1298c38
--- /dev/null
+++ b/tests/compiler/dartdevc_native/no_such_method_errors_test.dart
@@ -0,0 +1,94 @@
+// 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";
+
+_expectInErrorMessage(String expected, String actual) {
+  Expect.isTrue(
+      actual.contains(expected),
+      'Error message should contain "$expected", '
+      'but was ${actual.toString()}.');
+}
+
+class A {
+  int x = 42;
+  String arity1(int val) {
+    val += 10;
+    return val.toString();
+  }
+}
+
+String arity1(int val) {
+  val += 10;
+  return val.toString();
+}
+
+dynamic dynamicFunction = arity1;
+
+void main() {
+  dynamic instanceOfA = A();
+  // Call an instance of a class with no call() method.
+  try {
+    instanceOfA();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'call'", message);
+    _expectInErrorMessage("Receiver: Instance of 'A'", message);
+  }
+
+  dynamic tearOff = instanceOfA.arity1;
+  // Dynamic call of a class method with too many arguments.
+  try {
+    tearOff(1, 2);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'bound arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Dynamic call of a class method with too few arguments.
+  try {
+    tearOff();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'bound arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+
+  // Dynamic call of a top level funciton with too many arguments.
+  try {
+    dynamicFunction(1, 2);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Dynamic call of a top level funciton with too few arguments.
+  try {
+    dynamicFunction();
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+
+  // Function.apply() with too many arguments.
+  try {
+    Function.apply(dynamicFunction, [1, 2]);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too many arguments", message);
+  }
+
+  // Function.apply() with too few arguments.
+  try {
+    Function.apply(dynamicFunction, []);
+  } on NoSuchMethodError catch (error) {
+    var message = error.toString();
+    _expectInErrorMessage("NoSuchMethodError: 'arity1'", message);
+    _expectInErrorMessage("too few arguments", message);
+  }
+}