[vm, interpreter] noSuchMethod dispatchers as a bytecode.

Change-Id: I0e105a56b733322737b6f22a7641f7c6b8afac79
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100283
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 2086352..033ffbc 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4699,6 +4699,8 @@
                 "<invoke closure>");
   AddBaseObject(Object::invoke_field_bytecode().raw(), "Bytecode",
                 "<invoke field>");
+  AddBaseObject(Object::nsm_dispatcher_bytecode().raw(), "Bytecode",
+                "<nsm dispatcher>");
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
@@ -5160,6 +5162,7 @@
   AddBaseObject(Object::method_extractor_bytecode().raw());
   AddBaseObject(Object::invoke_closure_bytecode().raw());
   AddBaseObject(Object::invoke_field_bytecode().raw());
+  AddBaseObject(Object::nsm_dispatcher_bytecode().raw());
 
   for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
     AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i]);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 8cdb6d1..eb7f7d8 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -74,8 +74,11 @@
         function.AttachBytecode(Object::invoke_field_bytecode());
       }
       return;
-    default: {
-    }
+    case RawFunction::kNoSuchMethodDispatcher:
+      function.AttachBytecode(Object::nsm_dispatcher_bytecode());
+      return;
+    default:
+      break;
   }
 
   intptr_t code_offset = 0;
@@ -118,7 +121,8 @@
       (function.kind() != RawFunction::kImplicitSetter) &&
       (function.kind() != RawFunction::kImplicitStaticGetter) &&
       (function.kind() != RawFunction::kMethodExtractor) &&
-      (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
+      (function.kind() != RawFunction::kInvokeFieldDispatcher) &&
+      (function.kind() != RawFunction::kNoSuchMethodDispatcher)) {
     return;
   }
 
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 4d7919f..6d34295 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -946,6 +946,8 @@
         return B->BuildGraphOfDynamicInvocationForwarder(function);
       case RawFunction::kMethodExtractor:
         return B->BuildGraphOfMethodExtractor(function);
+      case RawFunction::kNoSuchMethodDispatcher:
+        return B->BuildGraphOfNoSuchMethodDispatcher(function);
       default:
         break;
     }
@@ -970,7 +972,8 @@
         (function.kind() != RawFunction::kImplicitSetter) &&
         (function.kind() != RawFunction::kImplicitStaticGetter) &&
         (function.kind() != RawFunction::kMethodExtractor) &&
-        (function.kind() != RawFunction::kInvokeFieldDispatcher)) {
+        (function.kind() != RawFunction::kInvokeFieldDispatcher) &&
+        (function.kind() != RawFunction::kNoSuchMethodDispatcher)) {
       BytecodeFlowGraphBuilder bytecode_compiler(
           flow_graph_builder_, parsed_function(),
           &(flow_graph_builder_->ic_data_array_));
diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc
index 8038fbd..efd4d67 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm.cc
@@ -837,7 +837,7 @@
   // R2: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
   const intptr_t kNumArgs = 4;
-  __ CallRuntime(kInvokeNoSuchMethodDispatcherRuntimeEntry, kNumArgs);
+  __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
   __ Drop(4);
   __ Pop(R0);  // Return value.
   __ LeaveStubFrame();
@@ -1768,11 +1768,14 @@
   __ ldr(R8, Address(IP, target::frame_layout.param_end_from_fp *
                              target::kWordSize));
 
+  // Load the function.
+  __ ldr(R6, FieldAddress(R8, target::Closure::function_offset()));
+
   // Push space for the return value.
   // Push the receiver.
   // Push arguments descriptor array.
   __ LoadImmediate(IP, 0);
-  __ PushList((1 << R4) | (1 << R8) | (1 << IP));
+  __ PushList((1 << R4) | (1 << R6) | (1 << R8) | (1 << IP));
 
   // Adjust arguments count.
   __ ldr(R3,
@@ -1784,8 +1787,8 @@
   // R2: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
 
-  const intptr_t kNumArgs = 3;
-  __ CallRuntime(kInvokeClosureNoSuchMethodRuntimeEntry, kNumArgs);
+  const intptr_t kNumArgs = 4;
+  __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
   // noSuchMethod on closures always throws an error, so it will never return.
   __ bkpt(0);
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 01a0e20..cfc7edc 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -887,7 +887,7 @@
   // R2: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
   const intptr_t kNumArgs = 4;
-  __ CallRuntime(kInvokeNoSuchMethodDispatcherRuntimeEntry, kNumArgs);
+  __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
   __ Drop(4);
   __ Pop(R0);  // Return value.
   __ LeaveStubFrame();
@@ -1827,12 +1827,13 @@
   __ LoadFromOffset(R6, TMP,
                     target::frame_layout.param_end_from_fp * target::kWordSize);
 
-  // Push space for the return value.
-  // Push the receiver.
-  // Push arguments descriptor array.
-  __ Push(ZR);
-  __ Push(R6);
-  __ Push(R4);
+  // Load the function.
+  __ LoadFieldFromOffset(TMP, R6, target::Closure::function_offset());
+
+  __ Push(ZR);   // Result slot.
+  __ Push(R6);   // Receiver.
+  __ Push(TMP);  // Function
+  __ Push(R4);   // Arguments descriptor.
 
   // Adjust arguments count.
   __ LoadFieldFromOffset(R3, R4,
@@ -1844,8 +1845,8 @@
   // R2: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
 
-  const intptr_t kNumArgs = 3;
-  __ CallRuntime(kInvokeClosureNoSuchMethodRuntimeEntry, kNumArgs);
+  const intptr_t kNumArgs = 4;
+  __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
   // noSuchMethod on closures always throws an error, so it will never return.
   __ brk(0);
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_ia32.cc b/runtime/vm/compiler/stub_code_compiler_ia32.cc
index 570d2f8..c899725 100644
--- a/runtime/vm/compiler/stub_code_compiler_ia32.cc
+++ b/runtime/vm/compiler/stub_code_compiler_ia32.cc
@@ -627,7 +627,7 @@
   // EDX: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
   const intptr_t kNumArgs = 4;
-  __ CallRuntime(kInvokeNoSuchMethodDispatcherRuntimeEntry, kNumArgs);
+  __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
   __ Drop(4);
   __ popl(EAX);  // Return value.
   __ LeaveFrame();
@@ -1471,8 +1471,12 @@
           Address(EBP, EDI, TIMES_2,
                   target::frame_layout.param_end_from_fp * target::kWordSize));
 
+  // Load the function.
+  __ movl(EBX, FieldAddress(EAX, target::Closure::function_offset()));
+
   __ pushl(Immediate(0));  // Setup space on stack for result from noSuchMethod.
   __ pushl(EAX);           // Receiver.
+  __ pushl(EBX);           // Function.
   __ pushl(EDX);           // Arguments descriptor array.
 
   // Adjust arguments count.
@@ -1488,8 +1492,8 @@
   // EDX: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
 
-  const intptr_t kNumArgs = 3;
-  __ CallRuntime(kInvokeClosureNoSuchMethodRuntimeEntry, kNumArgs);
+  const intptr_t kNumArgs = 4;
+  __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
   // noSuchMethod on closures always throws an error, so it will never return.
   __ int3();
 }
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 8c8e0be..1d14115 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -847,7 +847,7 @@
   // R10: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
   const intptr_t kNumArgs = 4;
-  __ CallRuntime(kInvokeNoSuchMethodDispatcherRuntimeEntry, kNumArgs);
+  __ CallRuntime(kNoSuchMethodFromCallStubRuntimeEntry, kNumArgs);
   __ Drop(4);
   __ popq(RAX);  // Return value.
   __ LeaveStubFrame();
@@ -1804,8 +1804,12 @@
           Address(RBP, R13, TIMES_4,
                   target::frame_layout.param_end_from_fp * target::kWordSize));
 
+  // Load the function.
+  __ movq(RBX, FieldAddress(RAX, target::Closure::function_offset()));
+
   __ pushq(Immediate(0));  // Result slot.
   __ pushq(RAX);           // Receiver.
+  __ pushq(RBX);           // Function.
   __ pushq(R10);           // Arguments descriptor array.
 
   // Adjust arguments count.
@@ -1821,8 +1825,8 @@
   // R10: Smi-tagged arguments array length.
   PushArrayOfArguments(assembler);
 
-  const intptr_t kNumArgs = 3;
-  __ CallRuntime(kInvokeClosureNoSuchMethodRuntimeEntry, kNumArgs);
+  const intptr_t kNumArgs = 4;
+  __ CallRuntime(kNoSuchMethodFromPrologueRuntimeEntry, kNumArgs);
   // noSuchMethod on closures always throws an error, so it will never return.
   __ int3();
 }
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 126a2f4..bc25ff8 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -538,7 +538,7 @@
   V(VMInternal_InvokeClosure,              0, ___, ___, ___)                   \
   V(VMInternal_InvokeField,                0, ___, ___, ___)                   \
   V(VMInternal_ForwardDynamicInvocation,   0, ___, ___, ___)                   \
-  V(VMInternal_DispatchNoSuchMethod,       0, ___, ___, ___)                   \
+  V(VMInternal_NoSuchMethodDispatcher,     0, ___, ___, ___)                   \
   V(VMInternal_ImplicitStaticClosure,      0, ___, ___, ___)                   \
   V(VMInternal_ImplicitInstanceClosure,    0, ___, ___, ___)                   \
 
diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc
index e7ac7c1..2b821ed 100644
--- a/runtime/vm/interpreter.cc
+++ b/runtime/vm/interpreter.cc
@@ -1425,10 +1425,6 @@
   RawBool* false_value = Bool::False().raw();
   RawObject* null_value = Object::null();
 
-#if defined(DEBUG)
-  Function& function_h = Function::Handle();
-#endif
-
 #ifdef DART_HAS_COMPUTED_GOTO
   static const void* dispatch[] = {
 #define TARGET(name, fmt, fmta, fmtb, fmtc) &&bc##name,
@@ -1472,7 +1468,7 @@
     const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
     const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
     if ((arg_count != num_fixed_params) || (pos_count != num_fixed_params)) {
-      goto ClosureNoSuchMethod;
+      goto NoSuchMethodFromPrologue;
     }
 
     // Initialize locals with null & set SP.
@@ -1499,7 +1495,7 @@
 
     // Check that got the right number of positional parameters.
     if ((min_num_pos_args > pos_count) || (pos_count > max_num_pos_args)) {
-      goto ClosureNoSuchMethod;
+      goto NoSuchMethodFromPrologue;
     }
 
     // Copy all passed position arguments.
@@ -1562,7 +1558,7 @@
       // between formal parameters and concrete arguments. This can only
       // occur if the current function is a closure.
       if (i != -1) {
-        goto ClosureNoSuchMethod;
+        goto NoSuchMethodFromPrologue;
       }
 
       // Skip LoadConstant-s encoding information about named parameters.
@@ -1576,7 +1572,7 @@
         // Function can't have both named and optional positional parameters.
         // This kind of mismatch can only occur if the current function
         // is a closure.
-        goto ClosureNoSuchMethod;
+        goto NoSuchMethodFromPrologue;
       }
 
       // Process the list of default values encoded as a sequence of
@@ -1659,7 +1655,7 @@
     const intptr_t type_args_len =
         InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
     if ((type_args_len != declared_type_args_len) && (type_args_len != 0)) {
-      goto ClosureNoSuchMethod;
+      goto NoSuchMethodFromPrologue;
     }
     if (type_args_len > 0) {
       // Decode arguments descriptor's argument count (excluding type args).
@@ -3362,11 +3358,10 @@
   }
 
   {
-    BYTECODE(VMInternal_DispatchNoSuchMethod, 0);
+    BYTECODE(VMInternal_NoSuchMethodDispatcher, 0);
     RawFunction* function = FrameFunction(FP);
     ASSERT(Function::kind(function) == RawFunction::kNoSuchMethodDispatcher);
-    UNIMPLEMENTED();
-    DISPATCH();
+    goto NoSuchMethodFromPrologue;
   }
 
   {
@@ -3444,62 +3439,47 @@
 
   // Helper used to handle noSuchMethod on closures.
   {
-  ClosureNoSuchMethod:
-#if defined(DEBUG)
-    function_h ^= FrameFunction(FP);
-    ASSERT(function_h.IsNull() || function_h.IsClosureFunction());
-#endif
+  NoSuchMethodFromPrologue:
+    RawFunction* function = FrameFunction(FP);
 
-    // Restore caller context as we are going to throw NoSuchMethod.
-    pc = SavedCallerPC(FP);
-
-    const bool has_dart_caller = !IsEntryFrameMarker(pc);
     const intptr_t type_args_len =
         InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
     const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
     const intptr_t argc =
         InterpreterHelpers::ArgDescArgCount(argdesc_) + receiver_idx;
+    RawObject** args = FrameArguments(FP, argc);
 
-    SP = FrameArguments(FP, 0);
-    RawObject** args = SP - argc;
-    FP = SavedCallerFP(FP);
-    NOT_IN_PRODUCT(fp_ = FP);  // For the profiler.
-    if (has_dart_caller) {
-      pp_ = InterpreterHelpers::FrameBytecode(FP)->ptr()->object_pool_;
-    }
-
-    *++SP = null_value;
-    *++SP = args[receiver_idx];  // Closure object.
-    *++SP = argdesc_;
-    *++SP = null_value;  // Array of arguments (will be filled).
+    SP[1] = null_value;
+    SP[2] = args[receiver_idx];
+    SP[3] = function;
+    SP[4] = argdesc_;
+    SP[5] = null_value;  // Array of arguments (will be filled).
 
     // Allocate array of arguments.
     {
-      SP[1] = Smi::New(argc);  // length
-      SP[2] = null_value;      // type
-      Exit(thread, FP, SP + 3, pc);
-      NativeArguments native_args(thread, 2, SP + 1, SP);
+      SP[6] = Smi::New(argc);  // length
+      SP[7] = null_value;      // type
+      Exit(thread, FP, SP + 8, pc);
+      NativeArguments native_args(thread, 2, SP + 6, SP + 5);
       if (!InvokeRuntime(thread, this, DRT_AllocateArray, native_args)) {
         HANDLE_EXCEPTION;
-      } else if (has_dart_caller) {
-        HANDLE_RETURN;
       }
 
       // Copy arguments into the newly allocated array.
-      RawArray* array = static_cast<RawArray*>(SP[0]);
+      RawArray* array = static_cast<RawArray*>(SP[5]);
       ASSERT(array->GetClassId() == kArrayCid);
       for (intptr_t i = 0; i < argc; i++) {
         array->ptr()->data()[i] = args[i];
       }
     }
 
-    // Invoke noSuchMethod passing down closure, argument descriptor and
-    // array of arguments.
+    // Invoke noSuchMethod passing down receiver, function, argument descriptor
+    // and array of arguments.
     {
-      Exit(thread, FP, SP + 1, pc);
-      NativeArguments native_args(thread, 3, SP - 2, SP - 3);
-      INVOKE_RUNTIME(DRT_InvokeClosureNoSuchMethod, native_args);
-      UNREACHABLE();
+      Exit(thread, FP, SP + 6, pc);
+      NativeArguments native_args(thread, 4, SP + 2, SP + 1);
+      INVOKE_RUNTIME(DRT_NoSuchMethodFromPrologue, native_args);
+      ++SP;  // Result at SP[0]
     }
 
     DISPATCH();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index b77157f..e25bf06 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -947,6 +947,18 @@
   invoke_field_bytecode_->set_exception_handlers(
       Object::empty_exception_handlers());
 
+  static const KBCInstr nsm_dispatcher_instr[2] = {
+      KernelBytecode::Encode(
+          KernelBytecode::kVMInternal_NoSuchMethodDispatcher),
+      KernelBytecode::Encode(KernelBytecode::kReturnTOS),
+  };
+  *nsm_dispatcher_bytecode_ = Bytecode::New(
+      reinterpret_cast<uword>(nsm_dispatcher_instr),
+      sizeof(nsm_dispatcher_instr), -1, Object::empty_object_pool());
+  nsm_dispatcher_bytecode_->set_pc_descriptors(Object::empty_descriptors());
+  nsm_dispatcher_bytecode_->set_exception_handlers(
+      Object::empty_exception_handlers());
+
   // Some thread fields need to be reinitialized as null constants have not been
   // initialized until now.
   Thread* thr = Thread::Current();
@@ -1016,6 +1028,8 @@
   ASSERT(invoke_closure_bytecode_->IsBytecode());
   ASSERT(!invoke_field_bytecode_->IsSmi());
   ASSERT(invoke_field_bytecode_->IsBytecode());
+  ASSERT(!nsm_dispatcher_bytecode_->IsSmi());
+  ASSERT(nsm_dispatcher_bytecode_->IsBytecode());
 }
 
 void Object::FinishInit(Isolate* isolate) {
@@ -5732,7 +5746,6 @@
     }
   }
   switch (kind()) {
-    case RawFunction::kNoSuchMethodDispatcher:
     case RawFunction::kDynamicInvocationForwarder:
     case RawFunction::kImplicitClosureFunction:
     case RawFunction::kIrregexpFunction:
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index bdf8c35..f0040cf 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -402,6 +402,7 @@
   V(Bytecode, method_extractor_bytecode)                                       \
   V(Bytecode, invoke_closure_bytecode)                                         \
   V(Bytecode, invoke_field_bytecode)                                           \
+  V(Bytecode, nsm_dispatcher_bytecode)                                         \
   V(Instance, sentinel)                                                        \
   V(Instance, transition_sentinel)                                             \
   V(Instance, unknown_constant)                                                \
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index da70f90..1f889cc 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1067,7 +1067,7 @@
     result = target_function.raw();
   }
   // May be null if --no-lazy-dispatchers, in which case dispatch will be
-  // handled by InvokeNoSuchMethodDispatcher.
+  // handled by NoSuchMethodFromCallStub.
   ASSERT(!result.IsNull() || !FLAG_lazy_dispatchers);
   return result.raw();
 }
@@ -1670,7 +1670,7 @@
 // Arg1: ICData or MegamorphicCache
 // Arg2: arguments descriptor array
 // Arg3: arguments array
-DEFINE_RUNTIME_ENTRY(InvokeNoSuchMethodDispatcher, 4) {
+DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
   ASSERT(!FLAG_lazy_dispatchers);
   const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
   const Object& ic_data_or_cache = Object::Handle(zone, arguments.ArgAt(1));
@@ -1778,24 +1778,31 @@
 }
 
 // Invoke appropriate noSuchMethod function.
-// Arg0: receiver (closure object)
+// Arg0: receiver
+// Arg1: function
 // Arg1: arguments descriptor array.
-// Arg2: arguments array.
-DEFINE_RUNTIME_ENTRY(InvokeClosureNoSuchMethod, 3) {
-  const Closure& receiver = Closure::CheckedHandle(zone, arguments.ArgAt(0));
+// Arg3: arguments array.
+DEFINE_RUNTIME_ENTRY(NoSuchMethodFromPrologue, 4) {
+  const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
+  const Function& function = Function::CheckedHandle(zone, arguments.ArgAt(1));
   const Array& orig_arguments_desc =
-      Array::CheckedHandle(zone, arguments.ArgAt(1));
-  const Array& orig_arguments = Array::CheckedHandle(zone, arguments.ArgAt(2));
+      Array::CheckedHandle(zone, arguments.ArgAt(2));
+  const Array& orig_arguments = Array::CheckedHandle(zone, arguments.ArgAt(3));
 
-  // For closure the function name is always 'call'. Replace it with the
-  // name of the closurized function so that exception contains more
-  // relevant information.
-  const Function& function = Function::Handle(receiver.function());
-  ASSERT(!function.IsNull());
-  const String& original_function_name =
-      String::Handle(function.QualifiedUserVisibleName());
-  const Object& result = Object::Handle(DartEntry::InvokeNoSuchMethod(
-      receiver, original_function_name, orig_arguments, orig_arguments_desc));
+  String& orig_function_name = String::Handle(zone);
+  if ((function.kind() == RawFunction::kClosureFunction) ||
+      (function.kind() == RawFunction::kImplicitClosureFunction)) {
+    // For closure the function name is always 'call'. Replace it with the
+    // name of the closurized function so that exception contains more
+    // relevant information.
+    orig_function_name = function.QualifiedUserVisibleName();
+  } else {
+    orig_function_name = function.name();
+  }
+
+  const Object& result = Object::Handle(
+      zone, DartEntry::InvokeNoSuchMethod(receiver, orig_function_name,
+                                          orig_arguments, orig_arguments_desc));
   ThrowIfError(result);
   arguments.SetReturn(result);
 }
diff --git a/runtime/vm/runtime_entry_list.h b/runtime/vm/runtime_entry_list.h
index 5ab459b..dd4159d 100644
--- a/runtime/vm/runtime_entry_list.h
+++ b/runtime/vm/runtime_entry_list.h
@@ -31,9 +31,9 @@
   V(NonBoolTypeError)                                                          \
   V(InstantiateType)                                                           \
   V(InstantiateTypeArguments)                                                  \
-  V(InvokeClosureNoSuchMethod)                                                 \
+  V(NoSuchMethodFromCallStub)                                                  \
+  V(NoSuchMethodFromPrologue)                                                  \
   V(InvokeNoSuchMethod)                                                        \
-  V(InvokeNoSuchMethodDispatcher)                                              \
   V(MegamorphicCacheMissHandler)                                               \
   V(OptimizeInvokedFunction)                                                   \
   V(TraceICCall)                                                               \
diff --git a/runtime/vm/simulator_dbc.cc b/runtime/vm/simulator_dbc.cc
index 6f2defd..9251eac 100644
--- a/runtime/vm/simulator_dbc.cc
+++ b/runtime/vm/simulator_dbc.cc
@@ -4095,8 +4095,11 @@
       pp_ = SimulatorHelpers::FrameCode(FP)->ptr()->object_pool_;
     }
 
+    RawClosure* closure =
+        Closure::RawCast(args[has_function_type_args ? 1 : 0]);
     *++SP = null_value;
-    *++SP = args[has_function_type_args ? 1 : 0];  // Closure object.
+    *++SP = closure;
+    *++SP = closure->ptr()->function_;
     *++SP = argdesc_;
     *++SP = null_value;  // Array of arguments (will be filled).
 
@@ -4124,8 +4127,8 @@
     // array of arguments.
     {
       Exit(thread, FP, SP + 1, pc);
-      NativeArguments native_args(thread, 3, SP - 2, SP - 3);
-      INVOKE_RUNTIME(DRT_InvokeClosureNoSuchMethod, native_args);
+      NativeArguments native_args(thread, 4, SP - 3, SP - 4);
+      INVOKE_RUNTIME(DRT_NoSuchMethodFromPrologue, native_args);
       UNREACHABLE();
     }
 
diff --git a/runtime/vm/stack_frame_test.cc b/runtime/vm/stack_frame_test.cc
index 9b89101..fa77c7d 100644
--- a/runtime/vm/stack_frame_test.cc
+++ b/runtime/vm/stack_frame_test.cc
@@ -306,7 +306,7 @@
         "     * dart frame corresponding to StackFrame.frameCount"
         "     * dart frame corresponding to StackFrame2Test.noSuchMethod"
         "     * entry frame"
-        "     * exit frame (call to runtime InvokeNoSuchMethodDispatcher)"
+        "     * exit frame (call to runtime NoSuchMethodFromCallStub)"
         "     * IC stub"
         "     * dart frame corresponding to StackFrame2Test.testMain"
         "     * entry frame"