[vm/resolve] Move class finalization out of lookup and resolve function methods.

This is to make sure that lookup can be done without getting a safepoint, which might be needed if class needs to be finalized.

Change-Id: I6ed52a56d53369327df0b85d7b04cf41a46ac690
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/159188
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 532f2f6..0bed69c 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -64,10 +64,12 @@
   args.SetAt(6, argument_names);
 
   const Library& libcore = Library::Handle(Library::CoreLibrary());
-  const Class& NoSuchMethodError =
+  const Class& cls =
       Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
-  const Function& throwNew = Function::Handle(
-      NoSuchMethodError.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
+  const Function& throwNew =
+      Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
   const Object& result =
       Object::Handle(DartEntry::InvokeFunction(throwNew, args));
   ASSERT(result.IsError());
diff --git a/runtime/vm/code_descriptors_test.cc b/runtime/vm/code_descriptors_test.cc
index 20c0e5e..c5faf2a 100644
--- a/runtime/vm/code_descriptors_test.cc
+++ b/runtime/vm/code_descriptors_test.cc
@@ -71,6 +71,8 @@
 
   // Now compile the two functions 'A.foo' and 'A.moo'
   String& function_moo_name = String::Handle(String::New("moo"));
+  const auto& error = cls.EnsureIsFinalized(thread);
+  EXPECT(error == Error::null());
   Function& function_moo =
       Function::Handle(cls.LookupStaticFunction(function_moo_name));
   EXPECT(CompilerTest::TestCompileFunction(function_moo));
diff --git a/runtime/vm/compilation_trace.cc b/runtime/vm/compilation_trace.cc
index 174c252..173652f 100644
--- a/runtime/vm/compilation_trace.cc
+++ b/runtime/vm/compilation_trace.cc
@@ -895,8 +895,10 @@
         // ensure no arity mismatch crashes.
         target_name_ = call_site_.target_name();
         args_desc_ = call_site_.arguments_descriptor();
-        target_ = Resolver::ResolveDynamicForReceiverClass(
-            cls_, target_name_, ArgumentsDescriptor(args_desc_));
+        if (cls_.EnsureIsFinalized(thread_) == Error::null()) {
+          target_ = Resolver::ResolveDynamicForReceiverClass(
+              cls_, target_name_, ArgumentsDescriptor(args_desc_));
+        }
         if (!target_.IsNull()) {
           if (num_checked_arguments == 1) {
             call_site_.AddReceiverCheck(cids[0], target_, entry_usage);
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 5890545..9196b2d 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -538,9 +538,10 @@
   if (receiver_maybe_null) {
     const Class& null_class =
         Class::Handle(zone(), isolate()->object_store()->null_class());
-    const Function& target = Function::Handle(
-        zone(),
-        Resolver::ResolveDynamicAnyArgs(zone(), null_class, method_name));
+    Function& target = Function::Handle(zone());
+    if (null_class.EnsureIsFinalized(thread()) == Error::null()) {
+      target = 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 3eddad6..83edc77 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -301,10 +301,14 @@
     // Nothing to do if type is already non-nullable.
     return;
   }
+  Thread* thread = Thread::Current();
   const Class& null_class =
-      Class::Handle(Isolate::Current()->object_store()->null_class());
-  const Function& target = Function::Handle(Resolver::ResolveDynamicAnyArgs(
-      Thread::Current()->zone(), null_class, function_name));
+      Class::Handle(thread->isolate()->object_store()->null_class());
+  Function& target = Function::Handle();
+  if (Error::Handle(null_class.EnsureIsFinalized(thread)).IsNull()) {
+    target = Resolver::ResolveDynamicAnyArgs(thread->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);
diff --git a/runtime/vm/compiler/cha_test.cc b/runtime/vm/compiler/cha_test.cc
index 1cd26e9..2e3d38f 100644
--- a/runtime/vm/compiler/cha_test.cc
+++ b/runtime/vm/compiler/cha_test.cc
@@ -38,6 +38,7 @@
   const Class& class_a =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
   EXPECT(!class_a.IsNull());
+  EXPECT(class_a.EnsureIsFinalized(thread) == Error::null());
 
   const Class& class_b =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "B"))));
@@ -46,10 +47,12 @@
   const Class& class_c =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "C"))));
   EXPECT(!class_c.IsNull());
+  EXPECT(class_c.EnsureIsFinalized(thread) == Error::null());
 
   const Class& class_d =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "D"))));
   EXPECT(!class_d.IsNull());
+  EXPECT(class_d.EnsureIsFinalized(thread) == Error::null());
 
   const String& function_foo_name = String::Handle(String::New("foo"));
   const String& function_bar_name = String::Handle(String::New("bar"));
@@ -74,7 +77,7 @@
       Function::Handle(class_d.LookupDynamicFunction(function_bar_name));
   EXPECT(!class_d_bar.IsNull());
 
-  CHA cha(Thread::Current());
+  CHA cha(thread);
 
   EXPECT(cha.HasSubclasses(kInstanceCid));
   EXPECT(!cha.HasSubclasses(kSmiCid));
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index 5ed4c10..ac59909 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1058,7 +1058,9 @@
     const auto& parent = Function::Handle(Z, function.parent_function());
     const auto& func_name = String::Handle(Z, parent.name());
     const auto& owner = Class::Handle(Z, parent.Owner());
-    function = owner.LookupFunction(func_name);
+    if (owner.EnsureIsFinalized(thread_) == Error::null()) {
+      function = owner.LookupFunction(func_name);
+    }
   }
 
   Object& options = Object::Handle(Z);
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 4d6eb95..69890d2 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -1452,7 +1452,10 @@
             cls.raw() == scoped_function_class_.raw()) {
           return scoped_function_.raw();
         }
-        FunctionPtr function = cls.LookupFunction(name);
+        FunctionPtr function = Function::null();
+        if (cls.EnsureIsFinalized(thread_) == Error::null()) {
+          function = cls.LookupFunction(name);
+        }
         if (function == Function::null()) {
           // When requesting a getter, also return method extractors.
           if (Field::IsGetterName(name)) {
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 671deaf..c2d252c 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -2454,11 +2454,16 @@
   return instructions;
 }
 
-static Function& GetNoSuchMethodOrDie(Zone* zone, const Class& klass) {
+static Function& GetNoSuchMethodOrDie(Thread* thread,
+                                      Zone* zone,
+                                      const Class& klass) {
   Function& nsm_function = Function::Handle(zone);
   Class& iterate_klass = Class::Handle(zone, klass.raw());
   while (!iterate_klass.IsNull()) {
-    nsm_function = iterate_klass.LookupDynamicFunction(Symbols::NoSuchMethod());
+    if (iterate_klass.EnsureIsFinalized(thread) == Error::null()) {
+      nsm_function =
+          iterate_klass.LookupDynamicFunction(Symbols::NoSuchMethod());
+    }
     if (!nsm_function.IsNull() && nsm_function.NumParameters() == 2 &&
         nsm_function.NumTypeParameters() == 0) {
       break;
@@ -2511,6 +2516,8 @@
   const Class& mirror_class =
       Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror()));
   ASSERT(!mirror_class.IsNull());
+  const auto& error = mirror_class.EnsureIsFinalized(thread());
+  ASSERT(error == Error::null());
   const Function& allocation_function = Function::ZoneHandle(
       Z, mirror_class.LookupStaticFunction(
              Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
@@ -2544,17 +2551,19 @@
   // method.
   Function& function = Function::Handle(Z);
   while (!klass.IsNull()) {
-    function = klass.LookupDynamicFunction(method_name);
-    if (!function.IsNull()) {
-      Function& target =
-          Function::ZoneHandle(Z, function.ImplicitClosureFunction());
-      ASSERT(!target.IsNull());
-      // Generate inline code for allocation closure object with context
-      // which captures `this`.
-      return BuildImplicitClosureCreation(target);
+    if (klass.EnsureIsFinalized(thread()) == Error::null()) {
+      function = klass.LookupDynamicFunction(method_name);
+      if (!function.IsNull()) {
+        Function& target =
+            Function::ZoneHandle(Z, function.ImplicitClosureFunction());
+        ASSERT(!target.IsNull());
+        // Generate inline code for allocation closure object with context
+        // which captures `this`.
+        return BuildImplicitClosureCreation(target);
+      }
+      function = klass.LookupDynamicFunction(getter_name);
+      if (!function.IsNull()) break;
     }
-    function = klass.LookupDynamicFunction(getter_name);
-    if (!function.IsNull()) break;
     klass = klass.SuperClass();
   }
 
@@ -2575,7 +2584,7 @@
         /* argument_names = */ Object::empty_array(), actuals_array,
         /* build_rest_of_actuals = */ Fragment());
 
-    Function& nsm_function = GetNoSuchMethodOrDie(Z, parent_klass);
+    Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, parent_klass);
     instructions +=
         StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()),
                    /* argument_count = */ 2, ICData::kNSMDispatch);
@@ -2603,8 +2612,10 @@
 
   const String& setter_name = ReadNameAsSetterName();  // read name.
 
-  Function& function =
-      Function::Handle(Z, H.LookupDynamicFunction(klass, setter_name));
+  Function& function = Function::Handle(Z);
+  if (klass.EnsureIsFinalized(thread()) == Error::null()) {
+    function = H.LookupDynamicFunction(klass, setter_name);
+  }
 
   Fragment instructions(MakeTemp());
   LocalVariable* value = MakeTemporary();  // this holds RHS value
@@ -2631,7 +2642,7 @@
 
     SkipInterfaceMemberNameReference();  // skip target_reference.
 
-    Function& nsm_function = GetNoSuchMethodOrDie(Z, klass);
+    Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, klass);
     instructions +=
         StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()),
                    /* argument_count = */ 2, ICData::kNSMDispatch);
@@ -3219,7 +3230,7 @@
 
     SkipInterfaceMemberNameReference();  //  skip target_reference.
 
-    Function& nsm_function = GetNoSuchMethodOrDie(Z, klass);
+    Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, klass);
     instructions += StaticCall(TokenPosition::kNoSource,
                                Function::ZoneHandle(Z, nsm_function.raw()),
                                /* argument_count = */ 2, ICData::kNSMDispatch);
@@ -3858,9 +3869,11 @@
 
   const Class& map_class =
       Class::Handle(Z, Library::LookupCoreClass(Symbols::Map()));
-  const Function& factory_method = Function::ZoneHandle(
-      Z, map_class.LookupFactory(
-             Library::PrivateCoreLibName(Symbols::MapLiteralFactory())));
+  Function& factory_method = Function::ZoneHandle(Z);
+  if (map_class.EnsureIsFinalized(H.thread()) == Error::null()) {
+    factory_method = map_class.LookupFactory(
+        Library::PrivateCoreLibName(Symbols::MapLiteralFactory()));
+  }
 
   return instructions +
          StaticCall(position, factory_method, 2, ICData::kStatic);
@@ -3964,6 +3977,8 @@
   if (position != NULL) *position = TokenPosition::kNoSource;
   const Class& future = Class::Handle(Z, I->object_store()->future_class());
   ASSERT(!future.IsNull());
+  const auto& error = future.EnsureIsFinalized(thread());
+  ASSERT(error == Error::null());
   const Function& constructor =
       Function::ZoneHandle(Z, future.LookupFunction(Symbols::FutureValue()));
   ASSERT(!constructor.IsNull());
@@ -4500,6 +4515,8 @@
       const Class& klass = Class::ZoneHandle(
           Z, Library::LookupCoreClass(Symbols::FallThroughError()));
       ASSERT(!klass.IsNull());
+      const auto& error = klass.EnsureIsFinalized(thread());
+      ASSERT(error == Error::null());
 
       GrowableHandlePtrArray<const String> pieces(Z, 3);
       pieces.Add(Symbols::FallThroughError());
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 712deec..eae3173 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -444,6 +444,8 @@
       Z, Library::LookupCoreClass(Symbols::LateInitializationError()));
   ASSERT(!klass.IsNull());
 
+  const auto& error = klass.EnsureIsFinalized(thread_);
+  ASSERT(error == Error::null());
   const Function& throw_new =
       Function::ZoneHandle(Z, klass.LookupStaticFunctionAllowPrivate(
                                   H.DartSymbolObfuscate("_throwNew")));
@@ -671,6 +673,8 @@
   const Class& klass = Class::ZoneHandle(
       Z, Library::LookupCoreClass(Symbols::NoSuchMethodError()));
   ASSERT(!klass.IsNull());
+  const auto& error = klass.EnsureIsFinalized(H.thread());
+  ASSERT(error == Error::null());
   const Function& throw_function = Function::ZoneHandle(
       Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew()));
   ASSERT(!throw_function.IsNull());
@@ -1552,6 +1556,8 @@
   const Class& klass =
       Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError()));
   ASSERT(!klass.IsNull());
+  const auto& error = klass.EnsureIsFinalized(H.thread());
+  ASSERT(error == Error::null());
   const Function& target = Function::ZoneHandle(
       Z, klass.LookupStaticFunctionAllowPrivate(Symbols::EvaluateAssertion()));
   ASSERT(!target.IsNull());
@@ -1904,6 +1910,8 @@
   const Class& mirror_class =
       Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror()));
   ASSERT(!mirror_class.IsNull());
+  const auto& error = mirror_class.EnsureIsFinalized(H.thread());
+  ASSERT(error == Error::null());
   const Function& allocation_function = Function::ZoneHandle(
       Z, mirror_class.LookupStaticFunction(
              Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
@@ -2637,6 +2645,8 @@
   const Class& mirror_class =
       Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror()));
   ASSERT(!mirror_class.IsNull());
+  const auto& error = mirror_class.EnsureIsFinalized(H.thread());
+  ASSERT(error == Error::null());
   const Function& allocation_function = Function::ZoneHandle(
       Z, mirror_class.LookupStaticFunction(Library::PrivateCoreLibName(
              Symbols::AllocateInvocationMirrorForClosure())));
@@ -2648,6 +2658,8 @@
     const Class& klass = Class::ZoneHandle(
         Z, Library::LookupCoreClass(Symbols::NoSuchMethodError()));
     ASSERT(!klass.IsNull());
+    const auto& error = klass.EnsureIsFinalized(H.thread());
+    ASSERT(error == Error::null());
     const Function& throw_function = Function::ZoneHandle(
         Z,
         klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNewInvocation()));
@@ -3566,6 +3578,8 @@
     const Class& klass = Class::ZoneHandle(
         Z, Library::LookupCoreClass(Symbols::AssertionError()));
     ASSERT(!klass.IsNull());
+    const auto& error = klass.EnsureIsFinalized(H.thread());
+    ASSERT(error == Error::null());
     throw_new_null_assertion_ =
         &Function::ZoneHandle(Z, klass.LookupStaticFunctionAllowPrivate(
                                      Symbols::ThrowNewNullAssertion()));
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 1785105..b185b81 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -623,6 +623,8 @@
   } else {
     ASSERT(IsClass(enclosing));
     Class& klass = Class::Handle(Z, LookupClassByKernelClass(enclosing));
+    const auto& error = klass.EnsureIsFinalized(thread_);
+    ASSERT(error == Error::null());
     Function& function = Function::ZoneHandle(
         Z, klass.LookupFunctionAllowPrivate(procedure_name));
     CheckStaticLookup(function);
@@ -646,6 +648,8 @@
     const Class& owner,
     NameIndex constructor) {
   ASSERT(IsConstructor(constructor));
+  const auto& error = owner.EnsureIsFinalized(thread_);
+  ASSERT(error == Error::null());
   Function& function = Function::Handle(
       Z, owner.LookupConstructorAllowPrivate(DartConstructorName(constructor)));
   CheckStaticLookup(function);
@@ -663,6 +667,8 @@
 
   String& new_name =
       String::ZoneHandle(Z, Symbols::FromConcatAll(thread_, pieces));
+  const auto& error = owner.EnsureIsFinalized(thread_);
+  ASSERT(error == Error::null());
   FunctionPtr function = owner.LookupConstructorAllowPrivate(new_name);
   ASSERT(function != Object::null());
   return function;
@@ -672,9 +678,10 @@
                                                     const String& method_name) {
   NameIndex kernel_class = EnclosingName(target);
   Class& klass = Class::Handle(Z, LookupClassByKernelClass(kernel_class));
-
-  Function& function =
-      Function::Handle(Z, klass.LookupFunctionAllowPrivate(method_name));
+  Function& function = Function::Handle(Z);
+  if (klass.EnsureIsFinalized(thread_) == Error::null()) {
+    function = klass.LookupFunctionAllowPrivate(method_name);
+  }
 #ifdef DEBUG
   if (function.IsNull()) {
     THR_Print("Unable to find \'%s\' in %s\n", method_name.ToCString(),
diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc
index fc900fa..219dc7c 100644
--- a/runtime/vm/compiler_test.cc
+++ b/runtime/vm/compiler_test.cc
@@ -36,6 +36,8 @@
   Class& cls =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
   EXPECT(!cls.IsNull());
+  const auto& error = cls.EnsureIsFinalized(thread);
+  EXPECT(error == Error::null());
   String& function_foo_name = String::Handle(String::New("foo"));
   Function& function_foo =
       Function::Handle(cls.LookupStaticFunction(function_foo_name));
@@ -75,6 +77,8 @@
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
   EXPECT(!cls.IsNull());
   String& function_foo_name = String::Handle(String::New("foo"));
+  const auto& error = cls.EnsureIsFinalized(thread);
+  EXPECT(error == Error::null());
   Function& func =
       Function::Handle(cls.LookupStaticFunction(function_foo_name));
   EXPECT(!func.HasCode());
@@ -116,6 +120,8 @@
   Class& cls =
       Class::Handle(lib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
   EXPECT(!cls.IsNull());
+  const auto& error = cls.EnsureIsFinalized(thread);
+  EXPECT(error == Error::null());
   String& function_foo_name = String::Handle(String::New("foo"));
   Function& func =
       Function::Handle(cls.LookupStaticFunction(function_foo_name));
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 95ae9bb..e867ce4 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -2828,6 +2828,11 @@
         "cls_type must be a Type object which represents a Class");
   }
 
+  const auto& error = klass.EnsureIsFinalized(Thread::Current());
+  if (error != Error::null()) {
+    return Api::NewHandle(T, error);
+  }
+
   const String& func_name = Api::UnwrapStringHandle(Z, function_name);
   if (func_name.IsNull()) {
     RETURN_TYPE_ERROR(Z, function_name, String);
@@ -4330,8 +4335,10 @@
                                     const String& constr_name,
                                     int num_args) {
   // The constructor must be present in the interface.
-  const Function& constructor =
-      Function::Handle(cls.LookupFunctionAllowPrivate(constr_name));
+  Function& constructor = Function::Handle();
+  if (cls.EnsureIsFinalized(Thread::Current()) == Error::null()) {
+    constructor = cls.LookupFunctionAllowPrivate(constr_name);
+  }
   if (constructor.IsNull() ||
       (!constructor.IsGenerativeConstructor() && !constructor.IsFactory())) {
     const String& lookup_class_name = String::Handle(cls.Name());
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 8cb10d9..96081a8 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -335,6 +335,9 @@
   Class& invocation_mirror_class = Class::Handle(core_lib.LookupClass(
       String::Handle(core_lib.PrivateName(Symbols::InvocationMirror()))));
   ASSERT(!invocation_mirror_class.IsNull());
+  Thread* thread = Thread::Current();
+  const auto& error = invocation_mirror_class.EnsureIsFinalized(thread);
+  ASSERT(error == Error::null());
   const String& function_name =
       String::Handle(core_lib.PrivateName(Symbols::AllocateInvocationMirror()));
   const Function& allocation_function = Function::Handle(
@@ -363,7 +366,6 @@
   if (function.IsNull()) {
     ASSERT(!FLAG_lazy_dispatchers);
     // If noSuchMethod(invocation) is not found, call Object::noSuchMethod.
-    Thread* thread = Thread::Current();
     function = Resolver::ResolveDynamicForReceiverClass(
         Class::Handle(thread->zone(),
                       thread->isolate()->object_store()->object_class()),
@@ -643,6 +645,9 @@
   const int kNumArguments = 1;  // Receiver.
   ArgumentsDescriptor args_desc(Array::Handle(
       ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments)));
+  const Class& receiver_class = Class::Handle(receiver.clazz());
+  const auto& error = receiver_class.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
   const Function& function = Function::Handle(
       Resolver::ResolveDynamic(receiver, Symbols::toString(), args_desc));
   ASSERT(!function.IsNull());
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 2ea406b..5f37ada 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -3991,8 +3991,11 @@
                   (library_url() != nullptr ? library_url() : script_url())));
     return LanguageError::New(msg);
   }
-  const Function& func =
-      Function::Handle(zone, cls.LookupStaticFunctionAllowPrivate(func_name));
+  Function& func = Function::Handle(zone);
+  const auto& error = cls.EnsureIsFinalized(thread);
+  if (error == Error::null()) {
+    func = cls.LookupStaticFunctionAllowPrivate(func_name);
+  }
   if (func.IsNull()) {
     const String& msg = String::Handle(
         zone, String::NewFormatted(
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 25c9549..cc72376 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3598,6 +3598,9 @@
   const Function& closure_function =
       Function::Handle(ImplicitClosureFunction());
   const Class& owner = Class::Handle(closure_function.Owner());
+  if (owner.EnsureIsFinalized(Thread::Current()) != Error::null()) {
+    return Function::null();
+  }
   Function& result = Function::Handle(owner.LookupDynamicFunction(getter_name));
   if (result.IsNull()) {
     result = CreateMethodExtractor(getter_name);
@@ -3947,10 +3950,13 @@
   args.SetAt(6, argument_names);
 
   const Library& libcore = Library::Handle(Library::CoreLibrary());
-  const Class& NoSuchMethodError =
+  const Class& cls =
       Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
-  const Function& throwNew = Function::Handle(
-      NoSuchMethodError.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
+  ASSERT(!cls.IsNull());
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
+  const Function& throwNew =
+      Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
   return DartEntry::InvokeFunction(throwNew, args);
 }
 
@@ -3966,10 +3972,12 @@
   args.SetAt(3, dst_name);
 
   const Library& libcore = Library::Handle(Library::CoreLibrary());
-  const Class& TypeError =
+  const Class& cls =
       Class::Handle(libcore.LookupClassAllowPrivate(Symbols::TypeError()));
-  const Function& throwNew = Function::Handle(
-      TypeError.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
+  const Function& throwNew =
+      Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
   return DartEntry::InvokeFunction(throwNew, args);
 }
 
@@ -5337,9 +5345,7 @@
 FunctionPtr Class::LookupFunction(const String& name, MemberKind kind) const {
   ASSERT(!IsNull());
   Thread* thread = Thread::Current();
-  if (EnsureIsFinalized(thread) != Error::null()) {
-    return Function::null();
-  }
+  RELEASE_ASSERT(is_finalized());
   REUSABLE_ARRAY_HANDLESCOPE(thread);
   REUSABLE_FUNCTION_HANDLESCOPE(thread);
   Array& funcs = thread->ArrayHandle();
@@ -5389,9 +5395,7 @@
                                               MemberKind kind) const {
   ASSERT(!IsNull());
   Thread* thread = Thread::Current();
-  if (EnsureIsFinalized(thread) != Error::null()) {
-    return Function::null();
-  }
+  RELEASE_ASSERT(is_finalized());
   REUSABLE_ARRAY_HANDLESCOPE(thread);
   REUSABLE_FUNCTION_HANDLESCOPE(thread);
   REUSABLE_STRING_HANDLESCOPE(thread);
@@ -9033,6 +9037,8 @@
   const auto& parent = Function::Handle(zone, parent_function());
   const auto& func_name = String::Handle(zone, parent.name());
   const auto& owner = Class::Handle(zone, parent.Owner());
+  const auto& error = owner.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
   auto& target = Function::Handle(zone, owner.LookupFunction(func_name));
 
   if (!target.IsNull() && (target.raw() != parent.raw())) {
@@ -13974,11 +13980,13 @@
       class_str = String::New(class_name);
       cls = lib.LookupClassAllowPrivate(class_str);
       if (!cls.IsNull()) {
-        func_str = String::New(function_name);
-        if (function_name[0] == '.') {
-          func_str = String::Concat(class_str, func_str);
+        if (cls.EnsureIsFinalized(thread) == Error::null()) {
+          func_str = String::New(function_name);
+          if (function_name[0] == '.') {
+            func_str = String::Concat(class_str, func_str);
+          }
+          func = cls.LookupFunctionAllowPrivate(func_str);
         }
-        func = cls.LookupFunctionAllowPrivate(func_str);
       }
     }
     if (!func.IsNull()) {
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 26a5c3a..37b39ac 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -191,6 +191,8 @@
   const Library& core_lib = Library::Handle(core_library());
   const String& mangled = String::ZoneHandle(core_lib.PrivateName(name));
   const Class& cls = Class::Handle(object_class());
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  ASSERT(error == Error::null());
   const Function& result = Function::Handle(cls.LookupDynamicFunction(mangled));
   ASSERT(!result.IsNull());
   return result.raw();
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 2d3b16e..68f2de9 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -3709,6 +3709,8 @@
 }
 
 static FunctionPtr GetFunction(const Class& cls, const char* name) {
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  EXPECT(error == Error::null());
   const Function& result = Function::Handle(
       cls.LookupDynamicFunction(String::Handle(String::New(name))));
   EXPECT(!result.IsNull());
@@ -3716,6 +3718,8 @@
 }
 
 static FunctionPtr GetStaticFunction(const Class& cls, const char* name) {
+  const auto& error = cls.EnsureIsFinalized(Thread::Current());
+  EXPECT(error == Error::null());
   const Function& result = Function::Handle(
       cls.LookupStaticFunction(String::Handle(String::New(name))));
   EXPECT(!result.IsNull());
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index a17e9c6..cf921ac 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1373,9 +1373,11 @@
   ASSERT(function_name.IsSymbol());
 
   const Class& receiver_class = Class::Handle(zone, receiver.clazz());
-  Function& target_function = Function::Handle(
-      zone, Resolver::ResolveDynamicForReceiverClass(
-                receiver_class, function_name, arguments_descriptor));
+  Function& target_function = Function::Handle(zone);
+  if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
+    target_function = Resolver::ResolveDynamicForReceiverClass(
+        receiver_class, function_name, arguments_descriptor);
+  }
 
   ObjectStore* store = thread->isolate()->object_store();
   if (target_function.raw() == store->simple_instance_of_function()) {
@@ -1742,16 +1744,19 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-static FunctionPtr Resolve(Zone* zone,
+static FunctionPtr Resolve(Thread* thread,
+                           Zone* zone,
                            const Class& receiver_class,
                            const String& name,
                            const Array& descriptor) {
   ASSERT(name.IsSymbol());
 
   ArgumentsDescriptor args_desc(descriptor);
-  Function& target_function =
-      Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
-                                 receiver_class, name, args_desc));
+  Function& target_function = Function::Handle(zone);
+  if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
+    target_function = Resolver::ResolveDynamicForReceiverClass(receiver_class,
+                                                               name, args_desc);
+  }
 
   if (target_function.IsNull()) {
     target_function = InlineCacheMissHelper(receiver_class, descriptor, name);
@@ -1777,7 +1782,8 @@
   const auto& old_receiver_class =
       Class::Handle(zone_, isolate_->class_table()->At(old_expected_cid));
   const auto& old_target = Function::Handle(
-      zone_, Resolve(zone_, old_receiver_class, name_, args_descriptor_));
+      zone_,
+      Resolve(thread_, zone_, old_receiver_class, name_, args_descriptor_));
 
   const ICData& ic_data = ICData::Handle(
       zone_, ICData::New(caller_function_, name_, args_descriptor_,
@@ -2052,7 +2058,7 @@
       UNREACHABLE();
   }
   const Class& cls = Class::Handle(zone_, receiver_.clazz());
-  return Resolve(zone_, cls, name_, args_descriptor_);
+  return Resolve(thread_, zone_, cls, name_, args_descriptor_);
 }
 
 void SwitchableCallHandler::HandleMiss(const Object& old_data,
@@ -2169,15 +2175,17 @@
   const Array& arg_desc = Array::CheckedHandle(zone, arguments.ArgAt(2));
 
   ArgumentsDescriptor arguments_descriptor(arg_desc);
-  Function& target_function = Function::Handle(
-      zone,
-      Resolver::ResolveDynamic(receiver, target_name, arguments_descriptor));
+  const Class& receiver_class = Class::Handle(zone, receiver.clazz());
+  Function& target_function = Function::Handle(zone);
+  if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
+    target_function =
+        Resolver::ResolveDynamic(receiver, target_name, arguments_descriptor);
+  }
 
   // TODO(regis): In order to substitute 'simple_instance_of_function', the 2nd
   // arg to the call, the type, is needed.
 
   if (target_function.IsNull()) {
-    const Class& receiver_class = Class::Handle(zone, receiver.clazz());
     target_function =
         InlineCacheMissHelper(receiver_class, arg_desc, target_name);
   }
@@ -2190,6 +2198,7 @@
 // invoking noSuchMethod when lazy dispatchers are disabled. Returns the
 // result of the invocation or an Error.
 static ObjectPtr InvokeCallThroughGetterOrNoSuchMethod(
+    Thread* thread,
     Zone* zone,
     const Instance& receiver,
     const String& target_name,
@@ -2222,7 +2231,9 @@
       // We don't generate dyn:* forwarders for method extractors so there is no
       // need to try to find a dyn:get:foo first (see assertion below)
       if (function.IsNull()) {
-        function = cls.LookupDynamicFunction(function_name);
+        if (cls.EnsureIsFinalized(thread) == Error::null()) {
+          function = cls.LookupDynamicFunction(function_name);
+        }
       }
       if (!function.IsNull()) {
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -2280,7 +2291,9 @@
     while (!cls.IsNull()) {
       // If there is a function with the target name but mismatched arguments
       // we need to call `receiver.noSuchMethod()`.
-      function = cls.LookupDynamicFunction(target_name);
+      if (cls.EnsureIsFinalized(thread) == Error::null()) {
+        function = cls.LookupDynamicFunction(target_name);
+      }
       if (!function.IsNull()) {
         ASSERT(!function.AreValidArguments(args_desc, NULL));
         break;  // mismatch, invoke noSuchMethod
@@ -2359,10 +2372,10 @@
     target_name = MegamorphicCache::Cast(ic_data_or_cache).target_name();
   }
 
-  const auto& result = Object::Handle(
-      zone,
-      InvokeCallThroughGetterOrNoSuchMethod(
-          zone, receiver, target_name, orig_arguments, orig_arguments_desc));
+  const auto& result =
+      Object::Handle(zone, InvokeCallThroughGetterOrNoSuchMethod(
+                               thread, zone, receiver, target_name,
+                               orig_arguments, orig_arguments_desc));
   ThrowIfError(result);
   arguments.SetReturn(result);
 }
@@ -2417,7 +2430,7 @@
     // Failing to find the method could be due to the lack of lazy invoke field
     // dispatchers, so attempt a deeper search before calling noSuchMethod.
     result = InvokeCallThroughGetterOrNoSuchMethod(
-        zone, receiver, original_function_name, orig_arguments,
+        thread, zone, receiver, original_function_name, orig_arguments,
         orig_arguments_desc);
   } else {
     result = DartEntry::InvokeNoSuchMethod(receiver, original_function_name,