[dartdevc] fix #36293, call nSM on callable objects if the lookup fails

Given `obj.foo(1, 2, 3)`, if `obj.foo` succeeds but `call()` on the
resulting object fails, DDC should call `noSuchMethod()` on the result
of `obj.foo`, not on `obj`. This CL also adds a few more test cases that
DDC was failing on.

Change-Id: I76c3cc73e4cc81791f961c6b2a55cbfea7a5240b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97701
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Jenny Messerly <jmesserly@google.com>
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
index b3865c1..6f67aa9 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart
@@ -254,6 +254,10 @@
     // We're not a function (and hence not a method either)
     // Grab the `call` method if it's not a function.
     if ($f != null) {
+      // Getting the member succeeded, so update the originalTarget.
+      // (we're now trying `call()` on `f`, so we want to call its nSM rather
+      // than the original target's nSM).
+      originalTarget = f;
       $f = ${bindCall(f, _canonicalMember(f, 'call'))};
       $ftype = null;
     }
diff --git a/tests/language_2/regress_36084_test.dart b/tests/language_2/regress_36084_test.dart
index 1d56eb5..d16706e 100644
--- a/tests/language_2/regress_36084_test.dart
+++ b/tests/language_2/regress_36084_test.dart
@@ -15,11 +15,27 @@
   }
 }
 
+class A2 {
+  // Same as A1 but without a call method.
+  int noSuchMethod(Invocation invocation) {
+    return 42;
+  }
+}
+
 class B {
   dynamic foo;
   dynamic get bar => foo;
 }
 
+class B1 {
+  dynamic foo;
+  dynamic get bar => foo;
+
+  int noSuchMethod(Invocation invocation) {
+    Expect.fail('B1.noSuchMethod should not be called.');
+  }
+}
+
 main() {
   B b = new B();
   b.foo = new A();
@@ -28,4 +44,19 @@
   b.foo = new A1();
   Expect.equals(42, b.foo(1, 2, 3));
   Expect.equals(42, b.bar(1, 2, 3));
+  b.foo = new A2();
+  Expect.equals(42, b.foo(1, 2, 3));
+  Expect.equals(42, b.bar(1, 2, 3));
+
+  // Same test but with B1, which has its own `noSuchMethod()` handler.
+  B1 b1 = new B1();
+  b1.foo = new A();
+  Expect.throwsNoSuchMethodError(() => b1.foo(1, 2, 3));
+  Expect.throwsNoSuchMethodError(() => b1.bar(1, 2, 3));
+  b1.foo = new A1();
+  Expect.equals(42, b1.foo(1, 2, 3));
+  Expect.equals(42, b1.bar(1, 2, 3));
+  b1.foo = new A2();
+  Expect.equals(42, b1.foo(1, 2, 3));
+  Expect.equals(42, b1.bar(1, 2, 3));
 }