Revert "[vm] Repair the resolver abstraction, take 2."

This reverts commit d79787771ec524dc08f84607bbb97cf1d0aff2e6.

Reason for revert:

This caused significant performance regressions on our protobuf_decode* benchmarks (probably on more benchmarks, but we don't yet have results due to some perf infra issue).

To avoid letting the regressions land for downstream users we'll revert it.

We should measure performance impact before landing this again.

Original change's description:
> [vm] Repair the resolver abstraction, take 2.
> 
>  - Move resolution logic that accumulated in the runtime entries into Resolver.
>  - Improve NoSuchMethodError for closures when --no-lazy-dispatchers
>  - Fix concurrent modification of Null class by nullability propagation.
> 
> Change-Id: Ib05b431a289d847785032dda46e1bbcb524b7343
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98428
> Commit-Queue: Ryan Macnak <rmacnak@google.com>
> Reviewed-by: RĂ©gis Crelier <regis@google.com>

TBR=rmacnak@google.com,alexmarkov@google.com,regis@google.com

Change-Id: I4f0f0a0d7c6018fc5968aafdce30f6d2e7495059
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99140
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/vm/compilation_trace.cc b/runtime/vm/compilation_trace.cc
index bceabea..48db21c 100644
--- a/runtime/vm/compilation_trace.cc
+++ b/runtime/vm/compilation_trace.cc
@@ -248,7 +248,22 @@
       field_ = cls_.LookupFieldAllowPrivate(function_name2_);
       if (!function_.IsNull() && !function_.is_static()) {
         // Maybe this was a method extractor.
-        // TODO(rmacnak)
+        function2_ =
+            Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_);
+        if (!function2_.IsNull()) {
+          error_ = CompileFunction(function2_);
+          if (error_.IsError()) {
+            if (FLAG_trace_compilation_trace) {
+              THR_Print(
+                  "Compilation trace: error compiling extractor %s for "
+                  "%s,%s,%s (%s)\n",
+                  function2_.ToCString(), uri_.ToCString(),
+                  class_name_.ToCString(), function_name_.ToCString(),
+                  Error::Cast(error_).ToErrorCString());
+            }
+            return error_.raw();
+          }
+        }
       }
     }
     if (field_.IsNull() && is_getter) {
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 3f206b0..e327435 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -499,7 +499,10 @@
   if (receiver_maybe_null) {
     const Class& null_class =
         Class::Handle(zone(), isolate()->object_store()->null_class());
-    if (Resolver::HasDefinition(zone(), null_class, method_name)) {
+    const Function& target = Function::Handle(
+        zone(),
+        Resolver::ResolveDynamicAnyArgs(zone(), null_class, method_name));
+    if (!target.IsNull()) {
       return ToCheck::kCheckCid;
     }
   }
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index b2a527c..0dec0c9 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -310,12 +310,11 @@
     // Nothing to do if type is already non-nullable.
     return;
   }
-
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
   const Class& null_class =
-      Class::Handle(zone, thread->isolate()->object_store()->null_class());
-  if (!Resolver::HasDefinition(zone, null_class, function_name)) {
+      Class::Handle(Isolate::Current()->object_store()->null_class());
+  const Function& target = Function::Handle(Resolver::ResolveDynamicAnyArgs(
+      Thread::Current()->zone(), null_class, function_name));
+  if (target.IsNull()) {
     // If the selector is not defined on Null, we can propagate non-nullness.
     CompileType* type = TypeOf(receiver);
     if (type->is_nullable()) {
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index cfb4b1f..a741a5e 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -267,14 +267,7 @@
   }
 
   // No compatible method or getter so invoke noSuchMethod.
-  String& function_name = String::Handle(zone);
-  if (instance.IsClosure()) {
-    function_name = Function::Handle(zone, Closure::Cast(instance).function())
-                        .QualifiedUserVisibleName();
-  } else {
-    function_name = Symbols::Call().raw();
-  }
-  return InvokeNoSuchMethod(instance, function_name, arguments,
+  return InvokeNoSuchMethod(instance, Symbols::Call(), arguments,
                             arguments_descriptor);
 }
 
diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h
index 82c3f2d..23342d3 100644
--- a/runtime/vm/dart_entry.h
+++ b/runtime/vm/dart_entry.h
@@ -47,7 +47,6 @@
   bool MatchesNameAt(intptr_t i, const String& other) const;
   // Returns array of argument names in the arguments order.
   RawArray* GetArgumentNames() const;
-  const Array& array() const { return array_; }
 
   // Generated code support.
   static intptr_t type_args_len_offset();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index dfb8f94..d427641 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3111,11 +3111,13 @@
   }
 
   // Check if function actually needs a dynamic invocation forwarder.
-  if ((kind() == RawFunction::kInvokeFieldDispatcher) ||
-      !kernel::NeedsDynamicInvocationForwarder(*this)) {
+  if (!kernel::NeedsDynamicInvocationForwarder(*this)) {
     result = raw();
   } else if (allow_add) {
     result = CreateDynamicInvocationForwarder(mangled_name);
+  }
+
+  if (allow_add) {
     owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
   }
 
@@ -3340,7 +3342,7 @@
 
   if (field.IsNull() || field.IsUninitialized()) {
     const String& internal_getter_name =
-        String::Handle(zone, Field::GetterSymbol(getter_name));
+        String::Handle(zone, Field::GetterName(getter_name));
     Function& getter =
         Function::Handle(zone, LookupStaticFunction(internal_getter_name));
 
@@ -3397,7 +3399,7 @@
   // Check for real fields and user-defined setters.
   const Field& field = Field::Handle(zone, LookupStaticField(setter_name));
   const String& internal_setter_name =
-      String::Handle(zone, Field::SetterSymbol(setter_name));
+      String::Handle(zone, Field::SetterName(setter_name));
 
   if (!field.IsNull() && check_is_entrypoint) {
     CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly));
@@ -3457,15 +3459,13 @@
   return value.raw();
 }
 
-RawObject* Class::Invoke(const String& function_name_in,
+RawObject* Class::Invoke(const String& function_name,
                          const Array& args,
                          const Array& arg_names,
                          bool respect_reflectable,
                          bool check_is_entrypoint) const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  const String& function_name =
-      String::Handle(zone, Symbols::New(thread, function_name_in));
 
   // TODO(regis): Support invocation of generic functions with type arguments.
   const int kTypeArgsLen = 0;
@@ -10972,12 +10972,12 @@
     // owner class.
     const Class& klass = Class::Handle(field.Owner());
     const String& internal_getter_name =
-        String::Handle(Field::GetterSymbol(getter_name));
+        String::Handle(Field::GetterName(getter_name));
     getter = klass.LookupStaticFunction(internal_getter_name);
   } else {
     // No field found. Check for a getter in the lib.
     const String& internal_getter_name =
-        String::Handle(Field::GetterSymbol(getter_name));
+        String::Handle(Field::GetterName(getter_name));
     obj = LookupLocalOrReExportObject(internal_getter_name);
     if (obj.IsFunction()) {
       getter = Function::Cast(obj).raw();
@@ -11028,7 +11028,7 @@
                                  bool check_is_entrypoint) const {
   Object& obj = Object::Handle(LookupLocalOrReExportObject(setter_name));
   const String& internal_setter_name =
-      String::Handle(Field::SetterSymbol(setter_name));
+      String::Handle(Field::SetterName(setter_name));
   AbstractType& setter_type = AbstractType::Handle();
   AbstractType& argument_type = AbstractType::Handle(value.GetType(Heap::kOld));
   if (obj.IsField()) {
@@ -11086,22 +11086,16 @@
   return DartEntry::InvokeFunction(setter, args);
 }
 
-RawObject* Library::Invoke(const String& function_name_in,
+RawObject* Library::Invoke(const String& function_name,
                            const Array& args,
                            const Array& arg_names,
                            bool respect_reflectable,
                            bool check_is_entrypoint) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  const String& function_name =
-      String::Handle(zone, Symbols::New(thread, function_name_in));
-
   // TODO(regis): Support invocation of generic functions with type arguments.
   const int kTypeArgsLen = 0;
 
-  Function& function = Function::Handle(zone);
-  Object& obj =
-      Object::Handle(zone, LookupLocalOrReExportObject(function_name));
+  Function& function = Function::Handle();
+  Object& obj = Object::Handle(LookupLocalOrReExportObject(function_name));
   if (obj.IsFunction()) {
     function ^= obj.raw();
   }
@@ -11112,39 +11106,37 @@
 
   if (function.IsNull()) {
     // Didn't find a method: try to find a getter and invoke call on its result.
-    const Object& getter_result = Object::Handle(
-        zone, InvokeGetter(function_name, false, respect_reflectable,
-                           check_is_entrypoint));
+    const Object& getter_result = Object::Handle(InvokeGetter(
+        function_name, false, respect_reflectable, check_is_entrypoint));
     if (getter_result.raw() != Object::sentinel().raw()) {
       if (check_is_entrypoint) {
         CHECK_ERROR(EntryPointFieldInvocationError(function_name));
       }
       // Make room for the closure (receiver) in arguments.
       intptr_t numArgs = args.Length();
-      const Array& call_args = Array::Handle(zone, Array::New(numArgs + 1));
-      Object& temp = Object::Handle(zone);
+      const Array& call_args = Array::Handle(Array::New(numArgs + 1));
+      Object& temp = Object::Handle();
       for (int i = 0; i < numArgs; i++) {
         temp = args.At(i);
         call_args.SetAt(i + 1, temp);
       }
       call_args.SetAt(0, getter_result);
       const Array& call_args_descriptor_array =
-          Array::Handle(zone, ArgumentsDescriptor::New(
-                                  kTypeArgsLen, call_args.Length(), arg_names));
+          Array::Handle(ArgumentsDescriptor::New(
+              kTypeArgsLen, call_args.Length(), arg_names));
       // Call closure.
       return DartEntry::InvokeClosure(call_args, call_args_descriptor_array);
     }
   }
 
   const Array& args_descriptor_array = Array::Handle(
-      zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
+      ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
   ArgumentsDescriptor args_descriptor(args_descriptor_array);
   const TypeArguments& type_args = Object::null_type_arguments();
   if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
       (respect_reflectable && !function.is_reflectable())) {
     return ThrowNoSuchMethod(
-        AbstractType::Handle(zone,
-                             Class::Handle(zone, toplevel_class()).RareType()),
+        AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
         function_name, args, arg_names, InvocationMirror::kTopLevel,
         InvocationMirror::kMethod);
   }
@@ -13478,14 +13470,20 @@
   bool is_smi_two_args_op = false;
 
   ASSERT(NumArgsTested() == 2);
+  const String& name = String::Handle(target_name());
+  const Class& smi_class = Class::Handle(Smi::Class());
   Zone* zone = Thread::Current()->zone();
-  const String& name = String::Handle(zone, target_name());
-  const Class& smi_class = Class::Handle(zone, Smi::Class());
-  const ArgumentsDescriptor& args_desc =
-      ArgumentsDescriptor(Array::Handle(zone, arguments_descriptor()));
-  Function& smi_op_target = Function::Handle(
-      zone,
-      Resolver::ResolveDynamicForReceiverClass(smi_class, name, args_desc));
+  Function& smi_op_target =
+      Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  if (smi_op_target.IsNull() &&
+      Function::IsDynamicInvocationForwarderName(name)) {
+    const String& demangled =
+        String::Handle(Function::DemangleDynamicInvocationForwarderName(name));
+    smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
+  }
+#endif
 
   if (NumberOfChecksIs(0)) {
     GrowableArray<intptr_t> class_ids(2);
@@ -15902,14 +15900,9 @@
   }
 
   const String& internal_getter_name =
-      String::Handle(zone, Field::GetterSymbol(getter_name));
-  const intptr_t kTypeArgsLen = 0;
-  const intptr_t kNumArgs = 1;
-  const ArgumentsDescriptor& args_desc = ArgumentsDescriptor(
-      Array::Handle(zone, ArgumentsDescriptor::New(kTypeArgsLen, kNumArgs)));
-  Function& function =
-      Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
-                                 klass, internal_getter_name, args_desc));
+      String::Handle(zone, Field::GetterName(getter_name));
+  Function& function = Function::Handle(
+      zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
 
   if (!function.IsNull() && check_is_entrypoint) {
     // The getter must correspond to either an entry-point field or a getter
@@ -15927,12 +15920,7 @@
 
   // Check for method extraction when method extractors are not created.
   if (function.IsNull() && !FLAG_lazy_dispatchers) {
-    const intptr_t kGetterNumArgs = 1;
-    const ArgumentsDescriptor& getter_args_desc =
-        ArgumentsDescriptor(Array::Handle(
-            zone, ArgumentsDescriptor::New(kTypeArgsLen, kGetterNumArgs)));
-    function = Resolver::ResolveDynamicForReceiverClass(klass, getter_name,
-                                                        getter_args_desc);
+    function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
 
     if (!function.IsNull() && check_is_entrypoint) {
       CHECK_ERROR(function.VerifyClosurizedEntryPoint());
@@ -15945,6 +15933,8 @@
     }
   }
 
+  const int kTypeArgsLen = 0;
+  const int kNumArgs = 1;
   const Array& args = Array::Handle(zone, Array::New(kNumArgs));
   args.SetAt(0, *this);
   const Array& args_descriptor = Array::Handle(
@@ -15968,14 +15958,9 @@
   }
 
   const String& internal_setter_name =
-      String::Handle(zone, Field::SetterSymbol(setter_name));
-  const intptr_t kTypeArgsLen = 0;
-  const intptr_t kNumArgs = 2;
-  const ArgumentsDescriptor& args_desc = ArgumentsDescriptor(
-      Array::Handle(zone, ArgumentsDescriptor::New(kTypeArgsLen, kNumArgs)));
-  const Function& setter =
-      Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
-                                 klass, internal_setter_name, args_desc));
+      String::Handle(zone, Field::SetterName(setter_name));
+  const Function& setter = Function::Handle(
+      zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name));
 
   if (check_is_entrypoint) {
     // The setter must correspond to either an entry-point field or a setter
@@ -15991,6 +15976,8 @@
     }
   }
 
+  const int kTypeArgsLen = 0;
+  const int kNumArgs = 2;
   const Array& args = Array::Handle(zone, Array::New(kNumArgs));
   args.SetAt(0, *this);
   args.SetAt(1, value);
@@ -16002,51 +15989,46 @@
                                 type_args);
 }
 
-RawObject* Instance::Invoke(const String& function_name_in,
+RawObject* Instance::Invoke(const String& function_name,
                             const Array& args,
                             const Array& arg_names,
                             bool respect_reflectable,
                             bool check_is_entrypoint) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  const String& function_name =
-      String::Handle(zone, Symbols::New(thread, function_name_in));
+  Zone* zone = Thread::Current()->zone();
   Class& klass = Class::Handle(zone, clazz());
-  // TODO(regis): Support invocation of generic functions with type arguments.
-  const intptr_t kTypeArgsLen = 0;
-  const Array& args_descriptor = Array::Handle(
-      zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
   Function& function = Function::Handle(
-      zone, Resolver::ResolveDynamicForReceiverClass(
-                klass, function_name, ArgumentsDescriptor(args_descriptor)));
+      zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
 
   if (!function.IsNull() && check_is_entrypoint) {
     CHECK_ERROR(function.VerifyCallEntryPoint());
   }
 
+  // TODO(regis): Support invocation of generic functions with type arguments.
+  const int kTypeArgsLen = 0;
+  const Array& args_descriptor = Array::Handle(
+      zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
+
   TypeArguments& type_args = TypeArguments::Handle(zone);
   if (klass.NumTypeArguments() > 0) {
     type_args ^= GetTypeArguments();
   }
 
-  if (function.IsNull() && !FLAG_lazy_dispatchers) {
+  if (function.IsNull()) {
     // Didn't find a method: try to find a getter and invoke call on its result.
     const String& getter_name =
-        String::Handle(zone, Field::GetterSymbol(function_name));
-    const int kGetterNumArgs = 1;
-    const Array& getter_args_descriptor = Array::Handle(
-        zone, ArgumentsDescriptor::New(kTypeArgsLen, kGetterNumArgs));
-    function = Resolver::ResolveDynamicForReceiverClass(
-        klass, getter_name, ArgumentsDescriptor(getter_args_descriptor));
+        String::Handle(zone, Field::GetterName(function_name));
+    function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
     if (!function.IsNull()) {
       if (check_is_entrypoint) {
         CHECK_ERROR(EntryPointFieldInvocationError(function_name));
       }
       ASSERT(function.kind() != RawFunction::kMethodExtractor);
       // Invoke the getter.
-      const Array& getter_args =
-          Array::Handle(zone, Array::New(kGetterNumArgs));
+      const int kNumArgs = 1;
+      const Array& getter_args = Array::Handle(zone, Array::New(kNumArgs));
       getter_args.SetAt(0, *this);
+      const Array& getter_args_descriptor = Array::Handle(
+          zone, ArgumentsDescriptor::New(kTypeArgsLen, getter_args.Length()));
       const Object& getter_result = Object::Handle(
           zone, InvokeInstanceFunction(*this, function, getter_name,
                                        getter_args, getter_args_descriptor,
@@ -22031,12 +22013,6 @@
   }
 #endif
   if (!is_marked_entrypoint) {
-    if (member.IsFunction() &&
-        Function::Cast(member).kind() == RawFunction::kInvokeFieldDispatcher) {
-      return EntryPointFieldInvocationError(
-          String::Handle(Function::Cast(member).name()));
-    }
-
     const char* member_cstring =
         member.IsFunction()
             ? OS::SCreate(
diff --git a/runtime/vm/object_reload.cc b/runtime/vm/object_reload.cc
index 04ad255..31eb620 100644
--- a/runtime/vm/object_reload.cc
+++ b/runtime/vm/object_reload.cc
@@ -713,15 +713,13 @@
       if (IsImmutable()) {
         return;
       }
-      const String& name = String::Handle(zone, target_name());
-      const Class& smi_class = Class::Handle(zone, Smi::Class());
-      const ArgumentsDescriptor& args_desc =
-          ArgumentsDescriptor(Array::Handle(zone, arguments_descriptor()));
+      Zone* zone = Thread::Current()->zone();
+      const String& name = String::Handle(target_name());
+      const Class& smi_class = Class::Handle(Smi::Class());
       const Function& smi_op_target = Function::Handle(
-          zone,
-          Resolver::ResolveDynamicForReceiverClass(smi_class, name, args_desc));
+          Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
       GrowableArray<intptr_t> class_ids(2);
-      Function& target = Function::Handle(zone);
+      Function& target = Function::Handle();
       GetCheckAt(0, &class_ids, &target);
       if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
           (class_ids[1] == kSmiCid)) {
@@ -729,7 +727,7 @@
         // count.
         ClearCountAt(0);
         WriteSentinelAt(1);
-        const Array& array = Array::Handle(zone, entries());
+        const Array& array = Array::Handle(entries());
         array.Truncate(2 * TestEntryLength());
         return;
       }
diff --git a/runtime/vm/resolver.cc b/runtime/vm/resolver.cc
index b50e120..d7a9fc6 100644
--- a/runtime/vm/resolver.cc
+++ b/runtime/vm/resolver.cc
@@ -16,20 +16,16 @@
 
 DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
 
-// Find the target of an instance call, which might be
-// - a user-defined method
-// - a method extractor (getter call to user-defined regular method)
-// - an invoke field dispatcher (regular method call to user-defined getter)
-// - a dynamic invocation forwarder (dynamic call to one of the above)
-// - a no-such-method dispatcher (no target or target with wrong number of
-//   positional arguments)
-//
-// Positional arguments are checked here: the number of positional arguments
-// doesn't match the target, a no-such-method-dispatcher will be returned.
-// Named arguments are checked in the target's prologue.
+// The actual names of named arguments are not checked by the dynamic resolver,
+// but by the method entry code. It is important that the dynamic resolver
+// checks that no named arguments are passed to a method that does not accept
+// them, since the entry code of such a method does not check for named
+// arguments. The dynamic resolver actually checks that a valid number of named
+// arguments is passed in.
 RawFunction* Resolver::ResolveDynamic(const Instance& receiver,
                                       const String& function_name,
                                       const ArgumentsDescriptor& args_desc) {
+  // Figure out type of receiver first.
   const Class& cls = Class::Handle(receiver.clazz());
   return ResolveDynamicForReceiverClass(cls, function_name, args_desc);
 }
@@ -42,54 +38,46 @@
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
 
-  if (FLAG_trace_resolving) {
-    THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
-              String::Handle(zone, receiver_class.Name()).ToCString());
-  }
-
   Function& function = Function::Handle(
-      zone, ResolveDynamicAnyArgs(zone, receiver_class, function_name,
-                                  args_desc, allow_add));
+      zone,
+      ResolveDynamicAnyArgs(zone, receiver_class, function_name, allow_add));
 
   if (function.IsNull() || !function.AreValidArguments(args_desc, NULL)) {
-    if (FLAG_lazy_dispatchers) {
-      String& demangled = String::Handle(zone);
-      if (Function::IsDynamicInvocationForwarderName(function_name)) {
-        demangled =
-            Function::DemangleDynamicInvocationForwarderName(function_name);
-      } else {
-        demangled = function_name.raw();
-      }
-      function = receiver_class.GetInvocationDispatcher(
-          demangled, args_desc.array(), RawFunction::kNoSuchMethodDispatcher,
-          allow_add);
+    // Return a null function to signal to the upper levels to dispatch to
+    // "noSuchMethod" function.
+    if (FLAG_trace_resolving) {
+      String& error_message =
+          String::Handle(zone, Symbols::New(thread, "function not found"));
       if (!function.IsNull()) {
-        function.set_is_reflectable(true);
+        // Obtain more detailed error message.
+        function.AreValidArguments(args_desc, &error_message);
       }
-    } else {
-      function = Function::null();
+      THR_Print("ResolveDynamic error '%s': %s.\n", function_name.ToCString(),
+                error_message.ToCString());
     }
+    return Function::null();
   }
-
-  // FLAG_lazy_dispatchers && allow_add -> !function.IsNull()
-  ASSERT(!function.IsNull() || !FLAG_lazy_dispatchers || !allow_add);
-
-  if (FLAG_trace_resolving) {
-    THR_Print("ResolveDynamic result: %s\n", function.ToCString());
-  }
-
   return function.raw();
 }
 
-RawFunction* Resolver::ResolveDynamicAnyArgs(
-    Zone* zone,
-    const Class& receiver_class,
-    const String& function_name,
-    const ArgumentsDescriptor& args_desc,
-    bool allow_add) {
+RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
+                                             const Class& receiver_class,
+                                             const String& function_name,
+                                             bool allow_add) {
   Class& cls = Class::Handle(zone, receiver_class.raw());
-  String& demangled = String::Handle(zone);
+  if (FLAG_trace_resolving) {
+    THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
+              String::Handle(zone, cls.Name()).ToCString());
+  }
   Function& function = Function::Handle(zone);
+
+  String& demangled = String::Handle(zone);
+
+  const bool is_getter = Field::IsGetterName(function_name);
+  if (is_getter) {
+    demangled ^= Field::NameFromGetter(function_name);
+  }
+
   if (Function::IsDynamicInvocationForwarderName(function_name)) {
     demangled ^=
         Function::DemangleDynamicInvocationForwarderName(function_name);
@@ -109,118 +97,42 @@
     // forwarder to do, see `kernel::DynamicInvocationForwarder`). For these
     // functions, we won't have built a `dyn:` version, but it's safe to just
     // return the original version directly.
-    return !function.IsNull()
-               ? function.raw()
-               : ResolveDynamicAnyArgs(zone, receiver_class, demangled,
-                                       args_desc, allow_add);
+    return !function.IsNull() ? function.raw()
+                              : ResolveDynamicAnyArgs(zone, receiver_class,
+                                                      demangled, allow_add);
 #else
-    function = ResolveDynamicAnyArgs(zone, receiver_class, demangled, args_desc,
-                                     allow_add);
-
-    if (function.IsNull() || !function.AreValidArguments(args_desc, NULL)) {
-      return Function::null();
-    }
-
-    return function.GetDynamicInvocationForwarder(function_name, allow_add);
+    function =
+        ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
+    return function.IsNull() ? function.raw()
+                             : function.GetDynamicInvocationForwarder(
+                                   function_name, allow_add);
 #endif
   }
 
-  const bool is_getter = Field::IsGetterName(function_name);
-  const bool is_setter = Field::IsSetterName(function_name);
-  if (is_getter) {
-    demangled = Field::NameFromGetter(function_name);
-  } else if (!is_setter) {
-    demangled = Field::GetterSymbol(function_name);
-  }
-
   // Now look for an instance function whose name matches function_name
   // in the class.
   while (!cls.IsNull()) {
-    function = cls.LookupDynamicFunction(function_name);
+    function ^= cls.LookupDynamicFunction(function_name);
     if (!function.IsNull()) {
       return function.raw();
     }
-    if (is_getter) {
-      // Getter invocation might actually be a method extraction.
-      ASSERT(!Field::IsGetterName(demangled));
-      function = cls.LookupDynamicFunction(demangled);
-      if (!function.IsNull()) {
-        if (FLAG_lazy_dispatchers && allow_add) {
-          return function.CreateMethodExtractor(function_name);
-        } else {
-          return Function::null();
-        }
-      }
-    } else if (!is_setter) {
-      // Regular invocation might actually be call-through-getter.
-      ASSERT(Field::IsGetterName(demangled));
-      function = cls.LookupDynamicFunction(demangled);
-      if (!function.IsNull()) {
-        if (FLAG_lazy_dispatchers && allow_add) {
-          bool is_reflectable = function.is_reflectable();
-          function = cls.GetInvocationDispatcher(
-              function_name, args_desc.array(),
-              RawFunction::kInvokeFieldDispatcher, allow_add);
-          function.set_is_reflectable(is_reflectable);
+    // Getter invocation might actually be a method extraction.
+    if (FLAG_lazy_dispatchers) {
+      if (is_getter && function.IsNull()) {
+        function ^= cls.LookupDynamicFunction(demangled);
+        if (!function.IsNull() && allow_add) {
+          // We were looking for the getter but found a method with the same
+          // name. Create a method extractor and return it.
+          // The extractor does not exist yet, so using GetMethodExtractor is
+          // not necessary here.
+          function ^= function.CreateMethodExtractor(function_name);
           return function.raw();
-        } else {
-          return Function::null();
         }
       }
     }
     cls = cls.SuperClass();
   }
-
-  return Function::null();
-}
-
-bool Resolver::HasDefinition(Zone* zone,
-                             const Class& receiver_class,
-                             const String& function_name) {
-  String& demangled = String::Handle(zone);
-  if (Function::IsDynamicInvocationForwarderName(function_name)) {
-    demangled = Function::DemangleDynamicInvocationForwarderName(function_name);
-    return HasDefinition(zone, receiver_class, demangled);
-  }
-
-  const bool is_getter = Field::IsGetterName(function_name);
-  const bool is_setter = Field::IsSetterName(function_name);
-  if (is_getter) {
-    demangled = Field::NameFromGetter(function_name);
-  } else if (!is_setter) {
-    demangled = Field::GetterSymbol(function_name);
-  }
-
-  Class& cls = Class::Handle(zone, receiver_class.raw());
-  Function& function = Function::Handle(zone);
-
-  // Now look for an instance function whose name matches function_name
-  // in the class.
-  while (!cls.IsNull()) {
-    function = cls.LookupDynamicFunction(function_name);
-    if (!function.IsNull()) {
-      return true;
-    }
-    if (is_getter) {
-      // Getter invocation might actually be a method extraction.
-      ASSERT(!Field::IsGetterName(demangled));
-      function = cls.LookupDynamicFunction(demangled);
-      if (!function.IsNull()) {
-        return true;
-      }
-    } else if (!is_setter) {
-      // Regular invocation might actually be call-through-getter.
-      ASSERT(Field::IsGetterName(demangled));
-      function = cls.LookupDynamicFunction(demangled);
-      if (!function.IsNull()) {
-        return true;
-      }
-    }
-    cls = cls.SuperClass();
-  }
-
-  // NoSuchMethod
-  return false;
+  return function.raw();
 }
 
 RawFunction* Resolver::ResolveStatic(const Library& library,
diff --git a/runtime/vm/resolver.h b/runtime/vm/resolver.h
index a9fd71a..8c5fc15 100644
--- a/runtime/vm/resolver.h
+++ b/runtime/vm/resolver.h
@@ -34,16 +34,11 @@
       const ArgumentsDescriptor& args_desc,
       bool allow_add = true);
 
-  // Answers whether the receiver class has a definition for given selector
-  // other than noSuchMethod. Helper threads such as the background compiler
-  // must use this instead of checking for null answer from ResolveDynamicX
-  // because a helper thread cannot safely use 'allow_add = true' because it
-  // may concurrently mutate a class, and 'allow_add = false' will give false
-  /// negatives if lazy dispatchers are disabled or a lazy dispatcher hasn't
-  // been created yet.
-  static bool HasDefinition(Zone* zone,
-                            const Class& receiver_class,
-                            const String& function_name);
+  // If 'allow_add' is true we may add a function to the class during lookup.
+  static RawFunction* ResolveDynamicAnyArgs(Zone* zone,
+                                            const Class& receiver_class,
+                                            const String& function_name,
+                                            bool allow_add = true);
 
   // Resolve specified dart static function. If library.IsNull, use
   // either application library or core library if no application library
@@ -73,15 +68,6 @@
                                                 intptr_t type_args_len,
                                                 intptr_t num_arguments,
                                                 const Array& argument_names);
-
- private:
-  // If 'allow_add' is true we may add a function to the class during lookup.
-  static RawFunction* ResolveDynamicAnyArgs(
-      Zone* zone,
-      const Class& receiver_class,
-      const String& function_name,
-      const ArgumentsDescriptor& args_desc,
-      bool allow_add = true);
 };
 
 }  // namespace dart
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 47eadaf..1396407 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -478,7 +478,7 @@
   const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
   const String& name = String::CheckedHandle(zone, arguments.ArgAt(1));
   const Class& receiver_class = Class::Handle(zone, receiver.clazz());
-  const String& getter_name = String::Handle(zone, Field::GetterSymbol(name));
+  const String& getter_name = String::Handle(zone, Field::GetterName(name));
   const int kTypeArgsLen = 0;
   const int kNumArguments = 1;
   ArgumentsDescriptor args_desc(Array::Handle(
@@ -997,6 +997,78 @@
 #endif
 }
 
+// An instance call of the form o.f(...) could not be resolved.  Check if
+// there is a getter with the same name.  If so, invoke it.  If the value is
+// a closure, invoke it with the given arguments.  If the value is a
+// non-closure, attempt to invoke "call" on it.
+static bool ResolveCallThroughGetter(const Instance& receiver,
+                                     const Class& receiver_class,
+                                     const String& target_name,
+                                     const Array& arguments_descriptor,
+                                     Function* result) {
+  // 1. Check if there is a getter with the same name.
+  const String& getter_name = String::Handle(Field::GetterName(target_name));
+  const int kTypeArgsLen = 0;
+  const int kNumArguments = 1;
+  ArgumentsDescriptor args_desc(
+      Array::Handle(ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
+  const Function& getter =
+      Function::Handle(Resolver::ResolveDynamicForReceiverClass(
+          receiver_class, getter_name, args_desc));
+  if (getter.IsNull() || getter.IsMethodExtractor()) {
+    return false;
+  }
+  const Function& target_function =
+      Function::Handle(receiver_class.GetInvocationDispatcher(
+          target_name, arguments_descriptor,
+          RawFunction::kInvokeFieldDispatcher, FLAG_lazy_dispatchers));
+  ASSERT(!target_function.IsNull() || !FLAG_lazy_dispatchers);
+  if (FLAG_trace_ic) {
+    OS::PrintErr(
+        "InvokeField IC miss: adding <%s> id:%" Pd " -> <%s>\n",
+        Class::Handle(receiver.clazz()).ToCString(), receiver.GetClassId(),
+        target_function.IsNull() ? "null" : target_function.ToCString());
+  }
+  *result = target_function.raw();
+  return true;
+}
+
+// Handle other invocations (implicit closures, noSuchMethod).
+RawFunction* InlineCacheMissHelper(const Instance& receiver,
+                                   const Array& args_descriptor,
+                                   const String& target_name) {
+  const Class& receiver_class = Class::Handle(receiver.clazz());
+
+  // Handle noSuchMethod for dyn:methodName by getting a noSuchMethod dispatcher
+  // (or a call-through getter for methodName).
+  if (Function::IsDynamicInvocationForwarderName(target_name)) {
+    const String& demangled = String::Handle(
+        Function::DemangleDynamicInvocationForwarderName(target_name));
+    return InlineCacheMissHelper(receiver, args_descriptor, demangled);
+  }
+
+  Function& result = Function::Handle();
+  if (!ResolveCallThroughGetter(receiver, receiver_class, target_name,
+                                args_descriptor, &result)) {
+    ArgumentsDescriptor desc(args_descriptor);
+    const Function& target_function =
+        Function::Handle(receiver_class.GetInvocationDispatcher(
+            target_name, args_descriptor, RawFunction::kNoSuchMethodDispatcher,
+            FLAG_lazy_dispatchers));
+    if (FLAG_trace_ic) {
+      OS::PrintErr(
+          "NoSuchMethod IC miss: adding <%s> id:%" Pd " -> <%s>\n",
+          Class::Handle(receiver.clazz()).ToCString(), receiver.GetClassId(),
+          target_function.IsNull() ? "null" : target_function.ToCString());
+    }
+    result = target_function.raw();
+  }
+  // May be null if --no-lazy-dispatchers, in which case dispatch will be
+  // handled by InvokeNoSuchMethodDispatcher.
+  ASSERT(!result.IsNull() || !FLAG_lazy_dispatchers);
+  return result.raw();
+}
+
 // Perform the subtype and return constant function based on the result.
 static RawFunction* ComputeTypeCheckTarget(const Instance& receiver,
                                            const AbstractType& type,
@@ -1037,6 +1109,11 @@
                    String::Handle(ic_data.target_name()).ToCString(),
                    receiver.ToCString());
     }
+    const Array& args_descriptor =
+        Array::Handle(ic_data.arguments_descriptor());
+    const String& target_name = String::Handle(ic_data.target_name());
+    target_function =
+        InlineCacheMissHelper(receiver, args_descriptor, target_name);
   }
   if (target_function.IsNull()) {
     ASSERT(!FLAG_lazy_dispatchers);
@@ -1186,8 +1263,7 @@
                            intptr_t lower_cid,
                            intptr_t upper_cid,
                            const Function& target,
-                           const String& name,
-                           const ArgumentsDescriptor& args_desc) {
+                           const String& name) {
   Class& cls = Class::Handle(zone);
   ClassTable* table = isolate->class_table();
   Function& other_target = Function::Handle(zone);
@@ -1196,8 +1272,8 @@
     cls = table->At(cid);
     if (cls.is_abstract()) continue;
     if (!cls.is_allocated()) continue;
-    other_target = Resolver::ResolveDynamicForReceiverClass(
-        cls, name, args_desc, false /* allow_add */);
+    other_target =
+        Resolver::ResolveDynamicAnyArgs(zone, cls, name, false /* allow_add */);
     if (other_target.raw() != target.raw()) {
       return false;
     }
@@ -1250,6 +1326,9 @@
   Function& target_function = Function::Handle(
       zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
   if (target_function.IsNull()) {
+    target_function = InlineCacheMissHelper(receiver, descriptor, name);
+  }
+  if (target_function.IsNull()) {
     ASSERT(!FLAG_lazy_dispatchers);
   } else {
     ic_data.AddReceiverCheck(receiver.GetClassId(), target_function);
@@ -1270,7 +1349,7 @@
     }
 
     if (IsSingleTarget(isolate, zone, unchecked_lower, unchecked_upper,
-                       target_function, name, args_desc)) {
+                       target_function, name)) {
       cache.set_lower_limit(lower);
       cache.set_upper_limit(upper);
       // Return the ICData. The single target stub will jump to continue in the
@@ -1321,6 +1400,9 @@
   Function& target_function = Function::Handle(
       zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
   if (target_function.IsNull()) {
+    target_function = InlineCacheMissHelper(receiver, descriptor, name);
+  }
+  if (target_function.IsNull()) {
     ASSERT(!FLAG_lazy_dispatchers);
   } else {
     ic_data.AddReceiverCheck(receiver.GetClassId(), target_function);
@@ -1401,6 +1483,9 @@
   Function& target_function = Function::Handle(
       zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
   if (target_function.IsNull()) {
+    target_function = InlineCacheMissHelper(receiver, descriptor, name);
+  }
+  if (target_function.IsNull()) {
     ASSERT(!FLAG_lazy_dispatchers);
   } else {
     ic_data.AddReceiverCheck(receiver.GetClassId(), target_function);
@@ -1416,8 +1501,7 @@
       upper = old_expected_cid.Value();
     }
 
-    if (IsSingleTarget(isolate, zone, lower, upper, target_function, name,
-                       args_desc)) {
+    if (IsSingleTarget(isolate, zone, lower, upper, target_function, name)) {
       const SingleTargetCache& cache =
           SingleTargetCache::Handle(SingleTargetCache::New());
       const Code& code = Code::Handle(target_function.CurrentCode());
@@ -1478,6 +1562,7 @@
   Function& target_function = Function::Handle(
       zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
   if (target_function.IsNull()) {
+    target_function = InlineCacheMissHelper(receiver, descriptor, name);
     if (target_function.IsNull()) {
       ASSERT(!FLAG_lazy_dispatchers);
       arguments.SetReturn(target_function);
@@ -1569,7 +1654,10 @@
   // TODO(regis): In order to substitute 'simple_instance_of_function', the 2nd
   // arg to the call, the type, is needed.
 
-  ASSERT(!target_function.IsNull() && FLAG_lazy_dispatchers);
+  if (target_function.IsNull()) {
+    target_function = InlineCacheMissHelper(receiver, arg_desc, target_name);
+  }
+  ASSERT(!target_function.IsNull());
   arguments.SetReturn(target_function);
 #endif
 }