Version 2.12.0-196.0.dev

Merge commit '12d77071797a77aa93c7b0b6b962d3443ee56316' into 'dev'
diff --git a/DEPS b/DEPS
index 59207c6..5b388d3 100644
--- a/DEPS
+++ b/DEPS
@@ -83,7 +83,7 @@
   "collection_rev": "e4bb038ce2d8e66fb15818aa40685c68d53692ab",
   "convert_rev": "6513985a1b1ea8a0b987fbef699250ce2cdc3cca",
   "crypto_rev": "c89a5be0375875fe7ff71625fa2b79f5a421f06d",
-  "csslib_rev": "6f77b3dcee957d3e2d5083f666221a220e9ed1f1",
+  "csslib_rev": "e411d862fd8cc50415c1badf2632e017373b3f47",
   "dart2js_info_rev" : "e0acfeb5affdf94c53067e68bd836adf589628fd",
 
   # Note: Updates to dart_style have to be coordinated with the infrastructure
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index cb53b76..3da8be8 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -759,10 +759,6 @@
   }
 }
 
-static Dart_QualifiedFunctionName no_entry_points[] = {
-    {NULL, NULL, NULL}  // Must be terminated with NULL entries.
-};
-
 static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) {
   uint8_t* kernel_buffer = NULL;
   intptr_t kernel_buffer_size = 0;
@@ -775,7 +771,6 @@
                             kernel_buffer, kernel_buffer_size);
   if (IsSnapshottingForPrecompilation()) {
     isolate_flags.obfuscate = obfuscate;
-    isolate_flags.entry_points = no_entry_points;
   }
 
   auto isolate_group_data = std::unique_ptr<IsolateGroupData>(
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 189a6b7..5cff8c3 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -588,12 +588,6 @@
  */
 DART_EXPORT const char* Dart_VersionString();
 
-typedef struct {
-  const char* library_uri;
-  const char* class_name;
-  const char* function_name;
-} Dart_QualifiedFunctionName;
-
 /**
  * Isolate specific flags are set when creating a new isolate using the
  * Dart_IsolateFlags structure.
@@ -610,7 +604,6 @@
   bool use_field_guards;
   bool use_osr;
   bool obfuscate;
-  Dart_QualifiedFunctionName* entry_points;
   bool load_vmservice_library;
   bool copy_parent_code;
   bool null_safety;
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index f0c2d76..d99a1b9 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -111,9 +111,10 @@
   if (setjmp(*jump.Set()) == 0) {
     kernel::KernelLoader loader(program.get(), /*uri_to_source_table=*/nullptr);
 
-    Isolate* isolate = thread->isolate();
+    auto isolate = thread->isolate();
+    auto isolate_group = thread->isolate_group();
 
-    if (isolate->obfuscate()) {
+    if (isolate_group->obfuscate()) {
       loader.ReadObfuscationProhibitions();
     }
 
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 175f985..a26ecbb 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1339,15 +1339,15 @@
 void ClassFinalizer::VerifyImplicitFieldOffsets() {
 #ifdef DEBUG
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
-  if (isolate->obfuscate()) {
+  if (isolate_group->obfuscate()) {
     // Field names are obfuscated.
     return;
   }
 
   Zone* zone = thread->zone();
-  const ClassTable& class_table = *(isolate->class_table());
+  const ClassTable& class_table = *(isolate_group->class_table());
   Class& cls = Class::Handle(zone);
   Array& fields_array = Array::Handle(zone);
   Field& field = Field::Handle(zone);
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index b4f91a6..37a4d1b 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -57,6 +57,7 @@
 
 #define T (thread())
 #define I (isolate())
+#define IG (isolate_group())
 #define Z (zone())
 
 DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynamic targets");
@@ -2348,7 +2349,7 @@
 #endif
 
 void Precompiler::Obfuscate() {
-  if (!I->obfuscate()) {
+  if (!IG->obfuscate()) {
     return;
   }
 
@@ -2412,7 +2413,7 @@
   }
 
   // Obfuscation is done. Move obfuscation map into malloced memory.
-  I->set_obfuscation_map(Obfuscator::SerializeMap(T));
+  IG->set_obfuscation_map(Obfuscator::SerializeMap(T));
 
   // Discard obfuscation mappings to avoid including them into snapshot.
   I->object_store()->set_obfuscation_map(Array::Handle(Z));
@@ -2808,15 +2809,15 @@
 
 Obfuscator::Obfuscator(Thread* thread, const String& private_key)
     : state_(NULL) {
-  Isolate* isolate = thread->isolate();
-  Zone* zone = thread->zone();
-  if (!isolate->obfuscate()) {
+  auto isolate_group = thread->isolate_group();
+  if (!isolate_group->obfuscate()) {
     // Nothing to do.
     return;
   }
+  auto zone = thread->zone();
 
   // Create ObfuscationState from ObjectStore::obfusction_map().
-  ObjectStore* store = thread->isolate()->object_store();
+  ObjectStore* store = isolate_group->object_store();
   Array& obfuscation_state = Array::Handle(zone, store->obfuscation_map());
 
   if (store->obfuscation_map() == Array::null()) {
@@ -2833,7 +2834,7 @@
   if (store->obfuscation_map() == Array::null()) {
     // We are just starting the obfuscation. Initialize the renaming map.
     // Note: InitializeRenamingMap uses state_.
-    InitializeRenamingMap(isolate);
+    InitializeRenamingMap();
   }
 }
 
@@ -2843,7 +2844,7 @@
   }
 }
 
-void Obfuscator::InitializeRenamingMap(Isolate* isolate) {
+void Obfuscator::InitializeRenamingMap() {
 // Prevent renaming of all pseudo-keywords and operators.
 // Note: not all pseudo-keywords are mentioned in DART_KEYWORD_LIST
 // (for example 'hide', 'show' and async related keywords are omitted).
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index a1da918..32cfe14 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -331,6 +331,7 @@
   Thread* thread() const { return thread_; }
   Zone* zone() const { return zone_; }
   Isolate* isolate() const { return isolate_; }
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
 
   Thread* thread_;
   Zone* zone_;
@@ -466,7 +467,7 @@
  private:
   // Populate renaming map with names that should have identity renaming.
   // (or in other words: with those names that should not be renamed).
-  void InitializeRenamingMap(Isolate* isolate);
+  void InitializeRenamingMap();
 
   // ObjectStore::obfuscation_map() is an Array with two elements:
   // first element is the last used rename and the second element is
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 521f371..be04219 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -2506,7 +2506,7 @@
   // supertype may yield a wrong result for Null in NNBD strong mode (because
   // Null also extends Object).
   if (!type_class.IsObjectClass() ||
-      !Isolate::Current()->use_strict_null_safety_checks()) {
+      !IsolateGroup::Current()->use_strict_null_safety_checks()) {
     // We don't use TypeTestABI::kScratchReg for the first scratch register as
     // it is not defined on IA32. Instead, we use the subtype test cache
     // register, as it is clobbered by the subtype test cache stub call anyway.
@@ -2986,7 +2986,7 @@
   if (dst_type.IsObjectType()) {
     // Special case: non-nullable Object.
     ASSERT(dst_type.IsNonNullable() &&
-           isolate()->use_strict_null_safety_checks());
+           isolate_group()->use_strict_null_safety_checks());
     __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
     __ BranchIf(NOT_EQUAL, done);
     // Fall back to type testing stub in caller to throw the exception.
@@ -3008,7 +3008,7 @@
     // Special case: Instantiate the type parameter on the caller side, invoking
     // the TTS of the corresponding type parameter in the caller.
     const TypeParameter& type_param = TypeParameter::Cast(dst_type);
-    if (isolate()->use_strict_null_safety_checks() &&
+    if (isolate_group()->use_strict_null_safety_checks() &&
         !type_param.IsNonNullable()) {
       // If the type parameter is nullable when running in strong mode, we need
       // to handle null before calling the TTS because the type parameter may be
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 69db3f6..f7390fa 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -946,6 +946,7 @@
 
   Thread* thread() const { return thread_; }
   Isolate* isolate() const { return thread_->isolate(); }
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
   Zone* zone() const { return zone_; }
 
   void AddStubCallTarget(const Code& code);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 1f9624b..272dcd3 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -360,7 +360,7 @@
     __ Comment("AssertAssignable for runtime type");
     // kDstTypeReg should already contain the destination type.
     const bool null_safety =
-        Isolate::Current()->use_strict_null_safety_checks();
+        IsolateGroup::Current()->use_strict_null_safety_checks();
     GenerateStubCall(source,
                      null_safety ? StubCode::TypeIsTopTypeForSubtypingNullSafe()
                                  : StubCode::TypeIsTopTypeForSubtyping(),
diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc
index 72f41e3..93a2a79 100644
--- a/runtime/vm/compiler/backend/typed_data_aot_test.cc
+++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc
@@ -45,7 +45,7 @@
   LoadIndexedInstr* load_indexed = nullptr;
 
   ILMatcher cursor(flow_graph, entry);
-  if (Isolate::Current()->null_safety()) {
+  if (IsolateGroup::Current()->null_safety()) {
     RELEASE_ASSERT(cursor.TryMatch({
         kMoveGlob,
         {kMatchAndMoveLoadField, &load_field},
@@ -182,7 +182,7 @@
 
     // Ensure the IL matches what we expect.
     ILMatcher cursor(flow_graph, entry);
-    if (Isolate::Current()->null_safety()) {
+    if (IsolateGroup::Current()->null_safety()) {
       EXPECT(cursor.TryMatch({
           // Before loop
           kMoveGlob,
@@ -327,7 +327,7 @@
 
     // Ensure the IL matches what we expect.
     ILMatcher cursor(flow_graph, entry, /*trace=*/true);
-    if (Isolate::Current()->null_safety()) {
+    if (IsolateGroup::Current()->null_safety()) {
       EXPECT(cursor.TryMatch({
           // LoadField length
           kMoveGlob,
@@ -435,7 +435,7 @@
   // With null safety nulls cannot be passed as non-nullable arguments, so
   // skip all error stages and only run the last stage.
   const intptr_t first_stage =
-      Isolate::Current()->null_safety() ? kLastStage : 0;
+      IsolateGroup::Current()->null_safety() ? kLastStage : 0;
   for (intptr_t stage = first_stage; stage <= kLastStage; ++stage) {
     run_test("Uint8List", "int", int8_list, int_value, stage);
     run_test("Int8List", "int", uint8_list, int_value, stage);
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index ed8dc49..38d4932 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -85,7 +85,7 @@
     // When obfuscation is enabled we need to obfuscate the name of the
     // class before looking it up.
     String& name = String::Handle(zone, Symbols::New(thread, "Comparable"));
-    if (thread->isolate()->obfuscate()) {
+    if (thread->isolate_group()->obfuscate()) {
       Obfuscator obfuscator(thread, Object::null_string());
       name = obfuscator.Rename(name);
     }
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index 3059114..83dffc8 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -118,7 +118,7 @@
 
 InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_offset) {
   // Get reader directly into raw bytes of constant table.
-  bool null_safety = H.thread()->isolate()->null_safety();
+  bool null_safety = H.thread()->isolate_group()->null_safety();
   KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
   reader.ReadUInt();  // skip variable-sized int for adjusted constant offset
   reader.SetOffset(reader.ReaderOffset() + constant_offset);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index cdc4f00..f248794 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -41,6 +41,7 @@
 #define H (translation_helper_)
 #define T (type_translator_)
 #define I Isolate::Current()
+#define IG IsolateGroup::Current()
 
 FlowGraphBuilder::FlowGraphBuilder(
     ParsedFunction* parsed_function,
@@ -2124,7 +2125,7 @@
     Fragment set,
     Fragment not_set) {
   // Required named arguments only exist if null_safety is enabled.
-  if (!I->use_strict_null_safety_checks()) return not_set;
+  if (!IG->use_strict_null_safety_checks()) return not_set;
 
   Fragment check_required;
   // First, we convert the index to be in terms of the number of optional
@@ -2299,7 +2300,7 @@
   // required named arguments.
   if (info.descriptor.NamedCount() == 0) {
     // No work to do if there are no possible required named parameters.
-    if (!I->use_strict_null_safety_checks()) {
+    if (!IG->use_strict_null_safety_checks()) {
       return Fragment();
     }
     // If the below changes, we can no longer assume that flag slots existing
@@ -4463,7 +4464,7 @@
 
 Fragment FlowGraphBuilder::BuildNullAssertions() {
   Fragment code;
-  if (I->null_safety() || !I->asserts() || !FLAG_null_assertions) {
+  if (IG->null_safety() || !I->asserts() || !FLAG_null_assertions) {
     return code;
   }
 
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index a1ee75d..748a25d 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -18,6 +18,7 @@
 #define H (translation_helper_)
 #define T (type_translator_)
 #define I Isolate::Current()
+#define IG IsolateGroup::Current()
 
 namespace dart {
 namespace kernel {
@@ -390,7 +391,7 @@
 const String& TranslationHelper::DartSymbolObfuscate(
     const char* content) const {
   String& result = String::ZoneHandle(Z, Symbols::New(thread_, content));
-  if (I->obfuscate()) {
+  if (IG->obfuscate()) {
     Obfuscator obfuscator(thread_, String::Handle(Z));
     result = obfuscator.Rename(result, true);
   }
@@ -406,7 +407,7 @@
   }
   String& result =
       String::ZoneHandle(Z, Symbols::FromUTF8(thread_, buffer, length));
-  if (I->obfuscate()) {
+  if (IG->obfuscate()) {
     Obfuscator obfuscator(thread_, String::Handle(Z));
     result = obfuscator.Rename(result, true);
   }
@@ -801,14 +802,14 @@
     const Library& library =
         Library::Handle(Z, LookupLibraryByKernelLibrary(parent));
     *name_to_modify = library.PrivateName(*name_to_modify);
-    if (obfuscate && I->obfuscate()) {
+    if (obfuscate && IG->obfuscate()) {
       const String& library_key = String::Handle(library.private_key());
       Obfuscator obfuscator(thread_, library_key);
       *name_to_modify = obfuscator.Rename(*name_to_modify);
     }
   } else if (symbolize) {
     *name_to_modify = Symbols::New(thread_, *name_to_modify);
-    if (obfuscate && I->obfuscate()) {
+    if (obfuscate && IG->obfuscate()) {
       const String& library_key = String::Handle();
       Obfuscator obfuscator(thread_, library_key);
       *name_to_modify = obfuscator.Rename(*name_to_modify);
@@ -823,14 +824,14 @@
                                              bool obfuscate) {
   if (name_to_modify->Length() >= 1 && name_to_modify->CharAt(0) == '_') {
     *name_to_modify = library.PrivateName(*name_to_modify);
-    if (obfuscate && I->obfuscate()) {
+    if (obfuscate && IG->obfuscate()) {
       const String& library_key = String::Handle(library.private_key());
       Obfuscator obfuscator(thread_, library_key);
       *name_to_modify = obfuscator.Rename(*name_to_modify);
     }
   } else if (symbolize) {
     *name_to_modify = Symbols::New(thread_, *name_to_modify);
-    if (obfuscate && I->obfuscate()) {
+    if (obfuscate && IG->obfuscate()) {
       const String& library_key = String::Handle();
       Obfuscator obfuscator(thread_, library_key);
       *name_to_modify = obfuscator.Rename(*name_to_modify);
diff --git a/runtime/vm/compiler/frontend/prologue_builder.cc b/runtime/vm/compiler/frontend/prologue_builder.cc
index 64fb780..6b1fee9 100644
--- a/runtime/vm/compiler/frontend/prologue_builder.cc
+++ b/runtime/vm/compiler/frontend/prologue_builder.cc
@@ -205,8 +205,8 @@
   } else {
     ASSERT(num_opt_named_params > 0);
 
-    bool check_required_params =
-        Isolate::Current()->use_strict_null_safety_checks();
+    const bool check_required_params =
+        IsolateGroup::Current()->use_strict_null_safety_checks();
     const intptr_t first_name_offset =
         compiler::target::ArgumentsDescriptor::first_named_entry_offset() -
         compiler::target::Array::data_offset();
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 7100f1a..a4bede9 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -278,7 +278,7 @@
         instructions_snapshot, nullptr, -1, api_flags));
     // ObjectStore should be created later, after null objects are initialized.
     auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr,
-                                  /*object_store=*/nullptr);
+                                  /*object_store=*/nullptr, api_flags);
     group->CreateHeap(/*is_vm_isolate=*/true,
                       /*is_service_or_kernel_isolate=*/false);
     IsolateGroup::RegisterIsolateGroup(group);
@@ -1053,8 +1053,9 @@
   }
 
   if (!Snapshot::IsAgnosticToNullSafety(kind)) {
-    if (isolate != NULL) {
-      if (isolate->null_safety()) {
+    auto isolate_group = isolate != nullptr ? isolate->group() : nullptr;
+    if (isolate_group != nullptr) {
+      if (isolate_group->null_safety()) {
         buffer.AddString(" null-safety");
       } else {
         buffer.AddString(" no-null-safety");
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 119d5a0..1894eec 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1352,7 +1352,7 @@
   std::unique_ptr<IsolateGroupSource> source(
       new IsolateGroupSource(script_uri, non_null_name, snapshot_data,
                              snapshot_instructions, nullptr, -1, *flags));
-  auto group = new IsolateGroup(std::move(source), isolate_group_data);
+  auto group = new IsolateGroup(std::move(source), isolate_group_data, *flags);
   group->CreateHeap(
       /*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name));
   IsolateGroup::RegisterIsolateGroup(group);
@@ -1385,7 +1385,7 @@
   std::shared_ptr<IsolateGroupSource> source(
       new IsolateGroupSource(script_uri, non_null_name, nullptr, nullptr,
                              kernel_buffer, kernel_buffer_size, *flags));
-  auto group = new IsolateGroup(source, isolate_group_data);
+  auto group = new IsolateGroup(source, isolate_group_data, *flags);
   IsolateGroup::RegisterIsolateGroup(group);
   group->CreateHeap(
       /*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name));
@@ -3082,7 +3082,8 @@
 DART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id,
                                        intptr_t length) {
   DARTSCOPE(Thread::Current());
-  if (T->isolate()->null_safety() && element_type_id != Dart_CoreType_Dynamic) {
+  if (T->isolate_group()->null_safety() &&
+      element_type_id != Dart_CoreType_Dynamic) {
     return Api::NewError(
         "Cannot use legacy types with --sound-null-safety enabled. "
         "Use Dart_NewListOfType or Dart_NewListOfTypeFilled instead.");
@@ -5640,7 +5641,7 @@
                                      Dart_Handle class_name,
                                      intptr_t number_of_type_arguments,
                                      Dart_Handle* type_arguments) {
-  if (Thread::Current()->isolate()->null_safety()) {
+  if (IsolateGroup::Current()->null_safety()) {
     return Api::NewError(
         "Cannot use legacy types with --sound-null-safety enabled. "
         "Use Dart_GetNullableType or Dart_GetNonNullableType instead.");
@@ -7043,7 +7044,7 @@
 #else
   Thread* thread = Thread::Current();
   DARTSCOPE(thread);
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
   if (buffer == NULL) {
     RETURN_NULL_ERROR(buffer);
@@ -7057,13 +7058,13 @@
   TextBuffer text_buffer(kInitialBufferSize);
 
   text_buffer.AddChar('[');
-  if (isolate->obfuscation_map() != NULL) {
-    for (intptr_t i = 0; isolate->obfuscation_map()[i] != NULL; i++) {
+  if (isolate_group->obfuscation_map() != nullptr) {
+    for (intptr_t i = 0; isolate_group->obfuscation_map()[i] != nullptr; i++) {
       if (i > 0) {
         text_buffer.AddChar(',');
       }
       text_buffer.AddChar('"');
-      text_buffer.AddEscapedString(isolate->obfuscation_map()[i]);
+      text_buffer.AddEscapedString(isolate_group->obfuscation_map()[i]);
       text_buffer.AddChar('"');
     }
   }
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 8761060..1638e3e 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -164,7 +164,7 @@
                                 Thread* thread) {
   ASSERT(!code.IsNull());
   ASSERT(thread->no_callback_scope_depth() == 0);
-  ASSERT(!Isolate::Current()->null_safety_not_set());
+  ASSERT(!IsolateGroup::Current()->null_safety_not_set());
 
   invokestub entrypoint =
       reinterpret_cast<invokestub>(StubCode::InvokeDartCode().EntryPoint());
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index ef08d8c..a98dae0 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -748,8 +748,7 @@
 }
 
 Trie<const char>* Dwarf::CreateReverseObfuscationTrie(Zone* zone) {
-  auto const I = Thread::Current()->isolate();
-  auto const map_array = I->obfuscation_map();
+  auto const map_array = IsolateGroup::Current()->obfuscation_map();
   if (map_array == nullptr) return nullptr;
 
   Trie<const char>* trie = nullptr;
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 26229f8..8973f04 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -329,7 +329,8 @@
 
 IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
                            void* embedder_data,
-                           ObjectStore* object_store)
+                           ObjectStore* object_store,
+                           Dart_IsolateFlags api_flags)
     : embedder_data_(embedder_data),
       thread_pool_(),
       isolates_lock_(new SafepointRwLock()),
@@ -374,6 +375,7 @@
       program_lock_(new SafepointRwLock()),
       active_mutators_monitor_(new Monitor()),
       max_active_mutators_(Scavenger::MaxMutatorThreadCount()) {
+  FlagsCopyFrom(api_flags);
   const bool is_vm_isolate = Dart::VmIsolateNameEquals(source_->name);
   if (!is_vm_isolate) {
     thread_pool_.reset(
@@ -388,8 +390,9 @@
 }
 
 IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
-                           void* embedder_data)
-    : IsolateGroup(source, embedder_data, new ObjectStore()) {
+                           void* embedder_data,
+                           Dart_IsolateFlags api_flags)
+    : IsolateGroup(source, embedder_data, new ObjectStore(), api_flags) {
   if (object_store() != nullptr) {
     object_store()->InitStubs();
   }
@@ -403,6 +406,13 @@
   // Ensure we destroy the heap before the other members.
   heap_ = nullptr;
   ASSERT(marking_stack_ == nullptr);
+
+  if (obfuscation_map_ != nullptr) {
+    for (intptr_t i = 0; obfuscation_map_[i] != nullptr; i++) {
+      delete[] obfuscation_map_[i];
+    }
+    delete[] obfuscation_map_;
+  }
 }
 
 void IsolateGroup::RegisterIsolate(Isolate* isolate) {
@@ -1522,28 +1532,72 @@
   return kOK;
 }
 
+void IsolateGroup::FlagsInitialize(Dart_IsolateFlags* api_flags) {
+  api_flags->version = DART_FLAGS_CURRENT_VERSION;
+#define INIT_FROM_FLAG(when, name, bitname, isolate_flag, flag)                \
+  api_flags->isolate_flag = flag;
+  BOOL_ISOLATE_GROUP_FLAG_LIST(INIT_FROM_FLAG)
+#undef INIT_FROM_FLAG
+  api_flags->copy_parent_code = false;
+}
+
+void IsolateGroup::FlagsCopyTo(Dart_IsolateFlags* api_flags) {
+  api_flags->version = DART_FLAGS_CURRENT_VERSION;
+#define INIT_FROM_FIELD(when, name, bitname, isolate_flag, flag)               \
+  api_flags->isolate_flag = name();
+  BOOL_ISOLATE_GROUP_FLAG_LIST(INIT_FROM_FIELD)
+#undef INIT_FROM_FIELD
+  api_flags->copy_parent_code = false;
+}
+
+void IsolateGroup::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) {
+#if defined(DART_PRECOMPILER)
+#define FLAG_FOR_PRECOMPILER(action) action
+#else
+#define FLAG_FOR_PRECOMPILER(action)
+#endif
+
+#if !defined(PRODUCT)
+#define FLAG_FOR_NONPRODUCT(action) action
+#else
+#define FLAG_FOR_NONPRODUCT(action)
+#endif
+
+#define FLAG_FOR_PRODUCT(action) action
+
+#define SET_FROM_FLAG(when, name, bitname, isolate_flag, flag)                 \
+  FLAG_FOR_##when(isolate_group_flags_ = bitname##Bit::update(                 \
+                      api_flags.isolate_flag, isolate_group_flags_));
+
+  BOOL_ISOLATE_GROUP_FLAG_LIST(SET_FROM_FLAG)
+  // Needs to be called manually, otherwise we don't set the null_safety_set
+  // bit.
+  set_null_safety(api_flags.null_safety);
+#undef FLAG_FOR_NONPRODUCT
+#undef FLAG_FOR_PRECOMPILER
+#undef FLAG_FOR_PRODUCT
+#undef SET_FROM_FLAG
+}
+
 void Isolate::FlagsInitialize(Dart_IsolateFlags* api_flags) {
-  const bool false_by_default = false;
-  const bool true_by_default = true;
-  USE(true_by_default);
-  USE(false_by_default);
+  IsolateGroup::FlagsInitialize(api_flags);
 
   api_flags->version = DART_FLAGS_CURRENT_VERSION;
 #define INIT_FROM_FLAG(when, name, bitname, isolate_flag, flag)                \
   api_flags->isolate_flag = flag;
   BOOL_ISOLATE_FLAG_LIST(INIT_FROM_FLAG)
 #undef INIT_FROM_FLAG
-  api_flags->entry_points = NULL;
   api_flags->copy_parent_code = false;
 }
 
 void Isolate::FlagsCopyTo(Dart_IsolateFlags* api_flags) const {
+  group()->FlagsCopyTo(api_flags);
+
   api_flags->version = DART_FLAGS_CURRENT_VERSION;
 #define INIT_FROM_FIELD(when, name, bitname, isolate_flag, flag)               \
   api_flags->isolate_flag = name();
   BOOL_ISOLATE_FLAG_LIST(INIT_FROM_FIELD)
 #undef INIT_FROM_FIELD
-  api_flags->entry_points = NULL;
   api_flags->copy_parent_code = false;
 }
 
@@ -1569,34 +1623,10 @@
 
   BOOL_ISOLATE_FLAG_LIST(SET_FROM_FLAG)
   isolate_flags_ = CopyParentCodeBit::update(copy_parent_code_, isolate_flags_);
-  // Needs to be called manually, otherwise we don't set the null_safety_set
-  // bit.
-  set_null_safety(api_flags.null_safety);
 #undef FLAG_FOR_NONPRODUCT
 #undef FLAG_FOR_PRECOMPILER
 #undef FLAG_FOR_PRODUCT
 #undef SET_FROM_FLAG
-
-  // Copy entry points list.
-  ASSERT(embedder_entry_points_ == NULL);
-  if (api_flags.entry_points != NULL) {
-    intptr_t count = 0;
-    while (api_flags.entry_points[count].function_name != NULL)
-      count++;
-    embedder_entry_points_ = new Dart_QualifiedFunctionName[count + 1];
-    for (intptr_t i = 0; i < count; i++) {
-      embedder_entry_points_[i].library_uri =
-          Utils::StrDup(api_flags.entry_points[i].library_uri);
-      embedder_entry_points_[i].class_name =
-          Utils::StrDup(api_flags.entry_points[i].class_name);
-      embedder_entry_points_[i].function_name =
-          Utils::StrDup(api_flags.entry_points[i].function_name);
-    }
-    memset(&embedder_entry_points_[count], 0,
-           sizeof(Dart_QualifiedFunctionName));
-  }
-
-  // Leave others at defaults.
 }
 
 #if defined(DEBUG)
@@ -1672,7 +1702,7 @@
   // move to the OSThread structure.
   set_user_tag(UserTags::kDefaultUserTag);
 
-  if (obfuscate()) {
+  if (group()->obfuscate()) {
     OS::PrintErr(
         "Warning: This VM has been configured to obfuscate symbol information "
         "which violates the Dart standard.\n"
@@ -1723,23 +1753,6 @@
   mutator_thread_->isolate_ = nullptr;
   delete mutator_thread_;
   mutator_thread_ = nullptr;
-
-  if (obfuscation_map_ != nullptr) {
-    for (intptr_t i = 0; obfuscation_map_[i] != nullptr; i++) {
-      delete[] obfuscation_map_[i];
-    }
-    delete[] obfuscation_map_;
-  }
-
-  if (embedder_entry_points_ != nullptr) {
-    for (intptr_t i = 0; embedder_entry_points_[i].function_name != nullptr;
-         i++) {
-      free(const_cast<char*>(embedder_entry_points_[i].library_uri));
-      free(const_cast<char*>(embedder_entry_points_[i].class_name));
-      free(const_cast<char*>(embedder_entry_points_[i].function_name));
-    }
-    delete[] embedder_entry_points_;
-  }
 }
 
 void Isolate::InitVM() {
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index a3b1b8a..c24cfd6 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -172,33 +172,35 @@
 
 // List of Isolate flags with corresponding members of Dart_IsolateFlags and
 // corresponding global command line flags.
-#define BOOL_ISOLATE_FLAG_LIST(V)                                              \
-  BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)                                     \
-  BOOL_ISOLATE_FLAG_LIST_CUSTOM_GETTER(V)
+#define BOOL_ISOLATE_FLAG_LIST(V) BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)
+
+#define BOOL_ISOLATE_GROUP_FLAG_LIST(V)                                        \
+  BOOL_ISOLATE_GROUP_FLAG_LIST_DEFAULT_GETTER(V)                               \
+  BOOL_ISOLATE_GROUP_FLAG_LIST_CUSTOM_GETTER(V)
 
 // List of Isolate flags with default getters.
 //
 //     V(when, name, bit-name, Dart_IsolateFlags-name, command-line-flag-name)
 //
+#define BOOL_ISOLATE_GROUP_FLAG_LIST_DEFAULT_GETTER(V)                         \
+  V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false)
+
 #define BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)                               \
   V(NONPRODUCT, asserts, EnableAsserts, enable_asserts, FLAG_enable_asserts)   \
   V(NONPRODUCT, use_field_guards, UseFieldGuards, use_field_guards,            \
     FLAG_use_field_guards)                                                     \
   V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr)                        \
-  V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false_by_default)            \
   V(PRODUCT, should_load_vmservice_library, ShouldLoadVmService,               \
-    load_vmservice_library, false_by_default)                                  \
-  V(PRODUCT, copy_parent_code, CopyParentCode, copy_parent_code,               \
-    false_by_default)                                                          \
-  V(PRODUCT, is_system_isolate, IsSystemIsolate, is_system_isolate,            \
-    false_by_default)
+    load_vmservice_library, false)                                             \
+  V(PRODUCT, copy_parent_code, CopyParentCode, copy_parent_code, false)        \
+  V(PRODUCT, is_system_isolate, IsSystemIsolate, is_system_isolate, false)
 
 // List of Isolate flags with custom getters named #name().
 //
 //     V(when, name, bit-name, Dart_IsolateFlags-name, default_value)
 //
-#define BOOL_ISOLATE_FLAG_LIST_CUSTOM_GETTER(V)                                \
-  V(PRODUCT, null_safety, NullSafety, null_safety, false_by_default)
+#define BOOL_ISOLATE_GROUP_FLAG_LIST_CUSTOM_GETTER(V)                          \
+  V(PRODUCT, null_safety, NullSafety, null_safety, false)
 
 // Represents the information used for spawning the first isolate within an
 // isolate group.
@@ -332,8 +334,11 @@
  public:
   IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
                void* embedder_data,
-               ObjectStore* object_store);
-  IsolateGroup(std::shared_ptr<IsolateGroupSource> source, void* embedder_data);
+               ObjectStore* object_store,
+               Dart_IsolateFlags api_flags);
+  IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
+               void* embedder_data,
+               Dart_IsolateFlags api_flags);
   ~IsolateGroup();
 
   IsolateGroupSource* source() const { return source_.get(); }
@@ -413,8 +418,60 @@
     return shared_class_table_.get();
   }
 
+  void set_obfuscation_map(const char** map) { obfuscation_map_ = map; }
+  const char** obfuscation_map() const { return obfuscation_map_; }
+
   bool is_system_isolate_group() const { return is_system_isolate_group_; }
 
+  // IsolateGroup-specific flag handling.
+  static void FlagsInitialize(Dart_IsolateFlags* api_flags);
+  void FlagsCopyTo(Dart_IsolateFlags* api_flags);
+  void FlagsCopyFrom(const Dart_IsolateFlags& api_flags);
+
+#if defined(DART_PRECOMPILER)
+#define FLAG_FOR_PRECOMPILER(from_field, from_flag) (from_field)
+#else
+#define FLAG_FOR_PRECOMPILER(from_field, from_flag) (from_flag)
+#endif
+
+#if !defined(PRODUCT)
+#define FLAG_FOR_NONPRODUCT(from_field, from_flag) (from_field)
+#else
+#define FLAG_FOR_NONPRODUCT(from_field, from_flag) (from_flag)
+#endif
+
+#define FLAG_FOR_PRODUCT(from_field, from_flag) (from_field)
+
+#define DECLARE_GETTER(when, name, bitname, isolate_flag_name, flag_name)      \
+  bool name() const {                                                          \
+    return FLAG_FOR_##when(bitname##Bit::decode(isolate_group_flags_),         \
+                           flag_name);                                         \
+  }
+  BOOL_ISOLATE_GROUP_FLAG_LIST_DEFAULT_GETTER(DECLARE_GETTER)
+#undef FLAG_FOR_NONPRODUCT
+#undef FLAG_FOR_PRECOMPILER
+#undef FLAG_FOR_PRODUCT
+#undef DECLARE_GETTER
+
+  bool null_safety_not_set() const {
+    return !NullSafetySetBit::decode(isolate_group_flags_);
+  }
+
+  bool null_safety() const {
+    ASSERT(!null_safety_not_set());
+    return NullSafetyBit::decode(isolate_group_flags_);
+  }
+
+  void set_null_safety(bool null_safety) {
+    isolate_group_flags_ = NullSafetySetBit::update(true, isolate_group_flags_);
+    isolate_group_flags_ =
+        NullSafetyBit::update(null_safety, isolate_group_flags_);
+  }
+
+  bool use_strict_null_safety_checks() const {
+    return null_safety() || FLAG_strict_null_safety_checks;
+  }
+
   StoreBuffer* store_buffer() const { return store_buffer_.get(); }
   ClassTable* class_table() const { return class_table_.get(); }
   ObjectStore* object_store() const { return object_store_.get(); }
@@ -650,9 +707,13 @@
   // For `object_store_shared_ptr()`, `class_table_shared_ptr()`
   friend class Isolate;
 
-#define ISOLATE_GROUP_FLAG_BITS(V) V(CompactionInProgress)
+#define ISOLATE_GROUP_FLAG_BITS(V)                                             \
+  V(CompactionInProgress)                                                      \
+  V(NullSafety)                                                                \
+  V(NullSafetySet)                                                             \
+  V(Obfuscate)
 
-  // Isolate specific flags.
+  // Isolate group specific flags.
   enum FlagBits {
 #define DECLARE_BIT(Name) k##Name##Bit,
     ISOLATE_GROUP_FLAG_BITS(DECLARE_BIT)
@@ -673,6 +734,8 @@
     return object_store_;
   }
 
+  const char** obfuscation_map_ = nullptr;
+
   bool is_vm_isolate_heap_ = false;
   void* embedder_data_ = nullptr;
 
@@ -1283,13 +1346,6 @@
     isolate_flags_ = ShouldLoadVmServiceBit::update(value, isolate_flags_);
   }
 
-  Dart_QualifiedFunctionName* embedder_entry_points() const {
-    return embedder_entry_points_;
-  }
-
-  void set_obfuscation_map(const char** map) { obfuscation_map_ = map; }
-  const char** obfuscation_map() const { return obfuscation_map_; }
-
   const DispatchTable* dispatch_table() const {
     return group()->dispatch_table();
   }
@@ -1315,8 +1371,6 @@
 
 #define DECLARE_GETTER(when, name, bitname, isolate_flag_name, flag_name)      \
   bool name() const {                                                          \
-    const bool false_by_default = false;                                       \
-    USE(false_by_default);                                                     \
     return FLAG_FOR_##when(bitname##Bit::decode(isolate_flags_), flag_name);   \
   }
   BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(DECLARE_GETTER)
@@ -1333,24 +1387,6 @@
   }
 #endif  // defined(PRODUCT)
 
-  bool null_safety_not_set() const {
-    return !NullSafetySetBit::decode(isolate_flags_);
-  }
-
-  bool null_safety() const {
-    ASSERT(!null_safety_not_set());
-    return NullSafetyBit::decode(isolate_flags_);
-  }
-
-  void set_null_safety(bool null_safety) {
-    isolate_flags_ = NullSafetySetBit::update(true, isolate_flags_);
-    isolate_flags_ = NullSafetyBit::update(null_safety, isolate_flags_);
-  }
-
-  bool use_strict_null_safety_checks() const {
-    return null_safety() || FLAG_strict_null_safety_checks;
-  }
-
   bool has_attempted_stepping() const {
     return HasAttemptedSteppingBit::decode(isolate_flags_);
   }
@@ -1525,11 +1561,8 @@
   V(EnableAsserts)                                                             \
   V(UseFieldGuards)                                                            \
   V(UseOsr)                                                                    \
-  V(Obfuscate)                                                                 \
   V(CopyParentCode)                                                            \
   V(ShouldLoadVmService)                                                       \
-  V(NullSafety)                                                                \
-  V(NullSafetySet)                                                             \
   V(IsSystemIsolate)
 
   // Isolate specific flags.
@@ -1632,9 +1665,6 @@
   HandlerInfoCache handler_info_cache_;
   CatchEntryMovesCache catch_entry_moves_cache_;
 
-  Dart_QualifiedFunctionName* embedder_entry_points_ = nullptr;
-  const char** obfuscation_map_ = nullptr;
-
   DispatchTable* dispatch_table_ = nullptr;
 
   // Used during message sending of messages between isolates.
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index 542d9ae..d0898d3 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -2161,8 +2161,7 @@
 
   void CheckStatics(const GrowableArray<const Field*>& fields) {
     Thread* thread = Thread::Current();
-    Isolate* isolate = thread->isolate();
-    bool null_safety = isolate->null_safety();
+    const bool null_safety = thread->isolate_group()->null_safety();
     HANDLESCOPE(thread);
     instantiator_type_arguments_ = TypeArguments::null();
     for (intptr_t i = 0; i < fields.length(); i++) {
@@ -2182,8 +2181,7 @@
 
   void CheckInstances(const GrowableArray<const Instance*>& instances) {
     Thread* thread = Thread::Current();
-    Isolate* isolate = thread->isolate();
-    bool null_safety = isolate->null_safety();
+    const bool null_safety = thread->isolate_group()->null_safety();
     HANDLESCOPE(thread);
     for (intptr_t i = 0; i < instances.length(); i++) {
       CheckInstance(null_safety, *instances[i]);
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index 5190afd..d3f300e 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -760,8 +760,10 @@
     // TODO(aam): Assert that isolate exists once we move CompileAndReadScript
     // compilation logic out of CreateIsolateAndSetupHelper and into
     // IsolateSetupHelper in main.cc.
-    Isolate* isolate =
-        Thread::Current() != NULL ? Thread::Current()->isolate() : NULL;
+    auto thread = Thread::Current();
+    auto isolate = thread != nullptr ? thread->isolate() : nullptr;
+    auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr;
+
     if (incremental_compile) {
       ASSERT(isolate != NULL);
     }
@@ -787,9 +789,10 @@
     Dart_CObject null_safety;
     null_safety.type = Dart_CObject_kInt32;
     null_safety.value.as_int32 =
-        (isolate != NULL) ? (isolate->null_safety() ? kNullSafetyOptionStrong
-                                                    : kNullSafetyOptionWeak)
-                          : FLAG_sound_null_safety;
+        (isolate_group != nullptr)
+            ? (isolate_group->null_safety() ? kNullSafetyOptionStrong
+                                            : kNullSafetyOptionWeak)
+            : FLAG_sound_null_safety;
 
     intptr_t num_experimental_flags = experimental_flags->length();
     Dart_CObject** experimental_flags_array =
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index f58c987..4b60444 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -29,7 +29,7 @@
 
 #define Z (zone_)
 #define I (isolate_)
-#define IG (isolate_->group())
+#define IG (thread_->isolate_group())
 #define T (type_translator_)
 #define H (translation_helper_)
 
@@ -1025,14 +1025,14 @@
         "null safety and not sound null safety.",
         String::Handle(library.url()).ToCString());
   }
-  if (!I->null_safety() && mode == NNBDCompiledMode::kStrong) {
+  if (!IG->null_safety() && mode == NNBDCompiledMode::kStrong) {
     H.ReportError(
         "Library '%s' was compiled with sound null safety (in strong mode) and "
         "it "
         "requires --sound-null-safety option at runtime",
         String::Handle(library.url()).ToCString());
   }
-  if (I->null_safety() && (mode == NNBDCompiledMode::kWeak)) {
+  if (IG->null_safety() && (mode == NNBDCompiledMode::kWeak)) {
     H.ReportError(
         "Library '%s' was compiled without sound null safety (in weak mode) "
         "and it "
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ec3d078..58f2685 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5065,11 +5065,11 @@
   }
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   // Nullability of left and right hand sides is verified in strong mode only.
-  const bool verified_nullability = !isolate->use_strict_null_safety_checks() ||
-                                    nullability != Nullability::kNullable ||
-                                    !other.IsNonNullable();
+  const bool verified_nullability =
+      !isolate_group->use_strict_null_safety_checks() ||
+      nullability != Nullability::kNullable || !other.IsNonNullable();
 
   // Right Object.
   if (other_cid == kObjectCid) {
@@ -7812,7 +7812,7 @@
   }
   // Verify that all argument names are valid parameter names.
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Zone* zone = thread->zone();
   String& argument_name = String::Handle(zone);
   String& parameter_name = String::Handle(zone);
@@ -7842,7 +7842,7 @@
       return false;
     }
   }
-  if (isolate->use_strict_null_safety_checks()) {
+  if (isolate_group->use_strict_null_safety_checks()) {
     // Verify that all required named parameters are filled.
     for (intptr_t j = num_parameters - NumOptionalNamedParameters();
          j < num_parameters; j++) {
@@ -8443,7 +8443,7 @@
   }
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   // Check the result type.
   const AbstractType& other_res_type =
       AbstractType::Handle(zone, other.result_type());
@@ -8491,7 +8491,7 @@
       return false;
     }
   }
-  if (isolate->use_strict_null_safety_checks()) {
+  if (isolate_group->use_strict_null_safety_checks()) {
     // Check that for each required named parameter in this function, there's a
     // corresponding required named parameter in the other function.
     String& param_name = other_param_name;
@@ -8988,7 +8988,7 @@
                               BaseTextBuffer* printer) const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   String& name = String::Handle(zone);
   const TypeArguments& type_params =
       TypeArguments::Handle(zone, type_parameters());
@@ -9006,7 +9006,7 @@
       // Do not print default bound or non-nullable Object bound in weak mode.
       if (!bound.IsNull() &&
           (!bound.IsObjectType() ||
-           (isolate->null_safety() && bound.IsNonNullable()))) {
+           (isolate_group->null_safety() && bound.IsNonNullable()))) {
         printer->AddString(" extends ");
         bound.PrintName(name_visibility, printer);
       }
@@ -9550,7 +9550,7 @@
   }
 #endif
 
-  if (Isolate::Current()->obfuscate() || FLAG_precompiled_mode ||
+  if (IsolateGroup::Current()->obfuscate() || FLAG_precompiled_mode ||
       (Dart::vm_snapshot_kind() != Snapshot::kNone)) {
     return true;  // The kernel structure has been altered, skip checking.
   }
@@ -18417,7 +18417,7 @@
   // In weak mode type casts, whether in legacy or opted-in libraries, the null
   // instance is detected and handled in inlined code and therefore cannot be
   // encountered here as a Dart null receiver.
-  ASSERT(Isolate::Current()->use_strict_null_safety_checks() || !IsNull());
+  ASSERT(IsolateGroup::Current()->use_strict_null_safety_checks() || !IsNull());
   // In strong mode, compute NNBD_SUBTYPE(runtimeType, other).
   // In weak mode, compute LEGACY_SUBTYPE(runtimeType, other).
   return RuntimeTypeIsSubtypeOf(other, other_instantiator_type_arguments,
@@ -18464,11 +18464,10 @@
 // stub_code_compiler.cc if any changes are made.
 bool Instance::NullIsAssignableTo(const AbstractType& other) {
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
-  Zone* zone = thread->zone();
+  auto isolate_group = thread->isolate_group();
 
   // In weak mode, Null is a bottom type (according to LEGACY_SUBTYPE).
-  if (!isolate->use_strict_null_safety_checks()) {
+  if (!isolate_group->use_strict_null_safety_checks()) {
     return true;
   }
   // "Left Null" rule: null is assignable when destination type is either
@@ -18479,7 +18478,7 @@
   }
   if (other.IsFutureOrType()) {
     return NullIsAssignableTo(
-        AbstractType::Handle(zone, other.UnwrapFutureOr()));
+        AbstractType::Handle(thread->zone(), other.UnwrapFutureOr()));
   }
   return false;
 }
@@ -18496,12 +18495,12 @@
     return true;
   }
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
-  Zone* zone = thread->zone();
+  auto isolate_group = thread->isolate_group();
   // In weak testing mode, Null type is a subtype of any type.
-  if (IsNull() && !isolate->use_strict_null_safety_checks()) {
+  if (IsNull() && !isolate_group->use_strict_null_safety_checks()) {
     return true;
   }
+  Zone* zone = thread->zone();
   const Class& cls = Class::Handle(zone, clazz());
   if (cls.IsClosureClass()) {
     if (other.IsDartFunctionType() || other.IsDartClosureType() ||
@@ -18564,7 +18563,7 @@
     return false;
   }
   if (IsNull()) {
-    ASSERT(isolate->use_strict_null_safety_checks());
+    ASSERT(isolate_group->use_strict_null_safety_checks());
     if (instantiated_other.IsNullType()) {
       return true;
     }
@@ -18833,7 +18832,7 @@
   Zone* zone = thread->zone();
 
   // In weak mode null can be assigned to any type.
-  if (!thread->isolate()->null_safety()) {
+  if (!thread->isolate_group()->null_safety()) {
     return false;
   }
 
@@ -19291,7 +19290,7 @@
     // NNBD weak mode uses LEGACY_SUBTYPE for assignability / 'as' tests,
     // and non-nullable Object is a top type according to LEGACY_SUBTYPE.
     return !IsNonNullable() ||
-           !Isolate::Current()->use_strict_null_safety_checks();
+           !IsolateGroup::Current()->use_strict_null_safety_checks();
   }
   if (cid == kFutureOrCid) {
     // FutureOr<T> where T is a top type behaves as a top type.
@@ -19403,7 +19402,7 @@
     return Instance::NullIsAssignableTo(other);
   }
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Zone* zone = thread->zone();
   // Type parameters cannot be handled by Class::IsSubtypeOf().
   // When comparing two uninstantiated function types, one returning type
@@ -19450,7 +19449,7 @@
   const bool other_is_dart_function_type = other.IsDartFunctionType();
   if (other_is_dart_function_type || other.IsFunctionType()) {
     if (IsFunctionType()) {
-      if (isolate->use_strict_null_safety_checks() && IsNullable() &&
+      if (isolate_group->use_strict_null_safety_checks() && IsNullable() &&
           other.IsNonNullable()) {
         return false;
       }
@@ -19861,10 +19860,10 @@
   Nullability this_type_nullability = nullability();
   Nullability other_type_nullability = other_type.nullability();
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Zone* zone = thread->zone();
   if (kind == TypeEquality::kInSubtypeTest) {
-    if (isolate->use_strict_null_safety_checks() &&
+    if (isolate_group->use_strict_null_safety_checks() &&
         this_type_nullability == Nullability::kNullable &&
         other_type_nullability == Nullability::kNonNullable) {
       return false;
@@ -20632,7 +20631,7 @@
   Nullability this_type_param_nullability = nullability();
   Nullability other_type_param_nullability = other_type_param.nullability();
   if (kind == TypeEquality::kInSubtypeTest) {
-    if (Isolate::Current()->use_strict_null_safety_checks() &&
+    if (IsolateGroup::Current()->use_strict_null_safety_checks() &&
         (this_type_param_nullability == Nullability::kNullable) &&
         (other_type_param_nullability == Nullability::kNonNullable)) {
       return false;
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 23f05e8..bcf1991 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -780,7 +780,8 @@
 
   // These are guaranteed on the calling side.
   ASSERT(!dst_type.IsDynamicType());
-  ASSERT(!src_instance.IsNull() || isolate->use_strict_null_safety_checks());
+  ASSERT(!src_instance.IsNull() ||
+         isolate->group()->use_strict_null_safety_checks());
 
   const bool is_instance_of = src_instance.IsAssignableTo(
       dst_type, instantiator_type_arguments, function_type_arguments);
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index f793f2e..e5b845a 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -2065,7 +2065,7 @@
     type ^= Api::UnwrapHandle(cls);  // Dart_GetClass actually returns a Type.
     const Class& clazz = Class::Handle(type.type_class());
     const bool required = clazz.RequireLegacyErasureOfConstants(zone.GetZone());
-    EXPECT(required == isolate->null_safety());
+    EXPECT(required == isolate->group()->null_safety());
 
     // Verify that snapshot writing succeeds if erasure is not required.
     if (!required) {
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 5f3d862..7964785 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -92,10 +92,10 @@
 CodePtr TypeTestingStubGenerator::DefaultCodeForType(
     const AbstractType& type,
     bool lazy_specialize /* = true */) {
-  auto isolate = Isolate::Current();
+  auto isolate_group = IsolateGroup::Current();
 
   if (type.IsTypeRef()) {
-    return isolate->use_strict_null_safety_checks()
+    return isolate_group->use_strict_null_safety_checks()
                ? StubCode::DefaultTypeTest().raw()
                : StubCode::DefaultNullableTypeTest().raw();
   }
@@ -283,7 +283,7 @@
 
   } else if (type.IsObjectType()) {
     ASSERT(type.IsNonNullable() &&
-           Isolate::Current()->use_strict_null_safety_checks());
+           IsolateGroup::Current()->use_strict_null_safety_checks());
     compiler::Label continue_checking;
     __ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
     __ BranchIf(EQUAL, &continue_checking);
@@ -495,7 +495,7 @@
     // Weak NNBD mode uses LEGACY_SUBTYPE which ignores nullability.
     // We don't need to check nullability of LHS for nullable and legacy RHS
     // ("Right Legacy", "Right Nullable" rules).
-    if (Isolate::Current()->use_strict_null_safety_checks() &&
+    if (IsolateGroup::Current()->use_strict_null_safety_checks() &&
         !type_arg.IsNullable() && !type_arg.IsLegacy()) {
       // Nullable type is not a subtype of non-nullable type.
       // TODO(dartbug.com/40736): Allocate a register for instance type argument
diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc
index 17bd0c4..6f7014d 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -578,10 +578,10 @@
              ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
 
   // obj as Object (with null safety)
-  Isolate* isolate = Isolate::Current();
-  if (isolate->null_safety()) {
+  auto isolate_group = IsolateGroup::Current();
+  if (isolate_group->null_safety()) {
     auto& type_non_nullable_object =
-        Type::Handle(isolate->object_store()->non_nullable_object_type());
+        Type::Handle(isolate_group->object_store()->non_nullable_object_type());
     RunTTSTest(obj_a, type_non_nullable_object, tav_null, tav_null,
                ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
     RunTTSTest(Object::null_object(), type_non_nullable_object, tav_null,
diff --git a/tools/VERSION b/tools/VERSION
index 1c971c8..b988d50 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 195
+PRERELEASE 196
 PRERELEASE_PATCH 0
\ No newline at end of file